diff --git a/.claude/package.json b/.claude/package.json new file mode 100644 index 000000000..729ac4d93 --- /dev/null +++ b/.claude/package.json @@ -0,0 +1 @@ +{"type":"commonjs"} diff --git a/.claude/skills/airtable-build-collection/SKILL.md b/.claude/skills/airtable-build-collection/SKILL.md new file mode 100644 index 000000000..260d1c9da --- /dev/null +++ b/.claude/skills/airtable-build-collection/SKILL.md @@ -0,0 +1,178 @@ +--- +name: airtable-build-collection +description: Query Airtable MCP to fetch experiment metadata and generate a DynaCLR collection YAML for training +--- + +# Build Collection from Airtable + +Build a collection YAML for DynaCLR training by querying the Computational Imaging Database on Airtable. + +## Airtable Configuration + +- **Base ID**: `app8vqaoWyOwa0sB5` (Computational Imaging Database) +- **Table ID**: `tblaFzrDMlVZHPZIj` (Datasets) + +Key fields in the Datasets table: + +| Field | Description | +|---|---| +| `dataset` | Experiment name (e.g. `2025_07_24_A549_SEC61_TOMM20_G3BP1_ZIKV`) | +| `well_id` | Well path (e.g. `B/2`) | +| `fov` | FOV identifier | +| `cell_state` | Condition label (e.g. `infected`, `uninfected`) | +| `marker` | Protein marker (e.g. `SEC61B`, `TOMM20`, `pAL10`) | +| `organelle` | Target organelle | +| `perturbation` | Perturbation applied | +| `hours_post_perturbation` | HPI at imaging start | +| `moi` | Multiplicity of infection | +| `time_interval_min` | Minutes between frames | +| `data_path` | Path to HCS OME-Zarr store (FOV-level — extract zarr root by trimming well/fov) | +| `tracks_path` | Path to tracking zarr (may be absent) | +| `channel_0_name` .. `channel_N_name` | Zarr channel names | +| `channel_0_marker` .. `channel_N_marker` | Protein marker for each channel | +| `t_shape`, `c_shape`, `z_shape`, `y_shape`, `x_shape` | Array dimensions | +| `pixel_size_xy_um`, `pixel_size_z_um` | Physical pixel sizes | + +## Usage + +The user will describe what they want in natural language, e.g.: + +- "fetch the dataset from 2025_07_24 with all the organelles from that experiment" +- "build a collection with the SEC61 and TOMM20 experiments from July 2025" +- "make a collection for all ZIKV infection datasets" + +## Process + +### Step 1: Query Airtable + +Search for matching records using `mcp__airtable__list_records` with `filterByFormula`. + +Common filter patterns: +- By dataset name: `SEARCH("2025_07_24", {dataset})` +- By organelle: `{organelle} = "SEC61"` +- By perturbation: `{perturbation} = "ZIKV"` +- Combined: `AND(SEARCH("2025_07", {dataset}), {organelle} = "TOMM20")` + +Use `mcp__airtable__list_records` with `filterByFormula` for precise filtering. +Use `mcp__airtable__search_records` for fuzzy text matching. + +### Step 2: Group and Summarize + +Group records by `dataset`. **If a single dataset contains multiple markers/organelles** (different `marker` values across wells), split it into one experiment entry per marker. The experiment name gets a `_{MARKER}` suffix (e.g. `2025_07_24_A549_SEC61_TOMM20_G3BP1_ZIKV_TOMM20`). All split entries share the same `data_path` and `tracks_path` but have different `perturbation_wells`, `marker`, and `organelle`. + +This is handled automatically by `build_collection()` in `packages/viscy-data/src/viscy_data/collection.py` via the `_group_records()` helper. + +Present a summary table to the user showing: + +- Dataset names found (with split entries if multi-organelle) +- Number of FOVs per dataset +- Organelles and markers +- Channel names and markers +- Conditions (inferred from `perturbation` field — see note below) +- Wells per condition +- Whether `tracks_path` is available + +**Note on cell_state**: In Airtable, `cell_state` is typically "Live" for all records. Infer infection status from the `perturbation` field: wells with a perturbation value are "infected", wells without are "uninfected". + +Ask the user to confirm which datasets to include. + +### Step 3: Determine Channels + +Each experiment entry has a `channels` list where each entry maps a zarr channel name to a protein marker: + +```yaml +channels: + - name: "Phase3D" # zarr channel name + marker: "Phase3D" # protein marker / semantic label + - name: "raw GFP EX488 EM525-45" + marker: "SEC61B" +``` + +Rules for mapping: +1. `channel_X_name` from Airtable → `name` field (the zarr channel name) +2. `channel_X_marker` from Airtable → `marker` field (the protein marker) +3. Only include channels relevant to the experiment — typically Phase3D (labelfree) and the fluorescence channel(s) for the marker of interest + +Present the proposed channel mapping to the user for confirmation: +``` +Channels per experiment: + 2025_07_24_SEC61: + - Phase3D → Phase3D + - raw GFP EX488 EM525-45 → SEC61B + 2024_08_14_ZIKV: + - Phase3D → Phase3D + - MultiCam_GFP_BF → pAL10 +``` + +### Step 4: Determine tracks_path + +Check the `tracks_path` field in Airtable. If missing, ask the user. + +### Step 5: Naming Convention + +Collection filenames follow: `{cell_line}_{perturbation}_{organelle}.yml` + +- **Single organelle**: use the organelle name, e.g. `A549_ZIKV_SEC61.yml` +- **Multiple organelles**: use `multiorganelle`, e.g. `A549_ZIKV_multiorganelle.yml` +- **No version suffix** — versioning is handled by git history +- The `name` field inside the YAML should match the filename (without `.yml`) + +### Step 6: Generate Collection YAML + +Use the Collection schema from `packages/viscy-data/src/viscy_data/collection.py`. + +The current schema uses per-experiment `channels` (list of `{name, marker}` entries), NOT `source_channels`: + +```yaml +name: +description: "" + +provenance: + airtable_base_id: app8vqaoWyOwa0sB5 + airtable_query: "" + record_ids: [] + created_at: "" + created_by: "" + +experiments: + - name: + data_path: + tracks_path: + channels: + - name: + marker: + - name: + marker: + perturbation_wells: + uninfected: + - + : + - + interval_minutes: + start_hpi: + marker: + organelle: + moi: + pixel_size_xy_um: + pixel_size_z_um: +``` + +Key notes: +- `data_path` should be the zarr store root (up to `.zarr`), NOT the FOV-level path from Airtable +- `perturbation_wells` uses `uninfected` / `` keys inferred from the `perturbation` field +- `channels` lists only the channels needed for training (not all channels in the zarr) +- `marker` at the experiment level is the primary marker for this experiment entry + +### Step 7: Save and Validate + +1. Save to `applications/dynaclr/configs/collections/.yml` +2. Validate by loading with `viscy_data.collection.load_collection(path)` using a quick Python check +3. Show the user the final YAML and validation result + +## Important Notes + +- `interval_minutes` must be > 0 +- `perturbation_wells` must not be empty +- Zarr channel names in `channels[].name` must match actual zarr channel names +- For multi-marker datasets, split into separate experiment entries per marker +- Reference existing collections in `applications/dynaclr/configs/collections/` for format examples diff --git a/.claude/skills/airtable-register/SKILL.md b/.claude/skills/airtable-register/SKILL.md new file mode 100644 index 000000000..26be88b92 --- /dev/null +++ b/.claude/skills/airtable-register/SKILL.md @@ -0,0 +1,192 @@ +--- +name: airtable-register +description: Register zarr positions into the Computational Imaging Database on Airtable, write channels_metadata/experiment_metadata to zarr .zattrs, or bulk-update Airtable records via MCP. Use when the user asks to "register a dataset", "register zarr positions", "update airtable from zarr", "write metadata to zarr", "run register on", "sync airtable", "populate channel markers", "update airtable records", "backfill fields", or "fill in missing fields". Also use for Marker Registry questions. +version: 3.0.0 +author: ai-x-imaging +tags: [Airtable, OME-Zarr, Metadata, Registration, DynaCLR, VisCy] +--- + +# Airtable Registration & Update Skill + +Manages bidirectional metadata sync between OME-Zarr datasets and the Computational Imaging Database on Airtable, and supports bulk field updates via MCP. + +## Airtable Configuration + +- **Base ID**: `app8vqaoWyOwa0sB5` (Computational Imaging Database) +- **Datasets table ID**: `tblaFzrDMlVZHPZIj` +- **Collections table ID**: `tblu0Rbj9OnLl7vJf` +- **Models table ID**: `tblVZhRA48tDMWj8U` +- **Marker Registry table**: `tblmP8l2GmpCeERyD` +- **Script**: `applications/airtable/scripts/write_experiment_metadata.py` +- **Core logic**: `applications/airtable/src/airtable_utils/registration.py` +- **Schemas**: `applications/airtable/src/airtable_utils/schemas.py` +- **Database interface**: `applications/airtable/src/airtable_utils/database.py` +- **Channel parsing**: `packages/viscy-data/src/viscy_data/channel_utils.py` +- `MAX_CHANNELS = 8` (defined in `schemas.py`) +- `AIRTABLE_API_KEY` and `AIRTABLE_BASE_ID` must be set in environment + +## Operations + +### 1. Register (zarr -> Airtable) + +Reads zarr metadata and writes per-FOV records to Airtable. + +**Fields written by register:** +- `data_path` — full path to zarr position +- `channel_{i}_name` — zarr channel names (up to 8) +- `channel_{i}_marker` — protein marker, derived from Marker Registry +- `t_shape`, `c_shape`, `z_shape`, `y_shape`, `x_shape` — array dimensions +- `pixel_size_xy_um`, `pixel_size_z_um` — from zarr coordinate transforms + +**Marker derivation rules:** +- **labelfree** channels -> marker = channel name (e.g. `"Phase3D"`, `"BF"`, `"DIC"`) +- **virtual_stain** channels -> marker = channel name (e.g. `"nuclei_prediction"`) +- **fluorescence** channels -> substring-match aliases against channel name -> protein marker from registry (e.g. `"TOMM20"`, `"SEC61B"`) + +#### Commands + +```bash +# Dry run first (always recommended) +uv run --package airtable-utils \ + applications/airtable/scripts/write_experiment_metadata.py \ + register --dry-run /path/to/dataset.zarr/*/*/* + +# Register all positions +uv run --package airtable-utils \ + applications/airtable/scripts/write_experiment_metadata.py \ + register /path/to/dataset.zarr/*/*/* + +# Single position +uv run --package airtable-utils \ + applications/airtable/scripts/write_experiment_metadata.py \ + register /path/to/dataset.zarr/A/1/000000 + +# Override dataset name (when zarr stem doesn't match Airtable) +uv run --package airtable-utils \ + applications/airtable/scripts/write_experiment_metadata.py \ + register --dataset my_dataset /path/to/dataset.zarr/*/*/* +``` + +#### Parquet Readiness Report + +After registration, the CLI prints a **Parquet Readiness** report that flags any fields still needed before a flat parquet cell index can be built. Fields are split by source: + +- **zarr** fields (auto-filled by `register`): `data_path`, `channel_N_name`, `channel_N_marker`, `pixel_size_xy_um`, `pixel_size_z_um` +- **platemap** fields (biologist fills in Airtable): `tracks_path`, `perturbation`, `time_interval_min`, `hours_post_perturbation`, `cell_type` + +If any platemap fields are missing, the report shows what to fill in and how (Airtable UI or MCP bulk update). + +### 2. Write (Airtable -> zarr) + +Writes `channels_metadata` and `experiment_metadata` to zarr `.zattrs`. + +```bash +uv run --package airtable-utils \ + applications/airtable/scripts/write_experiment_metadata.py \ + write /path/to/dataset.zarr/*/*/* +``` + +#### channels_metadata schema + +```json +{ + "Phase3D": { + "channel_type": "labelfree", + "biological_annotation": {"marker": "Phase3D"} + }, + "raw GFP EX488 EM525-45": { + "channel_type": "fluorescence", + "biological_annotation": { + "marker": "TOMM20", + "marker_type": "protein_tag", + "fluorophore": null + } + } +} +``` + +#### experiment_metadata schema + +```json +{ + "perturbations": [ + {"name": "ZIKV", "type": "unknown", "hours_post": 48.0, "moi": 5.0} + ], + "time_sampling_minutes": 15.0 +} +``` + +### 3. Bulk Update (via Airtable MCP) + +For updating fields that don't come from zarr (e.g. `tracks_path`, `organelle`, manually-curated fields). + +**Process:** + +1. Fetch target records with `mcp__airtable__list_records` using `filterByFormula` +2. Compute new values (python/jq) +3. Batch update with `mcp__airtable__update_records` (max 10 per call, send all batches in parallel) +4. Verify with the same filter query (must return zero remaining records) + +**Pagination warning:** `mcp__airtable__list_records` returns ~100 records max per call. If count equals ~100, re-query with tighter filters. + +## Datasets Table Fields + +| Field | Description | Written by register? | +|---|---|---| +| `dataset` | Experiment name | on create | +| `well_id` | Well path (e.g. `B/2`) | on create | +| `fov` | FOV identifier | on create | +| `data_path` | Path to HCS OME-Zarr position | yes | +| `tracks_path` | Path to tracking zarr | no (manual/MCP) | +| `channel_0_name` .. `channel_7_name` | Zarr channel names | yes | +| `channel_0_marker` .. `channel_7_marker` | Protein marker per channel | yes | +| `t_shape` .. `x_shape` | Array dimensions | yes | +| `pixel_size_xy_um` | Physical XY pixel size (um) | yes | +| `pixel_size_z_um` | Physical Z pixel size (um) | yes | +| `marker` | Well-level primary marker | template copy | +| `organelle` | Target organelle | template copy | +| `perturbation` | Perturbation applied | template copy | +| `cell_type` | Cell type (e.g. `A549`) | template copy | +| `cell_state` | Condition label | template copy | +| `time_interval_min` | Minutes between frames | template copy | +| `hours_post_perturbation` | HPI at imaging start | template copy | +| `moi` | Multiplicity of infection | template copy | +| `fluorescence_modality` | Imaging modality | template copy | + +## Marker Registry + +Table `tblmP8l2GmpCeERyD` — maps constructs to protein markers. + +| Field | Type | Example | +|---|---|---| +| `marker-fluorophore` | text (primary) | `TOMM20-GFP` | +| `channel_name_aliases` | text | `GFP, FITC` | +| `marker` | text | `TOMM20` | + +Matching is substring-based: `"GFP" in "raw GFP EX488 EM525-45"` -> match. + +## Flat Parquet Alignment + +The `register` command writes all fields needed to build a flat parquet cell index: +- `channel_{i}_name` -> parquet `channel_name` +- `channel_{i}_marker` -> parquet `marker` +- `pixel_size_xy_um`, `pixel_size_z_um` -> parquet pixel size columns +- `data_path` -> parquet `store_path` + +## Dataset Directory Conventions + +For **organelle_dynamics** datasets: +``` +data_path: /hpc/projects/intracellular_dashboard/organelle_dynamics/{EXP}/2-assemble/{EXP}.zarr +tracks_path: /hpc/projects/intracellular_dashboard/organelle_dynamics/{EXP}/1-preprocess/label-free/3-track/{EXP}_cropped.zarr +``` + +Other families (organelle_box, viral-sensor) have non-standard structures — check filesystem. + +## Example Invocations + +- "register this dataset /path/to/dataset.zarr/*/*/*" +- "write metadata to zarr for dataset X" +- "update tracks_path for all organelle_dynamics datasets" +- "fill in pixel_size_xy_um for all records where it's missing" +- "set organelle = 'mitochondria' for all 2024_11_21 records" diff --git a/.claude/skills/prepare-dataset/SKILL.md b/.claude/skills/prepare-dataset/SKILL.md new file mode 100644 index 000000000..4c273da73 --- /dev/null +++ b/.claude/skills/prepare-dataset/SKILL.md @@ -0,0 +1,117 @@ +--- +name: prepare-dataset +description: Prepare datasets for training on VAST storage (NFS -> VAST rechunked zarr v3 pipeline). Use when the user asks to "prepare a dataset", "run prepare", "rechunk dataset", "copy dataset to VAST", "run QC and preprocess", or references the `prepare` CLI. +--- + +# Prepare Dataset for Training (NFS -> VAST) + +## Overview + +This skill runs the `prepare` CLI from `applications/airtable/` to create rechunked zarr v3 copies of NFS datasets on VAST storage, with QC (focus slice) and preprocessing (normalization stats). + +## Pipeline Steps + +1. **Airtable validation** — dataset must be registered +2. **Discover wells/channels** — reads NFS zarr via iohub; auto-detects raw channels (`Phase3D` + `raw *`) +3. **biahub concatenate** — rechunks to zarr v3 with sharding (submits own SLURM jobs via submitit) +4. **Copy tracking zarr** — rsync from NFS +5. **QC + preprocess** — SLURM job running focus slice QC (GPU) and normalization (CPU) in parallel + +## Key Files + +- **CLI**: `applications/airtable/src/airtable_utils/prepare_cli.py` +- **Core logic**: `applications/airtable/src/airtable_utils/prepare.py` +- **Default config**: `applications/airtable/configs/prepare_config.yml` + +## Commands + +```bash +# Check status of one or more datasets +uv run --package airtable-utils \ + prepare status [ ...] \ + -c applications/airtable/configs/prepare_config.yml + +# Dry run (generate configs + scripts, don't execute) +uv run --package airtable-utils \ + prepare run \ + -c applications/airtable/configs/prepare_config.yml --dry-run + +# Full run +uv run --package airtable-utils \ + prepare run \ + -c applications/airtable/configs/prepare_config.yml + +# Force overwrite existing VAST zarr +uv run --package airtable-utils \ + prepare run \ + -c applications/airtable/configs/prepare_config.yml --force +``` + +## Running Multiple Datasets + +Run `prepare run` sequentially for each dataset. The concatenation step blocks until biahub's internal SLURM jobs complete, then submits the QC+preprocess SLURM job. Example: + +```bash +for ds in 2025_01_28_A549_G3BP1_ZIKV_DENV 2025_04_15_A549_H2B_CAAX_ZIKV_DENV; do + uv run --package airtable-utils \ + prepare run "$ds" \ + -c applications/airtable/configs/prepare_config.yml +done +``` + +## Output Layout + +``` +/hpc/projects/organelle_phenotyping/datasets/{dataset_name}/ + {dataset_name}.zarr # zarr v3 rechunked (OME-Zarr 0.5) + tracking.zarr # copied from NFS + crop_concat.yml # generated biahub config + qc_config.yml # generated QC config + sbatch_overrides.sh # SLURM overrides for biahub (if configured) + 01_concatenate.sh # bash: biahub concatenate + tracking copy + 02_qc_preprocess.sh # SLURM: QC + preprocess +``` + +## Config Reference + +The config at `applications/airtable/configs/prepare_config.yml` has these key settings: + +| Section | Field | Default | Notes | +|---|---|---|---| +| `concatenate` | `channel_names` | `null` (auto-detect) | Set explicitly to override; auto picks `Phase3D` + `raw *` | +| `concatenate` | `chunks_czyx` | `[1, 16, 256, 256]` | ~4MB chunks for training | +| `concatenate` | `shards_ratio` | `[1, 1, 8, 8, 8]` | Sharding for zarr v3 | +| `concatenate` | `sbatch_overrides` | `{partition: preempted}` | Overrides biahub's internal SLURM via `-sb` | +| `qc` | `channel_names` | `[Phase3D]` | Channels for focus slice detection | +| `slurm.qc_preprocess` | `partition` | `gpu` | QC needs GPU for torch FFT | +| `slurm.qc_preprocess` | `cpus_per_task` | `16` | | +| `slurm.qc_preprocess` | `time` | `01:00:00` | | + +## Extracting Dataset Names from a Collection YAML + +To get unique dataset names from a collection: + +```bash +uv run python3 -c " +import yaml +from pathlib import Path +with open('path/to/collection.yml') as f: + col = yaml.safe_load(f) +datasets = sorted(set( + Path(e['data_path']).parts[ + Path(e['data_path']).parts.index('organelle_dynamics') + 1 + ] + for e in col['experiments'] + if 'organelle_dynamics' in e['data_path'] +)) +for d in datasets: + print(d) +" +``` + +## Troubleshooting + +- **"Dataset not found in Airtable"**: Register it first with the `airtable-register` skill +- **Channel validation fails**: Check `channel_names` in config; set to `null` for auto-detection +- **biahub concatenate fails**: Check conda env exists (`conda run -n biahub which biahub`) +- **QC/preprocess SLURM job pending**: Check `squeue -u $USER` and partition availability diff --git a/.codecov.yml b/.codecov.yml deleted file mode 100644 index 6694b2a54..000000000 --- a/.codecov.yml +++ /dev/null @@ -1,14 +0,0 @@ -coverage: - precision: 2 - round: down - range: "70...100" - - status: - project: yes - patch: no - changes: no - -comment: - layout: "header, reach, diff, flags, files, footer" - behavior: default - require_changes: no diff --git a/.envrc b/.envrc new file mode 100644 index 000000000..866a29547 --- /dev/null +++ b/.envrc @@ -0,0 +1,3 @@ +export CUDA_PATH=/hpc/apps/cuda/12.8.0_570.86.10 +export PATH=$CUDA_PATH/bin:$PATH +export LD_LIBRARY_PATH=$CUDA_PATH/lib64:${LD_LIBRARY_PATH:-} diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 01f60ccea..000000000 --- a/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -docs/figures/*.svg linguist-generated=true diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 000000000..4c8ee9fd6 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,55 @@ +name: Documentation + +on: + push: + branches: [main] + tags: ["v*"] + pull_request: + branches: [main] + +permissions: + contents: write # mike pushes the built site to the gh-pages branch + +jobs: + # On pull requests, only verify that the documentation builds cleanly. + build: + if: github.event_name == 'pull_request' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: astral-sh/setup-uv@v7 + with: + python-version: "3.13" + enable-cache: true + - run: uv sync --all-packages --group doc + - run: uv run python docs/_gen_versions.py + - run: uv run zensical build --clean + + # On pushes, deploy with mike: main -> dev, vX.Y.Z tag -> that version + stable. + deploy: + if: github.event_name == 'push' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + with: + fetch-depth: 0 # full history so mike can update the gh-pages branch + - uses: astral-sh/setup-uv@v7 + with: + python-version: "3.13" + enable-cache: true + - run: uv sync --all-packages --group doc + - run: uv run python docs/_gen_versions.py + - run: git config user.name "github-actions[bot]" + - run: git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Deploy dev docs + if: github.ref == 'refs/heads/main' + run: uv run mike deploy --push dev + + - name: Deploy release docs + if: startsWith(github.ref, 'refs/tags/v') + run: uv run mike deploy --push --update-aliases "${GITHUB_REF_NAME#v}" stable + + - name: Set default to stable + if: startsWith(github.ref, 'refs/tags/v') + run: uv run mike set-default --push stable diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 000000000..ab5bb219c --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,29 @@ +name: Lint + +on: + push: + branches: [main, modular-viscy-staging] + pull_request: + branches: [main, modular-viscy-staging] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Set up uv with Python 3.13 + uses: astral-sh/setup-uv@v7 + with: + python-version: "3.13" + enable-cache: true + + - name: Run pre-commit hooks + run: uvx prek run --all-files diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml deleted file mode 100644 index e7b772ea0..000000000 --- a/.github/workflows/pr.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Lint and Test - -on: pull_request - -jobs: - lint: - name: Lint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: astral-sh/ruff-action@v3 - with: - src: viscy - args: check --verbose - - run: ruff format --check viscy tests - - test: - name: Test - needs: [lint] - runs-on: ubuntu-latest - strategy: - matrix: - python-version: ["3.11", "3.12", "3.13"] - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - # Install cpu wheels only to speed up the build - pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu - pip install ".[dev]" - - name: Test with pytest - run: pytest -v diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..616044d9a --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,175 @@ +name: Test + +on: + push: + branches: [main, modular-viscy-staging] + pull_request: + branches: [main, modular-viscy-staging] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} + +jobs: + test: + name: Test (${{ matrix.package }}, Python ${{ matrix.python-version }}, ${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: true + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + python-version: ["3.12", "3.13"] + package: [viscy-transforms, viscy-models] + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Set up uv with Python ${{ matrix.python-version }} + uses: astral-sh/setup-uv@v7 + with: + python-version: ${{ matrix.python-version }} + enable-cache: true + cache-suffix: ${{ matrix.os }}-${{ matrix.python-version }} + + - name: Install dependencies + run: uv sync --frozen --all-extras --dev + working-directory: packages/${{ matrix.package }} + + - name: Run tests with coverage + run: uv run --frozen pytest --cov=src/ --cov-report=term-missing + working-directory: packages/${{ matrix.package }} + + test-data: + name: Test Data (Python ${{ matrix.python-version }}, ${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: true + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + python-version: ["3.12", "3.13"] + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Set up uv with Python ${{ matrix.python-version }} + uses: astral-sh/setup-uv@v7 + with: + python-version: ${{ matrix.python-version }} + enable-cache: true + cache-suffix: ${{ matrix.os }}-${{ matrix.python-version }} + + - name: Install dependencies + run: uv sync --frozen --all-extras --dev + working-directory: packages/viscy-data + + - name: Run tests with coverage + run: uv run --frozen pytest --cov=viscy_data --cov-report=term-missing + working-directory: packages/viscy-data + + test-data-extras: + name: Test Data Extras (Python 3.13, ubuntu-latest) + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Set up uv with Python 3.13 + uses: astral-sh/setup-uv@v7 + with: + python-version: "3.13" + enable-cache: true + cache-suffix: ubuntu-latest-3.13 + + - name: Install dependencies + run: uv sync --frozen --all-extras --dev + working-directory: packages/viscy-data + + - name: Run tests with coverage + run: uv run --frozen pytest --cov=viscy_data --cov-report=term-missing + working-directory: packages/viscy-data + + test-applications: + name: Test (${{ matrix.application }}, Python 3.13, ubuntu-latest) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + application: [dynaclr, cytoland, airtable, qc] + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Set up uv with Python 3.13 + uses: astral-sh/setup-uv@v7 + with: + python-version: "3.13" + enable-cache: true + cache-suffix: ubuntu-latest-3.13 + + - name: Install dependencies + run: uv sync --frozen --all-extras --dev + working-directory: applications/${{ matrix.application }} + + - name: Run tests + run: uv run --frozen pytest + working-directory: applications/${{ matrix.application }} + + test-dynacell-configs: + name: Test dynacell benchmark configs (Python 3.13, ubuntu-latest) + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Set up uv with Python 3.13 + uses: astral-sh/setup-uv@v7 + with: + python-version: "3.13" + enable-cache: true + cache-suffix: ubuntu-latest-3.13 + + - name: Install dynacell with eval deps + test group (CPU) + # The eval-pipeline tests import dynacell.evaluation.pipeline, a hard + # consumer of the eval stack (cubic, cellpose, …). cubic is CPU-capable + # (falls back to numpy/scikit-image without CUDA), so the eval extra + # installs and runs on a GPU-less runner. eval_gpu (cupy/cucim) is NOT + # installed — those are the only CUDA-only deps. + run: uv sync --frozen --extra eval --group test + working-directory: applications/dynacell + + - name: Run benchmark-schema + submit-tool + eval-runtime tests + # test_benchmark_config_composition + test_submit_benchmark_job cover + # config composition + the launcher. test_runtime + the two + # test_evaluation_pipeline_parallel* suites cover the runtime module, + # the FovResult pickle contract, and evaluate_predictions end-to-end + # (serial vs spawn-process) on a tiny iohub fixture + prebuilt mask + # cache (target_name=er + require_complete_cache=true short-circuit the + # segmenter + feature-extractor model loads — but pipeline import still + # needs the eval stack). test_evaluation_grouped drives the + # multi-condition driver against the same cache-only fixture. + run: | + uv run --frozen pytest \ + tests/test_benchmark_config_composition.py \ + tests/test_submit_benchmark_job.py \ + tests/test_runtime.py \ + tests/test_evaluation_pipeline_parallel.py \ + tests/test_evaluation_pipeline_parallel_cpu.py \ + tests/test_evaluation_grouped.py \ + -v + working-directory: applications/dynacell + + check: + name: All tests pass + if: always() + needs: [test, test-data, test-data-extras, test-applications, test-dynacell-configs] + runs-on: ubuntu-latest + steps: + - name: Verify all test jobs succeeded + uses: re-actors/alls-green@release/v1 + with: + jobs: ${{ toJSON(needs) }} diff --git a/.gitignore b/.gitignore index c390d5982..c3f8ab9c9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,27 @@ -.idea +# Secrets +.env + +# Zensical documentation build output +/site/ + +# IDE/Editor +.idea/ +.vscode/ .DS_Store + +# Filesystem artifacts +.nfs* + +# Python bytecode __pycache__/ +*.py[cod] +*$py.class .ipynb_checkpoints/ -.vscode - -# written by setuptools_scm -*/_version.py -# slurm output files -slurm-* +# Virtual environments +.venv/ +venv/ +ENV/ # Distribution / packaging .Python @@ -31,19 +44,35 @@ share/python-wheels/ *.egg MANIFEST +# Linter/formatter caches +.ruff_cache/ +.mypy_cache/ + # Unit test / coverage reports htmlcov/ .tox/ +.nox/ .coverage .coverage.* .cache coverage.xml *.cover +*.py,cover .hypothesis/ .pytest_cache/ -# SLURM +# SLURM output files +slurm-* slurm*.out -#lightning_logs directory +# Lightning logs lightning_logs/ + +# NOTE: uv.lock is NOT ignored - it should be tracked for reproducibility + +checkpoints/ + +plot_related/ + +# Local-only planning docs (not for upstream) +applications/dynaclr/docs/DAGs/evaluation_matrix.md diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 860d1f9eb..07e1df789 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,15 +7,15 @@ default_stages: minimum_pre_commit_version: 2.16.0 repos: - repo: https://github.com/tox-dev/pyproject-fmt - rev: v2.8.0 + rev: v2.11.1 hooks: - id: pyproject-fmt - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.2 + rev: v0.14.14 hooks: - id: ruff-check types_or: [python, pyi, jupyter] - args: [--fix, --exit-non-zero-on-fix, --unsafe-fixes] + args: [--fix, --exit-non-zero-on-fix] - id: ruff-format types_or: [python, pyi, jupyter] - repo: https://github.com/pre-commit/pre-commit-hooks @@ -28,3 +28,6 @@ repos: args: [--fix=lf] - id: trailing-whitespace - id: check-case-conflict + # Check that there are no merge conflicts (could be generated by template sync) + - id: check-merge-conflict + args: [--assume-in-merge] diff --git a/.readthedocs.yaml b/.readthedocs.yaml deleted file mode 100644 index 6ad1b2362..000000000 --- a/.readthedocs.yaml +++ /dev/null @@ -1,28 +0,0 @@ -# Read the Docs configuration file -# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details - -# Required -version: 2 - -# Set the OS, Python version, and other tools you might need -build: - os: ubuntu-24.04 - tools: - python: "3.12" - jobs: - post_build: - - python docs/scripts/fix_md_links.py - -# Optionally, but recommended, -# declare the Python requirements required to build your documentation -# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html -python: - install: - - method: pip - path: . - extra_requirements: - - docs - -# Build documentation in the "docs/" directory with Sphinx -sphinx: - configuration: docs/conf.py diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..6801f8eba --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,240 @@ +# VisCy — Claude Code Reference + +## Project + +VisCy is a **uv workspace monorepo** for virtual staining and computational microscopy. Sub-packages live under `packages/`. + +## Repo Layout + +``` +pyproject.toml # Root config (ruff, pytest, uv workspace) +packages/ + viscy-data/ # Data loading and Lightning DataModules + viscy-models/ # Neural network architectures + viscy-transforms/ # Image transforms +src/viscy/ # Umbrella package (re-exports) +applications/ # Self-contained research applications +``` + +### Packages vs Applications + +- **Shared code belongs in `packages/`**, not in applications. +- **Applications must not import from each other.** If two applications need the same logic, move it to an existing package or create a new one. +- Applications are consumers of packages — the dependency graph always flows `applications/ → packages/`, never sideways. + +--- + +## Development + +### Environment Setup + +Use `uv` package manager. Run commands with `uv run `. Edit `pyproject.toml` to modify dependencies and sync to update `uv.lock`. + +```sh +uv venv -p 3.13 +uv sync --all-packages --all-extras +``` + +If `uv` is not installed: +```sh +curl -LsSf https://astral.sh/uv/install.sh | sh +``` + +On HPC, symlink the uv cache out of your home directory first: +```sh +mkdir -p /hpc/mydata/firstname.lastname/.cache/uv && ln -s /hpc/mydata/firstname.lastname/.cache/uv ~/.cache/uv +``` + +For full setup instructions (installing uv, creating a venv, syncing dependencies), see [CONTRIBUTING.md](./CONTRIBUTING.md). + +### SLURM scripts for Lightning DDP jobs + +When hand-writing `.slurm` scripts that launch Lightning via `srun`, always use `--ntasks-per-node=N` (not `--ntasks=N`). Lightning's `SLURMEnvironment` validates `SLURM_NTASKS_PER_NODE` at trainer init and raises `RuntimeError: You set --ntasks=N in your SLURM bash script, but this variable is not supported. HINT: Use --ntasks-per-node=N instead.` — the job then dies seconds into the allocation. + +Invariant: `#SBATCH --ntasks-per-node=N` must equal `trainer.devices` in the YAML config and `#SBATCH --gpus=N` (single-node) or `#SBATCH --gpus-per-node=N` (multi-node). + +The dynacell launcher (`applications/dynacell/tools/submit_benchmark_job.py`) already emits `--ntasks-per-node` correctly; this note is for hand-written scripts (e.g., `applications/cytoland/examples/configs/*/run_*.slurm`). + +### Job monitoring and inspection + +**Process state ≠ training completeness.** Wandb's `state: finished` only means `wandb.finish()` was called — Lightning calls it on clean SIGTERM teardown via `SLURMEnvironment`, so a `scancel`'d run shows `finished` identically to one that hit `max_epochs`. Always cross-check. + +**Liveness check (is the job alive *right now*?):** `wandb.Api().run(...).heartbeatAt` is authoritative. Do not infer liveness from `last.ckpt` mtime, internal step counter, or a single `nvidia-smi` snapshot. + +**Completeness check (did the job finish its goal?):** combine three sources, all required: +1. `sacct -j --format=JobID,State,ExitCode,Elapsed,TimeLimit` — `CANCELLED+` with `ExitCode 0:0` is the signature of a user `scancel`; `TIMEOUT` is wall-time hit; `COMPLETED` with ExitCode 0:0 is the only unambiguous success. +2. The resolved fit YAML at `/hpc/projects/comp.micro/virtual_staining/models/dynacell/.../resolved/fit_*_.yml` — read `trainer.max_epochs` / `trainer.max_steps`. The wandb `r.config` dict only stores model init args, **not trainer args**. +3. Wandb run summary — compare final `epoch` to `trainer.max_epochs`. Final epoch of e.g. `135/200` is killed mid-training, not done; `200/200` is the only credible success indicator. + +The `output.log` / `wandb-output.log` for the run will contain `Received SIGTERM: 15` if Lightning's signal handler caught a scancel — a useful confirmation when sacct is ambiguous. + +**Before cancelling jobs:** the job name in `squeue` is not a complete description. `FCMAE_VSCyto3D_Pretrained_A549_Membrane` could be a fit run OR a predict run — they share the trained-model directory naming. Verify the actual purpose via: +- The `Comment` field (`squeue -j -o "%k"`) if the launcher set one +- The resolved YAML path (fit vs predict subdirectory) +- The wandb run config for that job ID + +When the user says "cancel all jobs," scope it to **batch jobs only**, never the interactive nomachine session. Read job names carefully — a job that has been alive for >24 h on a multi-GPU allocation is almost certainly training, not a predict run that should be ~hours. + +**Subagent prompts for job status:** ask for completeness vs config, not just liveness. A prompt like "check the liveness of wandb run X" returns `state: finished` for a SIGTERM'd run and reads as success. Phrase it as "is run X complete relative to its configured `max_epochs`, and what was the exit reason (clean finish, scancel, OOM, timeout, exception)?" + +### Joint vs single-set training batch semantics + +`HCSDataModule` and `BatchedConcatDataModule` produce the same number of GPU samples per training step — but the YAML `batch_size` value that gets there is **different by a factor of `num_samples`**. Easy to misread either by skimming. + +| DataModule | `train_dataloader` divides by `num_samples`? | Samples per step | +|---|---|---| +| `HCSDataModule` (single-set) | yes (`hcs.py` `train_dataloader`) | `batch_size` | +| `ConcatDataModule` (parent class) | yes (`combined.py` `train_dataloader`) | `batch_size` | +| `BatchedConcatDataModule` (joint) | **no** (`combined.py` overrides; uses `batch_size` as-is) | `batch_size * num_samples` | + +To match the same effective per-step samples between a single-set and a joint config, **set `joint.batch_size = single_set.batch_size / num_samples`**. + +Examples (verified against the `applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/data_overlays/` overlays + their joint leaves): + +- FCMAE (`fcmae_vscyto3d_*`): single-set `batch_size: 32, num_samples: 4` → joint `batch_size: 8, num_samples: 4` → both yield **32 samples/step**. +- FNet3D (`fnet3d_paper`): single-set `batch_size: 48, num_samples: 8` → joint `batch_size: 6, num_samples: 8` → both yield **48 samples/step**. + +`HCSDataModule._train_transform` enforces `batch_size % num_samples == 0` for single-set use because `train_dataloader` would otherwise round down silently. The check is suppressed for `BatchedConcatDataModule` children via the `_is_batched_concat_child` flag set in the wrapper's `setup()` — joint configs are free to pick any `(batch_size, num_samples)` pair as long as the product is the desired sample count. **Do not** "fix" a joint config by raising `batch_size` to satisfy the divisibility rule; it would multiply effective samples by `num_samples`. + +When in doubt, read both `train_dataloader` overrides directly — they are short. Don't infer from comments alone. + +### Common Commands + +```sh +uvx ruff check packages/ # lint +uvx ruff check --fix packages/ # lint + auto-fix +uvx ruff format packages/ # format +uv run pytest # all tests +``` + +### Testing + +```sh +uv run pytest # all tests +uv run pytest packages/viscy-data/ # single package (data) +uv run pytest packages/viscy-models/ # single package (models) +``` + +Prefer `{file}_test.py` in the same directory as `{file}.py`, unless there are import issues, in which case use `tests/`. + +--- + +## Project Conventions + +- Ruff config is centralized in the root `pyproject.toml` only. Sub-packages must NOT have their own `[tool.ruff.*]` sections. Ruff does not inherit config — any `[tool.ruff.*]` in a sub-package silently overrides the entire root config (including `lint.select`, `per-file-ignores`, etc.). +- Run `uvx prek run --files {files_you_edited}` (unless the change was simple) and fix typing and linting errors. Use `# type: ignore` as needed. The precommit will give you type errors which is useful — especially to know if you have incorrect code — but for many minor changes it's better to do this after testing. Use a subagent to apply complex fixes. + +--- + +## Engineering Standards + +### Git Workflow + +- **NEVER** use `git commit --amend` or `git push --force` / `--force-with-lease` unless the user explicitly requests it. Always create NEW commits. +- ALWAYS use atomic commits: one logical change per commit. Never bundle unrelated changes. +- Never use `git add -A` or `git add .`. Always stage specific files by name. +- Always pull before pushing. If push is rejected, pull and retry — never force-push. + +### Code Style + +- Docstrings use **numpy style** (`convention = "numpy"`). +- Lint rules: `D, E, F, I, NPY, PD, W`. +- `D` rules are ignored in `**/tests/**` and notebooks. +- Format: double quotes, spaces, 120 char line length. +- Use a subagent to run tests and complex bash commands, especially those expected to return complex output. +- Run independent tasks (multi-file edits across separate concerns, cross-cutting verifications, distinct review angles) in parallel via concurrent subagents in a single message. Subagent startup overhead is negligible relative to sequential blocking. Only sequence subagents when a later task needs an earlier task's output. + +#### Avoid Backwards Compatibility + +In most cases it is incorrect to maintain backwards compatibility with a previous pipeline. This is a research codebase — changes are expected and encouraged. Keeping backwards compatibility risks MORE bugs, since someone can unknowingly run old code. + +If you believe it is important to maintain backwards compatibility, explicitly ask the user if you should do so during the planning stage. If the user says no, then do not maintain backwards compatibility. + +Delete and remove old code that is not used. + +#### Use Context Managers for Resources + +Always use context managers (`with` statements) when opening external resources like zarr stores, files, or database connections. Never assign them to a variable without a context manager — this leaks file handles and locks. + +```python +# correct +with open_ome_zarr(path, mode="r") as plate: + ... + +# wrong — resource never closed +plate = open_ome_zarr(path, mode="r") +``` + +#### Prefer Raising Errors + +Prefer raising errors instead of silently catching them. Errors are good and warn us of issues. For example, prefer `value = my_dictionary['key']` over `value = my_dictionary.get('key')` since the former will raise a `KeyError` to signal that the underlying data is not behaving as expected. + +Only catch errors when there is a good reason to do so: for example, catching HTTP errors in order to retry a request. + +If you find yourself writing an if statement, fallback, or except statement designed to avoid errors, ask yourself if it would be better to raise the error as a signal to the user. + +#### Use Real Integration Tests + +Tests should directly *import* the actual code we are trying to test. For example, if you are trying to test `my_function` on some sample data, your test should directly import `my_function` and run it on the sample data. Avoid testing "key behavior" or components in isolation when an integration test would catch more bugs. + +Ask yourself if your test is actually covering the true function. + +#### Imports + +- Import at the top of the file. No inline imports without strong reason. +- Use absolute imports (`from packages.my_directory.my_file`) instead of relative. +- Do not modify `sys.path` for imports. + +### Coding Philosophy + +#### 1. Think Before Coding + +Don't assume. Don't hide confusion. Surface tradeoffs. + +Before implementing: +- State your assumptions explicitly. If uncertain, ask. +- If multiple interpretations exist, present them — don't pick silently. +- If a simpler approach exists, say so. Push back when warranted. +- If something is unclear, stop. Name what's confusing. Ask. + +#### 2. Simplicity First + +Minimum code that solves the problem. Nothing speculative. + +- No features beyond what was asked. +- No abstractions for single-use code. +- No "flexibility" or "configurability" that wasn't requested. +- No error handling for impossible scenarios. +- If you write 200 lines and it could be 50, rewrite it. +- Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify. + +#### 3. Surgical Changes + +Touch only what you must. Clean up only your own mess. + +When editing existing code: +- Don't "improve" adjacent code, comments, or formatting. +- Don't refactor things that aren't broken. +- Match existing style, even if you'd do it differently. +- If you notice unrelated dead code, mention it — don't delete it. + +When your changes create orphans: +- Remove imports/variables/functions that YOUR changes made unused. +- Don't remove pre-existing dead code unless asked. + +The test: every changed line should trace directly to the user's request. + +#### 4. Goal-Driven Execution + +Define success criteria. Loop until verified. + +Transform tasks into verifiable goals: +- "Add validation" → "Write tests for invalid inputs, then make them pass" +- "Fix the bug" → "Write a test that reproduces it, then make it pass" +- "Refactor X" → "Ensure tests pass before and after" + +For multi-step tasks, state a brief plan: +1. [Step] → verify: [check] +2. [Step] → verify: [check] + +Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7ea97b287..892d749a1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,39 +1,245 @@ -# Contributing to viscy +# Contributing guide -## Development installation +Thanks for your interest in contributing to VisCy! -Clone or fork the repository, -then make an editable installation with all the development dependencies: +Please see the following steps for our workflow. + +## Getting started + +Please read the [README](./README.md) for an overview of the project +and how you can install and use the package. + +## Issues + +We use [issues](https://github.com/mehta-lab/VisCy/issues) to track +bug reports, feature requests, and provide user support. + +Before opening a new issue, please first search existing issues (including closed ones) +to see if there is an existing discussion about it. + +## Making changes + +Any change made to the `main` branch needs to be proposed in a +[pull request](https://github.com/mehta-lab/VisCy/pulls) (PR). + +If there is an issue that can be addressed by the PR, please reference it. +If there is not a relevant issue, please either open an issue first, +or describe the bug fixed or feature implemented in the PR. + +### Setting up development environment + +This project uses [uv](https://docs.astral.sh/uv/) for dependency management +and is organized as a [uv workspace](https://docs.astral.sh/uv/concepts/workspaces/) monorepo. + +#### Install uv + +> [!NOTE] +> If you are an HPC system, we suggest making a symlink elsewhere first for the `uv` cache as your home directory will quickly get filled up. +> `mkdir -p /hpc/mydata/firstname.lastname/.cache/uv && ln -s /hpc/mydata/firstname.lastname/.cache/uv ~/.cache/uv` + +See [uv installation docs](https://docs.astral.sh/uv/getting-started/installation/). + +#### Clone the repository + +If you have push permission to the repository: ```sh -# in project root directory (parent folder of pyproject.toml) -pip install -e ".[dev]" +git clone https://github.com/mehta-lab/VisCy.git ``` -Install pre-commit hooks to automatically check and format code before commits: +Otherwise, you can follow [these instructions](https://docs.github.com/en/get-started/quickstart/fork-a-repo) +to [fork](https://github.com/mehta-lab/VisCy/fork) the repository. + +#### Install dependencies + +First, create a virtual environment with a supported Python version (3.11-3.13): ```sh -pre-commit install +cd VisCy/ +uv venv -p 3.13 # or 3.11 or 3.12 ``` -To run pre-commit checks manually: +This makes a virtual environment in `.venv/` where the dependencies will be installed. + +Then sync dependencies: ```sh -# Run on staged files only -pre-commit run +uv sync ``` -## CI requirements +> **Note**: `uv sync` installs the [`dev` group by default](https://docs.astral.sh/uv/concepts/projects/sync/#syncing-development-dependencies), +> which includes all development dependencies. See [dependency groups](https://docs.astral.sh/uv/concepts/projects/dependencies/#dependency-groups) for more details. -Lint and format with Ruff: +#### Repository structure + +VisCy is organized as a workspace monorepo: + +```shell +viscy/ +├── pyproject.toml # Root workspace configuration +├── packages/ +│ └── viscy-transforms/ # Image transforms subpackage +│ ├── pyproject.toml +│ └── src/ +│ └── viscy_transforms/ +└── src/ + └── viscy/ # Umbrella package (re-exports from subpackages) +``` + +Each package in `packages/` is an independent Python package that can be: + +- Developed in isolation +- Published to PyPI separately +- Installed independently by users + +```python +# Import directly from subpackages +from viscy_transforms import NormalizeSampled +``` + +Then make the changes and [track them with Git](https://docs.github.com/en/get-started/using-git/about-git#example-contribute-to-an-existing-repository). + +### Testing + +If you made code changes, make sure that there are also tests for them! +Local test runs and coverage check can be invoked by: ```sh -ruff check viscy -ruff format viscy tests +# Run all tests +uv run pytest + +# Run tests for a specific package +uv run pytest packages/viscy-transforms/ + +# Run with coverage +uv run pytest --cov=viscy_transforms ``` -Run tests with `pytest`: +### Code style + +We use [prek](https://github.com/j178/prek) (a faster [pre-commit](https://pre-commit.com/) runner) +to automatically format and lint code prior to each commit. +To minimize test errors when submitting pull requests, install the hooks: + +```bash +uvx prek install +``` + +> `uvx` runs tools in isolated, cached environments—no binaries added to your PATH +> and no dependencies installed in your project venv. + +To run manually: + +```bash +uvx prek run # run on staged files only +uvx prek run --all-files # run on all files +``` + +We use [ruff](https://docs.astral.sh/ruff/) for linting and formatting: + +```bash +uvx ruff check . # lint +uvx ruff check --fix . # lint and auto-fix +uvx ruff format . # format +``` + +When executed within the project root directory, ruff automatically uses +the [project settings](./pyproject.toml). + +> **Important**: All ruff configuration lives in the **root** `pyproject.toml` only. +> Sub-packages must not define their own `[tool.ruff.*]` sections — ruff does not +> inherit config, so any `[tool.ruff.*]` in a sub-package silently overrides the +> entire root config (including `lint.select`, `per-file-ignores`, etc.). + +Docstrings follow the [numpy style](https://numpydoc.readthedocs.io/en/latest/format.html) +(`convention = "numpy"` in `[tool.ruff.lint.pydocstyle]`). + +### Documentation + +[Zensical](https://zensical.org/) builds one site for the whole monorepo, from +`zensical.toml` and `docs/` at the repo root. Doc tools live in the root `doc` +dependency group; subpackages carry none. + +Preview with live reload: + +```sh +uv sync --all-packages --group doc # --all-packages: mkdocstrings imports the packages +uv run python docs/_gen_versions.py # refresh the package version table +uv run zensical serve # http://localhost:8000 +``` + +Static build lands in `site/` (git-ignored): ```sh -pytest -v +uv run zensical build --clean ``` + +Authoring notes: + +- Markdown lives in `docs/`; `nav` in `zensical.toml` sets the order. +- `::: viscy_data` renders a package's API. A template override + (`docs/_templates/python/material/module.html.jinja`) hides each package's + top-level docstring — write overview prose in the Markdown page instead. +- `docs/_gen_versions.py` rewrites the version table in `docs/packages/index.md`. + +CI (`.github/workflows/docs.yml`) deploys via [`mike`](https://github.com/squidfunk/mike): +`main` updates `dev`, a `vX.Y.Z` tag publishes that version and moves `stable`. + +### Releasing packages + +We use git tags for versioning, and each package has it's own tag. Each package reads +[`uv-dynamic-versioning`](https://github.com/ninoseki/uv-dynamic-versioning); the tag +**prefix** decides which package gets the version. + +| Package | Tag prefix | Example tag | +|---|---|---| +| `viscy-data` | `viscy-data-` | `viscy-data-v0.2.1` | +| `viscy-models` | `viscy-models-` | `viscy-models-v0.4.0` | +| `viscy-transforms` | `viscy-transforms-` | `viscy-transforms-v0.1.3` | +| `viscy-utils` | `viscy-utils-` | `viscy-utils-v0.3.0` | +| `viscy` (umbrella) | none | `v0.6.0` | + +Per-package, independent. Tag bumps only its own package. Umbrella reads bare tags. + +To cut a release: + +```sh +# 1. clean main, latest tags +git checkout main && git pull && git fetch --tags + +# 2. tag (one per package you ship) +git tag viscy-data-v0.2.1 + +# 3. build — version derived from the tag +uv build --package viscy-data --out-dir dist/ + +# 4. verify wheel name matches the tag before pushing +ls dist/ # viscy_data-0.2.1-py3-none-any.whl + +# 5. push the tag (nothing public until this) +git push origin viscy-data-v0.2.1 +``` + +> **Note**: no version pins between workspace members. A built wheel's +> `Requires-Dist` for a sibling package (e.g. `viscy-utils` → `viscy-data`) carries +> **no** version constraint — `[tool.uv.sources]` workspace links resolve locally only, +> not in published metadata ([astral-sh/uv#9811](https://github.com/astral-sh/uv/issues/9811)). +> If you publish to PyPI, add an explicit pin (e.g. `"viscy-data>=0.1,<0.2"`) to the +> dependent package's `dependencies`. + +## Useful links + +### uv documentation + +- [uv Overview](https://docs.astral.sh/uv/) +- [uv sync](https://docs.astral.sh/uv/concepts/projects/sync/) - Sync dependencies and packages +- [uv Workspaces](https://docs.astral.sh/uv/concepts/workspaces/) - Monorepo management +- [uv add](https://docs.astral.sh/uv/concepts/projects/dependencies/) - Adding dependencies +- [uv run](https://docs.astral.sh/uv/concepts/projects/run/) - Running commands in the environment +- [Dependency groups](https://docs.astral.sh/uv/concepts/projects/dependencies/#dependency-groups) + +### Related tools + +- [ruff](https://docs.astral.sh/ruff/) - Fast Python linter and formatter +- [pytest](https://docs.pytest.org/) - Testing framework +- [prek](https://github.com/j178/prek) - Fast pre-commit runner diff --git a/README.md b/README.md index a26c1c919..003693e90 100644 --- a/README.md +++ b/README.md @@ -2,195 +2,51 @@ [![Python package index](https://img.shields.io/pypi/v/viscy.svg)](https://pypi.org/project/viscy) [![PyPI monthly downloads](https://img.shields.io/pypi/dm/viscy.svg)](https://pypistats.org/packages/viscy) -[![Total downloads](https://pepy.tech/badge/viscy)](https://pepy.tech/project/viscy) [![GitHub contributors](https://img.shields.io/github/contributors-anon/mehta-lab/VisCy)](https://github.com/mehta-lab/VisCy/graphs/contributors) ![GitHub Repo stars](https://img.shields.io/github/stars/mehta-lab/VisCy) -![GitHub forks](https://img.shields.io/github/forks/mehta-lab/VisCy) [![SPEC 0 — Minimum Supported Dependencies](https://img.shields.io/badge/SPEC-0-green?labelColor=%23004811&color=%235CA038)](https://scientific-python.org/specs/spec-0000/) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.15022186.svg)](https://doi.org/10.5281/zenodo.15022186) VisCy (blend of `vision` and `cyto`) is a deep learning pipeline for training and deploying computer vision models for image-based phenotyping at single-cell resolution. -This repository provides a pipeline for the following. - -- Image translation - - Robust virtual staining of landmark organelles with Cytoland -- Image representation learning - - Self-supervised learning of the cell state and organelle phenotypes with DynaCLR -- Semantic segmentation - - Supervised learning of of cell state (e.g. state of infection) - -> **Note:** -VisCy is under active development. -While we strive to maintain stability, -the main branch may occasionally be updated with backward-incompatible changes -which are subsequently shipped in releases following [semantic versioning](https://semver.org/). -Please choose a stable release from PyPI for production use. - -## Cytoland (Robust Virtual Staining) - -### Demo [![Open in Spaces](https://huggingface.co/datasets/huggingface/badges/resolve/main/open-in-hf-spaces-sm-dark.svg)](https://huggingface.co/spaces/chanzuckerberg/Cytoland) - -Try the 2D virtual staining demo of cell nuclei and membrane from label-free images on -[Hugging Face](https://huggingface.co/spaces/chanzuckerberg/Cytoland). - -

- -Virtual Staining App Demo - -

+## Packages -### Cytoland @ Virtual Cells Platform +VisCy is organized as a [uv workspace](https://docs.astral.sh/uv/concepts/workspaces/) monorepo: -Cytoland models are accessible via the Chan Zuckerberg Initiative's Virtual Cells Platform. -Notebooks are available as pre-rendered pages or on Colab: +| Package | Description | Install | +|---------|-------------|---------| +| [viscy-data](./packages/viscy-data/) | Data loading and Lightning DataModules for microscopy | `pip install viscy-data` | +| [viscy-models](./packages/viscy-models/) | Neural network architectures (UNet, contrastive, VAE) | `pip install viscy-models` | +| [viscy-transforms](./packages/viscy-transforms/) | GPU-accelerated image transforms for microscopy | `pip install viscy-transforms` | +| [viscy-utils](./packages/viscy-utils/) | Shared ML infrastructure for microscopy | `pip install viscy-utils` | -- [Model card](https://virtualcellmodels.cziscience.com/model/01961244-1970-7851-a4b9-fdbfa2fba9b2) -- [Quick-start (VSCyto2D)](https://virtualcellmodels.cziscience.com/quickstart/cytoland-quickstart) -- CLI tutorials: - - [VSCyto3D](https://virtualcellmodels.cziscience.com/tutorial/cytoland-tutorial) - - [VSNeuromast](https://virtualcellmodels.cziscience.com/tutorial/cytoland-neuromast) +## Applications -### Tutorials - -- [Virtual staining exercise](https://github.com/mehta-lab/VisCy/blob/main/examples/virtual_staining/dlmbl_exercise/solution.ipynb): -Notebook illustrating how to use VisCy to train, predict and evaluate the VSCyto2D model. This notebook was developed for the [DL@MBL2024](https://github.com/dlmbl/DL-MBL-2024) course and uses UNeXt2 architecture. - -- [Image translation demo](https://github.com/mehta-lab/VisCy/blob/main/examples/virtual_staining/img2img_translation/solution.ipynb): Fluorescence images can be predicted from label-free images. Can we predict label-free image from fluorescence? Find out using this notebook. - -- [Training Virtual Staining Models via CLI](https://github.com/mehta-lab/VisCy/wiki/virtual-staining-instructions): -Instructions for how to train and run inference on VisCy's virtual staining models (*VSCyto3D*, *VSCyto2D* and *VSNeuromast*). - -### Gallery - -Below are some examples of virtually stained images (click to play videos). -See the full gallery [here](https://github.com/mehta-lab/VisCy/wiki/Gallery). - -| VSCyto3D | VSNeuromast | VSCyto2D | -|:---:|:---:|:---:| -| [![HEK293T](https://github.com/mehta-lab/VisCy/blob/dde3e27482e58a30f7c202e56d89378031180c75/docs/figures/svideo_1.png?raw=true)](https://github.com/mehta-lab/VisCy/assets/67518483/d53a81eb-eb37-44f3-b522-8bd7bddc7755) | [![Neuromast](https://github.com/mehta-lab/VisCy/blob/dde3e27482e58a30f7c202e56d89378031180c75/docs/figures/svideo_3.png?raw=true)](https://github.com/mehta-lab/VisCy/assets/67518483/4cef8333-895c-486c-b260-167debb7fd64) | [![A549](https://github.com/mehta-lab/VisCy/blob/dde3e27482e58a30f7c202e56d89378031180c75/docs/figures/svideo_5.png?raw=true)](https://github.com/mehta-lab/VisCy/assets/67518483/287737dd-6b74-4ce3-8ee5-25fbf8be0018) | - -### References - -The Cytoland models and training protocols are reported in our recent [paper on robust virtual staining in Nature Machine Intelligence]([https://www.biorxiv.org/content/10.1101/2024.05.31.596901](https://www.nature.com/articles/s42256-025-01046-2)). - -This package evolved from the [TensorFlow version of virtual staining pipeline](https://github.com/mehta-lab/microDL), which we reported in [this paper in 2020 in eLife](https://elifesciences.org/articles/55502). - -
- Liu, Hirata-Miyasaki et al., 2025 - -

-  @article{liu_robust_2025,
-      title = {Robust virtual staining of landmark organelles with {Cytoland}},
-      copyright = {2025 The Author(s)},
-      issn = {2522-5839},
-      url = {https://www.nature.com/articles/s42256-025-01046-2},
-      doi = {10.1038/s42256-025-01046-2},
-      abstract = {Correlative live-cell imaging of landmark organelles—such as nuclei, nucleoli, cell membranes, nuclear envelope and lipid droplets—is critical for systems cell biology and drug discovery. However, achieving this with molecular labels alone remains challenging. Virtual staining of multiple organelles and cell states from label-free images with deep neural networks is an emerging solution. Virtual staining frees the light spectrum for imaging molecular sensors, photomanipulation or other tasks. Current methods for virtual staining of landmark organelles often fail in the presence of nuisance variations in imaging, culture conditions and cell types. Here we address this with Cytoland, a collection of models for robust virtual staining of landmark organelles across diverse imaging parameters, cell states and types. These models were trained with self-supervised and supervised pre-training using a flexible convolutional architecture (UNeXt2) and augmentations inspired by image formation of light microscopes. Cytoland models enable virtual staining of nuclei and membranes across multiple cell types—including human cell lines, zebrafish neuromasts, induced pluripotent stem cells (iPSCs) and iPSC-derived neurons—under a range of imaging conditions. We assess models using intensity, segmentation and application-specific measurements obtained from virtually and experimentally stained nuclei and membranes. These models rescue missing labels, correct non-uniform labelling and mitigate photobleaching. We share multiple pre-trained models, open-source software (VisCy) for training, inference and deployment, and the datasets.},
-      language = {en},
-      urldate = {2025-06-23},
-      journal = {Nature Machine Intelligence},
-      author = {Liu, Ziwen and Hirata-Miyasaki, Eduardo and Pradeep, Soorya and Rahm, Johanna V. and Foley, Christian and Chandler, Talon and Ivanov, Ivan E. and Woosley, Hunter O. and Lee, See-Chi and Khadka, Sudip and Lao, Tiger and Balasubramanian, Akilandeswari and Marreiros, Rita and Liu, Chad and Januel, Camille and Leonetti, Manuel D. and Aviner, Ranen and Arias, Carolina and Jacobo, Adrian and Mehta, Shalin B.},
-      month = jun,
-      year = {2025},
-      note = {Publisher: Nature Publishing Group},
-      pages = {1--15},
-      }
-  
-
- -
- Guo, Yeh, Folkesson et al., 2020 - -

-  @article {10.7554/eLife.55502,
-      article_type = {journal},
-      title = {Revealing architectural order with quantitative label-free imaging and deep learning},
-      author = {Guo, Syuan-Ming and Yeh, Li-Hao and Folkesson, Jenny and Ivanov, Ivan E and Krishnan, Anitha P and Keefe, Matthew G and Hashemi, Ezzat and Shin, David and Chhun, Bryant B and Cho, Nathan H and Leonetti, Manuel D and Han, May H and Nowakowski, Tomasz J and Mehta, Shalin B},
-      editor = {Forstmann, Birte and Malhotra, Vivek and Van Valen, David},
-      volume = 9,
-      year = 2020,
-      month = {jul},
-      pub_date = {2020-07-27},
-      pages = {e55502},
-      citation = {eLife 2020;9:e55502},
-      doi = {10.7554/eLife.55502},
-      url = {https://doi.org/10.7554/eLife.55502},
-      keywords = {label-free imaging, inverse algorithms, deep learning, human tissue, polarization, phase},
-      journal = {eLife},
-      issn = {2050-084X},
-      publisher = {eLife Sciences Publications, Ltd},
-      }
-  
-
- -### Library of Virtual Staining (VS) Models - -The robust virtual staining models (i.e *VSCyto2D*, *VSCyto3D*, *VSNeuromast*), and fine-tuned models can be found [here](https://github.com/mehta-lab/VisCy/wiki/Library-of-virtual-staining-(VS)-Models) - -## DynaCLR (Embedding Cell Dynamics via Contrastive Learning of Representations) - -DynaCLR is a self-supervised method for learning robust and temporally-regularized representations of cell and organelle dynamics from time-lapse microscopy using contrastive learning. It supports diverse downstream biological tasks -- including cell state classification with efficient human annotations, knowledge distillation across fluorescence and label-free imaging channels, and alignment of cell state dynamics. - -### Preprint - -[DynaCLR on arXiv](https://arxiv.org/abs/2410.11281): - -![DynaCLR schematic](https://github.com/mehta-lab/VisCy/blob/e5318d88e2bb5d404d3bae8d633b8cc07b1fbd61/docs/figures/DynaCLR_schematic_v2.png?raw=true) - -### Demo - -- [DynaCLR demos](examples/DynaCLR/README.md) - -- Example test dataset, model checkpoint, and predictions can be found -[here](https://public.czbiohub.org/comp.micro/viscy/DynaCLR_demo/). - -- See tutorial on exploration of learned embeddings with napari-iohub -[here](https://github.com/czbiohub-sf/napari-iohub/wiki/View-tracked-cells-and-their-associated-predictions/). +| Application | Description | Install | +|-------------|-------------|---------| +| [Cytoland](./applications/cytoland/) | Robust virtual staining of organelles from label-free images | `uv pip install -e "applications/cytoland"` | +| [DynaCLR](./applications/dynaclr/) | Self-supervised contrastive learning for cellular dynamics | `uv pip install -e "applications/dynaclr"` | ## Installation -1. We recommend using a new Conda/virtual environment. - - ```sh - conda create --name viscy python=3.11 - # OR specify a custom path since the dependencies are large: - # conda create --prefix /path/to/conda/envs/viscy python=3.11 - ``` - -2. Install a released version of VisCy from PyPI: - - ```sh - pip install viscy - ``` - - If evaluating virtually stained images for segmentation tasks, - install additional dependencies: - - ```sh - pip install "viscy[metrics]" - ``` - - Visualizing the model architecture requires `visual` dependencies: +Install individual packages (e.g.): - ```sh - pip install "viscy[visual]" - ``` +```sh +pip install viscy-models +``` -3. Verify installation by accessing the CLI help message: +Or install from source with all development dependencies: - ```sh - viscy --help - ``` +```sh +git clone https://github.com/mehta-lab/VisCy.git +cd VisCy +uv sync +``` -For development installation, see [the contributing guide](https://github.com/mehta-lab/VisCy/blob/main/CONTRIBUTING.md). +## Development -## Additional Notes +See [CONTRIBUTING.md](./CONTRIBUTING.md) for development setup and guidelines. -The pipeline is built using the [PyTorch Lightning](https://www.pytorchlightning.ai/index.html) framework. -The [iohub](https://github.com/czbiohub-sf/iohub) library is used -for reading and writing data in [OME-Zarr](https://www.nature.com/articles/s41592-021-01326-w) format. +## License -The full functionality is tested on Linux `x86_64` with NVIDIA Ampere/Hopper GPUs (CUDA 12.6). -Some features (e.g. mixed precision and distributed training) may not be available with other setups, -see [PyTorch documentation](https://pytorch.org) for details. +[BSD-3-Clause](./LICENSE) diff --git a/applications/DynaCLR/evaluation/README.md b/applications/DynaCLR/evaluation/README.md deleted file mode 100644 index 647d7f33b..000000000 --- a/applications/DynaCLR/evaluation/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# DynaCLR Evaluation - -Evaluation tools for DynaCLR cell embedding models. Each evaluation method lives in its own subdirectory. - -## Available Methods - -| Method | Directory | Description | -|--------|-----------|-------------| -| Linear classifiers | `linear_classifiers/` | Logistic regression on embeddings for supervised cell phenotyping | diff --git a/applications/DynaCLR/evaluation/linear_classifiers/README.md b/applications/DynaCLR/evaluation/linear_classifiers/README.md deleted file mode 100644 index 7cdbdf547..000000000 --- a/applications/DynaCLR/evaluation/linear_classifiers/README.md +++ /dev/null @@ -1,167 +0,0 @@ -# Linear Classifier for Cell Phenotyping - -Train and apply logistic regression classifiers on DynaCLR cell embeddings for supervised cell phenotyping tasks. - -## Overview - -This directory contains: - -| File | Description | -|------|-------------| -| `dataset_discovery.py` | Shared functions for discovering predictions, annotations, and gaps across datasets | -| `generate_prediction_scripts.py` | Generates SLURM `.sh`/`.yml` scripts for datasets missing embeddings | -| `generate_train_config.py` | Generates training YAML configs for all valid task x channel combinations | -| `train_linear_classifier.py` | CLI for training a classifier from a config | -| `apply_linear_classifier.py` | CLI for applying a trained classifier to new embeddings | - -## Prerequisites - -Install VisCy with the metrics extras: - -```bash -pip install -e ".[metrics]" -``` - -You also need a [Weights & Biases](https://wandb.ai) account for model storage and tracking. Log in before running: - -```bash -wandb login -``` - -## Workflow - -### 1. Discover datasets and generate prediction scripts - -If some annotated datasets don't have embeddings yet, generate the SLURM prediction scripts: - -```python -# Edit configuration in generate_prediction_scripts.py, then run cells -# Key parameters: -# embeddings_dir - base directory with dataset folders -# annotations_dir - base directory with annotation CSVs -# model - model directory glob pattern -# version - model version (e.g. "v3") -# ckpt_path - checkpoint to use for ALL datasets -``` - -This will: -- Discover which annotated datasets are missing predictions -- Use an existing dataset as a template -- Generate `predict_{phase,sensor,organelle}.{sh,yml}` and `run_all.sh` per dataset -- Enforce a single checkpoint across all generated scripts - -### 2. Generate training configs - -Once datasets have both embeddings and annotations: - -```python -# Edit configuration in generate_train_config.py, then run cells -# Generates one YAML config per (task, channel) combination -``` - -### 3. Train a classifier - -```bash -viscy-dynaclr train-linear-classifier -c configs/generated/cell_death_state_phase.yaml -``` - -### 4. Apply a trained classifier to new data - -```bash -viscy-dynaclr apply-linear-classifier -c configs/example_linear_classifier_inference.yaml -``` - -## Training Configuration - -Create a YAML config file (see `configs/example_linear_classifier_train.yaml`): - -```yaml -task: cell_death_state # infection_state | organelle_state | cell_division_state | cell_death_state -input_channel: phase # phase | sensor | organelle -embedding_model: DynaCLR-2D-BagOfChannels-timeaware-v3 - -train_datasets: - - embeddings: /path/to/dataset1/embeddings_phase.zarr - annotations: /path/to/dataset1/annotations.csv - - embeddings: /path/to/dataset2/embeddings_phase.zarr - annotations: /path/to/dataset2/annotations.csv - include_wells: ["A/1", "C/2"] # optional: filter by well prefix - -use_scaling: true -use_pca: false -n_pca_components: null -max_iter: 1000 -class_weight: balanced -solver: liblinear -split_train_data: 0.8 -random_seed: 42 - -wandb_project: DynaCLR-2D-linearclassifiers -wandb_entity: null -wandb_tags: [] -``` - -### Well filtering - -Each dataset entry can optionally specify `include_wells` — a list of well prefixes (e.g. `["A/1", "B/2"]`) to restrict which FOVs are used. The `fov_name` column in annotations follows the format `{row}/{col}/{position}` (e.g. `B/1/002001`), and filtering matches on the `{row}/{col}/` prefix. If `include_wells` is omitted or null, all wells are used. - -This is useful for the `organelle_state` task where different wells contain different organelle markers and remodeling phenotypes differ between them. - -### What happens during training - -1. Embeddings and annotations are loaded and matched on `(fov_name, id)` -2. If `include_wells` is specified, only matching FOVs are kept -3. Cells with missing or `"unknown"` labels are filtered out -4. Multiple datasets are concatenated -5. Optional preprocessing is applied (StandardScaler, PCA) -6. Data is split into train/validation sets (stratified) -7. A `LogisticRegression` classifier is trained -8. Metrics (accuracy, precision, recall, F1) are logged to W&B -9. The trained model pipeline is saved as a W&B artifact - -## Inference Configuration - -```yaml -wandb_project: DynaCLR-2D-linearclassifiers -model_name: linear-classifier-cell_death_state-phase -version: latest -wandb_entity: null -embeddings_path: /path/to/embeddings.zarr -output_path: /path/to/output_with_predictions.zarr -overwrite: false -``` - -### Output format - -```python -adata.obs[f"predicted_{task}"] # Predicted class labels -adata.obsm[f"predicted_{task}_proba"] # Class probabilities (n_cells x n_classes) -adata.uns[f"predicted_{task}_classes"] # Ordered list of class names -``` - -## Supported Tasks and Channels - -| Task | Description | Example Labels | -|------|-------------|----------------| -| `infection_state` | Viral infection status | `infected`, `uninfected` | -| `organelle_state` | Organelle morphology | `nonremodel`, `remodeled` | -| `cell_division_state` | Cell cycle phase | `mitosis`, `interphase` | -| `cell_death_state` | Cell viability/death | `alive`, `dead` | - -| Channel | Description | -|---------|-------------| -| `phase` | Phase contrast / brightfield | -| `sensor` | Fluorescent reporter | -| `organelle` | Organelle staining | - -## Model Naming Convention - -``` -linear-classifier-{task}-{channel}[-pca{n}] -``` - -Examples: `linear-classifier-cell_death_state-phase`, `linear-classifier-infection_state-sensor-pca32` - -## Further Reference - -See `annotations_and_linear_classifiers.md` for the full specification of the annotations schema and naming conventions. diff --git a/applications/DynaCLR/evaluation/linear_classifiers/apply_linear_classifier.py b/applications/DynaCLR/evaluation/linear_classifiers/apply_linear_classifier.py deleted file mode 100644 index e86bea050..000000000 --- a/applications/DynaCLR/evaluation/linear_classifiers/apply_linear_classifier.py +++ /dev/null @@ -1,130 +0,0 @@ -"""CLI for applying trained linear classifiers to new embeddings. - -Usage: - python -m applications.DynaCLR.evaluation.apply_linear_classifier --config path/to/config.yaml -""" - -from pathlib import Path - -import click -from anndata import read_zarr -from pydantic import ValidationError - -from viscy.representation.evaluation.linear_classifier import ( - load_pipeline_from_wandb, - predict_with_classifier, -) -from viscy.representation.evaluation.linear_classifier_config import ( - LinearClassifierInferenceConfig, -) -from viscy.utils.cli_utils import format_markdown_table, load_config - - -def format_predictions_markdown(adata, task: str) -> str: - """Format prediction summary as markdown. - - Parameters - ---------- - adata : anndata.AnnData - AnnData with predictions. - task : str - Task name. - - Returns - ------- - str - Markdown-formatted summary. - """ - lines = ["## Prediction Summary", ""] - - pred_col = f"predicted_{task}" - if pred_col in adata.obs.columns: - lines.append("### Class Distribution") - lines.append("") - counts = adata.obs[pred_col].value_counts().sort_index() - class_counts = {str(k): int(v) for k, v in counts.items()} - lines.append( - format_markdown_table(class_counts, headers=["Class", "Count"]).strip() - ) - lines.append("") - - lines.append(f"**Total predictions:** {len(adata)}") - lines.append("") - - proba_key = f"predicted_{task}_proba" - if proba_key in adata.obsm.keys(): - lines.append(f"**Probability matrix shape:** {adata.obsm[proba_key].shape}") - lines.append("") - - classes_key = f"predicted_{task}_classes" - if classes_key in adata.uns.keys(): - lines.append(f"**Classes:** {', '.join(adata.uns[classes_key])}") - lines.append("") - - return "\n".join(lines) - - -@click.command(context_settings={"help_option_names": ["-h", "--help"]}) -@click.option( - "-c", - "--config", - type=click.Path(exists=True, path_type=Path), - required=True, - help="Path to YAML configuration file", -) -def main(config: Path): - """Apply a trained linear classifier to new embeddings.""" - click.echo("=" * 60) - click.echo("LINEAR CLASSIFIER INFERENCE") - click.echo("=" * 60) - - try: - config_dict = load_config(config) - inference_config = LinearClassifierInferenceConfig(**config_dict) - except ValidationError as e: - click.echo(f"\n❌ Configuration validation failed:\n{e}", err=True) - raise click.Abort() - except Exception as e: - click.echo(f"\n❌ Failed to load configuration: {e}", err=True) - raise click.Abort() - - click.echo(f"\n✓ Configuration loaded: {config}") - click.echo(f" Model: {inference_config.model_name}") - click.echo(f" Version: {inference_config.version}") - click.echo(f" Embeddings: {inference_config.embeddings_path}") - click.echo(f" Output: {inference_config.output_path}") - - try: - pipeline, loaded_config = load_pipeline_from_wandb( - wandb_project=inference_config.wandb_project, - model_name=inference_config.model_name, - version=inference_config.version, - wandb_entity=inference_config.wandb_entity, - ) - - task = loaded_config["task"] - - click.echo(f"\nLoading embeddings from: {inference_config.embeddings_path}") - adata = read_zarr(inference_config.embeddings_path) - click.echo(f"✓ Loaded embeddings: {adata.shape}") - - adata = predict_with_classifier(adata, pipeline, task) - - output_path = Path(inference_config.output_path) - output_path.parent.mkdir(parents=True, exist_ok=True) - - click.echo(f"\nSaving predictions to: {output_path}") - adata.write_zarr(output_path) - click.echo("✓ Saved predictions") - - click.echo("\n" + format_predictions_markdown(adata, task)) - - click.echo("\n✓ Inference complete!") - - except Exception as e: - click.echo(f"\n❌ Inference failed: {e}", err=True) - raise click.Abort() - - -if __name__ == "__main__": - main() diff --git a/applications/DynaCLR/evaluation/linear_classifiers/configs/example_linear_classifier_inference.yaml b/applications/DynaCLR/evaluation/linear_classifiers/configs/example_linear_classifier_inference.yaml deleted file mode 100644 index a6e882365..000000000 --- a/applications/DynaCLR/evaluation/linear_classifiers/configs/example_linear_classifier_inference.yaml +++ /dev/null @@ -1,28 +0,0 @@ -# Example configuration for applying a trained linear classifier -# -# Usage: -# viscy-dynaclr apply-linear-classifier \ -# -c applications/DynaCLR/evaluation/linear_classifiers/configs/example_linear_classifier_inference.yaml - -# W&B project name where the model artifact is stored -wandb_project: DynaCLR-2D-linearclassifiers - -# Name of the model artifact in W&B -# (e.g., linear-classifier-cell_death_state-phase) -model_name: linear-classifier-cell_death_state-phase - -# Version of the model artifact -# Use 'latest' for the most recent version, or specific version like 'v0', 'v1' -version: latest - -# W&B entity (username or team, null for default) -wandb_entity: null - -# Path to embeddings zarr file for inference -embeddings_path: /path/to/embeddings.zarr - -# Path to save output zarr file with predictions -output_path: /path/to/output_with_predictions.zarr - -# Whether to overwrite output if it already exists -overwrite: false diff --git a/applications/DynaCLR/evaluation/linear_classifiers/configs/example_linear_classifier_train.yaml b/applications/DynaCLR/evaluation/linear_classifiers/configs/example_linear_classifier_train.yaml deleted file mode 100644 index f728f6b1f..000000000 --- a/applications/DynaCLR/evaluation/linear_classifiers/configs/example_linear_classifier_train.yaml +++ /dev/null @@ -1,45 +0,0 @@ -# Example configuration for training a linear classifier -# -# Usage: -# viscy-dynaclr train-linear-classifier \ -# -c applications/DynaCLR/evaluation/linear_classifiers/configs/example_linear_classifier_train.yaml - -# Classification task name -# Valid options: infection_state, organelle_state, cell_division_state, cell_death_state -task: cell_death_state - -# Input channel name used for embeddings -# Valid options: phase, sensor, organelle -input_channel: phase - -# Name of the embedding model -embedding_model: DynaCLR-2D-BagOfChannels-timeaware-v3 - -# Training datasets - list of exact file paths (no glob patterns) -# Each dataset must have both embeddings (zarr) and annotations (csv) -# Optionally specify include_wells to filter by well prefix (e.g. A/1, B/2) -train_datasets: - - embeddings: /path/to/dataset1/embeddings_phase.zarr - annotations: /path/to/dataset1/annotations.csv - - embeddings: /path/to/dataset2/embeddings_phase.zarr - annotations: /path/to/dataset2/annotations.csv - include_wells: ["A/1", "C/2"] # optional: only use these wells - -# Preprocessing -use_scaling: true # Apply StandardScaler normalization -use_pca: false # Apply PCA dimensionality reduction -n_pca_components: null # Number of PCA components (required if use_pca is true) - -# Classifier hyperparameters -max_iter: 1000 # Maximum number of iterations for solver -class_weight: balanced # Class weighting strategy ('balanced' or null) -solver: liblinear # Optimization algorithm - -# Training parameters -split_train_data: 0.8 # Fraction of data for training (rest for validation, 1.0 = use all) -random_seed: 42 # Random seed for reproducibility - -# Weights & Biases configuration -wandb_project: DynaCLR-2D-linearclassifiers # W&B project name -wandb_entity: null # W&B entity (username or team, null for default) -wandb_tags: [] # Tags to add to the run diff --git a/applications/DynaCLR/evaluation/linear_classifiers/train_linear_classifier.py b/applications/DynaCLR/evaluation/linear_classifiers/train_linear_classifier.py deleted file mode 100644 index c554f5be9..000000000 --- a/applications/DynaCLR/evaluation/linear_classifiers/train_linear_classifier.py +++ /dev/null @@ -1,139 +0,0 @@ -"""CLI for training linear classifiers on cell embeddings. - -Usage: - python -m applications.DynaCLR.evaluation.train_linear_classifier --config path/to/config.yaml -""" - -from pathlib import Path - -import click -from pydantic import ValidationError - -from viscy.representation.evaluation.linear_classifier import ( - load_and_combine_datasets, - save_pipeline_to_wandb, - train_linear_classifier, -) -from viscy.representation.evaluation.linear_classifier_config import ( - LinearClassifierTrainConfig, -) -from viscy.utils.cli_utils import format_markdown_table, load_config - - -def format_metrics_markdown(metrics: dict) -> str: - """Format metrics as markdown table. - - Parameters - ---------- - metrics : dict - Dictionary of metric names and values. - - Returns - ------- - str - Markdown-formatted table. - """ - lines = ["## Classification Metrics", ""] - - train_metrics = { - k.replace("train_", ""): v for k, v in metrics.items() if k.startswith("train_") - } - val_metrics = { - k.replace("val_", ""): v for k, v in metrics.items() if k.startswith("val_") - } - - if train_metrics: - lines.append("### Training Set") - lines.append("") - lines.append(format_markdown_table(train_metrics).strip()) - lines.append("") - - if val_metrics: - lines.append("### Validation Set") - lines.append("") - lines.append(format_markdown_table(val_metrics).strip()) - lines.append("") - - return "\n".join(lines) - - -@click.command(context_settings={"help_option_names": ["-h", "--help"]}) -@click.option( - "-c", - "--config", - type=click.Path(exists=True, path_type=Path), - required=True, - help="Path to YAML configuration file", -) -def main(config: Path): - """Train a linear classifier on cell embeddings.""" - click.echo("=" * 60) - click.echo("LINEAR CLASSIFIER TRAINING") - click.echo("=" * 60) - - try: - config_dict = load_config(config) - train_config = LinearClassifierTrainConfig(**config_dict) - except ValidationError as e: - click.echo(f"\n❌ Configuration validation failed:\n{e}", err=True) - raise click.Abort() - except Exception as e: - click.echo(f"\n❌ Failed to load configuration: {e}", err=True) - raise click.Abort() - - click.echo(f"\n✓ Configuration loaded: {config}") - click.echo(f" Task: {train_config.task}") - click.echo(f" Input channel: {train_config.input_channel}") - click.echo(f" Model: {train_config.embedding_model}") - click.echo(f" Datasets: {len(train_config.train_datasets)}") - - try: - click.echo("\n" + "=" * 60) - click.echo("LOADING TRAINING DATA") - click.echo("=" * 60) - - combined_adata = load_and_combine_datasets( - train_config.train_datasets, - train_config.task, - ) - - classifier_params = { - "max_iter": train_config.max_iter, - "class_weight": train_config.class_weight, - "solver": train_config.solver, - "random_state": train_config.random_seed, - } - - pipeline, metrics = train_linear_classifier( - adata=combined_adata, - task=train_config.task, - use_scaling=train_config.use_scaling, - use_pca=train_config.use_pca, - n_pca_components=train_config.n_pca_components, - classifier_params=classifier_params, - split_train_data=train_config.split_train_data, - random_seed=train_config.random_seed, - ) - - click.echo("\n" + format_metrics_markdown(metrics)) - - full_config = train_config.model_dump() - - artifact_name = save_pipeline_to_wandb( - pipeline=pipeline, - metrics=metrics, - config=full_config, - wandb_project=train_config.wandb_project, - wandb_entity=train_config.wandb_entity, - tags=train_config.wandb_tags, - ) - - click.echo(f"\n✓ Training complete! Artifact: {artifact_name}") - - except Exception as e: - click.echo(f"\n❌ Training failed: {e}", err=True) - raise click.Abort() - - -if __name__ == "__main__": - main() diff --git a/applications/DynaCLR/evaluation/linear_classifiers/utils.py b/applications/DynaCLR/evaluation/linear_classifiers/utils.py deleted file mode 100644 index 37b603008..000000000 --- a/applications/DynaCLR/evaluation/linear_classifiers/utils.py +++ /dev/null @@ -1,707 +0,0 @@ -"""Shared utilities for the linear_classifiers workflow. - -Constants, path resolution, config generation, dataset discovery, -and focus/z-range helpers used by both ``generate_batch_predictions.py`` -and ``generate_train_config.py``. -""" - -# %% -import re -from glob import glob -from pathlib import Path - -import pandas as pd -from natsort import natsorted - -from viscy.representation.evaluation.linear_classifier_config import ( - VALID_CHANNELS, - VALID_TASKS, -) - -CHANNELS = list(VALID_CHANNELS.__args__) -TASKS = list(VALID_TASKS.__args__) - -# --------------------------------------------------------------------------- -# Model templates -# --------------------------------------------------------------------------- - -MODEL_3D_BAG_TIMEAWARE = { - "name": "DynaCLR-3D-BagOfChannels-timeaware", - "in_stack_depth": 30, - "stem_kernel_size": [5, 4, 4], - "stem_stride": [5, 4, 4], - "patch_size": 192, - "data_path_type": "2-assemble", - "z_range": "auto", - # Fraction of z slices below the focus plane (0.33 = 1/3 below, 2/3 above). - "focus_below_fraction": 1 / 3, - "logger_base": "/hpc/projects/organelle_phenotyping/models/tb_logs", -} - -MODEL_2D_BAG_TIMEAWARE = { - "name": "DynaCLR-2D-BagOfChannels-timeaware", - "in_stack_depth": 1, - "stem_kernel_size": [1, 4, 4], - "stem_stride": [1, 4, 4], - "patch_size": 160, - "data_path_type": "train-test", - "z_range": [0, 1], - "logger_base": "/hpc/projects/organelle_phenotyping/models/embedding_logs", -} - -# --------------------------------------------------------------------------- -# Channel defaults -# --------------------------------------------------------------------------- - -CHANNEL_DEFAULTS: dict[str, dict] = { - "organelle": { - "keyword": "GFP", - "yaml_alias": "fluor", - "normalization_class": "viscy.transforms.ScaleIntensityRangePercentilesd", - "normalization_args": { - "lower": 50, - "upper": 99, - "b_min": 0.0, - "b_max": 1.0, - }, - "batch_size": {"2d": 32, "3d": 64}, - "num_workers": {"2d": 8, "3d": 16}, - }, - "phase": { - "keyword": "Phase", - "yaml_alias": "Ph", - "normalization_class": "viscy.transforms.NormalizeSampled", - "normalization_args": { - "level": "fov_statistics", - "subtrahend": "mean", - "divisor": "std", - }, - "batch_size": {"2d": 64, "3d": 64}, - "num_workers": {"2d": 16, "3d": 16}, - }, - "sensor": { - "keyword": "mCherry", - "yaml_alias": "fluor", - "normalization_class": "viscy.transforms.ScaleIntensityRangePercentilesd", - "normalization_args": { - "lower": 50, - "upper": 99, - "b_min": 0.0, - "b_max": 1.0, - }, - "batch_size": {"2d": 32, "3d": 64}, - "num_workers": {"2d": 8, "3d": 16}, - }, -} - -# --------------------------------------------------------------------------- -# Focus parameters (microscope-specific defaults) -# --------------------------------------------------------------------------- - -FOCUS_PARAMS = { - "NA_det": 1.35, - "lambda_ill": 0.450, - "pixel_size": 0.1494, - "device": "cuda", -} - - -# --------------------------------------------------------------------------- -# Checkpoint utilities -# --------------------------------------------------------------------------- - - -def extract_epoch(ckpt_path: str) -> str: - """Extract epoch number from a checkpoint filename. - - ``epoch=32-step=33066.ckpt`` -> ``"32"`` - """ - m = re.search(r"epoch=(\d+)", Path(ckpt_path).stem) - if m: - return m.group(1) - return Path(ckpt_path).stem - - -# --------------------------------------------------------------------------- -# Channel utilities -# --------------------------------------------------------------------------- - - -def resolve_channel_name( - channel_names: list[str], - channel_type: str, - channel_overrides: dict[str, str] | None = None, -) -> str | None: - """Find the full channel name by keyword substring match. - - When multiple channels match the keyword, the ``raw`` variant is - preferred (e.g. ``"raw GFP EX488 EM525-45"`` over ``"GFP EX488 EM525-45"``). - - Parameters - ---------- - channel_names : list[str] - Channel names from the zarr dataset. - channel_type : str - One of "organelle", "phase", "sensor". - channel_overrides : dict[str, str] or None - Optional mapping of channel_type -> keyword override. - - Returns - ------- - str or None - Matched channel name, or None if not found. - """ - keyword = channel_overrides.get(channel_type) if channel_overrides else None - if keyword is None: - keyword = CHANNEL_DEFAULTS[channel_type]["keyword"] - matches = [name for name in channel_names if keyword in name] - if not matches: - return None - # Prefer the "raw" variant when both raw and processed exist - raw = [m for m in matches if m.lower().startswith("raw")] - return raw[0] if raw else matches[0] - - -# --------------------------------------------------------------------------- -# Path resolution -# --------------------------------------------------------------------------- - - -def resolve_dataset_paths( - dataset_name: str, - base_dir: Path, - model_config: dict, -) -> dict: - """Resolve data_path and tracks_path for a dataset. - - Parameters - ---------- - dataset_name : str - Dataset folder name. - base_dir : Path - Base directory containing all datasets. - model_config : dict - Model template (used to determine data_path_type). - - Returns - ------- - dict - Keys: data_path, tracks_path (both as Path objects). - - Raises - ------ - FileNotFoundError - If required paths cannot be found. - """ - dataset_dir = base_dir / dataset_name - - # Data path - if model_config["data_path_type"] == "train-test": - matches = natsorted( - glob( - str( - dataset_dir - / "*phenotyping*" - / "*train-test*" - / f"{dataset_name}*.zarr" - ) - ) - ) - if not matches: - raise FileNotFoundError(f"No train-test zarr found for {dataset_name}") - data_path = Path(matches[0]) - else: - matches = natsorted( - glob(str(dataset_dir / "2-assemble" / f"{dataset_name}*.zarr")) - ) - if not matches: - raise FileNotFoundError(f"No 2-assemble zarr found for {dataset_name}") - data_path = Path(matches[0]) - - # Tracks path - tracks_matches = natsorted( - glob( - str( - dataset_dir - / "1-preprocess" - / "label-free" - / "3-track" - / f"{dataset_name}*cropped.zarr" - ) - ) - ) - if not tracks_matches: - raise FileNotFoundError(f"No tracking zarr found for {dataset_name}") - tracks_path = Path(tracks_matches[0]) - - return {"data_path": data_path, "tracks_path": tracks_path} - - -def find_phenotyping_predictions_dir( - dataset_dir: Path, - model_name: str, - version: str, -) -> Path: - """Locate or create the predictions output directory for a dataset.""" - pheno_matches = natsorted(glob(str(dataset_dir / "*phenotyping*"))) - if not pheno_matches: - pheno_dir = dataset_dir / "4-phenotyping" - else: - pheno_dir = Path(pheno_matches[0]) - - pred_matches = natsorted(glob(str(pheno_dir / "*prediction*"))) - pred_parent = Path(pred_matches[0]) if pred_matches else pheno_dir / "predictions" - - return pred_parent / model_name / version - - -# --------------------------------------------------------------------------- -# Focus / z-range -# --------------------------------------------------------------------------- - - -def get_z_range( - data_path: str | Path, - model_config: dict, - focus_params: dict | None = None, - phase_channel: str | None = None, -) -> list[int]: - """Determine z_range for prediction. - - For models with ``z_range="auto"``, reads focus_slice metadata from the - zarr. If metadata is missing, computes it on the fly. - - Parameters - ---------- - data_path : str or Path - Path to the OME-Zarr dataset. - model_config : dict - Model template dictionary. - focus_params : dict or None - Parameters for on-the-fly focus computation. - phase_channel : str or None - Name of the phase channel in the zarr. Used to look up focus_slice - metadata. If None, auto-detected by keyword match. - - Returns - ------- - list[int] - [z_start, z_end] range for prediction. - """ - from iohub import open_ome_zarr - - if model_config["z_range"] != "auto": - return list(model_config["z_range"]) - - plate = open_ome_zarr(str(data_path), mode="r") - - # Resolve phase channel name if not provided - if phase_channel is None: - phase_channel = resolve_channel_name(list(plate.channel_names), "phase") - if phase_channel is None: - plate.close() - raise ValueError( - f"Cannot determine z_range: no phase channel found in {data_path}" - ) - - focus_data = plate.zattrs.get("focus_slice", {}) - phase_stats = focus_data.get(phase_channel, {}).get("dataset_statistics", {}) - z_focus_mean = phase_stats.get("z_focus_mean") - - # Get total z depth from first position - for _, pos in plate.positions(): - z_total = pos["0"].shape[2] - break - plate.close() - - if z_focus_mean is None: - print(f" Focus metadata missing for {Path(data_path).name}, computing...") - z_focus_mean = _compute_focus( - str(data_path), focus_params or FOCUS_PARAMS, phase_channel - ) - - depth = model_config["in_stack_depth"] - below_frac = model_config.get("focus_below_fraction", 0.5) - slices_below = int(round(depth * below_frac)) - z_center = int(round(z_focus_mean)) - z_start = max(0, z_center - slices_below) - z_end = min(z_total, z_start + depth) - # Re-adjust start if we hit the ceiling - z_start = max(0, z_end - depth) - - return [z_start, z_end] - - -def _compute_focus(zarr_path: str, focus_params: dict, phase_channel: str) -> float: - """Compute focus_slice metadata and write it to the zarr. - - Returns the dataset-level z_focus_mean. - """ - from iohub import open_ome_zarr - - from viscy.preprocessing.focus import FocusSliceMetric - from viscy.preprocessing.qc_metrics import generate_qc_metadata - - metric = FocusSliceMetric( - NA_det=focus_params["NA_det"], - lambda_ill=focus_params["lambda_ill"], - pixel_size=focus_params["pixel_size"], - channel_names=[phase_channel], - device=focus_params.get("device", "cpu"), - ) - generate_qc_metadata(zarr_path, [metric]) - - plate = open_ome_zarr(zarr_path, mode="r") - z_focus_mean = plate.zattrs["focus_slice"][phase_channel]["dataset_statistics"][ - "z_focus_mean" - ] - plate.close() - return z_focus_mean - - -# --------------------------------------------------------------------------- -# Config generation -# --------------------------------------------------------------------------- - - -def model_dim_key(model_config: dict) -> str: - """Return '2d' or '3d' based on model template.""" - return "2d" if model_config["in_stack_depth"] == 1 else "3d" - - -def generate_yaml( - dataset_name: str, - data_path: Path, - tracks_path: Path, - model_config: dict, - channel_type: str, - channel_name: str, - z_range: list[int], - ckpt_path: str, - output_dir: Path, - version: str, -) -> str: - """Generate a prediction YAML config string. - - Uses YAML anchors to match the existing config style. - """ - dim = model_dim_key(model_config) - ch_cfg = CHANNEL_DEFAULTS[channel_type] - patch = model_config["patch_size"] - depth = model_config["in_stack_depth"] - epoch = extract_epoch(ckpt_path) - yaml_alias = ch_cfg["yaml_alias"] - - output_zarr = output_dir / f"timeaware_{channel_type}_{patch}patch_{epoch}ckpt.zarr" - - # Build normalization block - norm_class = ch_cfg["normalization_class"] - norm_args = dict(ch_cfg["normalization_args"]) - - # Format normalization init_args as YAML lines - norm_lines = [f" keys: [*{yaml_alias}]"] - for k, v in norm_args.items(): - norm_lines.append(f" {k}: {v}") - norm_block = "\n".join(norm_lines) - - logger_base = model_config["logger_base"] - model_name = model_config["name"] - logger_save_dir = f"{logger_base}/{dataset_name}" - logger_name = f"{model_name}/{version}/{channel_type}" - - yaml_str = f"""\ -seed_everything: 42 -trainer: - accelerator: gpu - strategy: auto - devices: auto - num_nodes: 1 - precision: 32-true - callbacks: - - class_path: viscy.representation.embedding_writer.EmbeddingWriter - init_args: - output_path: "{output_zarr}" - logger: - save_dir: "{logger_save_dir}" - name: "{logger_name}" - inference_mode: true -model: - class_path: viscy.representation.engine.ContrastiveModule - init_args: - encoder: - class_path: viscy.representation.contrastive.ContrastiveEncoder - init_args: - backbone: convnext_tiny - in_channels: 1 - in_stack_depth: {depth} - stem_kernel_size: {model_config["stem_kernel_size"]} - stem_stride: {model_config["stem_stride"]} - embedding_dim: 768 - projection_dim: 32 - drop_path_rate: 0.0 - example_input_array_shape: [1, 1, {depth}, {patch}, {patch}] -data: - class_path: viscy.data.triplet.TripletDataModule - init_args: - data_path: {data_path} - tracks_path: {tracks_path} - source_channel: - - &{yaml_alias} {channel_name} - z_range: {z_range} - batch_size: {ch_cfg["batch_size"][dim]} - num_workers: {ch_cfg["num_workers"][dim]} - initial_yx_patch_size: [{patch}, {patch}] - final_yx_patch_size: [{patch}, {patch}] - normalizations: - - class_path: {norm_class} - init_args: -{norm_block} -return_predictions: false -ckpt_path: {ckpt_path} -""" - return yaml_str - - -def generate_slurm_script( - channel_type: str, - output_dir: Path, - suffix: str = "", -) -> str: - """Generate a SLURM submission shell script.""" - config_file = output_dir / f"predict_{channel_type}{suffix}.yml" - slurm_out = output_dir / "slurm_out" / "pred_%j.out" - - return f"""\ -#!/bin/bash - -#SBATCH --job-name=dynaclr_pred -#SBATCH --nodes=1 -#SBATCH --ntasks-per-node=1 -#SBATCH --gres=gpu:1 -#SBATCH --partition=gpu -#SBATCH --cpus-per-task=32 -#SBATCH --mem-per-cpu=8G -#SBATCH --time=0-02:00:00 -#SBATCH --output={slurm_out} - -module load anaconda/latest -conda activate viscy - -cat {config_file} -srun viscy predict -c {config_file} -""" - - -# --------------------------------------------------------------------------- -# Dataset discovery -# --------------------------------------------------------------------------- - - -def discover_predictions( - embeddings_dir: Path, - model_name: str, - version: str, -) -> dict[str, Path]: - """Find datasets that have a predictions folder for the given model/version. - - Searches for paths matching: - {embeddings_dir}/{dataset}/*phenotyping*/*prediction*/{model_glob}/{version}/ - - Parameters - ---------- - embeddings_dir : Path - Base directory containing dataset folders. - model_name : str - Model directory name (supports glob patterns). - version : str - Version subdirectory (e.g. "v3"). - - Returns - ------- - dict[str, Path] - Mapping of dataset_name -> resolved predictions version directory. - """ - pattern = str( - embeddings_dir / "*" / "*phenotyping*" / "*prediction*" / model_name / version - ) - matches = natsorted(glob(pattern)) - - results = {} - for match in matches: - match_path = Path(match) - dataset_name = match_path.relative_to(embeddings_dir).parts[0] - results[dataset_name] = match_path - - return results - - -def find_channel_zarrs( - predictions_dir: Path, - channels: list[str] | None = None, -) -> dict[str, Path]: - """Find embedding zarr files for each channel in a predictions directory. - - Parameters - ---------- - predictions_dir : Path - Path to the version directory containing zarr files. - channels : list[str] or None - Channel names to search for. Defaults to CHANNELS. - - Returns - ------- - dict[str, Path] - Mapping of channel_name -> zarr path (only channels with a match). - """ - if channels is None: - channels = CHANNELS - channel_zarrs = {} - for channel in channels: - matches = natsorted(glob(str(predictions_dir / f"*{channel}*.zarr"))) - if matches: - channel_zarrs[channel] = Path(matches[0]) - return channel_zarrs - - -def find_annotation_csv(annotations_dir: Path, dataset_name: str) -> Path | None: - """Find the annotation CSV for a dataset. - - Parameters - ---------- - annotations_dir : Path - Base annotations directory. - dataset_name : str - Dataset folder name. - - Returns - ------- - Path or None - Path to CSV if found, None otherwise. - """ - dataset_dir = annotations_dir / dataset_name - if not dataset_dir.is_dir(): - return None - csvs = natsorted(glob(str(dataset_dir / "*.csv"))) - return Path(csvs[0]) if csvs else None - - -def get_available_tasks(csv_path: Path) -> list[str]: - """Read CSV header and return which valid task columns are present. - - Parameters - ---------- - csv_path : Path - Path to annotation CSV. - - Returns - ------- - list[str] - Task names found in the CSV columns. - """ - columns = pd.read_csv(csv_path, nrows=0).columns.tolist() - return [t for t in TASKS if t in columns] - - -def build_registry( - embeddings_dir: Path, - annotations_dir: Path, - model_name: str, - version: str, -) -> tuple[list[dict], list[dict], list[str], list[str]]: - """Build a registry of datasets with predictions and annotations. - - Parameters - ---------- - embeddings_dir : Path - Base directory containing dataset folders with embeddings. - annotations_dir : Path - Base directory containing dataset annotation folders. - model_name : str - Model directory name (supports glob patterns). - version : str - Version subdirectory (e.g. "v3"). - - Returns - ------- - registry : list[dict] - Datasets with both predictions and annotations. - skipped : list[dict] - Datasets with predictions but missing annotations or tasks. - annotations_only : list[str] - Annotation datasets with no matching predictions. - predictions_only : list[str] - Prediction datasets with no matching annotations. - """ - predictions = discover_predictions(embeddings_dir, model_name, version) - - registry: list[dict] = [] - skipped: list[dict] = [] - - for dataset_name, pred_dir in predictions.items(): - channel_zarrs = find_channel_zarrs(pred_dir) - csv_path = find_annotation_csv(annotations_dir, dataset_name) - - if not csv_path: - skipped.append({"dataset": dataset_name, "reason": "No annotation CSV"}) - continue - if not channel_zarrs: - skipped.append({"dataset": dataset_name, "reason": "No channel zarrs"}) - continue - - available_tasks = get_available_tasks(csv_path) - if not available_tasks: - skipped.append( - {"dataset": dataset_name, "reason": "No valid task columns in CSV"} - ) - continue - - registry.append( - { - "dataset": dataset_name, - "predictions_dir": pred_dir, - "channel_zarrs": channel_zarrs, - "annotations_csv": csv_path, - "available_tasks": available_tasks, - } - ) - - annotation_datasets = set(d.name for d in annotations_dir.iterdir() if d.is_dir()) - prediction_datasets = set(predictions.keys()) - - annotations_only = natsorted(annotation_datasets - prediction_datasets) - predictions_only = natsorted(prediction_datasets - annotation_datasets) - - return registry, skipped, annotations_only, predictions_only - - -def print_registry_summary( - registry: list[dict], - skipped: list[dict], - annotations_only: list[str], - predictions_only: list[str], -): - """Print a markdown summary of the dataset registry and gaps.""" - print("## Dataset Registry\n") - print("| Dataset | Annotations | Channels | Tasks |") - print("|---------|-------------|----------|-------|") - for entry in registry: - channels_str = ", ".join(sorted(entry["channel_zarrs"].keys())) - tasks_str = ", ".join(entry["available_tasks"]) - print( - f"| {entry['dataset']} | {entry['annotations_csv'].name} " - f"| {channels_str} | {tasks_str} |" - ) - - if annotations_only or predictions_only or skipped: - print("\n## Gaps\n") - print("| Dataset | Status |") - print("|---------|--------|") - for d in annotations_only: - print(f"| {d} | Annotations only (missing predictions) |") - for d in predictions_only: - print(f"| {d} | Predictions only (missing annotations) |") - for s in skipped: - print(f"| {s['dataset']} | {s['reason']} |") - - -# %% diff --git a/applications/airtable/README.md b/applications/airtable/README.md new file mode 100644 index 000000000..dce0a34a5 --- /dev/null +++ b/applications/airtable/README.md @@ -0,0 +1,313 @@ +# Airtable Utils + +Interface to the **Computational Imaging Database** on Airtable, with utilities for syncing experiment metadata between Airtable and OME-Zarr datasets. + +Part of the [VisCy](https://github.com/mehta-lab/VisCy) monorepo. + +--- + +## Setup + +### Installation + +```bash +# From the VisCy monorepo root +uv sync --all-packages --all-extras +``` + +### Environment Variables + +```bash +export AIRTABLE_API_KEY=patXXXXXXXXXXXXXX # Personal access token +export AIRTABLE_BASE_ID=app8vqaoWyOwa0sB5 # Computational Imaging Database +``` + +Add to your `.bashrc` or a `.env` file (gitignored). + +--- + +## Airtable Tables + +| Table | ID | Purpose | +|---|---|---| +| **Datasets** | `tblaFzrDMlVZHPZIj` | One record per FOV. Biologists fill well-level metadata; the registration script expands to per-FOV. | +| **Marker Registry** | `tblmP8l2GmpCeERyD` | LUT mapping each construct to its fluorescent channel aliases and biology. Used to auto-derive `channel_*_biology` at registration time. | + +### Marker Registry + +Each row maps one construct (one fluorophore) to a biology label: + +| Field | Example | Notes | +|---|---|---| +| `marker-fluorophore` | `TOMM20-GFP` | `PROTEIN-FLUOROPHORE` or `PLASMID-FLUOROPHORE`, dashes only | +| `channel_name_aliases` | `GFP, FITC` | Comma-separated substrings to substring-match against zarr channel names | +| `biology` | `mitochondria` | snake_case biological annotation | + +One row per construct. Compound lines (e.g. `TOMM20-GFP pAL40`) are represented as two linked entries: `TOMM20-GFP` (GFP→mitochondria) + `pAL40-mCherry` (mCherry→viral_sensor). + +### Datasets Schema + +Key fields (snake_case): + +| Field | Type | Source | +|---|---|---| +| `dataset` | text | Must match zarr stem | +| `well_id` | text | e.g. `B/1` | +| `fov` | text | e.g. `000000` — set by `register` | +| `cell_type` | select | e.g. `A549` | +| `cell_line` | linked records | Links to Marker Registry | +| `marker` | select | Primary protein marker | +| `organelle` | select | Target organelle | +| `perturbation` | select | e.g. `ZIKV`, `DENV` | +| `channel_N_name` | text | Zarr channel label — set by `register` (N = 0–7) | +| `channel_N_marker` | text | Protein marker per channel — derived from Marker Registry (N = 0–7) | +| `data_path` | text | Path to zarr position — set by `register` | +| `t/c/z/y/x_shape` | number | Array dimensions — set by `register` | + +--- + +## Workflow + +### Step 1 — Biologist: fill well-level platemap in Airtable + +Before the zarr exists, create one Datasets record per well with: +- `dataset`, `well_id`, `cell_type`, `cell_line` (linked to Marker Registry), `marker`, `organelle`, `perturbation`, `moi`, `time_interval_min`, `fluorescence_modality` + +Leave all zarr-derived fields empty — they are filled by `register`. + +> **New construct?** Add it to the Marker Registry first (with aliases + biology), then link it in the Datasets record. + +### Step 2 — Engineer: register zarr positions after QC + +```bash +# Dry run — see what would be created/updated +uv run --package airtable-utils \ + applications/airtable/scripts/write_experiment_metadata.py \ + register --dry-run /path/to/dataset.zarr/*/*/* + +# Run for real +uv run --package airtable-utils \ + applications/airtable/scripts/write_experiment_metadata.py \ + register /path/to/dataset.zarr/*/*/* +``` + +The atomic unit is a **position path** (e.g. `dataset.zarr/A/1/000000`). Shell globbing handles batch registration. For a single position: + +```bash +uv run --package airtable-utils \ + applications/airtable/scripts/write_experiment_metadata.py \ + register /path/to/dataset.zarr/A/1/000000 +``` + +What `register` does per position: +- Reads channel names (up to 8) and array shape from the zarr +- Fetches Marker Registry once per run +- Resolves `cell_line` linked records → aliases → derives `channel_*_biology` via substring match +- Creates new per-FOV record or updates existing one + +### Step 3 — Engineer: write metadata to zarr `.zattrs` + +```bash +uv run --package airtable-utils \ + applications/airtable/scripts/write_experiment_metadata.py \ + write /path/to/dataset.zarr/*/*/* +``` + +Writes `channels_metadata` and `experiment_metadata` to each position's `.zattrs`. + +--- + +## Dataset Preparation (NFS -> VAST) + +The `prepare` CLI automates the full pipeline to rechunk and preprocess a dataset for model training on VAST storage: + +1. Validate dataset is registered in Airtable +2. Discover positions and channels from the NFS zarr +3. Generate `crop_concat.yml` for `biahub concatenate` (zarr v3 with sharding) +4. Generate `qc_config.yml` for focus-slice QC +5. Generate and submit SLURM jobs (concatenation + tracking copy, then QC + preprocessing) + +### Usage + +```bash +# Check what exists on NFS/VAST for one or more datasets +uv run --package airtable-utils \ + prepare status 2025_01_22_A549_G3BP1_ZIKV_DENV -c applications/airtable/configs/prepare_config.yml + +# Run full pipeline (generate configs + submit SLURM jobs) +uv run --package airtable-utils \ + prepare run 2025_01_22_A549_G3BP1_ZIKV_DENV -c applications/airtable/configs/prepare_config.yml + +# Dry run (generate configs only, no SLURM submission) +uv run --package airtable-utils \ + prepare run 2025_01_22_A549_G3BP1_ZIKV_DENV -c applications/airtable/configs/prepare_config.yml --dry-run + +# Force overwrite an existing zarr v2 store +uv run --package airtable-utils \ + prepare run 2025_01_22_A549_G3BP1_ZIKV_DENV -c applications/airtable/configs/prepare_config.yml --force +``` + +### Output Layout + +``` +/hpc/projects/organelle_phenotyping/datasets/{dataset_name}/ + {dataset_name}.zarr # zarr v3 rechunked (OME-Zarr 0.5) + tracking.zarr # copied from NFS + crop_concat.yml # generated config for biahub + qc_config.yml # generated config for QC + 01_concatenate.sh # bash: biahub concatenate (submits SLURM via submitit) + tracking copy + 02_qc_preprocess.sh # SLURM: QC + preprocess (parallel, GPU) +``` + +### Config File + +The reference config is at `configs/prepare_config.yml`. Key settings: + +| Section | Key fields | Defaults | +|---|---|---| +| `concatenate` | `chunks_czyx`, `shards_ratio`, `conda_env` | `[1,16,256,256]`, `[1,1,8,8,8]`, `biahub` | +| `qc` | `channel_names`, `NA_det`, `pixel_size`, `device` | `[Phase3D]`, `1.35`, `0.1494`, `cuda` | +| `preprocess` | `channel_names`, `num_workers`, `block_size` | `-1` (all), `48`, `32` | +| `slurm.qc_preprocess` | `partition`, `gres`, `constraint` | `gpu`, `gpu:1`, `a100\|a40\|a6000` | + +> `biahub concatenate` manages its own SLURM jobs via submitit — no SLURM config needed for that stage. + +### Version Validation + +The `status` command reports zarr format version (v2 vs v3) and OME-Zarr version for existing VAST stores. The `run` command: +- **Skips** if the VAST zarr already exists and is zarr v3 + OME 0.5 + preprocessed +- **Requires `--force`** to overwrite an existing zarr v2 store + +--- + +## Using with Claude Code (AI-assisted workflows) + +This application ships with a **Claude Code skill** that lets you run registration tasks conversationally without flooding the context with large Airtable API responses. + +### Setup + +1. Install the skill (one-time, already done for `eduardo.hirata`): + +```bash +# The skill lives at: +~/.claude/skills/airtable-register/SKILL.md +``` + +2. Open Claude Code in the VisCy repo: + +```bash +cd /hpc/mydata/eduardo.hirata/repos/viscy +claude +``` + +### Usage + +Claude will automatically invoke the skill when you ask things like: + +``` +register 2025_07_24_A549_SEC61_TOMM20_G3BP1_ZIKV +``` +``` +re-register all datasets in Airtable +``` +``` +add LAMP2-GFP to the Marker Registry with biology lysosome +``` +``` +check which positions are missing channel biology +``` + +The skill runs registration as a subagent, keeping Airtable MCP responses out of the main conversation context. + +### Installing the skill for a new user + +Copy the skill to your Claude config: + +```bash +cp -r /hpc/mydata/eduardo.hirata/repos/viscy/applications/airtable/.claude/skills/airtable-register \ + ~/.claude/skills/ +``` + +> Note: The skill is not checked into the repo (it lives in `~/.claude/skills/`) because it contains HPC-specific paths. Copy and adapt as needed. + +--- + +## Python API + +```python +from airtable_utils import AirtableDatasets, DatasetRecord, parse_channel_name +from airtable_utils.registration import register_fovs, parse_position_path + +db = AirtableDatasets() + +# Get all FOV records for a dataset +records = db.get_dataset_records("2024_10_16_A549_SEC61_ZIKV_DENV") + +# Get Marker Registry (keyed by record ID) +registry = db.get_marker_registry() + +# Register positions programmatically +from pathlib import Path +positions = list(Path("/path/to/dataset.zarr").glob("*/*/*")) +result = register_fovs(positions, db=db) +print(f"created={len(result.created)} updated={len(result.updated)}") +if result.updated: + db.batch_update(result.updated) +if result.created: + db.batch_create(result.created) + +# Parse channel names from zarr labels +parse_channel_name("Phase3D") +# {'channel_type': 'labelfree'} +parse_channel_name("raw GFP EX488 EM525-45") +# {'channel_type': 'fluorescence', 'filter_cube': 'GFP', 'excitation_nm': 488, 'emission_nm': 525} +``` + +--- + +## `.zattrs` Schema + +Written to each position by the `write` subcommand: + +**`channels_metadata`** — keyed by channel name: +```json +{ + "Phase3D": {"channel_type": "labelfree", "biological_annotation": null}, + "raw GFP EX488 EM525-45": { + "channel_type": "fluorescence", + "biological_annotation": { + "organelle": "mitochondria", + "marker": "TOMM20", + "marker_type": "protein_tag", + "fluorophore": null + } + }, + "raw mCherry EX561 EM600-37": { + "channel_type": "fluorescence", + "biological_annotation": { + "organelle": "viral_sensor", + "marker": "unknown", + "marker_type": "protein_tag", + "fluorophore": null + } + } +} +``` + +**`experiment_metadata`**: +```json +{ + "perturbations": [{"name": "ZIKV", "type": "virus", "hours_post": 4.0, "moi": 5.0}], + "time_sampling_minutes": 30.0 +} +``` + +--- + +## Testing + +```bash +uv run pytest applications/airtable/ -v +``` + +Tests use mocked Airtable and zarr — no credentials or network required. diff --git a/applications/airtable/configs/prepare_config.yml b/applications/airtable/configs/prepare_config.yml new file mode 100644 index 000000000..da9eb5f7b --- /dev/null +++ b/applications/airtable/configs/prepare_config.yml @@ -0,0 +1,47 @@ +# Dataset preparation pipeline: NFS -> VAST rechunked zarr v3 +# Usage: prepare run -c prepare_config.yml [--dry-run] + +nfs_root: /hpc/projects/intracellular_dashboard/organelle_dynamics +vast_root: /hpc/projects/organelle_phenotyping/datasets +workspace_dir: /hpc/mydata/eduardo.hirata/repos/viscy + +concatenate: + # null = auto-detect raw channels (Phase3D + raw *). Set explicitly to override. + channel_names: null + chunks_czyx: [1, 16, 256, 256] + shards_ratio: [1, 1, 8, 8, 8] + output_ome_zarr_version: "0.5" + conda_env: biahub + # Override biahub's internal SLURM settings (passed via -sb flag) + # Set to null to use biahub defaults + sbatch_overrides: + partition: cpu + +qc: + channel_names: [Phase3D] + NA_det: 1.35 + lambda_ill: 0.450 + pixel_size: 0.1494 + midband_fractions: [0.125, 0.25] + device: cuda + num_workers: 16 + +preprocess: + channel_names: -1 + num_workers: 32 + block_size: 32 + +# biahub concatenate submits its own SLURM jobs via submitit (no config needed) +# QC and preprocess run as separate SLURM jobs (no race condition) +slurm: + qc: + partition: gpu + gres: "gpu:1" + cpus_per_task: 16 + mem_per_cpu: 4G + time: "00:30:00" + preprocess: + partition: cpu + cpus_per_task: 32 + mem_per_cpu: 4G + time: "04:00:00" diff --git a/applications/airtable/pyproject.toml b/applications/airtable/pyproject.toml new file mode 100644 index 000000000..4504690f7 --- /dev/null +++ b/applications/airtable/pyproject.toml @@ -0,0 +1,51 @@ +[build-system] +build-backend = "hatchling.build" +requires = [ "hatchling", "uv-dynamic-versioning" ] + +[project] +name = "airtable-utils" +description = "Interface to the Computational Imaging Airtable database" +keywords = [ "airtable", "metadata", "microscopy", "zarr" ] +license = "BSD-3-Clause" +authors = [ { name = "Biohub", email = "compmicro@czbiohub.org" } ] +requires-python = ">=3.12" +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Topic :: Scientific/Engineering :: Image Processing", +] +dynamic = [ "version" ] +dependencies = [ + "click", + "iohub>=0.3.6", + "pandas", + "pyairtable", + "pydantic", + "pyyaml", + "viscy-data", +] + +optional-dependencies.dev = [ "pytest" ] +urls.Homepage = "https://github.com/mehta-lab/VisCy" +urls.Issues = "https://github.com/mehta-lab/VisCy/issues" +urls.Repository = "https://github.com/mehta-lab/VisCy" + +scripts.prepare = "airtable_utils.prepare_cli:main" + +[tool.hatch.version] +source = "uv-dynamic-versioning" + +[tool.hatch.build.targets.wheel] +packages = [ "src/airtable_utils" ] + +[tool.uv-dynamic-versioning] +vcs = "git" +style = "pep440" +pattern-prefix = "airtable-utils-" +fallback-version = "0.0.0" diff --git a/applications/airtable/scripts/migrate_channel_markers.py b/applications/airtable/scripts/migrate_channel_markers.py new file mode 100644 index 000000000..e88fdaf18 --- /dev/null +++ b/applications/airtable/scripts/migrate_channel_markers.py @@ -0,0 +1,227 @@ +"""Migrate channel_N_marker values from organelle names to protein markers. + +Reads all Datasets records that have cell_line links, resolves each +cell_line record ID against the Marker Registry (which has the +canonical ``marker`` protein name and ``channel_name_aliases``), and +updates ``channel_N_marker`` fields in the Datasets table. + +Logic per channel slot (N=0..7): + +- If ``channel_N_name`` exists: use ``parse_channel_name`` to classify. + - labelfree -> set marker = channel_N_name + - virtual_stain -> set marker = channel_N_name + - fluorescence -> match against cell_line aliases -> set marker from registry +- If ``channel_N_name`` is absent but ``channel_N_marker`` exists: + the old marker is an organelle name. Use the FOV's cell_line link to + look up the registry ``marker`` for the first linked construct. Only + update fluorescence-like slots (skip slots whose old marker is + "brightfield", "labelfree", or starts with "virtual-stain"). + +Usage +----- + uv run --package airtable-utils \ + applications/airtable/scripts/migrate_channel_markers.py --dry-run + + uv run --package airtable-utils \ + applications/airtable/scripts/migrate_channel_markers.py +""" + +from __future__ import annotations + +import argparse +import logging +import os + +from pyairtable import Api + +from viscy_data.channel_utils import parse_channel_name + +logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s") +logger = logging.getLogger(__name__) + +REGISTRY_TABLE_ID = "tblmP8l2GmpCeERyD" +DATASETS_TABLE_ID = "tblaFzrDMlVZHPZIj" +MAX_CHANNELS = 8 + +LABELFREE_MARKERS = frozenset({"brightfield", "labelfree"}) + + +def _is_labelfree_or_virtual(marker_value: str) -> bool: + """Return True if the old marker value is labelfree or virtual-stain.""" + lower = marker_value.lower() + return lower in LABELFREE_MARKERS or lower.startswith("virtual-stain") or lower == "nucleus" + + +def _match_alias(channel_name: str, aliases: list[str]) -> bool: + """Return True if channel_name (lowercased) contains any alias (lowercased).""" + name_lower = channel_name.lower() + return any(alias.lower() in name_lower for alias in aliases) + + +def main(dry_run: bool = False, limit: int = 0) -> None: + """Run the migration. + + Parameters + ---------- + dry_run : bool + If True, print changes without writing. + limit : int + Max number of changes to print in dry-run mode (0 = all). + """ + api_key = os.environ["AIRTABLE_API_KEY"] + base_id = os.environ["AIRTABLE_BASE_ID"] + api = Api(api_key) + + registry_table = api.table(base_id, REGISTRY_TABLE_ID) + datasets_table = api.table(base_id, DATASETS_TABLE_ID) + + # Build Marker Registry lookup: record_id -> {marker_fluorophore, aliases, marker} + logger.info("Fetching Marker Registry...") + registry_raw = registry_table.all(fields=["marker-fluorophore", "channel_name_aliases", "marker"]) + registry: dict[str, dict] = {} + for rec in registry_raw: + fields = rec.get("fields", {}) + marker_fluor = fields.get("marker-fluorophore", "") + aliases_raw = fields.get("channel_name_aliases", "") + aliases = [a.strip() for a in aliases_raw.split(",") if a.strip()] + marker = fields.get("marker", "") + if marker_fluor and marker: + registry[rec["id"]] = { + "marker_fluorophore": marker_fluor, + "aliases": aliases, + "marker": marker, + } + logger.info("Registry has %d entries with marker values", len(registry)) + + # Fetch all Datasets fields we need + channel_fields = [] + for i in range(MAX_CHANNELS): + channel_fields.extend([f"channel_{i}_name", f"channel_{i}_marker"]) + fetch_fields = ["cell_line", "dataset", "well_id", "fov"] + channel_fields + + logger.info("Fetching Datasets records...") + raw_records = datasets_table.all(fields=fetch_fields) + logger.info("Fetched %d records", len(raw_records)) + + updates: list[dict] = [] + no_cell_line = 0 + no_change = 0 + unmatched_channels: list[str] = [] + + for rec in raw_records: + fields = rec["fields"] + cell_line_ids = fields.get("cell_line", []) + if not cell_line_ids: + no_cell_line += 1 + continue + + # Resolve cell_line IDs to registry entries + entries = [registry[rid] for rid in cell_line_ids if rid in registry] + if not entries: + continue + + new_fields: dict[str, str] = {} + + for i in range(MAX_CHANNELS): + ch_name = fields.get(f"channel_{i}_name") + old_marker = fields.get(f"channel_{i}_marker") + + if ch_name is not None: + # Have channel name: use parse_channel_name + parsed = parse_channel_name(ch_name) + ch_type = parsed.get("channel_type", "unknown") + + if ch_type == "labelfree": + new_marker = ch_name + elif ch_type == "virtual_stain": + new_marker = ch_name + elif ch_type == "fluorescence" or ch_type == "unknown": + # Match against registry aliases + matched = False + for entry in entries: + if _match_alias(ch_name, entry["aliases"]): + new_marker = entry["marker"] + matched = True + break + if not matched: + fov_id = f"{fields.get('dataset', '?')}_{fields.get('well_id', '?')}_{fields.get('fov', '')}" + unmatched_channels.append(f"{fov_id} ch{i}={ch_name}") + continue + else: + continue + + if old_marker != new_marker: + new_fields[f"channel_{i}_marker"] = new_marker + + elif old_marker is not None: + # No channel name but have old marker (organelle name). + # Skip labelfree/virtual-stain markers. + if _is_labelfree_or_virtual(old_marker): + continue + # For fluorescence slots: use the first cell_line entry's marker + # (most FOVs have a single construct) + new_marker = entries[0]["marker"] + if old_marker != new_marker: + new_fields[f"channel_{i}_marker"] = new_marker + + if new_fields: + updates.append({"id": rec["id"], "fields": new_fields}) + else: + no_change += 1 + + logger.info( + "Records to update: %d | no cell_line: %d | no change needed: %d | unmatched channels: %d", + len(updates), + no_cell_line, + no_change, + len(unmatched_channels), + ) + + if dry_run: + show = updates[:limit] if limit > 0 else updates + print(f"\n## Dry Run: {len(updates)} records to update (showing {len(show)})\n") + print("| record_id | field | old | new |") + print("|-----------|-------|-----|-----|") + for upd in show: + rec_id = upd["id"] + # Look up old values from original records + original = next(r for r in raw_records if r["id"] == rec_id) + for field_name, new_val in upd["fields"].items(): + old_val = original["fields"].get(field_name, "(empty)") + print(f"| {rec_id} | {field_name} | {old_val} | {new_val} |") + if limit > 0 and len(updates) > limit: + print(f"\n... and {len(updates) - limit} more records") + + if unmatched_channels: + print(f"\n## Unmatched fluorescence channels ({len(unmatched_channels)})\n") + for entry in unmatched_channels[:20]: + print(f"- `{entry}`") + if len(unmatched_channels) > 20: + print(f"- ... and {len(unmatched_channels) - 20} more") + return + + # Batch update in chunks of 10 (Airtable API limit) + for i in range(0, len(updates), 10): + batch = updates[i : i + 10] + datasets_table.batch_update(batch) + logger.info("Updated records %d-%d of %d", i + 1, i + len(batch), len(updates)) + + logger.info("Done. Updated %d records.", len(updates)) + + if unmatched_channels: + print(f"\n## Unmatched fluorescence channels ({len(unmatched_channels)})\n") + for entry in unmatched_channels[:20]: + print(f"- `{entry}`") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Migrate channel_N_marker from organelle names to protein markers") + parser.add_argument("--dry-run", action="store_true", help="Print changes without writing to Airtable") + parser.add_argument( + "--limit", + type=int, + default=0, + help="Max number of changes to show in dry-run mode (0 = all)", + ) + args = parser.parse_args() + main(dry_run=args.dry_run, limit=args.limit) diff --git a/applications/airtable/scripts/write_experiment_metadata.py b/applications/airtable/scripts/write_experiment_metadata.py new file mode 100644 index 000000000..6b0ce2853 --- /dev/null +++ b/applications/airtable/scripts/write_experiment_metadata.py @@ -0,0 +1,225 @@ +"""Manage experiment metadata between Airtable and OME-Zarr datasets. + +Two subcommands: + + register — expand well-level Airtable records to per-FOV records + using zarr position data (zarr -> Airtable) + write — write experiment_metadata to zarr .zattrs from Airtable + per-FOV records (Airtable -> zarr) + +Both operate at the position level. Use shell globbing for batch:: + + uv run --package airtable-utils \ + applications/airtable/scripts/write_experiment_metadata.py \ + register /path/to/dataset.zarr/A/1/000000 # single position + + uv run --package airtable-utils \ + applications/airtable/scripts/write_experiment_metadata.py \ + register /path/to/dataset.zarr/*/*/* # all positions + + uv run --package airtable-utils \ + applications/airtable/scripts/write_experiment_metadata.py \ + write /path/to/dataset.zarr/*/*/* # write zattrs +""" + +from __future__ import annotations + +import argparse +import logging +from pathlib import Path + +from iohub import open_ome_zarr + +from airtable_utils.database import AirtableDatasets +from airtable_utils.registration import ( + build_completeness_report, + build_validation_table, + format_register_summary, + parse_position_path, + register_fovs, +) +from airtable_utils.schemas import MAX_CHANNELS, DatasetRecord, parse_position_name + +logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s") +logger = logging.getLogger(__name__) + + +# --------------------------------------------------------------------------- +# register: zarr -> Airtable (well records -> per-FOV records) +# --------------------------------------------------------------------------- + + +def register(position_paths: list[Path], dry_run: bool = False, dataset: str | None = None) -> None: + """Register zarr positions as per-FOV records in Airtable.""" + db = AirtableDatasets() + result = register_fovs(position_paths, db=db, dataset_name=dataset) + + logger.info( + "FOVs to create: %d | existing to update: %d | unmatched: %d", + len(result.created), + len(result.updated), + len(result.unmatched), + ) + + if not dry_run: + if result.created: + db.batch_create(result.created) + logger.info("Created %d per-FOV records in Airtable", len(result.created)) + if result.updated: + db.batch_update(result.updated) + logger.info("Updated %d existing records", len(result.updated)) + if result.template_ids_to_delete: + db.batch_delete(result.template_ids_to_delete) + logger.info("Deleted %d well template records", len(result.template_ids_to_delete)) + + print(format_register_summary(result, dry_run=dry_run)) + + all_records = db.get_dataset_records(result.dataset) + validation = build_validation_table(result.dataset, result.channel_names, all_records) + print(f"## Channel Validation — {result.dataset}\n") + print(validation) + print() + + fov_records = [r for r in all_records if r.fov] + completeness = build_completeness_report(result.dataset, fov_records) + print(completeness) + + +# --------------------------------------------------------------------------- +# write: Airtable -> zarr (per-FOV records -> .zattrs) +# --------------------------------------------------------------------------- + + +def write(position_paths: list[Path], dry_run: bool = False) -> None: + """Write experiment_metadata from per-FOV Airtable records to zarr.""" + zarr_root, first_pos = parse_position_path(position_paths[0]) + dataset_name = zarr_root.stem + + pos_names: list[str] = [first_pos] + for p in position_paths[1:]: + root, pos = parse_position_path(p) + if root != zarr_root: + raise ValueError(f"All positions must belong to the same zarr store. Got {zarr_root} and {root}.") + pos_names.append(pos) + + logger.info( + "Writing experiment metadata for %d positions in %s", + len(pos_names), + dataset_name, + ) + + db = AirtableDatasets() + all_records = db.get_dataset_records(dataset_name) + + fov_records = [r for r in all_records if r.fov] + if not fov_records: + raise ValueError( + f"No per-FOV records for dataset '{dataset_name}'. Run 'register' first to expand well records." + ) + + record_lookup: dict[tuple[str, str], DatasetRecord] = {} + for rec in fov_records: + record_lookup[(rec.well_id, rec.fov)] = rec + + logger.info("Found %d per-FOV records in Airtable", len(fov_records)) + + fov_count = 0 + with open_ome_zarr(str(zarr_root), mode="r+" if not dry_run else "r") as plate: + channel_names = plate.channel_names + + for pos_name in pos_names: + well_path, fov = parse_position_name(pos_name) + + rec = record_lookup.get((well_path, fov)) + if rec is None: + logger.warning( + "No Airtable record for %s (well=%s, fov=%s), skipping", + pos_name, + well_path, + fov, + ) + continue + + for i, ch_name in enumerate(channel_names[:MAX_CHANNELS]): + setattr(rec, f"channel_{i}_name", ch_name) + + channels_metadata = rec.to_channels_metadata() + experiment_metadata = rec.to_experiment_metadata() + + if dry_run: + logger.info( + "[DRY RUN] %s\n channels_metadata: %s\n experiment_metadata: %s", + pos_name, + channels_metadata, + experiment_metadata, + ) + else: + pos = plate[pos_name] + pos.zattrs["channels_metadata"] = channels_metadata + pos.zattrs["experiment_metadata"] = experiment_metadata + fov_count += 1 + + # Write plate-level channels_metadata + if not dry_run and fov_records: + first_rec = fov_records[0] + for i, ch_name in enumerate(channel_names[:MAX_CHANNELS]): + setattr(first_rec, f"channel_{i}_name", ch_name) + plate.zattrs["channels_metadata"] = first_rec.to_channels_metadata() + + status = "dry_run" if dry_run else "success" + print("\n## Experiment Metadata Write Summary\n") + print("| dataset | zarr_path | num_fovs | status |") + print("|---------|-----------|----------|--------|") + print(f"| {dataset_name} | {zarr_root} | {fov_count} | {status} |") + print() + + +# --------------------------------------------------------------------------- +# CLI +# --------------------------------------------------------------------------- + + +def main(): # noqa: D103 + parser = argparse.ArgumentParser(description="Manage experiment metadata between Airtable and OME-Zarr") + subparsers = parser.add_subparsers(dest="command", required=True) + + reg_parser = subparsers.add_parser( + "register", + help="Register zarr positions as per-FOV Airtable records", + ) + reg_parser.add_argument( + "positions", + type=Path, + nargs="+", + help="Position path(s), e.g. /data/ds.zarr/A/1/000000 or /data/ds.zarr/*/*/*", + ) + reg_parser.add_argument("--dry-run", action="store_true", help="Log what would happen without writing") + reg_parser.add_argument( + "--dataset", + type=str, + default=None, + help="Airtable dataset name override (default: zarr stem). Use when zarr stem doesn't match.", + ) + + write_parser = subparsers.add_parser( + "write", + help="Write experiment_metadata from Airtable per-FOV records to zarr .zattrs", + ) + write_parser.add_argument( + "positions", + type=Path, + nargs="+", + help="Position path(s), e.g. /data/ds.zarr/A/1/000000 or /data/ds.zarr/*/*/*", + ) + write_parser.add_argument("--dry-run", action="store_true", help="Log what would happen without writing") + + args = parser.parse_args() + + if args.command == "register": + register(args.positions, dry_run=args.dry_run, dataset=args.dataset) + elif args.command == "write": + write(args.positions, dry_run=args.dry_run) + + +if __name__ == "__main__": + main() diff --git a/applications/airtable/src/airtable_utils/__init__.py b/applications/airtable/src/airtable_utils/__init__.py new file mode 100644 index 000000000..eb94bc0fc --- /dev/null +++ b/applications/airtable/src/airtable_utils/__init__.py @@ -0,0 +1,23 @@ +"""Interface to the Computational Imaging Airtable database.""" + +from airtable_utils.database import AirtableDatasets +from airtable_utils.schemas import ( + BiologicalAnnotation, + ChannelAnnotationEntry, + DatasetRecord, + Perturbation, + WellExperimentMetadata, + parse_channel_name, + parse_position_name, +) + +__all__ = [ + "AirtableDatasets", + "BiologicalAnnotation", + "ChannelAnnotationEntry", + "DatasetRecord", + "Perturbation", + "WellExperimentMetadata", + "parse_channel_name", + "parse_position_name", +] diff --git a/applications/airtable/src/airtable_utils/database.py b/applications/airtable/src/airtable_utils/database.py new file mode 100644 index 000000000..c1fd19a70 --- /dev/null +++ b/applications/airtable/src/airtable_utils/database.py @@ -0,0 +1,160 @@ +"""Thin interface to the Airtable Datasets table.""" + +from __future__ import annotations + +import os +from dataclasses import dataclass + +import pandas as pd +from pyairtable import Api + +from airtable_utils.schemas import DatasetRecord + +TABLE_NAME = "Datasets" +MARKER_REGISTRY_TABLE_ID = "tblmP8l2GmpCeERyD" + + +@dataclass +class MarkerRegistryEntry: + """A single entry from the Marker Registry. + + Parameters + ---------- + record_id : str + Airtable record ID. + marker_fluorophore : str + Construct name, e.g. ``"TOMM20-GFP"`` or ``"pAL40-mCherry"``. + channel_name_aliases : list[str] + Substring tokens to match against zarr channel names. + marker : str + Protein marker name, e.g. ``"TOMM20"``, ``"SEC61B"``. + """ + + record_id: str + marker_fluorophore: str + channel_name_aliases: list[str] + marker: str + + +class AirtableDatasets: + """Interface to the Datasets table in the Computational Imaging Database. + + Credentials are read exclusively from environment variables: + + - ``AIRTABLE_API_KEY``: Airtable personal access token. + - ``AIRTABLE_BASE_ID``: Airtable base ID. + + Raises + ------ + ValueError + If either environment variable is not set or empty. + """ + + def __init__(self) -> None: + api_key = os.environ.get("AIRTABLE_API_KEY", "") + base_id = os.environ.get("AIRTABLE_BASE_ID", "") + if not api_key: + raise ValueError("AIRTABLE_API_KEY environment variable is required but not set.") + if not base_id: + raise ValueError("AIRTABLE_BASE_ID environment variable is required but not set.") + api = Api(api_key) + self._table = api.table(base_id, TABLE_NAME) + self._registry_table = api.table(base_id, MARKER_REGISTRY_TABLE_ID) + + def list_records(self, filter_formula: str | None = None) -> pd.DataFrame: + """Return all FOV records as a DataFrame. + + Parameters + ---------- + filter_formula : str or None + Airtable formula to filter records. + """ + kwargs = {} + if filter_formula: + kwargs["formula"] = filter_formula + raw = self._table.all(**kwargs) + records = [DatasetRecord.from_airtable_record(r) for r in raw] + return pd.DataFrame([r.model_dump() for r in records]) + + def get_dataset_records(self, dataset_name: str) -> list[DatasetRecord]: + """Return FOV records for a specific dataset. + + Parameters + ---------- + dataset_name : str + Value of the ``dataset`` field to filter on. + """ + formula = f"{{dataset}} = '{dataset_name}'" + raw = self._table.all(formula=formula) + return [DatasetRecord.from_airtable_record(r) for r in raw] + + def get_unique_datasets(self) -> list[str]: + """Return sorted unique dataset names.""" + raw = self._table.all(fields=["dataset"]) + names = {r["fields"]["dataset"] for r in raw if r.get("fields", {}).get("dataset")} + return sorted(names) + + def batch_update(self, updates: list[dict]) -> None: + """Batch-update records. + + Parameters + ---------- + updates : list[dict] + Each dict has ``"id"`` (record ID) and ``"fields"`` keys. + """ + self._table.batch_update(updates) + + def get_marker_registry(self) -> dict[str, MarkerRegistryEntry]: + """Return the Marker Registry as a lookup by record ID. + + Returns + ------- + dict[str, MarkerRegistryEntry] + Mapping of Airtable record ID -> :class:`MarkerRegistryEntry`. + """ + raw = self._registry_table.all(fields=["marker-fluorophore", "channel_name_aliases", "marker"]) + registry: dict[str, MarkerRegistryEntry] = {} + for rec in raw: + fields = rec.get("fields", {}) + marker_fluorophore = fields.get("marker-fluorophore", "") + aliases_raw = fields.get("channel_name_aliases", "") + aliases = [a.strip() for a in aliases_raw.split(",") if a.strip()] + marker = fields.get("marker", "") + if marker_fluorophore and aliases and marker: + registry[rec["id"]] = MarkerRegistryEntry( + record_id=rec["id"], + marker_fluorophore=marker_fluorophore, + channel_name_aliases=aliases, + marker=marker, + ) + return registry + + def batch_create(self, records: list[dict]) -> list[dict]: + """Batch-create new records. + + Parameters + ---------- + records : list[dict] + Each dict has a ``"fields"`` key with field name/value pairs. + + Returns + ------- + list[dict] + Created records as returned by the Airtable API. + """ + return self._table.batch_create([r["fields"] for r in records]) + + def batch_delete(self, record_ids: list[str]) -> list[dict]: + """Batch-delete records by ID. + + Parameters + ---------- + record_ids : list[str] + Airtable record IDs to delete. + + Returns + ------- + list[dict] + Deletion confirmations from the Airtable API. + """ + return self._table.batch_delete(record_ids) diff --git a/applications/airtable/src/airtable_utils/prepare.py b/applications/airtable/src/airtable_utils/prepare.py new file mode 100644 index 000000000..d35333a11 --- /dev/null +++ b/applications/airtable/src/airtable_utils/prepare.py @@ -0,0 +1,672 @@ +"""Config-driven dataset preparation: NFS -> VAST rechunked zarr v3.""" + +from __future__ import annotations + +import json +import logging +from pathlib import Path +from textwrap import dedent + +import yaml +from iohub import open_ome_zarr +from pydantic import BaseModel, Field + +logger = logging.getLogger(__name__) + +# --------------------------------------------------------------------------- +# Pydantic config models +# --------------------------------------------------------------------------- + + +class ConcatenateConfig(BaseModel): + """Parameters for biahub concatenate.""" + + channel_names: list[str] | None = None + chunks_czyx: list[int] = [1, 16, 256, 256] + shards_ratio: list[int] = [1, 1, 8, 8, 8] + output_ome_zarr_version: str = "0.5" + conda_env: str = "biahub" + sbatch_overrides: dict[str, str] | None = None + + +class QCParams(BaseModel): + """Focus-slice QC parameters.""" + + channel_names: list[str] = ["Phase3D"] + NA_det: float = 1.35 + lambda_ill: float = 0.450 + pixel_size: float = 0.1494 + midband_fractions: tuple[float, float] = (0.125, 0.25) + device: str = "cuda" + num_workers: int = 16 + + +class PreprocessParams(BaseModel): + """Normalization preprocessing parameters.""" + + channel_names: int | list[str] = -1 + num_workers: int = 48 + block_size: int = 32 + + +class SlurmStageConfig(BaseModel): + """SLURM resource settings for one job stage.""" + + partition: str + cpus_per_task: int = 24 + mem_per_cpu: str = "4G" + time: str = "06:00:00" + gres: str | None = None + constraint: str | None = None + + +class SlurmConfig(BaseModel): + """SLURM settings for QC and preprocess stages (separate jobs). + + The concatenation stage is not a SLURM job — ``biahub concatenate`` + submits its own SLURM jobs internally via submitit. + """ + + qc: SlurmStageConfig = Field( + default_factory=lambda: SlurmStageConfig( + partition="gpu", + gres="gpu:1", + cpus_per_task=16, + mem_per_cpu="4G", + time="00:30:00", + ) + ) + preprocess: SlurmStageConfig = Field( + default_factory=lambda: SlurmStageConfig( + partition="preempted", + cpus_per_task=16, + mem_per_cpu="4G", + time="04:00:00", + ) + ) + + +class PrepareConfig(BaseModel): + """Top-level prepare pipeline configuration.""" + + nfs_root: Path = Path("/hpc/projects/intracellular_dashboard/organelle_dynamics") + vast_root: Path = Path("/hpc/projects/organelle_phenotyping/datasets") + workspace_dir: Path = Path("/hpc/mydata/eduardo.hirata/repos/viscy") + concatenate: ConcatenateConfig = Field(default_factory=ConcatenateConfig) + qc: QCParams = Field(default_factory=QCParams) + preprocess: PreprocessParams = Field(default_factory=PreprocessParams) + slurm: SlurmConfig = Field(default_factory=SlurmConfig) + + +# --------------------------------------------------------------------------- +# Path resolution +# --------------------------------------------------------------------------- + + +def resolve_nfs_paths(dataset_name: str, nfs_root: Path) -> dict[str, Path]: + """Return NFS zarr and tracking paths for a dataset. + + Parameters + ---------- + dataset_name : str + Dataset identifier, e.g. ``"2025_01_22_A549_G3BP1_ZIKV_DENV"``. + nfs_root : Path + Root of organelle_dynamics on NFS. + + Returns + ------- + dict[str, Path] + Keys: ``zarr``, ``tracking``. + + Raises + ------ + FileNotFoundError + If the assembled zarr does not exist on NFS. + """ + zarr_path = nfs_root / dataset_name / "2-assemble" / f"{dataset_name}.zarr" + tracking_path = nfs_root / dataset_name / "1-preprocess" / "label-free" / "3-track" / f"{dataset_name}_cropped.zarr" + if not zarr_path.exists(): + raise FileNotFoundError(f"NFS zarr not found: {zarr_path}") + return {"zarr": zarr_path, "tracking": tracking_path} + + +def resolve_vast_paths(dataset_name: str, vast_root: Path) -> dict[str, Path]: + """Return expected VAST output paths for a dataset. + + Parameters + ---------- + dataset_name : str + Dataset identifier. + vast_root : Path + Root of datasets directory on VAST. + + Returns + ------- + dict[str, Path] + Keys: ``output_dir``, ``zarr``, ``tracking``. + """ + output_dir = vast_root / dataset_name + return { + "output_dir": output_dir, + "zarr": output_dir / f"{dataset_name}.zarr", + "tracking": output_dir / "tracking.zarr", + } + + +# --------------------------------------------------------------------------- +# Zarr version validation +# --------------------------------------------------------------------------- + + +def check_zarr_version(zarr_path: Path) -> dict[str, int | str | None]: + """Check zarr format version and OME-Zarr version of an existing store. + + Parameters + ---------- + zarr_path : Path + Path to the zarr store root. + + Returns + ------- + dict[str, int | str | None] + Keys: ``zarr_format`` (2, 3, or None), ``ome_version`` (e.g. "0.5" or None). + """ + result: dict[str, int | str | None] = {"zarr_format": None, "ome_version": None} + + zarr_json = zarr_path / "zarr.json" + zgroup = zarr_path / ".zgroup" + + if zarr_json.exists(): + with open(zarr_json) as f: + meta = json.load(f) + result["zarr_format"] = meta.get("zarr_format", 3) + ome = meta.get("attributes", {}).get("ome", {}) + result["ome_version"] = ome.get("version") + elif zgroup.exists(): + with open(zgroup) as f: + meta = json.load(f) + result["zarr_format"] = meta.get("zarr_format", 2) + zattrs = zarr_path / ".zattrs" + if zattrs.exists(): + with open(zattrs) as f: + attrs = json.load(f) + result["ome_version"] = attrs.get("plate", {}).get("version") + + return result + + +def check_preprocessed(zarr_path: Path) -> bool: + """Check if normalization metadata has been written to the zarr store. + + Parameters + ---------- + zarr_path : Path + Path to the zarr store root. + + Returns + ------- + bool + True if normalization stats are present. + """ + zarr_json = zarr_path / "zarr.json" + zattrs = zarr_path / ".zattrs" + + if zarr_json.exists(): + with open(zarr_json) as f: + meta = json.load(f) + return "normalization" in meta.get("attributes", {}) + elif zattrs.exists(): + with open(zattrs) as f: + attrs = json.load(f) + return "normalization" in attrs + + return False + + +# --------------------------------------------------------------------------- +# Discovery (reads NFS zarr via iohub) +# --------------------------------------------------------------------------- + + +def discover_wells(nfs_zarr_path: Path) -> list[str]: + """Enumerate well paths from an NFS OME-Zarr plate. + + Returns well-level paths (e.g. ``"B/1"``) not full position paths. + The ``crop_concat.yml`` format expects ``{zarr}/{well}/*`` globs + so that biahub concatenate can discover positions within each well. + + Parameters + ---------- + nfs_zarr_path : Path + Path to the assembled zarr on NFS. + + Returns + ------- + list[str] + Sorted well paths like ``["A/1", "B/1", "C/2"]``. + """ + wells: list[str] = [] + with open_ome_zarr(str(nfs_zarr_path), mode="r") as plate: + for pos_path, _pos in plate.positions(): + # pos_path is like "A/1/000000" — extract well as "A/1" + well = "/".join(pos_path.split("/")[:2]) + if well not in wells: + wells.append(well) + return sorted(wells) + + +def discover_channels(nfs_zarr_path: Path) -> list[str]: + """Read channel names from an NFS OME-Zarr plate. + + Parameters + ---------- + nfs_zarr_path : Path + Path to the assembled zarr on NFS. + + Returns + ------- + list[str] + Channel names, e.g. ``["Phase3D", "raw GFP EX488 EM525-45", ...]``. + """ + with open_ome_zarr(str(nfs_zarr_path), mode="r") as plate: + return list(plate.channel_names) + + +RAW_CHANNEL_PREFIXES = ("Phase3D", "raw ") + + +def filter_raw_channels(channel_names: list[str]) -> list[str]: + """Filter to only raw imaging channels (Phase3D and raw fluorescence). + + Excludes virtual stains (``nuclei_prediction``, ``membrane_prediction``), + deconvolved channels (``GFP EX488 ...`` without ``raw`` prefix), and + other derived channels (``BF``). + + Parameters + ---------- + channel_names : list[str] + All channel names from the zarr. + + Returns + ------- + list[str] + Only channels starting with ``"Phase3D"`` or ``"raw "``. + """ + return [ch for ch in channel_names if ch.startswith(RAW_CHANNEL_PREFIXES)] + + +# --------------------------------------------------------------------------- +# Config generation +# --------------------------------------------------------------------------- + + +def generate_crop_concat_config( + nfs_zarr_path: Path, + wells: list[str], + channel_names: list[str], + concat_cfg: ConcatenateConfig, +) -> dict: + """Build a crop_concat.yml dict for biahub concatenate. + + Parameters + ---------- + nfs_zarr_path : Path + Path to the source zarr on NFS. + wells : list[str] + Well paths like ``["A/1", "B/2"]`` (row/col level). + Each becomes ``"{zarr}/{well}/*"`` so biahub globs positions within. + channel_names : list[str] + Channel names (repeated once per well entry). + concat_cfg : ConcatenateConfig + Concatenation parameters. + + Returns + ------- + dict + Config dict ready to write as YAML. + """ + concat_data_paths = [f"{nfs_zarr_path}/{well}/*" for well in wells] + return { + "concat_data_paths": concat_data_paths, + "time_indices": "all", + "channel_names": [channel_names] * len(wells), + "X_slice": "all", + "Y_slice": "all", + "Z_slice": "all", + "chunks_czyx": concat_cfg.chunks_czyx, + "shards_ratio": concat_cfg.shards_ratio, + "output_ome_zarr_version": concat_cfg.output_ome_zarr_version, + } + + +def generate_qc_config(data_path: Path, qc_params: QCParams) -> dict: + """Build a QC config dict compatible with ``qc run -c``. + + Parameters + ---------- + data_path : Path + Path to the VAST zarr (target of QC). + qc_params : QCParams + Focus-slice QC parameters. + + Returns + ------- + dict + Config dict ready to write as YAML. + """ + return { + "data_path": str(data_path), + "num_workers": qc_params.num_workers, + "focus_slice": { + "channel_names": qc_params.channel_names, + "NA_det": qc_params.NA_det, + "lambda_ill": qc_params.lambda_ill, + "pixel_size": qc_params.pixel_size, + "midband_fractions": list(qc_params.midband_fractions), + "device": qc_params.device, + }, + } + + +def write_yaml(config: dict, output_path: Path) -> None: + """Write a dict to a YAML file. + + Parameters + ---------- + config : dict + Config to serialize. + output_path : Path + Destination file path. + """ + + # Use a Dumper subclass that avoids YAML anchors/aliases for repeated + # lists. Patching yaml.Dumper directly leaks into every other yaml.dump + # in the same Python process. + class _NoAliasDumper(yaml.Dumper): + def ignore_aliases(self, data: object) -> bool: + return True + + with open(output_path, "w") as f: + yaml.dump(config, f, Dumper=_NoAliasDumper, default_flow_style=False, sort_keys=False) + + +# --------------------------------------------------------------------------- +# SLURM script generation +# --------------------------------------------------------------------------- + + +def _slurm_header(job_name: str, output_dir: Path, cfg: SlurmStageConfig) -> str: + """Build SBATCH header lines.""" + lines = [ + "#!/bin/bash", + f"#SBATCH --job-name={job_name}", + "#SBATCH --nodes=1", + "#SBATCH --ntasks-per-node=1", + f"#SBATCH --partition={cfg.partition}", + f"#SBATCH --cpus-per-task={cfg.cpus_per_task}", + f"#SBATCH --mem-per-cpu={cfg.mem_per_cpu}", + f"#SBATCH --time={cfg.time}", + f"#SBATCH --output={output_dir}/slurm_{job_name}_%j.out", + ] + if cfg.gres: + lines.append(f"#SBATCH --gres={cfg.gres}") + if cfg.constraint: + lines.append(f'#SBATCH --constraint="{cfg.constraint}"') + return "\n".join(lines) + + +def generate_sbatch_override_file(overrides: dict[str, str]) -> str: + """Generate content for a biahub sbatch override file. + + Parameters + ---------- + overrides : dict[str, str] + SLURM directive keys and values, e.g. + ``{"partition": "preempted", "mem-per-cpu": "16G"}``. + + Returns + ------- + str + File content with ``#SBATCH`` lines. + """ + lines = ["#!/bin/bash"] + for key, value in overrides.items(): + lines.append(f"#SBATCH --{key}={value}") + return "\n".join(lines) + "\n" + + +def generate_concatenate_script( + crop_concat_path: Path, + vast_zarr_path: Path, + nfs_tracking_path: Path, + vast_tracking_path: Path, + conda_env: str, + sbatch_override_path: Path | None = None, +) -> str: + """Generate a bash script for biahub concatenate + tracking copy. + + This is NOT a SLURM script. ``biahub concatenate`` submits its own + SLURM jobs internally via submitit. The ``-m`` flag makes it block + until those jobs complete. After concatenation, tracking is rsynced. + + Parameters + ---------- + crop_concat_path : Path + Path to the generated crop_concat.yml. + vast_zarr_path : Path + Target zarr output path. + nfs_tracking_path : Path + Source tracking zarr on NFS. + vast_tracking_path : Path + Target tracking zarr on VAST. + conda_env : str + Conda environment name for biahub. + sbatch_override_path : Path or None + Path to sbatch override file for biahub's internal SLURM jobs. + + Returns + ------- + str + Bash script content. + """ + # Build the biahub command as a single line to avoid conda run + # swallowing backslash continuations. + cmd_parts = [ + f"conda run -n {conda_env} biahub concatenate", + f'-c "{crop_concat_path}"', + f'-o "{vast_zarr_path}"', + "-m", + ] + if sbatch_override_path: + cmd_parts.append(f'-sb "{sbatch_override_path}"') + biahub_cmd = " ".join(cmd_parts) + + return dedent(f"""\ + #!/bin/bash + set -euo pipefail + + echo "=== Step 1: biahub concatenate (submits SLURM jobs via submitit) ===" + {biahub_cmd} + echo "Concatenation complete." + + echo "=== Step 2: Copy tracking zarr ===" + if [ -d "{nfs_tracking_path}" ]; then + rsync -a --copy-links "{nfs_tracking_path}/" "{vast_tracking_path}/" + echo "Tracking copy complete." + else + echo "WARNING: NFS tracking zarr not found at {nfs_tracking_path}, skipping." + fi + """) + + +def generate_qc_slurm( + dataset_name: str, + vast_output_dir: Path, + qc_config_path: Path, + workspace_dir: Path, + slurm_cfg: SlurmStageConfig, +) -> str: + """Generate SLURM script for focus-slice QC (needs GPU). + + Parameters + ---------- + dataset_name : str + Dataset identifier (used for job name). + vast_output_dir : Path + Output directory on VAST. + qc_config_path : Path + Path to the generated qc_config.yml. + workspace_dir : Path + Path to the viscy repo root. + slurm_cfg : SlurmStageConfig + SLURM resource parameters. + + Returns + ------- + str + Complete SLURM script content. + """ + header = _slurm_header(f"qc_{dataset_name}", vast_output_dir, slurm_cfg) + body = dedent(f"""\ + + export PYTHONNOUSERSITE=1 + + echo "=== QC: focus slice detection ===" + uv run --project "{workspace_dir}" --package qc \ + qc run -c "{qc_config_path}" + echo "QC complete." + """) + return header + "\n" + body + + +def generate_preprocess_slurm( + dataset_name: str, + vast_output_dir: Path, + vast_zarr_path: Path, + workspace_dir: Path, + preprocess_params: PreprocessParams, + slurm_cfg: SlurmStageConfig, +) -> str: + """Generate SLURM script for normalization preprocessing (CPU only). + + Parameters + ---------- + dataset_name : str + Dataset identifier (used for job name). + vast_output_dir : Path + Output directory on VAST. + vast_zarr_path : Path + Path to the rechunked zarr on VAST. + workspace_dir : Path + Path to the viscy repo root. + preprocess_params : PreprocessParams + Normalization preprocessing parameters. + slurm_cfg : SlurmStageConfig + SLURM resource parameters. + + Returns + ------- + str + Complete SLURM script content. + """ + header = _slurm_header(f"preprocess_{dataset_name}", vast_output_dir, slurm_cfg) + + ch_arg = preprocess_params.channel_names + if isinstance(ch_arg, int): + ch_flag = f"--channel_names={ch_arg}" + else: + ch_flag = " ".join(f"--channel_names={c}" for c in ch_arg) + + body = dedent(f"""\ + + export PYTHONNOUSERSITE=1 + + echo "=== Preprocess: normalization stats ===" + echo "Data: {vast_zarr_path}" + uv run --project "{workspace_dir}" --package dynaclr \ + viscy preprocess --data_path "{vast_zarr_path}" \ + {ch_flag} --num_workers {preprocess_params.num_workers} \ + --block_size {preprocess_params.block_size} + echo "Preprocess complete." + """) + return header + "\n" + body + + +# --------------------------------------------------------------------------- +# Status check +# --------------------------------------------------------------------------- + + +def check_dataset_status(dataset_name: str, nfs_root: Path, vast_root: Path) -> dict[str, str]: + """Check existence and version info for a dataset across NFS and VAST. + + Parameters + ---------- + dataset_name : str + Dataset identifier. + nfs_root : Path + NFS root directory. + vast_root : Path + VAST root directory. + + Returns + ------- + dict[str, str] + Status fields for the dataset. + """ + nfs_zarr = nfs_root / dataset_name / "2-assemble" / f"{dataset_name}.zarr" + vast = resolve_vast_paths(dataset_name, vast_root) + + nfs_exists = nfs_zarr.exists() + vast_zarr_exists = vast["zarr"].exists() + vast_tracking_exists = vast["tracking"].exists() + + zarr_fmt: str = "-" + ome_ver: str = "-" + preprocessed: str = "-" + + if vast_zarr_exists: + ver = check_zarr_version(vast["zarr"]) + zarr_fmt = str(ver["zarr_format"]) if ver["zarr_format"] else "?" + ome_ver = str(ver["ome_version"]) if ver["ome_version"] else "?" + preprocessed = "yes" if check_preprocessed(vast["zarr"]) else "no" + + return { + "dataset": dataset_name, + "nfs": "yes" if nfs_exists else "no", + "vast_zarr": "yes" if vast_zarr_exists else "no", + "zarr_version": zarr_fmt, + "ome_version": ome_ver, + "tracking": "yes" if vast_tracking_exists else "no", + "preprocessed": preprocessed, + } + + +def format_status_table(rows: list[dict[str, str]]) -> str: + """Format dataset status rows as a markdown table. + + Parameters + ---------- + rows : list[dict[str, str]] + Each dict from :func:`check_dataset_status`. + + Returns + ------- + str + Markdown table string. + """ + headers = [ + "dataset", + "nfs", + "vast_zarr", + "zarr_version", + "ome_version", + "tracking", + "preprocessed", + ] + col_widths = {h: max(len(h), *(len(r[h]) for r in rows)) for h in headers} + + header_line = "| " + " | ".join(h.ljust(col_widths[h]) for h in headers) + " |" + sep_line = "| " + " | ".join("-" * col_widths[h] for h in headers) + " |" + data_lines = ["| " + " | ".join(r[h].ljust(col_widths[h]) for h in headers) + " |" for r in rows] + return "\n".join([header_line, sep_line, *data_lines]) diff --git a/applications/airtable/src/airtable_utils/prepare_cli.py b/applications/airtable/src/airtable_utils/prepare_cli.py new file mode 100644 index 000000000..c4e9486bb --- /dev/null +++ b/applications/airtable/src/airtable_utils/prepare_cli.py @@ -0,0 +1,259 @@ +"""CLI for config-driven dataset preparation (NFS -> VAST).""" + +from __future__ import annotations + +import logging +import re +import subprocess + +import click + +from airtable_utils.prepare import ( + PrepareConfig, + check_dataset_status, + check_preprocessed, + check_zarr_version, + discover_channels, + discover_wells, + filter_raw_channels, + format_status_table, + generate_concatenate_script, + generate_crop_concat_config, + generate_preprocess_slurm, + generate_qc_config, + generate_qc_slurm, + generate_sbatch_override_file, + resolve_nfs_paths, + resolve_vast_paths, + write_yaml, +) + +logger = logging.getLogger(__name__) + +CONTEXT_SETTINGS = {"help_option_names": ["-h", "--help"]} + + +def _load_prepare_config(config_path: str) -> PrepareConfig: + """Load and validate a prepare config YAML.""" + from viscy_utils.cli_utils import load_config + + raw = load_config(config_path) + return PrepareConfig(**raw) + + +def _parse_slurm_job_id(sbatch_output: str) -> str: + """Extract job ID from sbatch stdout like 'Submitted batch job 12345'.""" + match = re.search(r"Submitted batch job (\d+)", sbatch_output) + if not match: + raise RuntimeError(f"Could not parse sbatch output: {sbatch_output}") + return match.group(1) + + +@click.group(context_settings=CONTEXT_SETTINGS) +def prepare(): + """Prepare datasets for training on VAST storage.""" + logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") + + +@prepare.command() +@click.argument("dataset_name") +@click.option( + "-c", + "--config", + "config_path", + required=True, + type=click.Path(exists=True), + help="Path to prepare config YAML.", +) +@click.option("--dry-run", is_flag=True, help="Generate configs without submitting SLURM jobs.") +@click.option("--force", is_flag=True, help="Overwrite existing VAST zarr even if it is zarr v2.") +def run(dataset_name: str, config_path: str, dry_run: bool, force: bool) -> None: + """Run the full preparation pipeline for DATASET_NAME. + + Steps: Airtable validation -> discover positions/channels -> generate + crop_concat.yml + qc_config.yml + SLURM scripts -> submit jobs. + """ + cfg = _load_prepare_config(config_path) + + # 1. Validate dataset is registered in Airtable + click.echo(f"Validating {dataset_name} in Airtable...") + from airtable_utils.database import AirtableDatasets + + db = AirtableDatasets() + records = db.get_dataset_records(dataset_name) + if not records: + raise click.ClickException( + f"Dataset '{dataset_name}' not found in Airtable. Register it first with the airtable-register workflow." + ) + click.echo(f" Found {len(records)} FOV records in Airtable.") + + # 2. Resolve NFS paths + nfs = resolve_nfs_paths(dataset_name, cfg.nfs_root) + click.echo(f" NFS zarr: {nfs['zarr']}") + + # 3. Resolve VAST paths + vast = resolve_vast_paths(dataset_name, cfg.vast_root) + click.echo(f" VAST output: {vast['output_dir']}") + + # 4. Check existing VAST zarr + if vast["zarr"].exists(): + ver = check_zarr_version(vast["zarr"]) + is_v3 = ver["zarr_format"] == 3 + is_ome05 = ver["ome_version"] == "0.5" + is_preprocessed = check_preprocessed(vast["zarr"]) + + if is_v3 and is_ome05 and is_preprocessed: + click.echo( + f" VAST zarr already exists: zarr v{ver['zarr_format']}, " + f"OME {ver['ome_version']}, preprocessed. Skipping." + ) + return + + if not force: + msg = ( + f"VAST zarr already exists at {vast['zarr']} " + f"(zarr v{ver['zarr_format']}, OME {ver['ome_version']}, " + f"preprocessed={is_preprocessed}). " + "Use --force to overwrite." + ) + raise click.ClickException(msg) + + click.echo(f" WARNING: Overwriting existing VAST zarr (zarr v{ver['zarr_format']}, OME {ver['ome_version']}).") + + # 5. Discover wells and resolve channels from NFS zarr + click.echo("Discovering wells and channels from NFS zarr...") + wells = discover_wells(nfs["zarr"]) + zarr_channels = discover_channels(nfs["zarr"]) + + if cfg.concatenate.channel_names is not None: + concat_channels = cfg.concatenate.channel_names + missing = [ch for ch in concat_channels if ch not in zarr_channels] + if missing: + raise click.ClickException(f"Channels {missing} from config not found in zarr. Available: {zarr_channels}") + else: + concat_channels = filter_raw_channels(zarr_channels) + if not concat_channels: + raise click.ClickException(f"No raw channels found in zarr. Available: {zarr_channels}") + + click.echo(f" Wells: {wells}") + click.echo(f" Zarr channels: {zarr_channels}") + click.echo(f" Extracting: {concat_channels}") + + # 6. Create output directory + vast["output_dir"].mkdir(parents=True, exist_ok=True) + + # 7. Generate crop_concat.yml + crop_concat_cfg = generate_crop_concat_config(nfs["zarr"], wells, concat_channels, cfg.concatenate) + crop_concat_path = vast["output_dir"] / "crop_concat.yml" + write_yaml(crop_concat_cfg, crop_concat_path) + click.echo(f" Wrote: {crop_concat_path}") + + # 8. Generate qc_config.yml + qc_cfg = generate_qc_config(vast["zarr"], cfg.qc) + qc_config_path = vast["output_dir"] / "qc_config.yml" + write_yaml(qc_cfg, qc_config_path) + click.echo(f" Wrote: {qc_config_path}") + + # 9. Generate scripts + sbatch_override_path = None + if cfg.concatenate.sbatch_overrides: + sbatch_content = generate_sbatch_override_file(cfg.concatenate.sbatch_overrides) + sbatch_override_path = vast["output_dir"] / "sbatch_overrides.sh" + sbatch_override_path.write_text(sbatch_content) + click.echo(f" Wrote: {sbatch_override_path}") + + concat_script = generate_concatenate_script( + crop_concat_path=crop_concat_path, + vast_zarr_path=vast["zarr"], + nfs_tracking_path=nfs["tracking"], + vast_tracking_path=vast["tracking"], + conda_env=cfg.concatenate.conda_env, + sbatch_override_path=sbatch_override_path, + ) + concat_script_path = vast["output_dir"] / "01_concatenate.sh" + concat_script_path.write_text(concat_script) + click.echo(f" Wrote: {concat_script_path}") + + qc_script = generate_qc_slurm( + dataset_name=dataset_name, + vast_output_dir=vast["output_dir"], + qc_config_path=qc_config_path, + workspace_dir=cfg.workspace_dir, + slurm_cfg=cfg.slurm.qc, + ) + qc_script_path = vast["output_dir"] / "02_qc.sh" + qc_script_path.write_text(qc_script) + click.echo(f" Wrote: {qc_script_path}") + + preprocess_script = generate_preprocess_slurm( + dataset_name=dataset_name, + vast_output_dir=vast["output_dir"], + vast_zarr_path=vast["zarr"], + workspace_dir=cfg.workspace_dir, + preprocess_params=cfg.preprocess, + slurm_cfg=cfg.slurm.preprocess, + ) + preprocess_script_path = vast["output_dir"] / "03_preprocess.sh" + preprocess_script_path.write_text(preprocess_script) + click.echo(f" Wrote: {preprocess_script_path}") + + if dry_run: + click.echo("\n--dry-run: configs and scripts generated, nothing executed.") + return + + # 10. Run concatenation (biahub submits its own SLURM jobs via submitit) + click.echo("\nRunning biahub concatenate + tracking copy...") + click.echo(" (biahub will submit SLURM jobs internally and -m will monitor them)") + subprocess.run(["bash", str(concat_script_path)], check=True) + click.echo("Concatenation and tracking copy complete.") + + # 11. Submit QC and preprocess as separate SLURM jobs (no dependency, no race condition) + click.echo("\nSubmitting QC and preprocess SLURM jobs...") + result_qc = subprocess.run( + ["sbatch", str(qc_script_path)], + capture_output=True, + text=True, + check=True, + ) + qc_job_id = _parse_slurm_job_id(result_qc.stdout) + click.echo(f" QC job: {qc_job_id} (GPU, ~5-20 min)") + + result_pp = subprocess.run( + ["sbatch", str(preprocess_script_path)], + capture_output=True, + text=True, + check=True, + ) + pp_job_id = _parse_slurm_job_id(result_pp.stdout) + click.echo(f" Preprocess job: {pp_job_id} (CPU, ~3 hrs)") + + click.echo(f"\nPipeline running for {dataset_name}.") + click.echo(f" Output: {vast['output_dir']}") + click.echo(f" Monitor: squeue -j {qc_job_id},{pp_job_id}") + + +@prepare.command() +@click.argument("dataset_names", nargs=-1, required=True) +@click.option( + "-c", + "--config", + "config_path", + required=True, + type=click.Path(exists=True), + help="Path to prepare config YAML.", +) +def status(dataset_names: tuple[str, ...], config_path: str) -> None: + """Check NFS/VAST existence and version status for one or more datasets.""" + cfg = _load_prepare_config(config_path) + + rows = [check_dataset_status(name, cfg.nfs_root, cfg.vast_root) for name in dataset_names] + click.echo(format_status_table(rows)) + + +def main() -> None: + """Entry point for the prepare CLI.""" + prepare() + + +if __name__ == "__main__": + main() diff --git a/applications/airtable/src/airtable_utils/registration.py b/applications/airtable/src/airtable_utils/registration.py new file mode 100644 index 000000000..e35072659 --- /dev/null +++ b/applications/airtable/src/airtable_utils/registration.py @@ -0,0 +1,500 @@ +"""Register zarr positions as per-FOV records in Airtable. + +The atomic unit is a single ``ngff.Position`` path, e.g.:: + + /data/dataset.zarr/A/1/000000 + +Shell globbing handles batch registration:: + + register /data/dataset.zarr/*/*/* +""" + +from __future__ import annotations + +import logging +from dataclasses import dataclass, field +from pathlib import Path + +from iohub import open_ome_zarr + +from airtable_utils.database import AirtableDatasets, MarkerRegistryEntry +from airtable_utils.schemas import MAX_CHANNELS, DatasetRecord, parse_channel_name, parse_position_name + +logger = logging.getLogger(__name__) +DIM_NAMES = ("t_shape", "c_shape", "z_shape", "y_shape", "x_shape") +WELL_TEMPLATE_FIELDS = ( + "cell_type", + "cell_state", + "cell_line", + "marker", + "organelle", + "perturbation", + "hours_post_perturbation", + "moi", + "time_interval_min", + "seeding_density", + "treatment_concentration_nm", + "fluorescence_modality", + "microscope", + "labelfree_modality", + "treatment", + "hours_post_treatment", +) + + +@dataclass +class RegisterResult: + """Result of registering one or more positions.""" + + dataset: str + created: list[dict] = field(default_factory=list) + updated: list[dict] = field(default_factory=list) + unmatched: list[str] = field(default_factory=list) + channel_names: list[str] = field(default_factory=list) + pixel_size_xy_um: float | None = None + pixel_size_z_um: float | None = None + template_ids_to_delete: list[str] = field(default_factory=list) + + +def parse_position_path(position_path: Path) -> tuple[Path, str]: + """Split a position path into zarr root and position name. + + Parameters + ---------- + position_path : Path + Full path to a position, e.g. + ``/data/dataset.zarr/A/1/000000``. + + Returns + ------- + tuple[Path, str] + ``(zarr_root, pos_name)`` — e.g. + ``(Path("/data/dataset.zarr"), "A/1/000000")``. + + Raises + ------ + ValueError + If the path does not contain a ``.zarr`` component. + """ + parts = position_path.parts + zarr_idx = None + for i, part in enumerate(parts): + if part.endswith(".zarr"): + zarr_idx = i + break + if zarr_idx is None: + raise ValueError(f"No .zarr component found in path: {position_path}") + + zarr_root = Path(*parts[: zarr_idx + 1]) + pos_name = "/".join(parts[zarr_idx + 1 :]) + return zarr_root, pos_name + + +def zarr_fields_for_position( + zarr_path: Path, + pos_name: str, + channel_names: list[str], + shape: tuple[int, ...], + scale: tuple[float, ...] | None = None, +) -> dict: + """Build Airtable field dict from zarr position data. + + Parameters + ---------- + zarr_path : Path + Root zarr store path. + pos_name : str + Position name within the zarr (e.g. ``"B/1/000000"``). + channel_names : list[str] + Channel names from the zarr store. + shape : tuple[int, ...] + Array shape ``(T, C, Z, Y, X)``. + scale : tuple[float, ...] or None + Physical scale ``(T, C, Z, Y, X)`` in micrometers from + the zarr coordinate transforms. + + Returns + ------- + dict + Airtable fields derived from the zarr position. + """ + fields: dict = {"data_path": str(zarr_path / pos_name)} + for i, ch_name in enumerate(channel_names[:MAX_CHANNELS]): + fields[f"channel_{i}_name"] = ch_name + for dim_name, dim_val in zip(DIM_NAMES, shape): + fields[dim_name] = dim_val + if scale is not None and len(scale) >= 5: + z_um, y_um, x_um = scale[2], scale[3], scale[4] + if not (z_um == 1.0 and y_um == 1.0 and x_um == 1.0): + if abs(x_um - y_um) > 0.001: + logger.warning("X pixel size (%.4f) != Y (%.4f) for %s — using Y", x_um, y_um, pos_name) + fields["pixel_size_xy_um"] = y_um + fields["pixel_size_z_um"] = z_um + else: + logger.warning("Scale is (1,1,1) for %s — skipping pixel sizes (likely uncalibrated)", pos_name) + return fields + + +def derive_channel_marker( + channel_names: list[str], + marker_entries: list[MarkerRegistryEntry], +) -> dict[str, str]: + """Derive channel marker annotations from Marker Registry entries. + + For each channel name, finds the first registry entry whose aliases + contain a substring match, and returns the protein marker name. + + Parameters + ---------- + channel_names : list[str] + Ordered channel names from the zarr store. + marker_entries : list[MarkerRegistryEntry] + Registry entries linked to the well record. + + Returns + ------- + dict[str, str] + Mapping of ``"channel_{i}_marker"`` -> marker label + for channels that matched a registry entry. + """ + result: dict[str, str] = {} + for i, ch_name in enumerate(channel_names[:MAX_CHANNELS]): + parsed = parse_channel_name(ch_name) + ch_type = parsed.get("channel_type", "") + + if ch_type == "labelfree": + result[f"channel_{i}_marker"] = ch_name + continue + + if ch_type == "virtual_stain": + result[f"channel_{i}_marker"] = ch_name + continue + + for entry in marker_entries: + if any(alias in ch_name for alias in entry.channel_name_aliases): + result[f"channel_{i}_marker"] = entry.marker + break + return result + + +def copy_well_template_fields(template: DatasetRecord) -> dict: + """Copy biologist-provided fields from a well template record. + + Parameters + ---------- + template : DatasetRecord + Well-level record with marker metadata. + + Returns + ------- + dict + Non-None metadata fields from the template. + """ + fields: dict = {} + for key in WELL_TEMPLATE_FIELDS: + val = getattr(template, key) + if val is not None: + fields[key] = val + for i in range(MAX_CHANNELS): + marker_val = getattr(template, f"channel_{i}_marker", None) + if marker_val is not None: + fields[f"channel_{i}_marker"] = marker_val + return fields + + +def build_validation_table( + dataset_name: str, + channel_names: list[str], + records: list[DatasetRecord], +) -> str: + """Build markdown validation table for channel / marker pairing. + + Parameters + ---------- + dataset_name : str + Name of the dataset. + channel_names : list[str] + Ordered channel names from the zarr. + records : list[DatasetRecord] + Airtable records (first record used for marker lookup). + + Returns + ------- + str + Markdown table string. + """ + lines = [ + "| dataset | idx | channel_name | type | filter_cube | marker (scientist) |", + "|---------|-----|--------------|------|-------------|---------------------|", + ] + + rec = records[0] if records else None + + for i, ch_name in enumerate(channel_names): + parsed = parse_channel_name(ch_name) + ch_type = parsed.get("channel_type", "—") + filter_cube = parsed.get("filter_cube", "—") + marker = "—" + if rec and i < MAX_CHANNELS: + marker_val = getattr(rec, f"channel_{i}_marker", None) + if marker_val: + marker = marker_val + lines.append(f"| {dataset_name} | {i} | {ch_name} | {ch_type} | {filter_cube} | {marker} |") + + return "\n".join(lines) + + +def format_register_summary(result: RegisterResult, dry_run: bool = False) -> str: + """Format registration results as markdown. + + Parameters + ---------- + result : RegisterResult + Output of :func:`register_fovs`. + dry_run : bool + Whether this was a dry run. + + Returns + ------- + str + Markdown summary string. + """ + status = "dry_run" if dry_run else "executed" + xy = f"{result.pixel_size_xy_um:.4f}" if result.pixel_size_xy_um is not None else "—" + z = f"{result.pixel_size_z_um:.4f}" if result.pixel_size_z_um is not None else "—" + lines = [ + f"\n## Register Summary — {result.dataset}\n", + "| metric | value |", + "|--------|-------|", + f"| created | {len(result.created)} |", + f"| updated | {len(result.updated)} |", + f"| unmatched | {len(result.unmatched)} |", + f"| templates_to_delete | {len(result.template_ids_to_delete)} |", + f"| pixel_size_xy_um | {xy} |", + f"| pixel_size_z_um | {z} |", + f"| status | {status} |", + "", + ] + + if result.unmatched: + lines.append("### Unmatched positions (no well template)\n") + for pos in result.unmatched[:20]: + lines.append(f"- `{pos}`") + if len(result.unmatched) > 20: + lines.append(f"- ... and {len(result.unmatched) - 20} more") + lines.append("") + + return "\n".join(lines) + + +# Fields required for a complete flat parquet cell index. +# "zarr" = written by register, "platemap" = biologist fills in Airtable. +PARQUET_REQUIRED_FIELDS: list[tuple[str, str]] = [ + ("data_path", "zarr"), + ("tracks_path", "platemap"), + ("channel_0_name", "zarr"), + ("channel_0_marker", "zarr"), + ("pixel_size_xy_um", "zarr"), + ("pixel_size_z_um", "zarr"), + ("perturbation", "platemap"), + ("time_interval_min", "platemap"), + ("hours_post_perturbation", "platemap"), + ("cell_type", "platemap"), +] + + +def build_completeness_report( + dataset_name: str, + records: list[DatasetRecord], +) -> str: + """Check a representative record for missing fields needed by the parquet pipeline. + + Parameters + ---------- + dataset_name : str + Name of the dataset. + records : list[DatasetRecord] + Airtable FOV records for the dataset. + + Returns + ------- + str + Markdown report with missing fields and suggested actions. + """ + if not records: + return "" + + rec = records[0] + missing: list[tuple[str, str]] = [] + for field_name, source in PARQUET_REQUIRED_FIELDS: + val = getattr(rec, field_name, None) + if val is None or val == "" or val == []: + missing.append((field_name, source)) + + if not missing: + return f"\n## Parquet Readiness — {dataset_name}\n\nAll required fields populated.\n" + + lines = [ + f"\n## Parquet Readiness — {dataset_name}\n", + f"**{len(missing)} field(s) still needed** before building a flat parquet:\n", + "| missing field | source | action |", + "|---------------|--------|--------|", + ] + for field_name, source in missing: + if source == "zarr": + action = "re-run `register` (should have been filled — check zarr metadata)" + else: + action = "fill in Airtable platemap or use MCP bulk update" + lines.append(f"| `{field_name}` | {source} | {action} |") + lines.append("") + + return "\n".join(lines) + + +def register_fovs( + position_paths: list[Path], + db: AirtableDatasets | None = None, + dataset_name: str | None = None, +) -> RegisterResult: + """Compute per-FOV records to create/update for the given positions. + + Parameters + ---------- + position_paths : list[Path] + Paths to individual zarr positions, e.g. + ``[Path("/data/ds.zarr/A/1/000000"), ...]``. + All must belong to the same zarr store. + db : AirtableDatasets or None + Airtable interface. Created from env vars if None. + dataset_name : str or None + Airtable dataset name to look up. Defaults to the zarr + store's stem (e.g. ``"my_dataset"`` from + ``my_dataset.zarr``). + + Returns + ------- + RegisterResult + Computed creates, updates, and unmatched positions. + + Raises + ------ + ValueError + If no Airtable records exist for the dataset, or paths + span multiple zarr stores. + """ + if db is None: + db = AirtableDatasets() + + if not position_paths: + raise ValueError("No position paths provided.") + + zarr_root, first_pos = parse_position_path(position_paths[0]) + if dataset_name is None: + dataset_name = zarr_root.stem + + # Validate all paths belong to the same zarr + pos_names: list[str] = [first_pos] + for p in position_paths[1:]: + root, pos = parse_position_path(p) + if root != zarr_root: + raise ValueError(f"All positions must belong to the same zarr store. Got {zarr_root} and {root}.") + pos_names.append(pos) + + existing_records = db.get_dataset_records(dataset_name) + if not existing_records: + raise ValueError( + f"No Airtable records for dataset '{dataset_name}'. Ensure the platemap has been filled first." + ) + + # Fetch Marker Registry once — keyed by Airtable record ID + registry = db.get_marker_registry() + logger.info("Loaded %d Marker Registry entries", len(registry)) + + well_templates: dict[str, DatasetRecord] = {} + fov_records: dict[tuple[str, str], DatasetRecord] = {} + for rec in existing_records: + if rec.fov: + fov_records[(rec.well_id, rec.fov)] = rec + else: + well_templates[rec.well_id] = rec + + logger.info( + "Found %d well templates, %d existing FOV records for '%s'", + len(well_templates), + len(fov_records), + dataset_name, + ) + + result = RegisterResult(dataset=dataset_name) + + # Filter to directories only — glob("*/*/*") also picks up zarr.json, .zattrs, .zgroup files + pos_names = [p for p in pos_names if (zarr_root / p).is_dir()] + + with open_ome_zarr(str(zarr_root), mode="r") as plate: + result.channel_names = plate.channel_names + + if len(plate.channel_names) > MAX_CHANNELS: + logger.warning( + "Zarr has %d channels but Airtable schema supports %d. Channels %d+ will not be recorded.", + len(plate.channel_names), + MAX_CHANNELS, + MAX_CHANNELS, + ) + + # Read pixel scale from the first position (uniform across plate) + first_pos = plate[pos_names[0]] + scale = tuple(first_pos.scale) if hasattr(first_pos, "scale") else None + if scale is not None and len(scale) >= 5: + z_um, y_um = scale[2], scale[3] + if not (z_um == 1.0 and y_um == 1.0): + result.pixel_size_xy_um = y_um + result.pixel_size_z_um = z_um + + for pos_name in pos_names: + well_id, fov = parse_position_name(pos_name) + pos = plate[pos_name] + shape = pos.data.shape + + zarr_fields = zarr_fields_for_position(zarr_root, pos_name, result.channel_names, shape, scale=scale) + + # Resolve cell_line linked records -> registry entries -> marker + rec_for_marker = fov_records.get((well_id, fov)) or well_templates.get(well_id) + if rec_for_marker is not None: + if not rec_for_marker.cell_line: + raise ValueError( + f"Well '{well_id}' has no cell_line set in Airtable. " + "cell_line is required for channel marker derivation — " + "fill it in the platemap before registering." + ) + marker_entries = [registry[rid] for rid in rec_for_marker.cell_line if rid in registry] + marker_fields = derive_channel_marker(result.channel_names, marker_entries) + zarr_fields.update(marker_fields) + + existing = fov_records.get((well_id, fov)) + if existing is not None: + if existing.record_id: + result.updated.append({"id": existing.record_id, "fields": zarr_fields}) + continue + + template = well_templates.get(well_id) + if template is None: + result.unmatched.append(pos_name) + continue + + fields = { + "dataset": dataset_name, + "well_id": well_id, + "fov": fov, + **zarr_fields, + **copy_well_template_fields(template), + } + result.created.append({"fields": fields}) + + # Collect well template record IDs to delete — only for wells where at least + # one FOV was created from the template in this batch. + used_wells: set[str] = {rec["fields"]["well_id"] for rec in result.created} + for well_id, template in well_templates.items(): + if well_id in used_wells and template.record_id: + result.template_ids_to_delete.append(template.record_id) + + return result diff --git a/applications/airtable/src/airtable_utils/schemas.py b/applications/airtable/src/airtable_utils/schemas.py new file mode 100644 index 000000000..1d608178b --- /dev/null +++ b/applications/airtable/src/airtable_utils/schemas.py @@ -0,0 +1,291 @@ +"""Pydantic models for Airtable Datasets table records and unified zattrs schema.""" + +from __future__ import annotations + +from typing import Literal + +from pydantic import BaseModel, Field, model_validator + +from viscy_data.channel_utils import parse_channel_name +from viscy_data.schemas import FOVRecord + +MAX_CHANNELS = 8 + + +def parse_position_name(name: str) -> tuple[str, str]: + """Split an OME-Zarr position name into well path and FOV. + + Parameters + ---------- + name : str + Position name, e.g. ``"B/1/000000"``. + + Returns + ------- + tuple[str, str] + ``(well_path, fov)`` — e.g. ``("B/1", "000000")``. + """ + parts = name.split("/") + well_path = "/".join(parts[:2]) + fov = parts[2] if len(parts) > 2 else "" + return well_path, fov + + +class BiologicalAnnotation(BaseModel): + """Biological meaning of a channel. + + Parameters + ---------- + organelle : str + Target organelle (e.g. "endoplasmic_reticulum", "nucleus"). + marker : str + Marker protein or dye name (e.g. "SEC61B", "H2B"). + marker_type : str + How the marker is attached to the target. + fluorophore : str or None + Fluorophore name if applicable (e.g. "eGFP", "mCherry"). + """ + + organelle: str | None = None + marker: str + marker_type: Literal["protein_tag", "direct_label", "nuclear_dye", "virtual_stain"] = "protein_tag" + fluorophore: str | None = None + + +class ChannelAnnotationEntry(BaseModel): + """Annotation for a single channel. + + Parameters + ---------- + channel_type : str + Modality of the channel. + biological_annotation : BiologicalAnnotation or None + Biological meaning; None for label-free channels. + """ + + channel_type: Literal["fluorescence", "labelfree", "virtual_stain"] + biological_annotation: BiologicalAnnotation | None = None + + +class Perturbation(BaseModel): + """A perturbation applied to a well. + + Extra fields (moi, concentration_nm, etc.) are allowed. + + Parameters + ---------- + name : str + Perturbation name (e.g. "ZIKV", "DMSO"). + type : str + Perturbation category (e.g. "virus", "drug", "control"). + hours_post : float + Hours post-perturbation at imaging time. + """ + + model_config = {"extra": "allow"} + + name: str + type: str = "unknown" + hours_post: float + + +class WellExperimentMetadata(BaseModel): + """Experiment metadata for a single well. + + Parameters + ---------- + perturbations : list[Perturbation] + Perturbations applied to this well. + time_sampling_minutes : float + Time interval between frames in minutes. + """ + + perturbations: list[Perturbation] = Field(default_factory=list) + time_sampling_minutes: float + + +class DatasetRecord(FOVRecord): + """A single FOV-level record from the Airtable Datasets table. + + Extends :class:`~viscy_data.schemas.FOVRecord` with Airtable-specific + raw channel fields (before flattening to ``channel_names``). + """ + + channel_0_name: str | None = None + channel_0_marker: str | None = None + channel_1_name: str | None = None + channel_1_marker: str | None = None + channel_2_name: str | None = None + channel_2_marker: str | None = None + channel_3_name: str | None = None + channel_3_marker: str | None = None + channel_4_name: str | None = None + channel_4_marker: str | None = None + channel_5_name: str | None = None + channel_5_marker: str | None = None + channel_6_name: str | None = None + channel_6_marker: str | None = None + channel_7_name: str | None = None + channel_7_marker: str | None = None + record_id: str | None = None + + @model_validator(mode="after") + def _derive_channel_names(self) -> DatasetRecord: + """Populate ``channel_names`` and ``channel_markers`` from ``channel_0..7_name/marker`` fields.""" + if not self.channel_names: + names = [] + for i in range(MAX_CHANNELS): + name = getattr(self, f"channel_{i}_name") + if name is not None: + names.append(name) + self.channel_names = names + if not self.channel_markers: + markers: dict[str, str] = {} + for i in range(MAX_CHANNELS): + name = getattr(self, f"channel_{i}_name") + marker = getattr(self, f"channel_{i}_marker") + if name is not None and marker is not None: + markers[name] = marker + self.channel_markers = markers + return self + + @classmethod + def from_airtable_record(cls, record: dict) -> DatasetRecord: + """Parse from an Airtable API response. + + Parameters + ---------- + record : dict + Raw Airtable record with ``"id"`` and ``"fields"`` keys. + """ + fields = record.get("fields", {}) + + # Select fields return dict with "name" key; extract just the name + def _select_val(v): + if isinstance(v, dict): + return v.get("name", v) + return v + + # multipleSelects return list of dicts + def _multi_select_val(v): + if isinstance(v, list): + return [item.get("name", item) if isinstance(item, dict) else item for item in v] + return v + + return cls( + dataset=fields.get("dataset", ""), + well_id=fields.get("well_id", ""), + fov=fields.get("fov"), + cell_type=_select_val(fields.get("cell_type")), + cell_state=_select_val(fields.get("cell_state")), + cell_line=_multi_select_val(fields.get("cell_line")), + marker=_select_val(fields.get("marker")), + organelle=_select_val(fields.get("organelle")), + perturbation=_select_val(fields.get("perturbation")), + hours_post_perturbation=fields.get("hours_post_perturbation"), + moi=fields.get("moi"), + time_interval_min=fields.get("time_interval_min"), + seeding_density=fields.get("seeding_density"), + treatment_concentration_nm=fields.get("treatment_concentration_nm"), + **{ + f"channel_{i}_{attr}": ( + fields.get(f"channel_{i}_{attr}") + if attr == "name" + else _select_val(fields.get(f"channel_{i}_{attr}")) + ) + for i in range(MAX_CHANNELS) + for attr in ("name", "marker") + }, + data_path=fields.get("data_path"), + tracks_path=fields.get("tracks_path"), + fluorescence_modality=_select_val(fields.get("fluorescence_modality")), + microscope=_select_val(fields.get("microscope")), + labelfree_modality=_select_val(fields.get("labelfree_modality")), + treatment=_select_val(fields.get("treatment")), + hours_post_treatment=fields.get("hours post treatment"), + t_shape=fields.get("t_shape"), + c_shape=fields.get("c_shape"), + z_shape=fields.get("z_shape"), + y_shape=fields.get("y_shape"), + x_shape=fields.get("x_shape"), + pixel_size_xy_um=fields.get("pixel_size_xy_um"), + pixel_size_z_um=fields.get("pixel_size_z_um"), + record_id=record.get("id"), + ) + + def to_channels_metadata(self) -> dict[str, dict]: + """Return dict for writing to ``.zattrs["channels_metadata"]``. + + Maps each channel name to a ``ChannelAnnotationEntry``-compatible dict + with ``channel_type`` (derived from channel name parsing) and + ``biological_annotation`` with the marker from Airtable. + + For labelfree channels, ``marker`` defaults to the channel name + (e.g., Phase3D). For fluorescence channels, ``marker`` comes from + the ``channel_N_marker`` Airtable field (e.g., TOMM20, SEC61). + """ + annotation: dict[str, dict] = {} + for i in range(MAX_CHANNELS): + name = getattr(self, f"channel_{i}_name") + if name is None: + continue + parsed = parse_channel_name(name) + ch_type = parsed.get("channel_type", "unknown") + if ch_type not in ("fluorescence", "labelfree", "virtual_stain"): + ch_type = "labelfree" + + marker_value = getattr(self, f"channel_{i}_marker") + bio_dict = None + if ch_type == "labelfree": + bio_dict = {"marker": name} + elif marker_value is not None: + bio_dict = { + "marker": marker_value, + "marker_type": "protein_tag", + "fluorophore": None, + } + + annotation[name] = { + "channel_type": ch_type, + "biological_annotation": bio_dict, + } + return annotation + + def to_experiment_metadata(self) -> dict: + """Return dict for writing to ``.zattrs["experiment_metadata"]``. + + Produces the unified schema: ``perturbations`` list + + ``time_sampling_minutes``. + """ + perturbations: list[dict] = [] + if self.perturbation is not None: + p: dict = { + "name": self.perturbation, + "type": "unknown", + "hours_post": self.hours_post_perturbation or 0.0, + } + if self.moi is not None: + p["moi"] = self.moi + if self.treatment_concentration_nm is not None: + p["concentration_nm"] = self.treatment_concentration_nm + perturbations.append(p) + + return { + "perturbations": perturbations, + "time_sampling_minutes": self.time_interval_min or 0.0, + } + + def to_airtable_fields(self) -> dict: + """Return dict for creating/updating an Airtable record. + + Only includes non-None fields. Excludes ``record_id`` and + ``dataset``/``well_id`` which are typically not updated. + """ + fields: dict = {} + exclude = {"record_id", "dataset", "well_id", "fov"} + + for key, val in self.model_dump(exclude_none=True).items(): + if key not in exclude: + fields[key] = val + + return fields diff --git a/applications/airtable/tests/conftest.py b/applications/airtable/tests/conftest.py new file mode 100644 index 000000000..2a3b7fddd --- /dev/null +++ b/applications/airtable/tests/conftest.py @@ -0,0 +1,143 @@ +"""Shared fixtures for airtable_utils tests.""" + +from __future__ import annotations + +from unittest.mock import MagicMock, patch + +import pytest + +# --------------------------------------------------------------------------- +# Sample Airtable API response records +# --------------------------------------------------------------------------- + +SAMPLE_AIRTABLE_RECORDS = [ + { + "id": "rec001", + "fields": { + "dataset": "dataset_alpha", + "well_id": "A/1", + "fov": "000000", + "cell_type": {"name": "HEK293T"}, + "cell_state": {"name": "healthy"}, + "cell_line": [{"name": "HEK293T-H2B-mCherry"}], + "organelle": {"name": "nucleus"}, + "perturbation": {"name": "DMSO"}, + "hours_post_perturbation": 24.0, + "moi": None, + "time_interval_min": 5.0, + "seeding_density": 50000, + "treatment_concentration_nm": 100.0, + "channel_0_name": "Phase3D", + "channel_0_marker": {"name": "Membrane"}, + "channel_1_name": "raw GFP EX488 EM525-45", + "channel_1_marker": {"name": "Endoplasmic Reticulum"}, + "channel_2_name": None, + "channel_2_marker": None, + "channel_3_name": None, + "channel_3_marker": None, + "data_path": "/hpc/datasets/alpha.zarr", + "fluorescence_modality": {"name": "widefield"}, + "microscope": {"name": "mantis"}, + "labelfree_modality": {"name": "widefield"}, + "treatment": {"name": "DMSO"}, + "hours post treatment": 2.0, + "t_shape": 50, + "c_shape": 2, + "z_shape": 30, + "y_shape": 2048, + "x_shape": 2048, + }, + }, + { + "id": "rec002", + "fields": { + "dataset": "dataset_beta", + "well_id": "B/2", + "fov": "000001", + "cell_type": "A549", + "cell_state": "infected", + "cell_line": None, + "organelle": "mitochondria", + "perturbation": "ZIKV", + "hours_post_perturbation": 48.0, + "moi": 0.5, + "time_interval_min": 10.0, + "seeding_density": None, + "treatment_concentration_nm": None, + "channel_0_name": "BF_LED_Matrix_Full", + "channel_0_marker": None, + "channel_1_name": "nuclei_prediction", + "channel_1_marker": {"name": "Nucleus"}, + "channel_2_name": None, + "channel_2_marker": None, + "channel_3_name": None, + "channel_3_marker": None, + "data_path": "/hpc/datasets/beta.zarr", + "fluorescence_modality": None, + "microscope": "dragonfly", + "labelfree_modality": "oblique", + "treatment": None, + "hours post treatment": None, + "t_shape": 100, + "c_shape": 2, + "z_shape": 15, + "y_shape": 1024, + "x_shape": 1024, + }, + }, +] + +DATASET_NAMES_RECORDS = [ + {"id": "rec001", "fields": {"dataset": "dataset_alpha"}}, + {"id": "rec002", "fields": {"dataset": "dataset_beta"}}, + {"id": "rec003", "fields": {"dataset": "dataset_alpha"}}, +] + + +# --------------------------------------------------------------------------- +# Fixtures +# --------------------------------------------------------------------------- + + +@pytest.fixture() +def sample_airtable_records(): + """Return sample Airtable API response records.""" + return SAMPLE_AIRTABLE_RECORDS + + +@pytest.fixture() +def dataset_names_records(): + """Return sample dataset-names-only records.""" + return DATASET_NAMES_RECORDS + + +@pytest.fixture() +def mock_env(monkeypatch): + """Set required Airtable environment variables.""" + monkeypatch.setenv("AIRTABLE_API_KEY", "patFAKEKEY123") + monkeypatch.setenv("AIRTABLE_BASE_ID", "appFAKEBASE456") + + +@pytest.fixture() +def mock_table(): + """Return a MagicMock that stands in for ``pyairtable.Table``.""" + return MagicMock() + + +@pytest.fixture() +def mock_api(mock_table): + """Patch ``pyairtable.Api`` so it returns ``mock_table`` on ``.table()``.""" + with patch("airtable_utils.database.Api") as api_cls: + api_instance = MagicMock() + api_instance.table.return_value = mock_table + api_cls.return_value = api_instance + yield api_cls + + +@pytest.fixture() +def airtable_datasets(mock_env, mock_api, mock_table): + """Return an ``AirtableDatasets`` instance backed by mocks.""" + from airtable_utils.database import AirtableDatasets + + ds = AirtableDatasets() + return ds diff --git a/applications/airtable/tests/test_database.py b/applications/airtable/tests/test_database.py new file mode 100644 index 000000000..42f483fba --- /dev/null +++ b/applications/airtable/tests/test_database.py @@ -0,0 +1,225 @@ +"""Tests for airtable_utils.database.""" + +from __future__ import annotations + +from unittest.mock import patch + +import pandas as pd +import pytest + +# --------------------------------------------------------------------------- +# Initialization +# --------------------------------------------------------------------------- + + +class TestAirtableDatasetsInit: + """Test AirtableDatasets constructor and env var handling.""" + + def test_init_with_env_vars(self, mock_env, mock_api): + """Constructor succeeds when both env vars are set.""" + from airtable_utils.database import AirtableDatasets + + AirtableDatasets() + # Api was called with the fake key + mock_api.assert_called_once_with("patFAKEKEY123") + # .table() is called twice: once for Datasets, once for Marker Registry + mock_api.return_value.table.assert_any_call("appFAKEBASE456", "Datasets") + + def test_init_raises_when_api_key_missing(self, monkeypatch): + """ValueError is raised when AIRTABLE_API_KEY is not set.""" + monkeypatch.delenv("AIRTABLE_API_KEY", raising=False) + monkeypatch.setenv("AIRTABLE_BASE_ID", "appFAKEBASE456") + + from airtable_utils.database import AirtableDatasets + + with patch("airtable_utils.database.Api"): + with pytest.raises(ValueError, match="AIRTABLE_API_KEY"): + AirtableDatasets() + + def test_init_raises_when_base_id_missing(self, monkeypatch): + """ValueError is raised when AIRTABLE_BASE_ID is not set.""" + monkeypatch.setenv("AIRTABLE_API_KEY", "patFAKEKEY123") + monkeypatch.delenv("AIRTABLE_BASE_ID", raising=False) + + from airtable_utils.database import AirtableDatasets + + with patch("airtable_utils.database.Api"): + with pytest.raises(ValueError, match="AIRTABLE_BASE_ID"): + AirtableDatasets() + + def test_init_raises_when_both_missing(self, monkeypatch): + """ValueError is raised when both env vars are missing.""" + monkeypatch.delenv("AIRTABLE_API_KEY", raising=False) + monkeypatch.delenv("AIRTABLE_BASE_ID", raising=False) + + from airtable_utils.database import AirtableDatasets + + with patch("airtable_utils.database.Api"): + with pytest.raises(ValueError): + AirtableDatasets() + + def test_init_raises_when_api_key_empty(self, monkeypatch): + """ValueError is raised when AIRTABLE_API_KEY is set to empty string.""" + monkeypatch.setenv("AIRTABLE_API_KEY", "") + monkeypatch.setenv("AIRTABLE_BASE_ID", "appFAKEBASE456") + + from airtable_utils.database import AirtableDatasets + + with patch("airtable_utils.database.Api"): + with pytest.raises(ValueError, match="AIRTABLE_API_KEY"): + AirtableDatasets() + + def test_no_constructor_params_accepted(self): + """Constructor does not accept api_key or base_id parameters.""" + import inspect + + from airtable_utils.database import AirtableDatasets + + sig = inspect.signature(AirtableDatasets.__init__) + params = list(sig.parameters.keys()) + # Only 'self' should be a parameter + assert params == ["self"], ( + f"Expected only 'self', got {params}. api_key/base_id must not be constructor parameters." + ) + + +# --------------------------------------------------------------------------- +# get_unique_datasets +# --------------------------------------------------------------------------- + + +class TestGetUniqueDatasets: + """Test AirtableDatasets.get_unique_datasets().""" + + def test_returns_sorted_unique_names(self, airtable_datasets, mock_table, dataset_names_records): + mock_table.all.return_value = dataset_names_records + result = airtable_datasets.get_unique_datasets() + mock_table.all.assert_called_once_with(fields=["dataset"]) + assert result == ["dataset_alpha", "dataset_beta"] + + def test_empty_table_returns_empty_list(self, airtable_datasets, mock_table): + mock_table.all.return_value = [] + result = airtable_datasets.get_unique_datasets() + assert result == [] + + def test_skips_records_without_dataset_field(self, airtable_datasets, mock_table): + mock_table.all.return_value = [ + {"id": "rec001", "fields": {"dataset": "alpha"}}, + {"id": "rec002", "fields": {}}, # missing dataset + {"id": "rec003", "fields": {"dataset": "beta"}}, + ] + result = airtable_datasets.get_unique_datasets() + assert result == ["alpha", "beta"] + + +# --------------------------------------------------------------------------- +# get_dataset_records +# --------------------------------------------------------------------------- + + +class TestGetDatasetRecords: + """Test AirtableDatasets.get_dataset_records().""" + + def test_returns_dataset_records(self, airtable_datasets, mock_table, sample_airtable_records): + mock_table.all.return_value = [sample_airtable_records[0]] + result = airtable_datasets.get_dataset_records("dataset_alpha") + mock_table.all.assert_called_once_with(formula="{dataset} = 'dataset_alpha'") + assert len(result) == 1 + assert result[0].dataset == "dataset_alpha" + assert result[0].well_id == "A/1" + assert result[0].record_id == "rec001" + + def test_empty_result(self, airtable_datasets, mock_table): + mock_table.all.return_value = [] + result = airtable_datasets.get_dataset_records("nonexistent") + assert result == [] + + +# --------------------------------------------------------------------------- +# list_records +# --------------------------------------------------------------------------- + + +class TestListRecords: + """Test AirtableDatasets.list_records().""" + + def test_returns_dataframe(self, airtable_datasets, mock_table, sample_airtable_records): + mock_table.all.return_value = sample_airtable_records + df = airtable_datasets.list_records() + mock_table.all.assert_called_once_with() + assert isinstance(df, pd.DataFrame) + assert len(df) == 2 + assert list(df["dataset"]) == ["dataset_alpha", "dataset_beta"] + + def test_with_filter_formula(self, airtable_datasets, mock_table, sample_airtable_records): + mock_table.all.return_value = [sample_airtable_records[0]] + formula = "{cell_type} = 'HEK293T'" + df = airtable_datasets.list_records(filter_formula=formula) + mock_table.all.assert_called_once_with(formula=formula) + assert len(df) == 1 + + def test_without_filter_formula(self, airtable_datasets, mock_table): + mock_table.all.return_value = [] + df = airtable_datasets.list_records(filter_formula=None) + mock_table.all.assert_called_once_with() + assert len(df) == 0 + + def test_dataframe_columns(self, airtable_datasets, mock_table, sample_airtable_records): + mock_table.all.return_value = [sample_airtable_records[0]] + df = airtable_datasets.list_records() + expected_cols = { + "dataset", + "well_id", + "fov", + "cell_type", + "cell_state", + "cell_line", + "marker", + "organelle", + "perturbation", + "hours_post_perturbation", + "moi", + "time_interval_min", + "seeding_density", + "treatment_concentration_nm", + "channel_names", + "channel_markers", + *(f"channel_{i}_{attr}" for i in range(8) for attr in ("name", "marker")), + "data_path", + "tracks_path", + "fluorescence_modality", + "microscope", + "labelfree_modality", + "treatment", + "hours_post_treatment", + "t_shape", + "c_shape", + "z_shape", + "y_shape", + "x_shape", + "pixel_size_xy_um", + "pixel_size_z_um", + "record_id", + } + assert set(df.columns) == expected_cols + + +# --------------------------------------------------------------------------- +# batch_delete +# --------------------------------------------------------------------------- + + +class TestBatchDelete: + """Test AirtableDatasets.batch_delete().""" + + def test_delegates_to_table(self, airtable_datasets, mock_table): + mock_table.batch_delete.return_value = [{"id": "rec001", "deleted": True}] + result = airtable_datasets.batch_delete(["rec001"]) + mock_table.batch_delete.assert_called_once_with(["rec001"]) + assert result == [{"id": "rec001", "deleted": True}] + + def test_passes_multiple_ids(self, airtable_datasets, mock_table): + ids = ["rec001", "rec002", "rec003"] + mock_table.batch_delete.return_value = [] + airtable_datasets.batch_delete(ids) + mock_table.batch_delete.assert_called_once_with(ids) diff --git a/applications/airtable/tests/test_register_fovs.py b/applications/airtable/tests/test_register_fovs.py new file mode 100644 index 000000000..aaddcd8e0 --- /dev/null +++ b/applications/airtable/tests/test_register_fovs.py @@ -0,0 +1,495 @@ +"""Tests for airtable_utils.registration.""" + +from __future__ import annotations + +from pathlib import Path +from unittest.mock import MagicMock, patch + +import pytest + +from airtable_utils.registration import ( + MAX_CHANNELS, + copy_well_template_fields, + parse_position_path, + register_fovs, + zarr_fields_for_position, +) +from airtable_utils.schemas import DatasetRecord + +# --------------------------------------------------------------------------- +# Fixtures +# --------------------------------------------------------------------------- + + +def _make_well_template(well_id: str, record_id: str | None = None, **overrides) -> DatasetRecord: + """Create a well-level DatasetRecord (no fov).""" + defaults = { + "dataset": "test_dataset", + "well_id": well_id, + "fov": None, + "cell_type": "A549", + "cell_state": "Live", + "cell_line": ["recCELLLINE1"], + "marker": "TOMM20", + "organelle": "mitochondria", + "perturbation": "ZIKV", + "hours_post_perturbation": 5.0, + "moi": 5.0, + "time_interval_min": 30.0, + "fluorescence_modality": "Light-sheet", + "microscope": "mantis", + "labelfree_modality": "widefield", + "treatment": "DMSO", + "hours_post_treatment": 2.0, + "channel_0_marker": "brightfield", + "channel_1_marker": "mitochondria", + "record_id": record_id, + } + defaults.update(overrides) + return DatasetRecord(**defaults) + + +def _make_fov_record(well_id: str, fov: str, record_id: str, **overrides) -> DatasetRecord: + """Create a per-FOV DatasetRecord.""" + defaults = { + "dataset": "test_dataset", + "well_id": well_id, + "fov": fov, + "cell_type": "A549", + "cell_line": ["recCELLLINE1"], + "marker": "TOMM20", + "organelle": "mitochondria", + "record_id": record_id, + } + defaults.update(overrides) + return DatasetRecord(**defaults) + + +def _make_mock_plate(positions_data: dict[str, tuple[int, ...]], channel_names: list[str] | None = None): + """Build a mock plate with given positions and shapes. + + Parameters + ---------- + positions_data : dict[str, tuple[int, ...]] + Mapping of position name -> array shape. + channel_names : list[str] or None + Channel names. Defaults to Phase3D + GFP + mCherry. + """ + if channel_names is None: + channel_names = ["Phase3D", "raw GFP EX488 EM525-45", "raw mCherry EX561 EM600-37"] + + plate = MagicMock() + plate.channel_names = channel_names + + def _getitem(key): + pos = MagicMock() + pos.data.shape = positions_data[key] + return pos + + plate.__getitem__ = MagicMock(side_effect=_getitem) + plate.__enter__ = MagicMock(return_value=plate) + plate.__exit__ = MagicMock(return_value=False) + return plate + + +# --------------------------------------------------------------------------- +# parse_position_path +# --------------------------------------------------------------------------- + + +class TestParsePositionPath: + """Tests for parse_position_path.""" + + def test_standard_path(self): + root, pos = parse_position_path(Path("/data/test_dataset.zarr/A/1/000000")) + assert root == Path("/data/test_dataset.zarr") + assert pos == "A/1/000000" + + def test_deep_zarr_path(self): + root, pos = parse_position_path(Path("/hpc/projects/org/ds.zarr/B/2/001001")) + assert root == Path("/hpc/projects/org/ds.zarr") + assert pos == "B/2/001001" + + def test_no_zarr_raises(self): + with pytest.raises(ValueError, match="No .zarr component"): + parse_position_path(Path("/data/not_a_zarr/A/1/000000")) + + +# --------------------------------------------------------------------------- +# register_fovs +# --------------------------------------------------------------------------- + + +class TestRegisterFovs: + """Tests for the register_fovs core function.""" + + def test_creates_new_fov_records_from_well_templates(self): + """New FOVs get created with template fields + zarr-derived fields.""" + template_a1 = _make_well_template("A/1", record_id="recWELL1") + db = MagicMock() + db.get_dataset_records.return_value = [template_a1] + + positions = { + "A/1/000000": (10, 3, 1, 512, 512), + "A/1/000001": (10, 3, 1, 512, 512), + } + mock_plate = _make_mock_plate(positions) + + paths = [ + Path("/data/test_dataset.zarr/A/1/000000"), + Path("/data/test_dataset.zarr/A/1/000001"), + ] + with ( + patch("airtable_utils.registration.open_ome_zarr", return_value=mock_plate), + patch("pathlib.Path.is_dir", return_value=True), + ): + result = register_fovs(paths, db=db) + + assert result.dataset == "test_dataset" + assert len(result.created) == 2 + assert len(result.updated) == 0 + assert len(result.unmatched) == 0 + + rec0 = result.created[0]["fields"] + assert rec0["dataset"] == "test_dataset" + assert rec0["well_id"] == "A/1" + assert rec0["fov"] == "000000" + assert rec0["data_path"] == "/data/test_dataset.zarr/A/1/000000" + assert rec0["channel_0_name"] == "Phase3D" + assert rec0["channel_1_name"] == "raw GFP EX488 EM525-45" + assert rec0["channel_2_name"] == "raw mCherry EX561 EM600-37" + assert rec0["t_shape"] == 10 + assert rec0["c_shape"] == 3 + assert rec0["z_shape"] == 1 + assert rec0["y_shape"] == 512 + assert rec0["x_shape"] == 512 + assert rec0["cell_type"] == "A549" + assert rec0["marker"] == "TOMM20" + assert rec0["organelle"] == "mitochondria" + assert rec0["perturbation"] == "ZIKV" + assert rec0["moi"] == 5.0 + assert rec0["microscope"] == "mantis" + assert rec0["labelfree_modality"] == "widefield" + assert rec0["treatment"] == "DMSO" + assert rec0["hours_post_treatment"] == 2.0 + assert rec0["channel_0_marker"] == "brightfield" + assert rec0["channel_1_marker"] == "mitochondria" + assert result.template_ids_to_delete == ["recWELL1"] + + def test_updates_existing_fov_records(self): + """Existing per-FOV records get updated with zarr-derived fields only.""" + existing = _make_fov_record("A/1", "000000", record_id="recFOV1") + db = MagicMock() + db.get_dataset_records.return_value = [existing] + + positions = {"A/1/000000": (20, 3, 1, 256, 256)} + mock_plate = _make_mock_plate(positions) + + paths = [Path("/data/test_dataset.zarr/A/1/000000")] + with ( + patch("airtable_utils.registration.open_ome_zarr", return_value=mock_plate), + patch("pathlib.Path.is_dir", return_value=True), + ): + result = register_fovs(paths, db=db) + + assert len(result.created) == 0 + assert len(result.updated) == 1 + + upd = result.updated[0] + assert upd["id"] == "recFOV1" + assert upd["fields"]["data_path"] == "/data/test_dataset.zarr/A/1/000000" + assert upd["fields"]["t_shape"] == 20 + assert upd["fields"]["channel_0_name"] == "Phase3D" + assert "cell_type" not in upd["fields"] + assert "marker" not in upd["fields"] + + def test_unmatched_positions(self): + """Positions without a well template or existing record are unmatched.""" + template_a1 = _make_well_template("A/1") + db = MagicMock() + db.get_dataset_records.return_value = [template_a1] + + positions = { + "A/1/000000": (10, 3, 1, 512, 512), + "B/2/000000": (10, 3, 1, 512, 512), + } + mock_plate = _make_mock_plate(positions) + + paths = [ + Path("/data/test_dataset.zarr/A/1/000000"), + Path("/data/test_dataset.zarr/B/2/000000"), + ] + with ( + patch("airtable_utils.registration.open_ome_zarr", return_value=mock_plate), + patch("pathlib.Path.is_dir", return_value=True), + ): + result = register_fovs(paths, db=db) + + assert len(result.created) == 1 + assert len(result.unmatched) == 1 + assert result.unmatched[0] == "B/2/000000" + + def test_mixed_create_and_update(self): + """Mix of new FOVs (create) and existing FOVs (update).""" + template_a1 = _make_well_template("A/1") + existing_a1_fov0 = _make_fov_record("A/1", "000000", record_id="recEXIST") + db = MagicMock() + db.get_dataset_records.return_value = [template_a1, existing_a1_fov0] + + positions = { + "A/1/000000": (10, 3, 1, 512, 512), + "A/1/000001": (10, 3, 1, 512, 512), + } + mock_plate = _make_mock_plate(positions) + + paths = [ + Path("/data/test_dataset.zarr/A/1/000000"), + Path("/data/test_dataset.zarr/A/1/000001"), + ] + with ( + patch("airtable_utils.registration.open_ome_zarr", return_value=mock_plate), + patch("pathlib.Path.is_dir", return_value=True), + ): + result = register_fovs(paths, db=db) + + assert len(result.updated) == 1 + assert result.updated[0]["id"] == "recEXIST" + assert len(result.created) == 1 + assert result.created[0]["fields"]["fov"] == "000001" + + def test_raises_on_no_airtable_records(self): + """ValueError raised when no Airtable records exist for dataset.""" + db = MagicMock() + db.get_dataset_records.return_value = [] + + paths = [Path("/data/test_dataset.zarr/A/1/000000")] + with pytest.raises(ValueError, match="No Airtable records"): + register_fovs(paths, db=db) + + def test_raises_on_empty_paths(self): + """ValueError raised when no position paths provided.""" + db = MagicMock() + with pytest.raises(ValueError, match="No position paths"): + register_fovs([], db=db) + + def test_raises_on_mixed_zarr_stores(self): + """ValueError raised when paths span multiple zarr stores.""" + db = MagicMock() + paths = [ + Path("/data/store_a.zarr/A/1/000000"), + Path("/data/store_b.zarr/A/1/000000"), + ] + with pytest.raises(ValueError, match="same zarr store"): + register_fovs(paths, db=db) + + def test_raises_when_cell_line_missing(self): + """ValueError raised when a well template has no cell_line set.""" + template_no_cell_line = _make_well_template("A/1", cell_line=None) + db = MagicMock() + db.get_dataset_records.return_value = [template_no_cell_line] + + positions = {"A/1/000000": (10, 3, 1, 512, 512)} + mock_plate = _make_mock_plate(positions) + + paths = [Path("/data/test_dataset.zarr/A/1/000000")] + with ( + patch("airtable_utils.registration.open_ome_zarr", return_value=mock_plate), + patch("pathlib.Path.is_dir", return_value=True), + ): + with pytest.raises(ValueError, match="cell_line is required"): + register_fovs(paths, db=db) + + def test_all_records_already_per_fov_no_templates(self): + """When all records are per-FOV and no templates exist, only updates happen.""" + existing = _make_fov_record("A/1", "000000", record_id="recFOV1") + db = MagicMock() + db.get_dataset_records.return_value = [existing] + + positions = { + "A/1/000000": (10, 3, 1, 512, 512), + "A/1/000001": (10, 3, 1, 512, 512), + } + mock_plate = _make_mock_plate(positions) + + paths = [ + Path("/data/test_dataset.zarr/A/1/000000"), + Path("/data/test_dataset.zarr/A/1/000001"), + ] + with ( + patch("airtable_utils.registration.open_ome_zarr", return_value=mock_plate), + patch("pathlib.Path.is_dir", return_value=True), + ): + result = register_fovs(paths, db=db) + + assert len(result.updated) == 1 + assert len(result.created) == 0 + assert len(result.unmatched) == 1 + assert result.unmatched[0] == "A/1/000001" + + +# --------------------------------------------------------------------------- +# zarr_fields_for_position +# --------------------------------------------------------------------------- + + +class TestZarrFieldsForPosition: + """Tests for zarr_fields_for_position helper.""" + + def test_basic_fields(self): + fields = zarr_fields_for_position( + zarr_path=Path("/data/ds.zarr"), + pos_name="A/1/000000", + channel_names=["Phase3D", "GFP"], + shape=(10, 2, 1, 256, 256), + ) + assert fields["data_path"] == "/data/ds.zarr/A/1/000000" + assert fields["channel_0_name"] == "Phase3D" + assert fields["channel_1_name"] == "GFP" + assert "channel_2_name" not in fields + assert fields["t_shape"] == 10 + assert fields["c_shape"] == 2 + assert fields["z_shape"] == 1 + assert fields["y_shape"] == 256 + assert fields["x_shape"] == 256 + + def test_truncates_at_max_channels(self): + num_channels = MAX_CHANNELS + 2 + channels = [f"ch_{i}" for i in range(num_channels)] + fields = zarr_fields_for_position( + zarr_path=Path("/data/ds.zarr"), + pos_name="A/1/000000", + channel_names=channels, + shape=(1, num_channels, 1, 64, 64), + ) + for i in range(MAX_CHANNELS): + assert fields[f"channel_{i}_name"] == f"ch_{i}" + assert f"channel_{MAX_CHANNELS}_name" not in fields + + +# --------------------------------------------------------------------------- +# copy_well_template_fields +# --------------------------------------------------------------------------- + + +class TestCopyWellTemplateFields: + """Tests for copy_well_template_fields helper.""" + + def test_copies_non_none_fields(self): + template = _make_well_template("A/1") + fields = copy_well_template_fields(template) + + assert fields["cell_type"] == "A549" + assert fields["marker"] == "TOMM20" + assert fields["organelle"] == "mitochondria" + assert fields["perturbation"] == "ZIKV" + assert fields["moi"] == 5.0 + assert fields["time_interval_min"] == 30.0 + assert fields["microscope"] == "mantis" + assert fields["labelfree_modality"] == "widefield" + assert fields["treatment"] == "DMSO" + assert fields["hours_post_treatment"] == 2.0 + assert fields["channel_0_marker"] == "brightfield" + assert fields["channel_1_marker"] == "mitochondria" + + def test_skips_none_fields(self): + template = _make_well_template( + "A/1", + seeding_density=None, + treatment_concentration_nm=None, + microscope=None, + labelfree_modality=None, + ) + fields = copy_well_template_fields(template) + + assert "seeding_density" not in fields + assert "treatment_concentration_nm" not in fields + assert "microscope" not in fields + assert "labelfree_modality" not in fields + + +# --------------------------------------------------------------------------- +# template deletion tracking +# --------------------------------------------------------------------------- + + +class TestTemplateDeletion: + """Tests for template_ids_to_delete population in register_fovs.""" + + def test_template_deleted_when_fov_created(self): + """Template record ID appears in deletion list when FOVs are created from it.""" + template_a1 = _make_well_template("A/1", record_id="recWELL1") + db = MagicMock() + db.get_dataset_records.return_value = [template_a1] + + positions = {"A/1/000000": (10, 3, 1, 512, 512)} + mock_plate = _make_mock_plate(positions) + + paths = [Path("/data/test_dataset.zarr/A/1/000000")] + with ( + patch("airtable_utils.registration.open_ome_zarr", return_value=mock_plate), + patch("pathlib.Path.is_dir", return_value=True), + ): + result = register_fovs(paths, db=db) + + assert len(result.created) == 1 + assert result.template_ids_to_delete == ["recWELL1"] + + def test_template_not_deleted_when_all_positions_unmatched(self): + """Template with no created FOVs is not in deletion list.""" + template_a1 = _make_well_template("A/1", record_id="recWELL1") + db = MagicMock() + db.get_dataset_records.return_value = [template_a1] + + # B/2 has no template — will be unmatched + positions = {"B/2/000000": (10, 3, 1, 512, 512)} + mock_plate = _make_mock_plate(positions) + + paths = [Path("/data/test_dataset.zarr/B/2/000000")] + with ( + patch("airtable_utils.registration.open_ome_zarr", return_value=mock_plate), + patch("pathlib.Path.is_dir", return_value=True), + ): + result = register_fovs(paths, db=db) + + assert len(result.unmatched) == 1 + assert result.template_ids_to_delete == [] + + def test_only_used_templates_deleted(self): + """Only templates where at least one FOV was created appear in deletion list.""" + template_a1 = _make_well_template("A/1", record_id="recWELL_A1") + template_b2 = _make_well_template("B/2", record_id="recWELL_B2") + db = MagicMock() + db.get_dataset_records.return_value = [template_a1, template_b2] + + # A/1 gets a FOV; B/2 gets no positions in this batch + positions = {"A/1/000000": (10, 3, 1, 512, 512)} + mock_plate = _make_mock_plate(positions) + + paths = [Path("/data/test_dataset.zarr/A/1/000000")] + with ( + patch("airtable_utils.registration.open_ome_zarr", return_value=mock_plate), + patch("pathlib.Path.is_dir", return_value=True), + ): + result = register_fovs(paths, db=db) + + assert len(result.created) == 1 + assert result.template_ids_to_delete == ["recWELL_A1"] + + def test_template_without_record_id_not_added(self): + """Template with no record_id is skipped in deletion list.""" + template_a1 = _make_well_template("A/1", record_id=None) + db = MagicMock() + db.get_dataset_records.return_value = [template_a1] + + positions = {"A/1/000000": (10, 3, 1, 512, 512)} + mock_plate = _make_mock_plate(positions) + + paths = [Path("/data/test_dataset.zarr/A/1/000000")] + with ( + patch("airtable_utils.registration.open_ome_zarr", return_value=mock_plate), + patch("pathlib.Path.is_dir", return_value=True), + ): + result = register_fovs(paths, db=db) + + assert len(result.created) == 1 + assert result.template_ids_to_delete == [] diff --git a/applications/airtable/tests/test_schemas.py b/applications/airtable/tests/test_schemas.py new file mode 100644 index 000000000..11e611355 --- /dev/null +++ b/applications/airtable/tests/test_schemas.py @@ -0,0 +1,419 @@ +"""Tests for airtable_utils.schemas.""" + +from __future__ import annotations + +import pytest +from pydantic import ValidationError + +from airtable_utils.schemas import ( + BiologicalAnnotation, + ChannelAnnotationEntry, + DatasetRecord, + Perturbation, + WellExperimentMetadata, + parse_channel_name, + parse_position_name, +) + +# ============================================================================ +# parse_channel_name +# ============================================================================ + + +class TestParseChannelName: + """Test parse_channel_name for various channel label formats.""" + + # -- fluorescence -------------------------------------------------------- + + def test_fluorescence_full_pattern(self): + result = parse_channel_name("raw GFP EX488 EM525-45") + assert result["channel_type"] == "fluorescence" + assert result["filter_cube"] == "GFP" + assert result["excitation_nm"] == 488 + assert result["emission_nm"] == 525 + + def test_fluorescence_no_bandwidth(self): + result = parse_channel_name("raw DAPI EX405 EM450") + assert result["channel_type"] == "fluorescence" + assert result["filter_cube"] == "DAPI" + assert result["excitation_nm"] == 405 + assert result["emission_nm"] == 450 + + def test_fluorescence_case_insensitive(self): + result = parse_channel_name("RAW mCherry ex561 em600-50") + assert result["channel_type"] == "fluorescence" + assert result["filter_cube"] == "mCherry" + + def test_fluorescence_fallback_ex_em_without_raw(self): + """EX/EM pattern without 'raw' prefix still detected as fluorescence.""" + result = parse_channel_name("GFP EX488 EM525") + assert result["channel_type"] == "fluorescence" + assert result["excitation_nm"] == 488 + assert result["emission_nm"] == 525 + # filter_cube not extracted in fallback path + assert "filter_cube" not in result + + # -- labelfree ----------------------------------------------------------- + + def test_labelfree_phase(self): + result = parse_channel_name("Phase3D") + assert result["channel_type"] == "labelfree" + + def test_labelfree_brightfield(self): + result = parse_channel_name("Brightfield_LED") + assert result["channel_type"] == "labelfree" + + def test_labelfree_retardance(self): + result = parse_channel_name("Retardance_PolScope") + assert result["channel_type"] == "labelfree" + + def test_labelfree_bf_prefix(self): + result = parse_channel_name("BF_LED_Matrix_Full") + assert result["channel_type"] == "labelfree" + + def test_labelfree_dic(self): + result = parse_channel_name("DIC") + assert result["channel_type"] == "labelfree" + + # -- virtual_stain ------------------------------------------------------- + + def test_virtual_stain_prediction(self): + result = parse_channel_name("nuclei_prediction") + assert result["channel_type"] == "virtual_stain" + + def test_virtual_stain_virtual(self): + result = parse_channel_name("virtual_fluorescence") + assert result["channel_type"] == "virtual_stain" + + def test_virtual_stain_vs_prefix(self): + result = parse_channel_name("vs_nucleus") + assert result["channel_type"] == "virtual_stain" + + # -- unknown / edge cases ------------------------------------------------ + + def test_unknown_channel(self): + result = parse_channel_name("some_random_channel") + assert result["channel_type"] == "unknown" + + def test_empty_string(self): + result = parse_channel_name("") + assert result["channel_type"] == "unknown" + + +# ============================================================================ +# parse_position_name +# ============================================================================ + + +class TestParsePositionName: + """Test parse_position_name for OME-Zarr position paths.""" + + def test_standard_three_part_path(self): + well, fov = parse_position_name("B/1/000000") + assert well == "B/1" + assert fov == "000000" + + def test_deep_path(self): + well, fov = parse_position_name("A/3/000005") + assert well == "A/3" + assert fov == "000005" + + def test_two_part_path_no_fov(self): + well, fov = parse_position_name("C/2") + assert well == "C/2" + assert fov == "" + + def test_single_part_path(self): + well, fov = parse_position_name("A") + assert well == "A" + assert fov == "" + + def test_four_part_path(self): + """Extra parts beyond 3 are ignored; only first 2 form the well.""" + well, fov = parse_position_name("D/4/000010/extra") + assert well == "D/4" + assert fov == "000010" + + +# ============================================================================ +# DatasetRecord.from_airtable_record +# ============================================================================ + + +class TestDatasetRecordFromAirtable: + """Test DatasetRecord.from_airtable_record with various response shapes.""" + + def test_full_record_with_select_dicts(self, sample_airtable_records): + """Record where select fields are dicts with 'name' key.""" + rec = DatasetRecord.from_airtable_record(sample_airtable_records[0]) + assert rec.dataset == "dataset_alpha" + assert rec.well_id == "A/1" + assert rec.fov == "000000" + assert rec.cell_type == "HEK293T" + assert rec.cell_state == "healthy" + assert rec.cell_line == ["HEK293T-H2B-mCherry"] + assert rec.organelle == "nucleus" + assert rec.perturbation == "DMSO" + assert rec.hours_post_perturbation == 24.0 + assert rec.time_interval_min == 5.0 + assert rec.seeding_density == 50000 + assert rec.treatment_concentration_nm == 100.0 + assert rec.channel_0_name == "Phase3D" + assert rec.channel_0_marker == "Membrane" + assert rec.channel_1_name == "raw GFP EX488 EM525-45" + assert rec.channel_1_marker == "Endoplasmic Reticulum" + assert rec.data_path == "/hpc/datasets/alpha.zarr" + assert rec.fluorescence_modality == "widefield" + assert rec.microscope == "mantis" + assert rec.labelfree_modality == "widefield" + assert rec.treatment == "DMSO" + assert rec.hours_post_treatment == 2.0 + assert rec.t_shape == 50 + assert rec.c_shape == 2 + assert rec.z_shape == 30 + assert rec.y_shape == 2048 + assert rec.x_shape == 2048 + assert rec.record_id == "rec001" + + def test_record_with_plain_string_fields(self, sample_airtable_records): + """Record where select fields are plain strings (no dict wrapper).""" + rec = DatasetRecord.from_airtable_record(sample_airtable_records[1]) + assert rec.dataset == "dataset_beta" + assert rec.cell_type == "A549" + assert rec.cell_state == "infected" + assert rec.organelle == "mitochondria" + assert rec.perturbation == "ZIKV" + assert rec.moi == 0.5 + assert rec.cell_line is None + assert rec.microscope == "dragonfly" + assert rec.labelfree_modality == "oblique" + assert rec.treatment is None + assert rec.hours_post_treatment is None + + def test_minimal_record(self): + """Record with only required fields.""" + minimal = { + "id": "recMIN", + "fields": { + "dataset": "minimal_ds", + "well_id": "A/1", + }, + } + rec = DatasetRecord.from_airtable_record(minimal) + assert rec.dataset == "minimal_ds" + assert rec.well_id == "A/1" + assert rec.fov is None + assert rec.cell_type is None + assert rec.channel_0_name is None + assert rec.record_id == "recMIN" + + def test_empty_fields_record(self): + """Record with empty 'fields' dict.""" + empty = {"id": "recEMPTY", "fields": {}} + rec = DatasetRecord.from_airtable_record(empty) + assert rec.dataset == "" + assert rec.well_id == "" + assert rec.record_id == "recEMPTY" + + def test_record_without_id(self): + """Record without an 'id' key.""" + no_id = {"fields": {"dataset": "no_id_ds", "well_id": "X/1"}} + rec = DatasetRecord.from_airtable_record(no_id) + assert rec.record_id is None + assert rec.dataset == "no_id_ds" + + def test_multiselect_cell_line(self): + """cell_line with list-of-dicts multipleSelects format.""" + record = { + "id": "recMS", + "fields": { + "dataset": "multi", + "well_id": "A/1", + "cell_line": [ + {"name": "Line-A"}, + {"name": "Line-B"}, + ], + }, + } + rec = DatasetRecord.from_airtable_record(record) + assert rec.cell_line == ["Line-A", "Line-B"] + + def test_multiselect_cell_line_plain_strings(self): + """cell_line with list-of-strings format.""" + record = { + "id": "recMS2", + "fields": { + "dataset": "multi2", + "well_id": "B/2", + "cell_line": ["Line-C", "Line-D"], + }, + } + rec = DatasetRecord.from_airtable_record(record) + assert rec.cell_line == ["Line-C", "Line-D"] + + +# ============================================================================ +# BiologicalAnnotation +# ============================================================================ + + +class TestBiologicalAnnotation: + """Test BiologicalAnnotation pydantic model validation.""" + + def test_valid_protein_tag(self): + ba = BiologicalAnnotation( + organelle="nucleus", + marker="H2B", + marker_type="protein_tag", + fluorophore="mCherry", + ) + assert ba.organelle == "nucleus" + assert ba.marker == "H2B" + assert ba.marker_type == "protein_tag" + assert ba.fluorophore == "mCherry" + + def test_valid_without_fluorophore(self): + ba = BiologicalAnnotation( + organelle="mitochondria", + marker="COX8A", + marker_type="direct_label", + ) + assert ba.fluorophore is None + + def test_valid_nuclear_dye(self): + ba = BiologicalAnnotation( + organelle="nucleus", + marker="Hoechst", + marker_type="nuclear_dye", + ) + assert ba.marker_type == "nuclear_dye" + + def test_valid_virtual_stain(self): + ba = BiologicalAnnotation( + organelle="endoplasmic_reticulum", + marker="predicted", + marker_type="virtual_stain", + ) + assert ba.marker_type == "virtual_stain" + + def test_invalid_marker_type_rejected(self): + with pytest.raises(ValidationError): + BiologicalAnnotation( + organelle="nucleus", + marker="H2B", + marker_type="invalid_type", + ) + + def test_missing_required_field_rejected(self): + with pytest.raises(ValidationError): + BiologicalAnnotation(organelle="nucleus") + + +# ============================================================================ +# Perturbation +# ============================================================================ + + +class TestPerturbation: + """Test Perturbation pydantic model validation.""" + + def test_valid_perturbation(self): + p = Perturbation(name="ZIKV", type="virus", hours_post=48.0) + assert p.name == "ZIKV" + assert p.type == "virus" + assert p.hours_post == 48.0 + + def test_default_type(self): + p = Perturbation(name="DMSO", hours_post=24.0) + assert p.type == "unknown" + + def test_extra_fields_allowed(self): + p = Perturbation( + name="ZIKV", + type="virus", + hours_post=48.0, + moi=0.5, + concentration_nm=100.0, + ) + assert p.moi == 0.5 + assert p.concentration_nm == 100.0 + + def test_missing_name_rejected(self): + with pytest.raises(ValidationError): + Perturbation(hours_post=24.0) + + def test_missing_hours_post_rejected(self): + with pytest.raises(ValidationError): + Perturbation(name="DMSO") + + +# ============================================================================ +# WellExperimentMetadata (aliased as ExperimentMetadata in the request) +# ============================================================================ + + +class TestWellExperimentMetadata: + """Test WellExperimentMetadata pydantic model validation.""" + + def test_valid_metadata(self): + m = WellExperimentMetadata( + perturbations=[ + Perturbation(name="ZIKV", type="virus", hours_post=48.0), + ], + time_sampling_minutes=5.0, + ) + assert len(m.perturbations) == 1 + assert m.time_sampling_minutes == 5.0 + + def test_empty_perturbations(self): + m = WellExperimentMetadata(time_sampling_minutes=10.0) + assert m.perturbations == [] + + def test_missing_time_sampling_rejected(self): + with pytest.raises(ValidationError): + WellExperimentMetadata( + perturbations=[], + ) + + def test_multiple_perturbations(self): + m = WellExperimentMetadata( + perturbations=[ + Perturbation(name="ZIKV", type="virus", hours_post=48.0), + Perturbation(name="Drug_A", type="drug", hours_post=24.0), + ], + time_sampling_minutes=5.0, + ) + assert len(m.perturbations) == 2 + assert m.perturbations[0].name == "ZIKV" + assert m.perturbations[1].name == "Drug_A" + + +# ============================================================================ +# ChannelAnnotationEntry +# ============================================================================ + + +class TestChannelAnnotationEntry: + """Test ChannelAnnotationEntry pydantic model.""" + + def test_fluorescence_with_annotation(self): + entry = ChannelAnnotationEntry( + channel_type="fluorescence", + biological_annotation=BiologicalAnnotation( + organelle="nucleus", + marker="H2B", + marker_type="protein_tag", + fluorophore="mCherry", + ), + ) + assert entry.channel_type == "fluorescence" + assert entry.biological_annotation.organelle == "nucleus" + + def test_labelfree_without_annotation(self): + entry = ChannelAnnotationEntry(channel_type="labelfree") + assert entry.channel_type == "labelfree" + assert entry.biological_annotation is None + + def test_invalid_channel_type_rejected(self): + with pytest.raises(ValidationError): + ChannelAnnotationEntry(channel_type="invalid") diff --git a/applications/benchmarking/DynaCLR/DINOV3/config_dinov3_convnext_tiny.yml b/applications/benchmarking/DynaCLR/DINOV3/config_dinov3_convnext_tiny.yml deleted file mode 100644 index 195aa6db9..000000000 --- a/applications/benchmarking/DynaCLR/DINOV3/config_dinov3_convnext_tiny.yml +++ /dev/null @@ -1,65 +0,0 @@ -datamodule_class: viscy.data.triplet.TripletDataModule -datamodule: - data_path: /hpc/projects/intracellular_dashboard/organelle_dynamics/2024_02_04_A549_DENV_ZIKV_timelapse/8-train-test-split/registered_test.zarr - tracks_path: /hpc/projects/intracellular_dashboard/organelle_dynamics/2024_02_04_A549_DENV_ZIKV_timelapse/8-train-test-split/track_test.zarr - batch_size: 32 - final_yx_patch_size: - - 256 - - 256 - include_fov_names: null - include_track_ids: null - initial_yx_patch_size: - - 256 - - 256 - normalizations: - - class_path: viscy.transforms.ScaleIntensityRangePercentilesd - init_args: - b_max: 1.0 - b_min: 0.0 - keys: - - RFP - lower: 50 - upper: 99 - - class_path: viscy.transforms.NormalizeIntensityd - init_args: - keys: - - Phase3D - num_workers: 10 - source_channel: - - RFP - - Phase3D - z_range: - - 15 - - 45 - -embedding: - pca_kwargs: - n_components: 8 - phate_kwargs: - decay: 40 - knn: 5 - n_components: 2 - n_jobs: -1 - random_state: 42 - reductions: - - PHATE - - PCA - -execution: - overwrite: false - save_config: true - show_config: true - -model: - model_name: facebook/dinov3-convnext-tiny-pretrain-lvd1689m - pooling_method: mean # Options: "mean", "max", "cls_token" - middle_slice_index: 18 # Specific z-slice index (if null, uses D//2) - channel_reduction_methods: - Phase3D: middle_slice - RFP: max - channel_names: - - RFP - - Phase3D - -paths: - output_path: /hpc/mydata/eduardo.hirata/repos/viscy/applications/benchmarking/DynaCLR/DINOV3/embeddings_convnext_tiny_mean.zarr diff --git a/applications/benchmarking/DynaCLR/DINOV3/dinov3_embeddings.py b/applications/benchmarking/DynaCLR/DINOV3/dinov3_embeddings.py deleted file mode 100644 index 38164e523..000000000 --- a/applications/benchmarking/DynaCLR/DINOV3/dinov3_embeddings.py +++ /dev/null @@ -1,170 +0,0 @@ -import sys -from pathlib import Path -from typing import Dict, List, Literal, Optional - -import numpy as np -import torch -from PIL import Image -from skimage.exposure import rescale_intensity -from transformers import AutoImageProcessor, AutoModel - -sys.path.append(str(Path(__file__).parent.parent)) - -from base_embedding_module import BaseEmbeddingModule, create_embedding_cli - - -class DINOv3Module(BaseEmbeddingModule): - def __init__( - self, - model_name: str = "facebook/dinov3-vitb16-pretrain-lvd1689m", - channel_reduction_methods: Optional[ - Dict[str, Literal["middle_slice", "mean", "max"]] - ] = None, - channel_names: Optional[List[str]] = None, - pooling_method: Literal["mean", "max", "cls_token"] = "mean", - middle_slice_index: Optional[int] = None, - ): - super().__init__(channel_reduction_methods, channel_names, middle_slice_index) - self.model_name = model_name - self.pooling_method = pooling_method - - self.model = None - self.processor = None - - @classmethod - def from_config(cls, cfg): - """Create model instance from configuration.""" - model_config = cfg.get("model", {}) - return cls( - model_name=model_config.get( - "model_name", "facebook/dinov3-vitb16-pretrain-lvd1689m" - ), - pooling_method=model_config.get("pooling_method", "mean"), - channel_reduction_methods=model_config.get("channel_reduction_methods", {}), - channel_names=model_config.get("channel_names", []), - middle_slice_index=model_config.get("middle_slice_index", None), - ) - - def on_predict_start(self): - if self.model is None: - self.processor = AutoImageProcessor.from_pretrained(self.model_name) - self.model = AutoModel.from_pretrained(self.model_name) - self.model.eval() - self.model.to(self.device) - - def _process_input(self, x: torch.Tensor): - """Convert tensor to PIL Images for DINOv3 processing.""" - return self._convert_to_pil_images(x) - - def _extract_features(self, pil_images): - """Extract features using DINOv3 model.""" - inputs = self.processor(pil_images, return_tensors="pt") - inputs = {k: v.to(self.device) for k, v in inputs.items()} - - with torch.no_grad(): - outputs = self.model(**inputs) - token_features = outputs.last_hidden_state - features = self._pool_features(token_features) - - return features - - def _convert_to_pil_images(self, x: torch.Tensor) -> List[Image.Image]: - """ - Convert tensor to list of PIL Images for DINOv3 processing. - - Parameters - ---------- - x : torch.Tensor - Input tensor with shape (B, C, H, W). - - Returns - ------- - list of PIL.Image.Image - List of PIL Images ready for DINOv3 processing. - """ - images = [] - - for b in range(x.shape[0]): - img_tensor = x[b] # (C, H, W) - - if img_tensor.shape[0] == 1: - # Single channel - convert to grayscale PIL - img_array = img_tensor[0].cpu().numpy() - # Normalize to 0-255 - img_normalized = ( - (img_array - img_array.min()) - / (img_array.max() - img_array.min()) - * 255 - ).astype(np.uint8) - pil_img = Image.fromarray(img_normalized, mode="L") - - elif img_tensor.shape[0] == 2: - img_array = img_tensor.cpu().numpy() - rgb_array = np.zeros( - (img_array.shape[1], img_array.shape[2], 3), dtype=np.uint8 - ) - - ch0_norm = rescale_intensity(img_array[0], out_range=(0, 255)).astype( - np.uint8 - ) - ch1_norm = rescale_intensity(img_array[1], out_range=(0, 255)).astype( - np.uint8 - ) - - rgb_array[:, :, 0] = ch0_norm # Red - rgb_array[:, :, 1] = ch1_norm # Green - rgb_array[:, :, 2] = (ch0_norm + ch1_norm) // 2 # Blue - - pil_img = Image.fromarray(rgb_array, mode="RGB") - - elif img_tensor.shape[0] == 3: - # Three channels - direct RGB - img_array = img_tensor.cpu().numpy().transpose(1, 2, 0) # HWC - img_normalized = rescale_intensity( - img_array, out_range=(0, 255) - ).astype(np.uint8) - pil_img = Image.fromarray(img_normalized, mode="RGB") - - else: - # More than 3 channels - use first 3 - img_array = img_tensor[:3].cpu().numpy().transpose(1, 2, 0) # HWC - img_normalized = rescale_intensity( - img_array, out_range=(0, 255) - ).astype(np.uint8) - pil_img = Image.fromarray(img_normalized, mode="RGB") - - images.append(pil_img) - - return images - - def _pool_features(self, features: torch.Tensor) -> torch.Tensor: - """ - Pool spatial features from DINOv3 tokens. - - Parameters - ---------- - features : torch.Tensor - Token features with shape (B, num_tokens, hidden_dim). - - Returns - ------- - torch.Tensor - Pooled features with shape (B, hidden_dim). - """ - if self.pooling_method == "cls_token": - # For ViT models, first token is usually CLS token - if "vit" in self.model_name.lower(): - return features[:, 0, :] # CLS token - else: - # For ConvNeXt, no CLS token, fall back to mean - return features.mean(dim=1) - - elif self.pooling_method == "max": - return features.max(dim=1)[0] - else: # mean pooling - return features.mean(dim=1) - - -if __name__ == "__main__": - main = create_embedding_cli(DINOv3Module, "DINOv3") - main() diff --git a/applications/benchmarking/DynaCLR/ImageNet/config.yml b/applications/benchmarking/DynaCLR/ImageNet/config.yml deleted file mode 100644 index 630ec8f99..000000000 --- a/applications/benchmarking/DynaCLR/ImageNet/config.yml +++ /dev/null @@ -1,45 +0,0 @@ -datamodule: - batch_size: 32 - final_yx_patch_size: - - 160 - - 160 - include_fov_names: null - include_track_ids: null - initial_yx_patch_size: - - 160 - - 160 - normalizations: - - class_path: viscy.transforms.ScaleIntensityRangePercentilesd - init_args: - b_max: 1.0 - b_min: 0.0 - keys: - - RFP - lower: 50 - upper: 99 - num_workers: 60 - source_channel: - - RFP - z_range: - - 15 - - 45 -embedding: - pca_kwargs: - n_components: 8 - phate_kwargs: - decay: 40 - knn: 5 - n_components: 2 - n_jobs: -1 - random_state: 42 -execution: - overwrite: true - save_config: true - show_config: true -model: - channel_reduction_methods: - RFP: max -paths: - data_path: /hpc/projects/intracellular_dashboard/organelle_dynamics/2024_02_04_A549_DENV_ZIKV_timelapse/8-train-test-split/registered_test.zarr - output_path: /home/eduardo.hirata/repos/viscy/applications/benchmarking/DynaCLR/ImageNet/20240204_A549_DENV_ZIKV_sensor_only_imagenet.zarr - tracks_path: /hpc/projects/intracellular_dashboard/organelle_dynamics/2024_02_04_A549_DENV_ZIKV_timelapse/8-train-test-split/track_test.zarr diff --git a/applications/benchmarking/DynaCLR/ImageNet/imagenet_embeddings.py b/applications/benchmarking/DynaCLR/ImageNet/imagenet_embeddings.py deleted file mode 100644 index 4b431dcac..000000000 --- a/applications/benchmarking/DynaCLR/ImageNet/imagenet_embeddings.py +++ /dev/null @@ -1,388 +0,0 @@ -""" -Generate embeddings using a pre-trained ImageNet model and save them to a zarr store -using VisCy Trainer and EmbeddingWriter callback. -""" - -import importlib -import logging -import os -from pathlib import Path -from typing import Dict, List, Literal, Optional - -import click -import timm -import torch -import yaml -from lightning.pytorch import LightningModule - -from viscy.data.triplet import TripletDataModule -from viscy.representation.embedding_writer import EmbeddingWriter -from viscy.trainer import VisCyTrainer - -logger = logging.getLogger(__name__) - - -class ImageNetModule(LightningModule): - def __init__( - self, - model_name: str = "convnext_tiny", - channel_reduction_methods: Optional[ - Dict[str, Literal["middle_slice", "mean", "max"]] - ] = None, - channel_names: Optional[List[str]] = None, - ): - """Initialize the ImageNet module. - - Args: - model_name: Name of the pre-trained ImageNet model to use - channel_reduction_methods: Dict mapping channel names to reduction methods: - - "middle_slice": Take the middle slice along the depth dimension - - "mean": Average across the depth dimension - - "max": Take the maximum value across the depth dimension - channel_names: List of channel names corresponding to the input channels - """ - super().__init__() - self.channel_reduction_methods = channel_reduction_methods or {} - self.channel_names = channel_names or [] - - try: - torch.set_float32_matmul_precision("high") - self.model = timm.create_model(model_name, pretrained=True) - self.model.eval() - except ImportError: - raise ImportError("Please install the timm library: pip install timm") - - def _reduce_5d_input(self, x: torch.Tensor) -> torch.Tensor: - """Reduce 5D input (B, C, D, H, W) to 4D (B, C, H, W) using specified methods. - - Args: - x: 5D input tensor - - Returns: - 4D tensor after applying reduction methods - """ - if x.dim() != 5: - return x - - B, C, D, H, W = x.shape - result = torch.zeros((B, C, H, W), device=x.device) - - # Process all channels at once for each reduction method to minimize loops - middle_slice_indices = [] - mean_indices = [] - max_indices = [] - - # Group channels by reduction method - for c in range(C): - channel_name = ( - self.channel_names[c] if c < len(self.channel_names) else f"channel_{c}" - ) - method = self.channel_reduction_methods.get(channel_name, "middle_slice") - - if method == "mean": - mean_indices.append(c) - elif method == "max": - max_indices.append(c) - else: # Default to middle_slice for any unknown method - middle_slice_indices.append(c) - - # Apply middle_slice reduction to all relevant channels at once - if middle_slice_indices: - indices = torch.tensor(middle_slice_indices, device=x.device) - result[:, indices] = x[:, indices, D // 2] - - # Apply mean reduction to all relevant channels at once - if mean_indices: - indices = torch.tensor(mean_indices, device=x.device) - result[:, indices] = x[:, indices].mean(dim=2) - - # Apply max reduction to all relevant channels at once - if max_indices: - indices = torch.tensor(max_indices, device=x.device) - result[:, indices] = x[:, indices].max(dim=2)[0] - - return result - - def _convert_to_rgb(self, x: torch.Tensor) -> torch.Tensor: - """Convert input tensor to 3-channel RGB format as needed. - - Args: - x: Input tensor with 1, 2, or 3+ channels - - Returns: - 3-channel tensor suitable for ImageNet models - """ - if x.shape[1] == 3: - return x - elif x.shape[1] == 1: - # Convert to RGB by repeating the channel 3 times - return x.repeat(1, 3, 1, 1) - elif x.shape[1] == 2: - # Normalize each channel independently to handle different scales - B, _, H, W = x.shape - x_3ch = torch.zeros((B, 3, H, W), device=x.device, dtype=x.dtype) - - # Normalize each channel to 0-1 range - ch0 = x[:, 0:1] - ch1 = x[:, 1:2] - - ch0_min = ch0.reshape(B, -1).min(dim=1, keepdim=True)[0].reshape(B, 1, 1, 1) - ch0_max = ch0.reshape(B, -1).max(dim=1, keepdim=True)[0].reshape(B, 1, 1, 1) - ch0_range = ch0_max - ch0_min + 1e-7 # Add epsilon for numerical stability - ch0_norm = (ch0 - ch0_min) / ch0_range - - ch1_min = ch1.reshape(B, -1).min(dim=1, keepdim=True)[0].reshape(B, 1, 1, 1) - ch1_max = ch1.reshape(B, -1).max(dim=1, keepdim=True)[0].reshape(B, 1, 1, 1) - ch1_range = ch1_max - ch1_min + 1e-7 # Add epsilon for numerical stability - ch1_norm = (ch1 - ch1_min) / ch1_range - - # Create blended RGB channels - map each normalized channel to different colors - x_3ch[:, 0] = ch0_norm.squeeze(1) # R channel from first input - x_3ch[:, 1] = ch1_norm.squeeze(1) # G channel from second input - x_3ch[:, 2] = 0.5 * ( - ch0_norm.squeeze(1) + ch1_norm.squeeze(1) - ) # B channel as blend - - return x_3ch - else: - # For more than 3 channels, use the first 3 - return x[:, :3] - - def predict_step(self, batch, batch_idx, dataloader_idx=0): - """Extract features from the input images. - - Returns: - Dictionary with features, properly shaped empty projections tensor, and index information - """ - x = batch["anchor"] - - # Handle 5D input (B, C, D, H, W) using configured reduction methods - if x.dim() == 5: - x = self._reduce_5d_input(x) - - # Convert input to RGB format - x = self._convert_to_rgb(x) - - # Get embeddings - with torch.no_grad(): - features = self.model.forward_features(x) - - # Average pooling to get feature vector - if features.dim() > 2: - features = features.mean(dim=[2, 3]) - - # Return features and empty projections with correct batch dimension - return { - "features": features, - "projections": torch.zeros((features.shape[0], 0), device=features.device), - "index": batch["index"], - } - - -def load_config(config_file): - """Load configuration from a YAML file.""" - with open(config_file, "r") as f: - config = yaml.safe_load(f) - return config - - -def load_normalization_from_config(norm_config): - """Load a normalization transform from a configuration dictionary.""" - class_path = norm_config["class_path"] - init_args = norm_config.get("init_args", {}) - - # Split module and class name - module_path, class_name = class_path.rsplit(".", 1) - - # Import the module - module = importlib.import_module(module_path) - - # Get the class - transform_class = getattr(module, class_name) - - # Instantiate the transform - return transform_class(**init_args) - - -@click.command() -@click.option( - "--config", - "-c", - type=click.Path(exists=True), - required=True, - help="Path to YAML configuration file", -) -@click.option( - "--model", - "-m", - type=str, - default="convnext_tiny", - help="Name of the pre-trained ImageNet model to use", -) -def main(config, model): - """Extract ImageNet embeddings and save to zarr format using VisCy Trainer.""" - # Configure logging - logging.basicConfig(level=logging.INFO) - logger = logging.getLogger(__name__) - - # Load config file - cfg = load_config(config) - logger.info(f"Loaded configuration from {config}") - - # Prepare datamodule parameters - dm_params = {} - - # Add data and tracks paths from the paths section - if "paths" not in cfg: - raise ValueError("Configuration must contain a 'paths' section") - - if "data_path" not in cfg["paths"]: - raise ValueError( - "Data path is required in the configuration file (paths.data_path)" - ) - dm_params["data_path"] = cfg["paths"]["data_path"] - - if "tracks_path" not in cfg["paths"]: - raise ValueError( - "Tracks path is required in the configuration file (paths.tracks_path)" - ) - dm_params["tracks_path"] = cfg["paths"]["tracks_path"] - - # Add datamodule parameters - if "datamodule" not in cfg: - raise ValueError("Configuration must contain a 'datamodule' section") - - # Prepare normalizations - if ( - "normalizations" not in cfg["datamodule"] - or not cfg["datamodule"]["normalizations"] - ): - raise ValueError( - "Normalizations are required in the configuration file (datamodule.normalizations)" - ) - - norm_configs = cfg["datamodule"]["normalizations"] - normalizations = [load_normalization_from_config(norm) for norm in norm_configs] - dm_params["normalizations"] = normalizations - - # Copy all other datamodule parameters - for param, value in cfg["datamodule"].items(): - if param != "normalizations": - # Handle patch sizes - if param == "patch_size": - dm_params["initial_yx_patch_size"] = value - dm_params["final_yx_patch_size"] = value - else: - dm_params[param] = value - - # Set up the data module - logger.info("Setting up data module") - dm = TripletDataModule(**dm_params) - - # Get model parameters for handling 5D inputs - channel_reduction_methods = {} - - if "model" in cfg and "channel_reduction_methods" in cfg["model"]: - channel_reduction_methods = cfg["model"]["channel_reduction_methods"] - - # Initialize ImageNet model with reduction settings - logger.info(f"Loading ImageNet model: {model}") - model_module = ImageNetModule( - model_name=model, - channel_reduction_methods=channel_reduction_methods, - channel_names=dm_params.get("source_channel", []), - ) - - # Get dimensionality reduction parameters from config - phate_kwargs = None - pca_kwargs = None - - if "embedding" in cfg: - # Check for both capitalization variants and normalize - if "phate_kwargs" in cfg["embedding"]: - phate_kwargs = cfg["embedding"]["phate_kwargs"] - - if "umap_kwargs" in cfg["embedding"]: - cfg["embedding"]["umap_kwargs"] - - if "pca_kwargs" in cfg["embedding"]: - pca_kwargs = cfg["embedding"]["pca_kwargs"] - - # Check if output path exists and should be overwritten - if "output_path" not in cfg["paths"]: - raise ValueError( - "Output path is required in the configuration file (paths.output_path)" - ) - - output_path = Path(cfg["paths"]["output_path"]) - output_dir = output_path.parent - output_dir.mkdir(parents=True, exist_ok=True) - - overwrite = False - if "execution" in cfg and "overwrite" in cfg["execution"]: - overwrite = cfg["execution"]["overwrite"] - elif output_path.exists(): - logger.warning(f"Output path {output_path} already exists, will overwrite") - overwrite = True - - # Set up EmbeddingWriter callback - embedding_writer = EmbeddingWriter( - output_path=output_path, - phate_kwargs=phate_kwargs, - pca_kwargs=pca_kwargs, - overwrite=overwrite, - ) - - # Set up and run VisCy trainer - logger.info("Setting up VisCy trainer") - trainer = VisCyTrainer( - accelerator="gpu" if torch.cuda.is_available() else "cpu", - devices=1, - callbacks=[embedding_writer], - inference_mode=True, - ) - - logger.info(f"Running prediction and saving to {output_path}") - trainer.predict(model_module, datamodule=dm) - - # Save configuration if requested - save_config_flag = True - show_config_flag = True - - if "execution" in cfg: - if "save_config" in cfg["execution"]: - save_config_flag = cfg["execution"]["save_config"] - if "show_config" in cfg["execution"]: - show_config_flag = cfg["execution"]["show_config"] - - # Save configuration if requested - if save_config_flag: - config_path = os.path.join(output_dir, "config.yml") - with open(config_path, "w") as f: - yaml.dump(cfg, f, default_flow_style=False) - logger.info(f"Configuration saved to {config_path}") - - # Display configuration if requested - if show_config_flag: - click.echo("\nConfiguration used:") - click.echo("-" * 40) - for key, value in cfg.items(): - click.echo(f"{key}:") - if isinstance(value, dict): - for subkey, subvalue in value.items(): - if isinstance(subvalue, list) and subkey == "normalizations": - click.echo(f" {subkey}:") - for norm in subvalue: - click.echo(f" - class_path: {norm['class_path']}") - click.echo(f" init_args: {norm['init_args']}") - else: - click.echo(f" {subkey}: {subvalue}") - else: - click.echo(f" {value}") - click.echo("-" * 40) - - logger.info("Done!") - - -if __name__ == "__main__": - main() diff --git a/applications/benchmarking/DynaCLR/OpenPhenom/config_template.yml b/applications/benchmarking/DynaCLR/OpenPhenom/config_template.yml deleted file mode 100644 index 826e894a0..000000000 --- a/applications/benchmarking/DynaCLR/OpenPhenom/config_template.yml +++ /dev/null @@ -1,67 +0,0 @@ -# OpenPhenom Embeddings Configuration - -# Paths section -paths: - output_path: "/home/eduardo.hirata/repos/viscy/applications/benchmarking/DynaCLR/OpenPhenom/openphenom_sec61b_n_phase_3.zarr" - -# Model configuration -model: - # Channel-specific 5D input handling methods - # Options: "middle_slice", "mean", "max" - # Default is "middle_slice" if not specified - channel_reduction_methods: - "Phase3D": "middle_slice" # For phase contrast, middle slice often works well - "raw GFP EX488 EM525-45": "max" - -# Data module configuration -datamodule_class: viscy.data.triplet.TripletDataModule -datamodule: - data_path: /hpc/projects/intracellular_dashboard/organelle_dynamics/2024_11_07_A549_SEC61_ZIKV_DENV/2-assemble/2024_11_07_A549_SEC61_DENV.zarr - tracks_path: /hpc/projects/intracellular_dashboard/organelle_dynamics/2024_11_07_A549_SEC61_ZIKV_DENV/1-preprocess/label-free/4-track-gt/2024_11_07_A549_SEC61_ZIKV_DENV_2_cropped.zarr - source_channel: - - Phase3D - - "raw GFP EX488 EM525-45" - z_range: [25, 40] - batch_size: 32 - num_workers: 10 - initial_yx_patch_size: [192, 192] - final_yx_patch_size: [192, 192] - predict_cells: true - include_fov_names: - - "/C/2/000000" - - "/C/2/000000" - - "/C/2/000000" - - "/C/2/000000" - include_track_ids: [33,60,57,65] - normalizations: - - class_path: viscy.transforms.ScaleIntensityRangePercentilesd - init_args: - keys: ["Phase3D"] - lower: 50 - upper: 99 - b_min: 0.0 - b_max: 1.0 - - class_path: viscy.transforms.ScaleIntensityRangePercentilesd - init_args: - keys: ["raw GFP EX488 EM525-45"] - lower: 50 - upper: 99 - b_min: 0.0 - b_max: 1.0 - -# Embedding parameters -embedding: - phate_kwargs: - n_components: 2 - knn: 5 - decay: 40 - n_jobs: -1 - random_state: 42 - pca_kwargs: - n_components: 2 - -# Execution configuration -execution: - overwrite: false - save_config: true - show_config: true diff --git a/applications/benchmarking/DynaCLR/OpenPhenom/openphenom_embeddings.py b/applications/benchmarking/DynaCLR/OpenPhenom/openphenom_embeddings.py deleted file mode 100644 index 11d9eaee9..000000000 --- a/applications/benchmarking/DynaCLR/OpenPhenom/openphenom_embeddings.py +++ /dev/null @@ -1,69 +0,0 @@ -import sys -from pathlib import Path -from typing import Dict, List, Literal, Optional - -import torch -from transformers import AutoModel - -sys.path.append(str(Path(__file__).parent.parent)) - -from base_embedding_module import BaseEmbeddingModule, create_embedding_cli - - -class OpenPhenomModule(BaseEmbeddingModule): - def __init__( - self, - channel_reduction_methods: Optional[ - Dict[str, Literal["middle_slice", "mean", "max"]] - ] = None, - channel_names: Optional[List[str]] = None, - middle_slice_index: Optional[int] = None, - ): - super().__init__(channel_reduction_methods, channel_names, middle_slice_index) - - try: - self.model = AutoModel.from_pretrained( - "recursionpharma/OpenPhenom", trust_remote_code=True - ) - self.model.eval() - except ImportError: - raise ImportError( - "Please install the OpenPhenom dependencies: pip install transformers" - ) - - @classmethod - def from_config(cls, cfg): - """Create model instance from configuration.""" - model_config = cfg.get("model", {}) - dm_config = cfg.get("datamodule", {}) - - return cls( - channel_reduction_methods=model_config.get("channel_reduction_methods", {}), - channel_names=dm_config.get("source_channel", []), - ) - - def on_predict_start(self): - """Move model to GPU when prediction starts.""" - self.model.to(self.device) - - def _process_input(self, x: torch.Tensor): - """Convert to uint8 as OpenPhenom expects uint8 inputs.""" - if x.dtype != torch.uint8: - x = ( - ((x - x.min()) / (x.max() - x.min()) * 255) - .clamp(0, 255) - .to(torch.uint8) - ) - return x - - def _extract_features(self, processed_input): - """Extract features using OpenPhenom model.""" - # Get embeddings - self.model.return_channelwise_embeddings = False - features = self.model.predict(processed_input) - return features - - -if __name__ == "__main__": - main = create_embedding_cli(OpenPhenomModule, "OpenPhenom") - main() diff --git a/applications/benchmarking/DynaCLR/SAM2/run_sam2.sh b/applications/benchmarking/DynaCLR/SAM2/run_sam2.sh deleted file mode 100644 index 9bf781bc0..000000000 --- a/applications/benchmarking/DynaCLR/SAM2/run_sam2.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -#SBATCH --job-name=dynaclr_imagenet -#SBATCH --nodes=1 -#SBATCH --ntasks-per-node=1 -#SBATCH --partition=gpu -#SBATCH --gres=gpu:1 -#SBATCH --cpus-per-task=64 -#SBATCH --mem-per-cpu=7G -#SBATCH --time=0-02:00:00 -#SBATCH --output=./slurm_logs/%j_dynaclr_sam2.out - - -module load anaconda/latest -conda activate viscy - -CONFIG_PATH=/home/eduardo.hirata/repos/viscy/applications/benchmarking/DynaCLR/SAM2/sam2_sensor_only.yml -python /home/eduardo.hirata/repos/viscy/applications/benchmarking/DynaCLR/SAM2/sam2_embeddings.py -c $CONFIG_PATH diff --git a/applications/benchmarking/DynaCLR/SAM2/sam2_config.yml b/applications/benchmarking/DynaCLR/SAM2/sam2_config.yml deleted file mode 100644 index 5e3771d25..000000000 --- a/applications/benchmarking/DynaCLR/SAM2/sam2_config.yml +++ /dev/null @@ -1,60 +0,0 @@ -datamodule_class: viscy.data.triplet.TripletDataModule -datamodule: - data_path: /hpc/projects/intracellular_dashboard/organelle_dynamics/2024_11_07_A549_SEC61_ZIKV_DENV/2-assemble/2024_11_07_A549_SEC61_DENV.zarr - tracks_path: /hpc/projects/intracellular_dashboard/organelle_dynamics/2024_11_07_A549_SEC61_ZIKV_DENV/1-preprocess/label-free/4-track-gt/2024_11_07_A549_SEC61_ZIKV_DENV_2_cropped.zarr - batch_size: 32 - final_yx_patch_size: - - 192 - - 192 - include_fov_names: null - include_track_ids: null - initial_yx_patch_size: - - 192 - - 192 - normalizations: - - class_path: viscy.transforms.ScaleIntensityRangePercentilesd - init_args: - b_max: 1.0 - b_min: 0.0 - keys: - - Phase3D - lower: 50 - upper: 99 - - class_path: viscy.transforms.ScaleIntensityRangePercentilesd - init_args: - b_max: 1.0 - b_min: 0.0 - keys: - - raw GFP EX488 EM525-45 - lower: 50 - upper: 99 - num_workers: 10 - source_channel: - - Phase3D - - raw GFP EX488 EM525-45 - z_range: - - 25 - - 40 -embedding: - pca_kwargs: - n_components: 8 - phate_kwargs: - decay: 40 - knn: 5 - n_components: 2 - n_jobs: -1 - random_state: 42 - reductions: - - PHATE - - PCA -execution: - overwrite: false - save_config: true - show_config: true -model: - model_name: facebook/sam2-hiera-base-plus - channel_reduction_methods: - Phase3D: middle_slice - raw GFP EX488 EM525-45: max -paths: - output_path: /home/eduardo.hirata/repos/viscy/applications/benchmarking/DynaCLR/SAM2/sam2_sec61b_n_phase_all_highresfeats0.zarr diff --git a/applications/benchmarking/DynaCLR/SAM2/sam2_embeddings.py b/applications/benchmarking/DynaCLR/SAM2/sam2_embeddings.py deleted file mode 100644 index c664ca5cd..000000000 --- a/applications/benchmarking/DynaCLR/SAM2/sam2_embeddings.py +++ /dev/null @@ -1,101 +0,0 @@ -import sys -from pathlib import Path -from typing import Dict, List, Literal, Optional - -import torch -from sam2.sam2_image_predictor import SAM2ImagePredictor -from skimage.exposure import rescale_intensity - -sys.path.append(str(Path(__file__).parent.parent)) - -from base_embedding_module import BaseEmbeddingModule, create_embedding_cli - - -class SAM2Module(BaseEmbeddingModule): - def __init__( - self, - model_name: str = "facebook/sam2-hiera-base-plus", - channel_reduction_methods: Optional[ - Dict[str, Literal["middle_slice", "mean", "max"]] - ] = None, - channel_names: Optional[List[str]] = None, - middle_slice_index: Optional[int] = None, - ): - super().__init__(channel_reduction_methods, channel_names, middle_slice_index) - self.model_name = model_name - self.model = None # Initialize in on_predict_start when device is set - - @classmethod - def from_config(cls, cfg): - """Create model instance from configuration.""" - model_config = cfg.get("model", {}) - - return cls( - model_name=model_config.get("model_name", "facebook/sam2-hiera-base-plus"), - channel_reduction_methods=model_config.get("channel_reduction_methods", {}), - middle_slice_index=model_config.get("middle_slice_index", None), - ) - - def on_predict_start(self): - """Initialize model with proper device when prediction starts.""" - if self.model is None: - self.model = SAM2ImagePredictor.from_pretrained( - self.model_name, device=self.device - ) - - def _process_input(self, x: torch.Tensor): - """Convert input tensor to 3-channel RGB format as needed for SAM2.""" - return self._convert_to_rgb(x) - - def _extract_features(self, image_list): - """Extract features using SAM2 model.""" - self.model.set_image_batch(image_list) - # Extract high-resolution features and apply global average pooling - features = self.model._features["high_res_feats"][0].mean(dim=(2, 3)) - return features - - def _convert_to_rgb(self, x: torch.Tensor) -> list: - """ - Convert input tensor to 3-channel RGB format as needed for SAM2. - - Parameters - ---------- - x : torch.Tensor - Input tensor with 1, 2, or 3+ channels and shape (B, C, H, W). - - Returns - ------- - list of numpy.ndarray - List of numpy arrays in HWC format for SAM2 processing. - """ - # Convert to RGB and scale to [0, 255] range for SAM2 - if x.shape[1] == 1: - x_rgb = x.repeat(1, 3, 1, 1) * 255.0 - elif x.shape[1] == 2: - x_3ch = torch.zeros( - (x.shape[0], 3, x.shape[2], x.shape[3]), device=x.device - ) - x[:, 0] = rescale_intensity(x[:, 0], out_range="uint8") - x[:, 1] = rescale_intensity(x[:, 1], out_range="uint8") - - x_3ch[:, 0] = x[:, 0] - x_3ch[:, 1] = x[:, 1] - x_3ch[:, 2] = 0.5 * (x[:, 0] + x[:, 1]) # B channel as blend - x_rgb = x_3ch - - elif x.shape[1] == 3: - x_rgb = rescale_intensity(x, out_range="uint8") - else: - # More than 3 channels, normalize first 3 and scale - x_3ch = x[:, :3] - x_rgb = rescale_intensity(x_3ch, out_range="uint8") - - # Convert to list of numpy arrays in HWC format for SAM2 - return [ - x_rgb[i].cpu().numpy().transpose(1, 2, 0) for i in range(x_rgb.shape[0]) - ] - - -if __name__ == "__main__": - main = create_embedding_cli(SAM2Module, "SAM2") - main() diff --git a/applications/benchmarking/DynaCLR/SAM2/sam2_visualizations.py b/applications/benchmarking/DynaCLR/SAM2/sam2_visualizations.py deleted file mode 100644 index 46a9b2eac..000000000 --- a/applications/benchmarking/DynaCLR/SAM2/sam2_visualizations.py +++ /dev/null @@ -1,213 +0,0 @@ -# %% -""" -Test script to visualize SAM2 input images and feature processing. -This script helps debug what images are being passed to SAM2 and how they're processed. -""" - -import os -from pathlib import Path - -import matplotlib.pyplot as plt -from sam2_embeddings import SAM2Module, load_config, load_normalization_from_config - -from viscy.data.triplet import TripletDataModule - - -def visualize_rgb_conversion(x_original, x_rgb_list, save_dir="./debug_images"): - """Visualize the RGB conversion process""" - os.makedirs(save_dir, exist_ok=True) - - print(f"Original input shape: {x_original.shape}") - print(f"Original input range: [{x_original.min():.3f}, {x_original.max():.3f}]") - - # Plot original channels - B, C = x_original.shape[:2] - fig, axes = plt.subplots(3, max(3, C), figsize=(15, 12)) - - # Plot original channels - for c in range(C): - ax = axes[0, c] if C > 1 else axes[0, 0] - img = x_original[0, c].cpu().numpy() - im = ax.imshow(img, cmap="gray") - ax.set_title(f"Original Channel {c}") - ax.axis("off") - plt.colorbar(im, ax=ax) - - # Plot RGB conversion - rgb_img = x_rgb_list[0] # First batch item - print(f"RGB image shape: {rgb_img.shape}") - print(f"RGB image range: [{rgb_img.min():.3f}, {rgb_img.max():.3f}]") - - for c in range(3): - ax = axes[1, c] - im = ax.imshow(rgb_img[:, :, c], cmap="gray") - ax.set_title(f"RGB Channel {c}") - ax.axis("off") - plt.colorbar(im, ax=ax) - - # Plot merged RGB image - ax = axes[2, 0] - # Normalize to 0-1 for display - rgb_display = rgb_img.copy() - rgb_display = (rgb_display - rgb_display.min()) / ( - rgb_display.max() - rgb_display.min() - ) - ax.imshow(rgb_display) - ax.set_title("Merged RGB Image") - ax.axis("off") - - # Check if RGB is properly scaled to 0-255 - ax = axes[2, 1] - ax.text( - 0.1, - 0.8, - f"RGB Range: [{rgb_img.min():.1f}, {rgb_img.max():.1f}]", - transform=ax.transAxes, - ) - ax.text(0.1, 0.6, "Expected: [0, 255]", transform=ax.transAxes) - ax.text( - 0.1, - 0.4, - f"Properly scaled: {rgb_img.min() >= 0 and rgb_img.max() <= 255}", - transform=ax.transAxes, - ) - ax.text(0.1, 0.2, f"Mean: {rgb_img.mean():.1f}", transform=ax.transAxes) - ax.set_title("RGB Scaling Check") - ax.axis("off") - - plt.tight_layout() - plt.savefig(f"{save_dir}/rgb_conversion.png", dpi=150, bbox_inches="tight") - plt.close() - - -def test_sam2_processing(config_path, num_samples=3): - """Test SAM2 processing with visualization""" - - # Load configuration - cfg = load_config(config_path) - print(f"Loaded config from: {config_path}") - - # Setup data module (same as in main function) - dm_params = {} - dm_params["data_path"] = cfg["paths"]["data_path"] - dm_params["tracks_path"] = cfg["paths"]["tracks_path"] - - # Setup normalizations - norm_configs = cfg["datamodule"]["normalizations"] - normalizations = [load_normalization_from_config(norm) for norm in norm_configs] - dm_params["normalizations"] = normalizations - - # Copy other datamodule parameters - for param, value in cfg["datamodule"].items(): - if param != "normalizations": - if param == "patch_size": - dm_params["initial_yx_patch_size"] = value - dm_params["final_yx_patch_size"] = value - else: - dm_params[param] = value - - print("Setting up data module...") - dm = TripletDataModule(**dm_params) - dm.setup(stage="predict") - - # Get model parameters - channel_reduction_methods = {} - if "model" in cfg and "channel_reduction_methods" in cfg["model"]: - channel_reduction_methods = cfg["model"]["channel_reduction_methods"] - - # Initialize SAM2 model - print("Loading SAM2 model...") - model = SAM2Module( - model_name=cfg["model"]["model_name"], - channel_reduction_methods=channel_reduction_methods, - ) - - # Get dataloader - predict_dataloader = dm.predict_dataloader() - - print(f"Testing with {num_samples} samples...") - - # Test processing - for i, batch in enumerate(predict_dataloader): - if i >= num_samples: - break - - print(f"\n--- Sample {i + 1} ---") - x = batch["anchor"] - print(f"Input tensor shape: {x.shape}") - print(f"Input tensor range: [{x.min():.3f}, {x.max():.3f}]") - - # Test 5D reduction if needed - if x.dim() == 5: - print("Applying 5D reduction...") - x_reduced = model._reduce_5d_input(x) - print(f"After 5D reduction: {x_reduced.shape}") - print(f"Reduction methods: {model.channel_reduction_methods}") - else: - x_reduced = x - - # Test RGB conversion - print("Converting to RGB...") - x_rgb_list = model._convert_to_rgb(x_reduced) - print(f"RGB conversion result: {len(x_rgb_list)} images") - print(f"First RGB image shape: {x_rgb_list[0].shape}") - - # Visualize the conversion - visualize_rgb_conversion(x_reduced, x_rgb_list, f"./debug_images/sample_{i}") - - # Test feature extraction (if model is available) - try: - print("Testing feature extraction...") - model.model = model.model or model.on_predict_start() - model.model.set_image_batch(x_rgb_list) - - # Check what features are available - features_dict = model.model._features - print(f"Available features: {list(features_dict.keys())}") - - if "high_res_feats" in features_dict: - high_res_feats = features_dict["high_res_feats"] - print(f"High-res features length: {len(high_res_feats)}") - for j, feat in enumerate(high_res_feats): - print(f" Layer {j}: {feat.shape}") - - if "image_embed" in features_dict: - image_embed = features_dict["image_embed"] - print(f"Image embed shape: {image_embed.shape}") - - # Extract final features (current approach) - features = model.model._features["high_res_feats"][1].mean(dim=(2, 3)) - print(f"Final features shape: {features.shape}") - print(f"Final features range: [{features.min():.3f}, {features.max():.3f}]") - - except Exception as e: - print(f"Feature extraction failed: {e}") - - print("-" * 50) - - -def main(): - """Main function to run the test""" - config_path = "/home/eduardo.hirata/repos/viscy/applications/benchmarking/DynaCLR/SAM2/sam2_sensor_only.yml" - - if not Path(config_path).exists(): - print(f"Config file not found: {config_path}") - print("Please provide a valid config file path") - return - - try: - test_sam2_processing(config_path, num_samples=3) - print("\nTest completed successfully!") - print("Check ./debug_images/ for visualization outputs") - except Exception as e: - print(f"Test failed: {e}") - import traceback - - traceback.print_exc() - - -# %% -if __name__ == "__main__": - main() - -# %% diff --git a/applications/contrastive_phenotyping/evaluation/archive/ALFI_MSD_v2.py b/applications/contrastive_phenotyping/evaluation/archive/ALFI_MSD_v2.py deleted file mode 100644 index ed86e0b4c..000000000 --- a/applications/contrastive_phenotyping/evaluation/archive/ALFI_MSD_v2.py +++ /dev/null @@ -1,215 +0,0 @@ -# %% -from pathlib import Path - -import matplotlib.pyplot as plt -import numpy as np -import xarray as xr -from scipy import stats - -from viscy.representation.embedding_writer import read_embedding_dataset -from viscy.representation.evaluation.distance import ( - compute_track_displacement, -) - -# Paths to datasets -feature_paths = { - "7 min interval": "/hpc/projects/organelle_phenotyping/ALFI_ntxent_loss/logs_alfi_ntxent_time_intervals/predictions/ALFI_7mins.zarr", - "14 min interval": "/hpc/projects/organelle_phenotyping/ALFI_ntxent_loss/logs_alfi_ntxent_time_intervals/predictions/ALFI_14mins.zarr", - "28 min interval": "/hpc/projects/organelle_phenotyping/ALFI_ntxent_loss/logs_alfi_ntxent_time_intervals/predictions/ALFI_28mins.zarr", - "56 min interval": "/hpc/projects/organelle_phenotyping/ALFI_ntxent_loss/logs_alfi_ntxent_time_intervals/predictions/ALFI_56mins.zarr", - "91 min interval": "/hpc/projects/organelle_phenotyping/ALFI_ntxent_loss/logs_alfi_ntxent_time_intervals/predictions/ALFI_91mins.zarr", -} - - -cmap = plt.get_cmap("tab10") # or use "Set2", "tab20", etc. -labels = list(feature_paths.keys()) -interval_colors = {label: cmap(i % cmap.N) for i, label in enumerate(labels)} - -# Print and check each path -for label, path in feature_paths.items(): - print(f"{label} color: {interval_colors[label]}") - assert Path(path).exists(), f"Path {path} does not exist" - -# %% Compute MSD for each dataset -results = {} -raw_displacements = {} - -DISTANCE_METRIC = "cosine" -for label, path in feature_paths.items(): - results[label] = {} - print(f"\nProcessing {label}...") - embedding_dataset = read_embedding_dataset(Path(path)) - - # Compute displacements - displacements_per_tau = compute_track_displacement( - embedding_dataset=embedding_dataset, - distance_metric=DISTANCE_METRIC, - ) - - # Store displacements with conditional normalization - if DISTANCE_METRIC == "cosine": - # Cosine distance is already scale-invariant, no normalization needed - for tau, displacements in displacements_per_tau.items(): - results[label][tau] = displacements - else: - # Normalize by embeddings variance for euclidean distance - embeddings_variance = np.var(embedding_dataset["features"].values) - for tau, displacements in displacements_per_tau.items(): - results[label][tau] = [disp / embeddings_variance for disp in displacements] - - -# %% Plot MSD vs time (linear scale) -show_power_law_fits = True -log_scale = True -title = "Mean Track Displacement vs Time Shift" - -fig, ax = plt.subplots(figsize=(10, 7)) - -for model_type, msd_data in results.items(): - time_lags = sorted(msd_data.keys()) - msd_means = [] - msd_stds = [] - - # Compute mean and std of MSD for each time lag - for tau in time_lags: - displacements = np.array(msd_data[tau]) - msd_means.append(np.mean(displacements)) - msd_stds.append(np.std(displacements) / np.sqrt(len(displacements))) - - time_lags = np.array(time_lags) - msd_means = np.array(msd_means) - msd_stds = np.array(msd_stds) - - # Plot with error bars - color = interval_colors.get(model_type, "#1f77b4") - ax.errorbar( - time_lags, - msd_means, - yerr=msd_stds, - marker="o", - label=f"{model_type.replace('_', ' ').title()}", - color=color, - capsize=3, - capthick=1, - linewidth=2, - markersize=6, - ) - # Fit power law if requested - if show_power_law_fits and len(time_lags) > 3: - valid_mask = (time_lags > 0) & (msd_means > 0) - if np.sum(valid_mask) > 3: - log_tau = np.log(time_lags[valid_mask]) - log_msd = np.log(msd_means[valid_mask]) - - slope, intercept, r_value, p_value, std_err = stats.linregress( - log_tau, log_msd - ) - - # Plot fit line - tau_fit = np.linspace( - time_lags[valid_mask][0], time_lags[valid_mask][-1], 50 - ) - msd_fit = np.exp(intercept) * tau_fit**slope - - ax.plot( - tau_fit, - msd_fit, - "--", - color=color, - alpha=0.7, - label=f"{model_type}: α={slope:.2f} (R²={r_value**2:.3f})", - ) - - ax.set_xlabel("Time Lag (τ)", fontsize=12) - ax.set_ylabel("Mean Track Displacement", fontsize=12) - ax.set_title(title, fontsize=14) - - if log_scale: - ax.set_xscale("log") - ax.set_yscale("log") - ax.grid(True, alpha=0.3) - - ax.legend() - plt.tight_layout() -plt.savefig(f"msd_vs_time_shift_{DISTANCE_METRIC}.png", dpi=300) -# %% -# Step size analysis - - -def extract_step_sizes(embedding_dataset: xr.Dataset): - """Extract step sizes with simple coordinate access.""" - - unique_tracks_df = ( - embedding_dataset[["fov_name", "track_id"]].to_dataframe().drop_duplicates() - ) - all_step_sizes = [] - - for fov_name, track_id in zip( - unique_tracks_df["fov_name"], unique_tracks_df["track_id"] - ): - track_data = embedding_dataset.where( - (embedding_dataset["fov_name"] == fov_name) - & (embedding_dataset["track_id"] == track_id), - drop=True, - ) - time_order = np.argsort(track_data["t"].values) - times = track_data["t"].values[time_order] - track_embeddings = track_data["features"].values[time_order] - if len(times) != len(np.unique(times)): - print(f"Duplicates found in FOV {fov_name}, track {track_id}") - - if len(track_embeddings) > 1: - steps = np.diff(track_embeddings, axis=0) - step_sizes = np.linalg.norm(steps, axis=1) - all_step_sizes.extend(step_sizes) - - return np.array(all_step_sizes) - - -all_step_data = {} -cv_values = [] -labels = [] - -for label, path in feature_paths.items(): - print(f"\nProcessing {label}...") - embedding_dataset = read_embedding_dataset(Path(path)) - steps = extract_step_sizes(embedding_dataset) - all_step_data[label] = steps - - # Calculate coefficient of variation - cv = np.std(steps) / np.mean(steps) - cv_values.append(cv) - labels.append(label.replace("_", " ").title()) - -# %% -# Plot histograms -ax1, ax2 = plt.subplots(1, 2, figsize=(15, 6))[1] - -for model_type, steps in all_step_data.items(): - color = interval_colors.get(model_type, "#1f77b4") - ax1.hist( - steps, - bins=50, - alpha=0.7, - color=color, - label=f"{model_type.replace('_', ' ').title()} (n={len(steps)}, μ={np.mean(steps):.3f}, σ={np.std(steps):.3f})", - ) - -ax1.set_xlabel("Step Size") -ax1.set_ylabel("Frequency") -ax1.set_title("Step Size Distributions") -ax1.legend() - -# Plot coefficient of variation -bar_colors = [ - interval_colors.get(model_type, "#1f77b4") for model_type in results.keys() -] -bars = ax2.bar(labels, cv_values, color=bar_colors, alpha=0.7) -ax2.set_ylabel("Coefficient of Variation (σ/μ)") -ax2.set_title("Step Size Variability") -ax2.tick_params(axis="x", rotation=45) -plt.tight_layout() -# plt.show() -plt.savefig(f"step_size_distributions_{DISTANCE_METRIC}.png", dpi=300) - -# %% diff --git a/applications/contrastive_phenotyping/evaluation/archive/analyze_embeddings.py b/applications/contrastive_phenotyping/evaluation/archive/analyze_embeddings.py deleted file mode 100644 index 9ff774e8e..000000000 --- a/applications/contrastive_phenotyping/evaluation/archive/analyze_embeddings.py +++ /dev/null @@ -1,108 +0,0 @@ -# %% Imports -from pathlib import Path - -import matplotlib.pyplot as plt -import numpy as np -import seaborn as sns - -from viscy.representation.embedding_writer import read_embedding_dataset -from viscy.representation.evaluation import load_annotation -from viscy.representation.evaluation.dimensionality_reduction import ( - compute_pca, - compute_umap, -) - -# %% Paths and parameters - -path_embedding = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/time_sampling_strategies/time_interval/predict/feb_test_time_interval_1_epoch_51.zarr" -) -path_annotations_infection = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/2024_02_04_A549_DENV_ZIKV_timelapse/8-train-test-split/supervised_inf_pred/extracted_inf_state.csv" -) -path_annotations_division = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/2024_02_04_A549_DENV_ZIKV_timelapse/8-train-test-split/" -) - -path_tracks = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/2024_06_13_SEC61_TOMM20_ZIKV_DENGUE_1/4.1-tracking/test_tracking_4.zarr" -) - -path_images = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/2024_06_13_SEC61_TOMM20_ZIKV_DENGUE_1/2-register/registered_chunked.zarr" -) - -# %% Load embeddings and annotations. - -dataset = read_embedding_dataset(path_embedding) -# load all unprojected features: -features = dataset["features"] -# or select a well: -# features - features[features["fov_name"].str.contains("B/4")] -features - -feb_infection = load_annotation( - dataset, - path_annotations_infection, - "infection_state", - {0.0: "background", 1.0: "uninfected", 2.0: "infected"}, -) - -# %% interactive quality control: principal components -# Compute principal components and ranks of embeddings and projections. - -# compute rank -rank_features = np.linalg.matrix_rank(dataset["features"].values) -rank_projections = np.linalg.matrix_rank(dataset["projections"].values) - -pca_features, pca_projections, pca_df = compute_pca(dataset) - -# Plot explained variance and rank -plt.plot( - pca_features.explained_variance_ratio_, label=f"features, rank={rank_features}" -) -plt.plot( - pca_projections.explained_variance_ratio_, - label=f"projections, rank={rank_projections}", -) -plt.legend() -plt.xlabel("n_components") -plt.ylabel("explained variance ratio") -plt.xlim([0, 50]) -plt.show() - -# Density plot of first two principal components of features and projections. -fig, ax = plt.subplots(1, 2, figsize=(10, 5)) -sns.kdeplot(data=pca_df, x="PCA1", y="PCA2", ax=ax[0], fill=True, cmap="Blues") -sns.kdeplot(data=pca_df, x="PCA1_proj", y="PCA2_proj", ax=ax[1], fill=True, cmap="Reds") -ax[0].set_title("Density plot of PCA1 vs PCA2 (features)") -ax[1].set_title("Density plot of PCA1 vs PCA2 (projections)") -plt.show() - -# %% interactive quality control: UMAP -# Compute UMAP embeddings -umap_features, umap_projections, umap_df = compute_umap(dataset) - -# %% -# Plot UMAP embeddings as density plots -fig, ax = plt.subplots(1, 2, figsize=(10, 5)) -sns.kdeplot(data=umap_df, x="UMAP1", y="UMAP2", ax=ax[0], fill=True, cmap="Blues") -sns.kdeplot( - data=umap_df, x="UMAP1_proj", y="UMAP2_proj", ax=ax[1], fill=True, cmap="Reds" -) -ax[0].set_title("Density plot of UMAP1 vs UMAP2 (features)") -ax[1].set_title("Density plot of UMAP1 vs UMAP2 (projections)") -plt.show() - -# %% interactive quality control: pairwise distances - - -# %% Evaluation: infection score - -## Overlay UMAP and infection state -## Linear classification accuracy -## Clustering accuracy - -# %% Evaluation: cell division - -# %% Evaluation: correlation between principal components and computed features diff --git a/applications/contrastive_phenotyping/evaluation/archive/cosine_dissimilarity_dataset.py b/applications/contrastive_phenotyping/evaluation/archive/cosine_dissimilarity_dataset.py deleted file mode 100644 index 01228915c..000000000 --- a/applications/contrastive_phenotyping/evaluation/archive/cosine_dissimilarity_dataset.py +++ /dev/null @@ -1,285 +0,0 @@ -# %% -from pathlib import Path -from typing import Optional - -import matplotlib.pyplot as plt -import numpy as np -import pandas as pd -import seaborn as sns -from numpy.typing import NDArray -from scipy.optimize import minimize_scalar -from scipy.stats import gaussian_kde -from sklearn.preprocessing import StandardScaler -from tqdm import tqdm - -from viscy.representation.embedding_writer import read_embedding_dataset -from viscy.representation.evaluation.clustering import ( - compare_time_offset, - pairwise_distance_matrix, - rank_nearest_neighbors, - select_block, -) - -plt.style.use("../evaluation/figure.mplstyle") - - -def compute_piece_wise_dissimilarity( - features_df: pd.DataFrame, cross_dist: NDArray, rank_fractions: NDArray -): - """ - Computing the smoothness and dynamic range - - Get the off diagonal per block and compute the mode - - The blocks are not square, so we need to get the off diagonal elements - - Get the 1 and 99 percentile of the off diagonal per block - """ - piece_wise_dissimilarity_per_track = [] - piece_wise_rank_difference_per_track = [] - for name, subdata in features_df.groupby(["fov_name", "track_id"]): - if len(subdata) > 1: - indices = subdata.index.values - single_track_dissimilarity = select_block(cross_dist, indices) - single_track_rank_fraction = select_block(rank_fractions, indices) - piece_wise_dissimilarity = compare_time_offset( - single_track_dissimilarity, time_offset=1 - ) - piece_wise_rank_difference = compare_time_offset( - single_track_rank_fraction, time_offset=1 - ) - piece_wise_dissimilarity_per_track.append(piece_wise_dissimilarity) - piece_wise_rank_difference_per_track.append(piece_wise_rank_difference) - return piece_wise_dissimilarity_per_track, piece_wise_rank_difference_per_track - - -def plot_histogram( - data, title, xlabel, ylabel, color="blue", alpha=0.5, stat="frequency" -): - plt.figure() - plt.title(title) - sns.histplot(data, bins=30, kde=True, color=color, alpha=alpha, stat=stat) - plt.xlabel(xlabel) - plt.ylabel(ylabel) - plt.tight_layout() - plt.show() - - -def find_distribution_peak(data: np.ndarray) -> float: - """ - Find the peak (mode) of a distribution using kernel density estimation. - - Args: - data: Array of values to find the peak for - - Returns: - float: The x-value where the peak occurs - """ - kde = gaussian_kde(data) - # Find the peak (maximum) of the KDE - result = minimize_scalar( - lambda x: -kde(x), bounds=(np.min(data), np.max(data)), method="bounded" - ) - return result.x - - -def analyze_embedding_smoothness( - prediction_path: Path, - verbose: bool = False, - output_path: Optional[str] = None, - loss_name: Optional[str] = None, - overwrite: bool = False, -) -> dict: - """ - Analyze the smoothness and dynamic range of embeddings. - - Args: - prediction_path: Path to the embedding dataset - verbose: If True, generates additional plots - output_path: Path to save the final plot (optional) - loss_name: Name of the loss function used (optional) - overwrite: If True, overwrites existing files. If False, raises error if file exists (default: False) - - Returns: - dict: Dictionary containing metrics including: - - dissimilarity_mean: Mean of adjacent frame dissimilarity - - dissimilarity_std: Standard deviation of adjacent frame dissimilarity - - dissimilarity_median: Median of adjacent frame dissimilarity - - dissimilarity_peak: Peak of adjacent frame distribution - - dissimilarity_p99: 99th percentile of adjacent frame dissimilarity - - dissimilarity_p1: 1st percentile of adjacent frame dissimilarity - - dissimilarity_distribution: Full distribution of adjacent frame dissimilarities - - random_mean: Mean of random sampling dissimilarity - - random_std: Standard deviation of random sampling dissimilarity - - random_median: Median of random sampling dissimilarity - - random_peak: Peak of random sampling distribution - - random_distribution: Full distribution of random sampling dissimilarities - - dynamic_range: Difference between random and adjacent peaks - """ - # Read the dataset - embeddings = read_embedding_dataset(prediction_path) - features = embeddings["features"] - - scaled_features = StandardScaler().fit_transform(features.values) - # Compute the cosine dissimilarity - cross_dist = pairwise_distance_matrix(scaled_features, metric="cosine") - rank_fractions = rank_nearest_neighbors(cross_dist, normalize=True) - - # Compute piece-wise dissimilarity and rank difference - features_df = features["sample"].to_dataframe().reset_index(drop=True) - piece_wise_dissimilarity_per_track, piece_wise_rank_difference_per_track = ( - compute_piece_wise_dissimilarity(features_df, cross_dist, rank_fractions) - ) - - all_dissimilarity = np.concatenate(piece_wise_dissimilarity_per_track) - - p99_piece_wise_dissimilarity = np.array( - [np.percentile(track, 99) for track in piece_wise_dissimilarity_per_track] - ) - p1_percentile_piece_wise_dissimilarity = np.array( - [np.percentile(track, 1) for track in piece_wise_dissimilarity_per_track] - ) - - # Random sampling values in the dissimilarity matrix with same size as adjacent frame measurements - n_samples = len(all_dissimilarity) - random_indices = np.random.randint(0, len(cross_dist), size=(n_samples, 2)) - sampled_values = cross_dist[random_indices[:, 0], random_indices[:, 1]] - - # Compute the peaks of both distributions using KDE - adjacent_peak = float(find_distribution_peak(all_dissimilarity)) - random_peak = float(find_distribution_peak(sampled_values)) - dynamic_range = float(random_peak - adjacent_peak) - - metrics = { - "dissimilarity_mean": float(np.mean(all_dissimilarity)), - "dissimilarity_std": float(np.std(all_dissimilarity)), - "dissimilarity_median": float(np.median(all_dissimilarity)), - "dissimilarity_peak": adjacent_peak, - "dissimilarity_p99": p99_piece_wise_dissimilarity, - "dissimilarity_p1": p1_percentile_piece_wise_dissimilarity, - "dissimilarity_distribution": all_dissimilarity, - "random_mean": float(np.mean(sampled_values)), - "random_std": float(np.std(sampled_values)), - "random_median": float(np.median(sampled_values)), - "random_peak": random_peak, - "random_distribution": sampled_values, - "dynamic_range": dynamic_range, - } - - if verbose: - # Plot cross distance matrix - plt.figure() - plt.imshow(cross_dist) - plt.show() - - # Plot histograms - plot_histogram( - piece_wise_dissimilarity_per_track, - "Adjacent Frame Dissimilarity per Track", - "Cosine Dissimilarity", - "Frequency", - ) - - # Plot the comparison histogram and save if output_path is provided - fig = plt.figure() - sns.histplot( - metrics["dissimilarity_distribution"], - bins=30, - kde=True, - color="cyan", - alpha=0.5, - stat="density", - ) - sns.histplot( - metrics["random_distribution"], - bins=30, - kde=True, - color="red", - alpha=0.5, - stat="density", - ) - plt.xlabel("Cosine Dissimilarity") - plt.ylabel("Density") - # Add vertical lines for the peaks - plt.axvline( - x=metrics["dissimilarity_peak"], color="cyan", linestyle="--", alpha=0.8 - ) - plt.axvline(x=metrics["random_peak"], color="red", linestyle="--", alpha=0.8) - plt.tight_layout() - plt.legend(["Adjacent Frame", "Random Sample", "Adjacent Peak", "Random Peak"]) - - if output_path and loss_name: - output_file = Path( - f"{output_path}/cosine_dissimilarity_smoothness_{prediction_path.stem}_{loss_name}.pdf" - ) - if output_file.exists() and not overwrite: - raise FileExistsError( - f"File {output_file} already exists and overwrite=False" - ) - fig.savefig( - output_file, - dpi=600, - ) - plt.show() - - return metrics - - -# Example usage: -if __name__ == "__main__": - # plotting - VERBOSE = True - - PATH_TO_GDRIVE_FIGUE = "./" - - prediction_path_1 = Path( - "/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/trainng_logs/SEC61/rev6_NTXent_sensorPhase_infection/2chan_160patch_98ckpt_rev6_2.zarr" - ) - prediction_path_2 = Path( - "/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/trainng_logs/SEC61/rev5_sensorPhase_infection/2chan_160patch_97ckpt_rev5_2.zarr" - ) - - # Create a list of models to evaluate - models = [ - (prediction_path_1, "ntxent"), - (prediction_path_2, "triplet"), - ] - - # Evaluate each model - for prediction_path, loss_name in tqdm(models, desc="Evaluating models"): - print(f"\nAnalyzing model: {prediction_path.stem} (Loss: {loss_name})") - print("-" * 80) - - metrics = analyze_embedding_smoothness( - prediction_path, - verbose=VERBOSE, - output_path=PATH_TO_GDRIVE_FIGUE, - loss_name=loss_name, - overwrite=True, - ) - - # Print adjacent frame dissimilarity statistics - print("\nAdjacent Frame Dissimilarity Statistics:") - print(f"{'Mean:':<15} {metrics['dissimilarity_mean']:.3f}") - print(f"{'Std:':<15} {metrics['dissimilarity_std']:.3f}") - print(f"{'Median:':<15} {metrics['dissimilarity_median']:.3f}") - print(f"{'Peak:':<15} {metrics['dissimilarity_peak']:.3f}") - print(f"{'P1:':<15} {np.mean(metrics['dissimilarity_p1']):.3f}") - print(f"{'P99:':<15} {np.mean(metrics['dissimilarity_p99']):.3f}") - - # Print random sampling statistics - print("\nRandom Sampling Statistics:") - print(f"{'Mean:':<15} {metrics['random_mean']:.3f}") - print(f"{'Std:':<15} {metrics['random_std']:.3f}") - print(f"{'Median:':<15} {metrics['random_median']:.3f}") - print(f"{'Peak:':<15} {metrics['random_peak']:.3f}") - - # Print dynamic range - print("\nComparison Metrics:") - print(f"{'Dynamic Range:':<15} {metrics['dynamic_range']:.3f}") - - # Print distribution sizes - print("\nDistribution Sizes:") - print( - f"{'Adjacent Frame:':<15} {len(metrics['dissimilarity_distribution']):,d} samples" - ) - print(f"{'Random:':<15} {len(metrics['random_distribution']):,d} samples") - -# %% diff --git a/applications/contrastive_phenotyping/evaluation/archive/cosine_similarity.py b/applications/contrastive_phenotyping/evaluation/archive/cosine_similarity.py deleted file mode 100644 index 3755c8698..000000000 --- a/applications/contrastive_phenotyping/evaluation/archive/cosine_similarity.py +++ /dev/null @@ -1,546 +0,0 @@ -# %% -# Import necessary libraries, try euclidean distance for both features and -from pathlib import Path - -import matplotlib.pyplot as plt -import numpy as np -import pandas as pd -import seaborn as sns -from sklearn.preprocessing import StandardScaler -from umap import UMAP - -from viscy.representation.embedding_writer import read_embedding_dataset -from viscy.representation.evaluation.distance import ( - calculate_cosine_similarity_cell, - compute_displacement, - compute_displacement_mean_std, -) - -# %% Paths and parameters. - - -features_path_30_min = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/time_sampling_strategies/time_interval/predict/feb_test_time_interval_1_epoch_178.zarr" -) - - -feature_path_no_track = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/time_sampling_strategies/negpair_random_sampling2/feb_fixed_test_predict.zarr" -) - - -features_path_any_time = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/time_sampling_strategies/negpair_difcell_randomtime_sampling/Ver2_updateTracking_refineModel/predictions/Feb_2chan_128patch_32projDim/2chan_128patch_56ckpt_FebTest.zarr" -) - - -data_path = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/2024_02_04_A549_DENV_ZIKV_timelapse/8-train-test-split/registered_test.zarr" -) - - -tracks_path = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/2024_02_04_A549_DENV_ZIKV_timelapse/8-train-test-split/track_test.zarr" -) - - -# %% Load embedding datasets for all three sampling -fov_name = "/B/4/6" -track_id = 52 - -embedding_dataset_30_min = read_embedding_dataset(features_path_30_min) -embedding_dataset_no_track = read_embedding_dataset(feature_path_no_track) -embedding_dataset_any_time = read_embedding_dataset(features_path_any_time) - -# Calculate cosine similarities for each sampling -time_points_30_min, cosine_similarities_30_min = calculate_cosine_similarity_cell( - embedding_dataset_30_min, fov_name, track_id -) -time_points_no_track, cosine_similarities_no_track = calculate_cosine_similarity_cell( - embedding_dataset_no_track, fov_name, track_id -) -time_points_any_time, cosine_similarities_any_time = calculate_cosine_similarity_cell( - embedding_dataset_any_time, fov_name, track_id -) - -# %% Plot cosine similarities over time for all three conditions - -plt.figure(figsize=(10, 6)) - -plt.plot( - time_points_no_track, - cosine_similarities_no_track, - marker="o", - label="classical contrastive (no tracking)", -) -plt.plot( - time_points_any_time, cosine_similarities_any_time, marker="o", label="cell aware" -) -plt.plot( - time_points_30_min, - cosine_similarities_30_min, - marker="o", - label="cell & time aware (interval 30 min)", -) - -plt.xlabel("Time Delay (t)") -plt.ylabel("Cosine Similarity with First Time Point") -plt.title("Cosine Similarity Over Time for Infected Cell") - -# plt.savefig('infected_cell_example.pdf', format='pdf') - - -plt.grid(True) - -plt.legend() - -plt.savefig("new_example_cell.svg", format="svg") - - -plt.show() -# %% - - -# %% import statements - - -# %% Paths to datasets -features_path_30_min = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/time_sampling_strategies/time_interval/predict/feb_test_time_interval_1_epoch_178.zarr" -) -feature_path_no_track = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/time_sampling_strategies/negpair_random_sampling2/feb_fixed_test_predict.zarr" -) -# features_path_any_time = Path("/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/time_sampling_strategies/negpair_difcell_randomtime_sampling/Ver2_updateTracking_refineModel/predictions/Feb_1chan_128patch_32projDim/1chan_128patch_63ckpt_FebTest.zarr") - - -# %% Read embedding datasets -embedding_dataset_30_min = read_embedding_dataset(features_path_30_min) -embedding_dataset_no_track = read_embedding_dataset(feature_path_no_track) -# embedding_dataset_any_time = read_embedding_dataset(features_path_any_time) - - -# %% Compute displacements for both datasets (using Euclidean distance and Cosine similarity) -max_tau = 10 # Maximum time shift (tau) to compute displacements - - -# mean_displacement_30_min, std_displacement_30_min = compute_displacement_mean_std(embedding_dataset_30_min, max_tau, use_cosine=False, use_dissimilarity=False) -# mean_displacement_no_track, std_displacement_no_track = compute_displacement_mean_std(embedding_dataset_no_track, max_tau, use_cosine=False, use_dissimilarity=False) -# mean_displacement_any_time, std_displacement_any_time = compute_displacement_mean_std(embedding_dataset_any_time, max_tau, use_cosine=False) - - -mean_displacement_30_min_cosine, std_displacement_30_min_cosine = ( - compute_displacement_mean_std( - embedding_dataset_30_min, max_tau, use_cosine=True, use_dissimilarity=False - ) -) -mean_displacement_no_track_cosine, std_displacement_no_track_cosine = ( - compute_displacement_mean_std( - embedding_dataset_no_track, max_tau, use_cosine=True, use_dissimilarity=False - ) -) -# mean_displacement_any_time_cosine, std_displacement_any_time_cosine = compute_displacement_mean_std(embedding_dataset_any_time, max_tau, use_cosine=True) -# %% Plot 1: Euclidean Displacements -plt.figure(figsize=(10, 6)) - - -taus = list(mean_displacement_30_min_cosine.keys()) -mean_values_30_min = list(mean_displacement_30_min_cosine.values()) -std_values_30_min = list(std_displacement_30_min_cosine.values()) - - -mean_values_no_track = list(mean_displacement_no_track_cosine.values()) -std_values_no_track = list(std_displacement_no_track_cosine.values()) - - -# mean_values_any_time = list(mean_displacement_any_time.values()) -# std_values_any_time = list(std_displacement_any_time.values()) - - -# Plotting Euclidean displacements -plt.plot( - taus, mean_values_30_min, marker="o", label="Cell & Time Aware (30 min interval)" -) -plt.fill_between( - taus, - np.array(mean_values_30_min) - np.array(std_values_30_min), - np.array(mean_values_30_min) + np.array(std_values_30_min), - color="gray", - alpha=0.3, - label="Std Dev (30 min interval)", -) - - -plt.plot( - taus, mean_values_no_track, marker="o", label="Classical Contrastive (No Tracking)" -) -plt.fill_between( - taus, - np.array(mean_values_no_track) - np.array(std_values_no_track), - np.array(mean_values_no_track) + np.array(std_values_no_track), - color="blue", - alpha=0.3, - label="Std Dev (No Tracking)", -) - - -plt.xlabel("Time Shift (τ)") -plt.ylabel("Displacement") -plt.title("Embedding Displacement Over Time") -plt.grid(True) -plt.legend() - - -# plt.savefig('embedding_displacement_euclidean.svg', format='svg') -# plt.savefig('embedding_displacement_euclidean.pdf', format='pdf') - - -# Show the Euclidean plot -plt.show() - - -# %% Plot 2: Cosine Displacements -plt.figure(figsize=(10, 6)) - -taus = list(mean_displacement_30_min_cosine.keys()) - -# Plotting Cosine displacements -mean_values_30_min_cosine = list(mean_displacement_30_min_cosine.values()) -std_values_30_min_cosine = list(std_displacement_30_min_cosine.values()) - - -mean_values_no_track_cosine = list(mean_displacement_no_track_cosine.values()) -std_values_no_track_cosine = list(std_displacement_no_track_cosine.values()) - - -plt.plot( - taus, - mean_values_30_min_cosine, - marker="o", - label="Cell & Time Aware (30 min interval)", -) -plt.fill_between( - taus, - np.array(mean_values_30_min_cosine) - np.array(std_values_30_min_cosine), - np.array(mean_values_30_min_cosine) + np.array(std_values_30_min_cosine), - color="gray", - alpha=0.3, - label="Std Dev (30 min interval)", -) - - -plt.plot( - taus, - mean_values_no_track_cosine, - marker="o", - label="Classical Contrastive (No Tracking)", -) -plt.fill_between( - taus, - np.array(mean_values_no_track_cosine) - np.array(std_values_no_track_cosine), - np.array(mean_values_no_track_cosine) + np.array(std_values_no_track_cosine), - color="blue", - alpha=0.3, - label="Std Dev (No Tracking)", -) - - -plt.xlabel("Time Shift (τ)") -plt.ylabel("Cosine Similarity") -plt.title("Embedding Displacement Over Time") - - -plt.grid(True) -plt.legend() -plt.savefig("1_std_cosine_plot.svg", format="svg") - -# Show the Cosine plot -plt.show() -# %% - - -# %% Paths to datasets -features_path_30_min = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/time_sampling_strategies/time_interval/predict/feb_test_time_interval_1_epoch_178.zarr" -) -feature_path_no_track = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/time_sampling_strategies/negpair_random_sampling2/feb_fixed_test_predict.zarr" -) - - -# %% Read embedding datasets -embedding_dataset_30_min = read_embedding_dataset(features_path_30_min) -embedding_dataset_no_track = read_embedding_dataset(feature_path_no_track) - - -# %% Compute displacements for both datasets (using Cosine similarity) -max_tau = 10 # Maximum time shift (tau) to compute displacements - - -# Compute displacements for Cell & Time Aware (30 min interval) using Cosine similarity -displacement_per_tau_aware_cosine = compute_displacement( - embedding_dataset_30_min, - max_tau, - use_cosine=True, - use_dissimilarity=False, - use_umap=False, -) - - -# Compute displacements for Classical Contrastive (No Tracking) using Cosine similarity -displacement_per_tau_contrastive_cosine = compute_displacement( - embedding_dataset_no_track, - max_tau, - use_cosine=True, - use_dissimilarity=False, - use_umap=False, -) - - -# %% Prepare data for violin plot -def prepare_violin_data(taus, displacement_aware, displacement_contrastive): - # Create a list to hold the data - data = [] - - # Populate the data for Cell & Time Aware - for tau in taus: - displacements_aware = displacement_aware.get(tau, []) - for displacement in displacements_aware: - data.append( - { - "Time Shift (τ)": tau, - "Displacement": displacement, - "Sampling": "Cell & Time Aware (30 min interval)", - } - ) - - # Populate the data for Classical Contrastive - for tau in taus: - displacements_contrastive = displacement_contrastive.get(tau, []) - for displacement in displacements_contrastive: - data.append( - { - "Time Shift (τ)": tau, - "Displacement": displacement, - "Sampling": "Classical Contrastive (No Tracking)", - } - ) - - # Convert to a DataFrame - df = pd.DataFrame(data) - return df - - -taus = list(displacement_per_tau_aware_cosine.keys()) - - -# Prepare the violin plot data -df = prepare_violin_data( - taus, displacement_per_tau_aware_cosine, displacement_per_tau_contrastive_cosine -) - - -# Create a violin plot using seaborn -plt.figure(figsize=(12, 8)) -sns.violinplot( - x="Time Shift (τ)", - y="Displacement", - hue="Sampling", - data=df, - palette="Set2", - scale="width", - bw=0.2, - inner=None, - split=True, - cut=0, -) - - -# Add labels and title -plt.xlabel("Time Shift (τ)", fontsize=14) -plt.ylabel("Cosine Similarity", fontsize=14) -plt.title("Cosine Similarity Distribution on Features", fontsize=16) -plt.grid(True, linestyle="--", alpha=0.6) # Lighter grid lines for less distraction -plt.legend(title="Sampling", fontsize=12, title_fontsize=14) - - -# plt.ylim(0.5, 1.0) - - -# Save the violin plot as SVG and PDF -plt.savefig("1fixed_violin_plot_cosine_similarity.svg", format="svg") -# plt.savefig('violin_plot_cosine_similarity.pdf', format='pdf') - - -# Show the plot -plt.show() -# %% using umap violin plot - -# %% Paths to datasets -features_path_30_min = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/time_sampling_strategies/time_interval/predict/feb_test_time_interval_1_epoch_178.zarr" -) -feature_path_no_track = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/time_sampling_strategies/negpair_random_sampling2/feb_fixed_test_predict.zarr" -) - -# %% Read embedding datasets -embedding_dataset_30_min = read_embedding_dataset(features_path_30_min) -embedding_dataset_no_track = read_embedding_dataset(feature_path_no_track) - - -# %% Compute UMAP on features -def compute_umap(dataset): - features = dataset["features"] - scaled_features = StandardScaler().fit_transform(features.values) - umap = UMAP(n_components=2) # Reduce to 2 dimensions - embedding = umap.fit_transform(scaled_features) - - # Add UMAP coordinates using xarray functionality - umap_features = features.assign_coords( - UMAP1=("sample", embedding[:, 0]), UMAP2=("sample", embedding[:, 1]) - ) - return umap_features - - -# Apply UMAP to both datasets -umap_features_30_min = compute_umap(embedding_dataset_30_min) -umap_features_no_track = compute_umap(embedding_dataset_no_track) - -# %% -print(umap_features_30_min) -# %% Visualize UMAP embeddings -# # Visualize UMAP embeddings for the 30 min interval -# plt.figure(figsize=(8, 6)) -# plt.scatter(umap_features_30_min[:, 0], umap_features_30_min[:, 1], c=embedding_dataset_30_min["t"].values, cmap='viridis') -# plt.colorbar(label='Timepoints') -# plt.title('UMAP Projection of Features (30 min Interval)') -# plt.xlabel('UMAP1') -# plt.ylabel('UMAP2') -# plt.show() - -# # Visualize UMAP embeddings for the No Tracking dataset -# plt.figure(figsize=(8, 6)) -# plt.scatter(umap_features_no_track[:, 0], umap_features_no_track[:, 1], c=embedding_dataset_no_track["t"].values, cmap='viridis') -# plt.colorbar(label='Timepoints') -# plt.title('UMAP Projection of Features (No Tracking)') -# plt.xlabel('UMAP1') -# plt.ylabel('UMAP2') -# plt.show() -# %% Compute displacements using UMAP coordinates (using Cosine similarity) -max_tau = 10 # Maximum time shift (tau) to compute displacements - -# Compute displacements for UMAP-processed Cell & Time Aware (30 min interval) -displacement_per_tau_aware_umap_cosine = compute_displacement( - umap_features_30_min, - max_tau, - use_cosine=True, - use_dissimilarity=False, - use_umap=True, -) - -# Compute displacements for UMAP-processed Classical Contrastive (No Tracking) -displacement_per_tau_contrastive_umap_cosine = compute_displacement( - umap_features_no_track, - max_tau, - use_cosine=True, - use_dissimilarity=False, - use_umap=True, -) - - -# %% Prepare data for violin plot -def prepare_violin_data(taus, displacement_aware, displacement_contrastive): - # Create a list to hold the data - data = [] - - # Populate the data for Cell & Time Aware - for tau in taus: - displacements_aware = displacement_aware.get(tau, []) - for displacement in displacements_aware: - data.append( - { - "Time Shift (τ)": tau, - "Displacement": displacement, - "Sampling": "Cell & Time Aware (30 min interval)", - } - ) - - # Populate the data for Classical Contrastive - for tau in taus: - displacements_contrastive = displacement_contrastive.get(tau, []) - for displacement in displacements_contrastive: - data.append( - { - "Time Shift (τ)": tau, - "Displacement": displacement, - "Sampling": "Classical Contrastive (No Tracking)", - } - ) - - # Convert to a DataFrame - df = pd.DataFrame(data) - return df - - -taus = list(displacement_per_tau_aware_umap_cosine.keys()) - -# Prepare the violin plot data -df = prepare_violin_data( - taus, - displacement_per_tau_aware_umap_cosine, - displacement_per_tau_contrastive_umap_cosine, -) - -# %% Create a violin plot using seaborn -plt.figure(figsize=(12, 8)) -sns.violinplot( - x="Time Shift (τ)", - y="Displacement", - hue="Sampling", - data=df, - palette="Set2", - scale="width", - bw=0.2, - inner=None, - split=True, - cut=0, -) - -# Add labels and title -plt.xlabel("Time Shift (τ)", fontsize=14) -plt.ylabel("Cosine Similarity", fontsize=14) -plt.title("Cosine Similarity Distribution using UMAP Features", fontsize=16) -plt.grid(True, linestyle="--", alpha=0.6) # Lighter grid lines for less distraction -plt.legend(title="Sampling", fontsize=12, title_fontsize=14) - -# plt.ylim(0, 1) - -# Save the violin plot as SVG and PDF -plt.savefig("fixed_plot_cosine_similarity.svg", format="svg") -# plt.savefig('violin_plot_cosine_similarity_umap.pdf', format='pdf') - -# Show the plot -plt.show() - - -# %% -# %% Visualize Displacement Distributions (Example Code) -# Compare displacement distributions for τ = 1 -# plt.figure(figsize=(10, 6)) -# sns.histplot(displacement_per_tau_aware_umap_cosine[1], kde=True, label='UMAP - 30 min Interval', color='blue') -# sns.histplot(displacement_per_tau_contrastive_umap_cosine[1], kde=True, label='UMAP - No Tracking', color='green') -# plt.legend() -# plt.title('Comparison of Displacement Distributions for τ = 1 (UMAP)') -# plt.xlabel('Displacement') -# plt.show() - -# # Compare displacement distributions for the full feature set (same τ = 1) -# plt.figure(figsize=(10, 6)) -# sns.histplot(displacement_per_tau_aware_cosine[1], kde=True, label='Full Features - 30 min Interval', color='red') -# sns.histplot(displacement_per_tau_contrastive_cosine[1], kde=True, label='Full Features - No Tracking', color='orange') -# plt.legend() -# plt.title('Comparison of Displacement Distributions for τ = 1 (Full Features)') -# plt.xlabel('Displacement') -# plt.show() -# # %% diff --git a/applications/contrastive_phenotyping/evaluation/archive/linear_probing.py b/applications/contrastive_phenotyping/evaluation/archive/linear_probing.py deleted file mode 100644 index 9796bd5f4..000000000 --- a/applications/contrastive_phenotyping/evaluation/archive/linear_probing.py +++ /dev/null @@ -1,54 +0,0 @@ -# %% Imports -from pathlib import Path - -from viscy.representation.embedding_writer import read_embedding_dataset -from viscy.representation.evaluation import load_annotation -from viscy.representation.evaluation.lca import fit_logistic_regression - -# %% -TRAIN_FOVS = ["/A/3/7", "/A/3/8", "/A/3/9", "/B/4/6", "/B/4/7"] - - -model_embeddings = { - "no-track": Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/time_sampling_strategies/negpair_random_sampling2/feb_fixed_test_predict.zarr" - ), - "cell-aware-2ch": Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/time_sampling_strategies/negpair_difcell_randomtime_sampling/Ver2_updateTracking_refineModel/predictions/Feb_2chan_128patch_32projDim/2chan_128patch_56ckpt_FebTest.zarr" - ), - "cell-aware-1ch": Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/time_sampling_strategies/negpair_difcell_randomtime_sampling/Ver2_updateTracking_refineModel/predictions/Feb_1chan_128patch_32projDim/1chan_128patch_63ckpt_FebTest.zarr" - ), - "time-cell-aware": Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/time_sampling_strategies/time_interval/predict/feb_test_time_interval_1_epoch_178.zarr" - ), -} -path_annotations_infection = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/2024_02_04_A549_DENV_ZIKV_timelapse/8-train-test-split/supervised_inf_pred/extracted_inf_state.csv" -) - -# %% -for model_name, path_embedding in model_embeddings.items(): - print(f"Model: {model_name}") - dataset = read_embedding_dataset(path_embedding) - features = dataset["features"] - - infection = load_annotation( - dataset, - path_annotations_infection, - "infection_state", - {0.0: "background", 1.0: "uninfected", 2.0: "infected"}, - ) - - log_reg = fit_logistic_regression( - features, - infection, - train_fovs=TRAIN_FOVS, - remove_background_class=True, - scale_features=False, - class_weight="balanced", - solver="liblinear", - random_state=42, - ) - -# %% diff --git a/applications/contrastive_phenotyping/evaluation/archive/log_regresssion_training.py b/applications/contrastive_phenotyping/evaluation/archive/log_regresssion_training.py deleted file mode 100644 index 7afc38dd6..000000000 --- a/applications/contrastive_phenotyping/evaluation/archive/log_regresssion_training.py +++ /dev/null @@ -1,105 +0,0 @@ -# %% -from pathlib import Path - -import pandas as pd - -from viscy.representation.embedding_writer import read_embedding_dataset -from viscy.representation.evaluation import load_annotation - -# %% Paths and parameters. - - -features_path = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/time_sampling_strategies/time_interval/predict/feb_test_time_interval_1_epoch_178.zarr" -) -data_path = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/2024_02_04_A549_DENV_ZIKV_timelapse/8-train-test-split/registered_test.zarr" -) -tracks_path = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/2024_02_04_A549_DENV_ZIKV_timelapse/8-train-test-split/track_test.zarr" -) - - -# %% -embedding_dataset = read_embedding_dataset(features_path) -embedding_dataset - -# %% -# Compute UMAP over all features -features = embedding_dataset["features"] -# or select a well: -# features = features[features["fov_name"].str.contains("B/4")] - -# %% OVERLAY INFECTION ANNOTATION -ann_root = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/2024_02_04_A549_DENV_ZIKV_timelapse/8-train-test-split/supervised_inf_pred" -) - - -infection = load_annotation( - features, - ann_root / "extracted_inf_state.csv", - "infection_state", - {0.0: "background", 1.0: "uninfected", 2.0: "infected"}, -) - -# %% plot the umap - -infection_npy = infection.cat.codes.values - -# Filter out the background class -infection_npy_filtered = infection_npy[infection_npy != 0] - -feature_npy = features.values -feature_npy_filtered = feature_npy[infection_npy != 0] - -# %% combine the umap, pca and infection annotation in one dataframe - -data = pd.DataFrame({"infection": infection_npy_filtered}) - -# add time and well info into dataframe -time_npy = features["t"].values -time_npy_filtered = time_npy[infection_npy != 0] -data["time"] = time_npy_filtered - -fov_name_list = features["fov_name"].values -fov_name_list_filtered = fov_name_list[infection_npy != 0] -data["fov_name"] = fov_name_list_filtered - -# Add all 768 features to the dataframe -for i in range(768): - data[f"feature_{i + 1}"] = feature_npy_filtered[:, i] - -# %% manually split the dataset into training and testing set by well name - -# dataframe for training set, fov names starts with "/B/4/6" or "/B/4/7" or "/A/3/" -data_train_val = data[ - data["fov_name"].str.contains("/B/4/6") - | data["fov_name"].str.contains("/B/4/7") - | data["fov_name"].str.contains("/A/3/") -] - -# dataframe for testing set, fov names starts with "/B/4/8" or "/B/4/9" or "/A/4/" -data_test = data[ - data["fov_name"].str.contains("/B/4/8") - | data["fov_name"].str.contains("/B/4/9") - | data["fov_name"].str.contains("/B/3/") -] - -# %% train a linear classifier to predict infection state from PCA components - -from sklearn.linear_model import LogisticRegression # noqa: E402 - -x_train = data_train_val.drop(columns=["infection", "fov_name", "time"]) -y_train = data_train_val["infection"] - -# train a logistic regression model -clf = LogisticRegression(random_state=0).fit(x_train, y_train) - -x_test = data_test.drop(columns=["infection", "fov_name", "time"]) -y_test = data_test["infection"] - -# predict the infection state for the testing set -y_pred = clf.predict(x_test) - -# %% diff --git a/applications/contrastive_phenotyping/evaluation/archive/time_decay_knn.py b/applications/contrastive_phenotyping/evaluation/archive/time_decay_knn.py deleted file mode 100644 index 31d0c4202..000000000 --- a/applications/contrastive_phenotyping/evaluation/archive/time_decay_knn.py +++ /dev/null @@ -1,99 +0,0 @@ -# %% -from pathlib import Path - -import matplotlib.pyplot as plt -import seaborn as sns -from sklearn.preprocessing import StandardScaler - -from viscy.representation.embedding_writer import read_embedding_dataset -from viscy.representation.evaluation.clustering import ( - compare_time_offset, - pairwise_distance_matrix, - rank_nearest_neighbors, - select_block, -) - -# %% -prediction_path = Path( - "/hpc/projects/organelle_phenotyping/ALFI_benchmarking/predictions_final/ALFI_opp_7mins.zarr" -) - -embeddings = read_embedding_dataset(prediction_path) -features = embeddings["features"] - -# %% -scaled_features = StandardScaler().fit_transform(features.values) - -# %% -cross_dist = pairwise_distance_matrix(scaled_features, metric="cosine") -rank_fractions = rank_nearest_neighbors(cross_dist, normalize=True) - -# %% -# select a single track in a single fov -fov = "/0/0/0" -fov_idx = (features["fov_name"] == fov).values - -track_id = 1 -track_idx = (features["track_id"] == track_id).values - -fov_and_track_idx = fov_idx & track_idx - -single_track_dissimilarity = select_block(cross_dist, fov_and_track_idx) -single_track_rank_fraction = select_block(rank_fractions, fov_and_track_idx) - -piece_wise_dissimilarity = compare_time_offset( - single_track_dissimilarity, time_offset=1 -) -piece_wise_rank_difference = compare_time_offset( - single_track_rank_fraction, time_offset=1 -) - -# %% -f = plt.figure(figsize=(8, 12)) -f.suptitle(f"Track {track_id} in FOV {fov}") -subfigs = f.subfigures(2, 1, height_ratios=[1, 2]) - -umap = subfigs[0].subplots(1, 1) -single_cell_features = features.sel(fov_name=fov, track_id=track_id).sortby("t") -sns.lineplot( - x=single_cell_features["UMAP1"], - y=single_cell_features["UMAP2"], - ax=umap, - color="k", - alpha=0.5, -) -sns.scatterplot( - x=features["UMAP1"], y=features["UMAP2"], ax=umap, color="k", s=100, alpha=0.01 -) -sns.scatterplot( - x=single_cell_features["UMAP1"], - y=single_cell_features["UMAP2"], - hue=single_cell_features["t"], - ax=umap, - palette="RdYlGn", -) - -f1 = subfigs[1] -ax = f1.subplots(2, 2) - -sns.heatmap(single_track_dissimilarity, ax=ax[0, 0], square=True) -ax[0, 0].set_title("Cosine dissimilarity") -ax[0, 0].set_xlabel("Frame") -ax[0, 0].set_ylabel("Frame") - -sns.heatmap(single_track_rank_fraction, ax=ax[0, 1], square=True) -ax[0, 1].set_title("Column-wise normalized neighborhood distance") -ax[0, 1].set_xlabel("Frame") -ax[0, 1].set_ylabel("Frame") - -sns.lineplot(piece_wise_dissimilarity, ax=ax[1, 0]) -ax[1, 0].set_title("$1 - \cos{(t_i, t_{i+1})}$") -ax[1, 0].set_xlabel("Frame") -ax[1, 0].set_ylabel("Cosine dissimilarity") - -sns.lineplot(piece_wise_rank_difference, ax=ax[1, 1]) -ax[1, 1].set_title("Nearest neighbor fraction difference") -ax[1, 1].set_xlabel("Frame") -ax[1, 1].set_ylabel("Rank fraction") - -# %% diff --git a/applications/contrastive_phenotyping/evaluation/imagenet/imagenet_pretrained_features.py b/applications/contrastive_phenotyping/evaluation/imagenet/imagenet_pretrained_features.py deleted file mode 100644 index 83cb4b7b2..000000000 --- a/applications/contrastive_phenotyping/evaluation/imagenet/imagenet_pretrained_features.py +++ /dev/null @@ -1,156 +0,0 @@ -"""Use pre-trained ImageNet models to extract features from images.""" - -# %% -from pathlib import Path - -import numpy as np -import pandas as pd -import seaborn as sns -import timm -import torch -from sklearn.decomposition import PCA -from sklearn.linear_model import LogisticRegression -from sklearn.preprocessing import StandardScaler -from tqdm import tqdm - -from viscy.data.triplet import TripletDataModule -from viscy.transforms import ScaleIntensityRangePercentilesd - -# %% -model = timm.create_model("convnext_tiny", pretrained=True).eval().to("cuda") - -# %% -dm = TripletDataModule( - data_path="/hpc/projects/organelle_phenotyping/ALFI_models_data/datasets/zarr_datasets/float_phase_ome_zarr_output_test.zarr", - tracks_path="/hpc/projects/organelle_phenotyping/ALFI_models_data/datasets/zarr_datasets/track_phase_ome_zarr_output_test.zarr", - source_channel=["DIC"], - z_range=(0, 1), - batch_size=128, - num_workers=8, - initial_yx_patch_size=(128, 128), - final_yx_patch_size=(128, 128), - normalizations=[ - ScaleIntensityRangePercentilesd( - keys=["DIC"], lower=50, upper=99, b_min=0.0, b_max=1.0 - ) - ], -) -dm.prepare_data() -dm.setup("predict") - -# %% -features = [] -indices = [] - -with torch.inference_mode(): - for batch in tqdm(dm.predict_dataloader()): - image = batch["anchor"][:, :, 0] - rgb_image = image.repeat(1, 3, 1, 1).to("cuda") - features.append(model.forward_features(rgb_image)) - indices.append(batch["index"]) - -# %% -pooled = torch.cat(features).mean(dim=(2, 3)).cpu().numpy() -tracks = pd.concat([pd.DataFrame(idx) for idx in indices]) - -# %% -scaled_features = StandardScaler().fit_transform(pooled) -pca = PCA(n_components=2) -pca_features = pca.fit_transform(scaled_features) - -# %% add pooled to dataframe naming each column with feature_i -for i, feature in enumerate(pooled.T): - tracks[f"feature_{i}"] = feature -# add pca features to dataframe naming each column with pca_i -for i, feature in enumerate(pca_features.T): - tracks[f"pc_{i}"] = feature - -# # save the dataframe as csv -# tracks.to_csv("/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/code/ALFI/imagenet_pretrained_features.csv", index=False) - -# %% load the dataframe -# tracks = pd.read_csv("/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/code/ALFI/imagenet_pretrained_features.csv") - -# %% load annotations - -ann_root = Path( - "/hpc/projects/organelle_phenotyping/ALFI_models_data/datasets/zarr_datasets" -) -ann_path = ann_root / "test_annotations.csv" -annotation = pd.read_csv(ann_path) - -# add division column from annotation to tracks -tracks["division"] = annotation["division"] - -# %% -ax = sns.scatterplot( - x=tracks["pc_0"], - y=tracks["pc_1"], - hue=tracks["division"], - legend="full", -) -ax.set_xlabel("PC1") -ax.set_ylabel("PC2") - -# %% compute the accuracy of the model using a linear classifier - -# remove rows with division = -1 -tracks = tracks[tracks["division"] != -1] - -# dataframe for training set, fov names starts with "/B/4/6" or "/B/4/7" or "/A/3/" -data_train_val = tracks[ - tracks["fov_name"].str.contains("/0/0/0") - | tracks["fov_name"].str.contains("/0/1/0") - | tracks["fov_name"].str.contains("/0/2/0") -] - -data_test = tracks[ - tracks["fov_name"].str.contains("/0/3/0") - | tracks["fov_name"].str.contains("/0/4/0") -] - -x_train = data_train_val.drop( - columns=[ - "division", - "fov_name", - "t", - "track_id", - "id", - "parent_id", - "parent_track_id", - "pc_0", - "pc_1", - ] -) -y_train = data_train_val["division"] - -# train a logistic regression model -clf = LogisticRegression(random_state=0).fit(x_train, y_train) - -# test the trained classifer on the other half of the data - -x_test = data_test.drop( - columns=[ - "division", - "fov_name", - "t", - "track_id", - "id", - "parent_id", - "parent_track_id", - "pc_0", - "pc_1", - ] -) -y_test = data_test["division"] - -# predict the infection state for the testing set -y_pred = clf.predict(x_test) - -# compute the accuracy of the classifier - -accuracy = np.mean(y_pred == y_test) -# save the accuracy for final ploting -print(f"Accuracy of model: {accuracy}") - -# %% diff --git a/applications/contrastive_phenotyping/evaluation/knowledge_distillation/knowledge_distillation.py b/applications/contrastive_phenotyping/evaluation/knowledge_distillation/knowledge_distillation.py deleted file mode 100644 index b3a1a75b8..000000000 --- a/applications/contrastive_phenotyping/evaluation/knowledge_distillation/knowledge_distillation.py +++ /dev/null @@ -1,122 +0,0 @@ -# metrics for the knowledge distillation figure - -# %% -from pathlib import Path - -import matplotlib.pyplot as plt -import pandas as pd -import seaborn as sns -from sklearn.metrics import accuracy_score, classification_report, f1_score - -# %% -# Mantis -test_virus = ["C/2/000000", "C/2/001001"] -test_mock = ["B/3/000000", "B/3/000001"] - -# Mantis -TRAIN_FOVS = ["C/2/000001", "C/2/001000", "B/3/001000", "B/3/001001"] - -VAL_FOVS = test_virus + test_mock - -# %% -prediction_from_scratch = pd.read_csv( - "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/bootstrap-labels/test/from-scratch-last-1126.csv" -) -prediction_from_scratch["pretraining"] = "ImageNet" - -prediction_finetuned = pd.read_csv( - "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/bootstrap-labels/test/fine-tune-last-1126.csv" -) -pretrained_name = "DynaCLR" -prediction_finetuned["pretraining"] = pretrained_name - -prediction = pd.concat([prediction_from_scratch, prediction_finetuned], axis=0) - -# %% -prediction = prediction[prediction["fov_name"].isin(VAL_FOVS)] -prediction["prediction_binary"] = prediction["prediction"] > 0.5 -prediction - -# %% -print( - classification_report( - prediction["label"], prediction["prediction_binary"], digits=3 - ) -) - -# %% -prediction["HPI"] = prediction["t"] / 6 + 3 - -bins = [3, 6, 9, 12, 15, 18, 21, 24] -labels = [f"{start}-{end}" for start, end in zip(bins[:-1], bins[1:])] -prediction["stage"] = pd.cut(prediction["HPI"], bins=bins, labels=labels, right=True) -prediction["well"] = prediction["fov_name"].apply( - lambda x: "ZIKV" if x in test_virus else "Mock" -) -comparison = prediction.melt( - id_vars=["fov_name", "id", "HPI", "well", "stage", "pretraining"], - value_vars=["label", "prediction_binary"], - var_name="source", - value_name="value", -) -with sns.axes_style("whitegrid"): - ax = sns.lineplot( - data=comparison[comparison["pretraining"] == pretrained_name], - x="HPI", - y="value", - hue="well", - hue_order=["Mock", "ZIKV"], - style="source", - errorbar=None, - color="gray", - ) - ax.set_ylabel("Infection ratio") - -# %% -id_vars = ["stage", "pretraining"] - -accuracy_by_t = prediction.groupby(id_vars).apply( - lambda x: float(accuracy_score(x["label"], x["prediction_binary"])) -) -f1_by_t = prediction.groupby(id_vars).apply( - lambda x: float(f1_score(x["label"], x["prediction_binary"])) -) - -metrics_df = pd.DataFrame( - data={"accuracy": accuracy_by_t.values, "F1": f1_by_t.values}, - index=f1_by_t.index, -).reset_index() - -metrics_long = metrics_df.melt( - id_vars=id_vars, - value_vars=["accuracy"], - var_name="metric", - value_name="score", -) - -with sns.axes_style("ticks"): - plt.style.use("../figures/figure.mplstyle") - g = sns.catplot( - data=metrics_long, - x="stage", - y="score", - hue="pretraining", - kind="point", - linewidth=1.5, - linestyles="--", - ) - g.set_axis_labels("HPI", "accuracy") - sns.move_legend(g, "upper left", bbox_to_anchor=(0.35, 1.1)) - g.figure.set_size_inches(3.5, 1.5) - g.set(xlim=(-1, 7), ylim=(0.6, 1.0)) - plt.show() - - -# %% -g.figure.savefig( - Path.home() - / "gdrive/publications/dynaCLR/2025_dynaCLR_paper/fig_manuscript_svg/figure_knowledge_distillation/figure_parts/accuracy_students.pdf", - dpi=300, -) - -# %% diff --git a/applications/contrastive_phenotyping/evaluation/knowledge_distillation/knowledge_distillation_teacher.py b/applications/contrastive_phenotyping/evaluation/knowledge_distillation/knowledge_distillation_teacher.py deleted file mode 100644 index bc988faec..000000000 --- a/applications/contrastive_phenotyping/evaluation/knowledge_distillation/knowledge_distillation_teacher.py +++ /dev/null @@ -1,148 +0,0 @@ -# %% -from pathlib import Path - -import matplotlib.pyplot as plt -import pandas as pd -import seaborn as sns -from sklearn.linear_model import LogisticRegression -from sklearn.metrics import accuracy_score, classification_report, f1_score - -from viscy.representation.embedding_writer import read_embedding_dataset - -# %% -train_annotations = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/2024_11_26_A549_ZIKA-sensor_ZIKV/3-phenotype/annotate-infection/combined_annotations.csv" -) -train_embeddings = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/bootstrap-labels/generate-labels/sensor-2024-11-26.zarr" -) -val_annotations = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/2024_08_14_ZIKV_pal17_48h/6-phenotype/combined_annotations.csv" - # "/hpc/projects/intracellular_dashboard/viral-sensor/2024_11_05_A549_pAL10_24h/4-phenotype/annotate-infection/combined_annotations.csv" -) -val_embeddings = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/bootstrap-labels/generate-labels/sensor-2024-08-14-annotation.zarr" - # "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/bootstrap-labels/generate-labels/sensor-2024-11-05.zarr" -) - - -# %% -def filter_train_fovs(fov_name: pd.Series) -> pd.Series: - return fov_name.str[1:4].isin(["C/2", "B/3"]) - - -def filter_val_fovs(fov_name: pd.Series) -> pd.Series: - return fov_name.str[1:4].isin(["0/3"]) | (fov_name == "/0/4/000001") - # return fov_name.isin( - # ["/0/15/000001", "/0/11/002000", "/0/11/002001", "/0/11/002002"] - # ) - - -def all_fovs(fov_name: pd.Series) -> pd.Series: - return None - - -def load_features_and_annotations(embedding_path, annotation_path, filter_fn): - dataset = read_embedding_dataset(embedding_path) - features = dataset["features"][filter_fn(dataset["fov_name"])] - annotation = pd.read_csv(annotation_path) - annotation["fov_name"] = "/" + annotation["fov_name"] - annotation = annotation.set_index(["fov_name", "id"]) - index = features["sample"].to_dataframe().reset_index(drop=True)[["fov_name", "id"]] - selected = pd.merge( - left=index, right=annotation, on=["fov_name", "id"], how="inner" - ) - selected["infection_state"] = selected["infection_state"].astype("category") - return features, selected["infection_state"], selected - - -# %% -train_features, train_annotation, train_selected = load_features_and_annotations( - train_embeddings, train_annotations, filter_fn=filter_train_fovs -) -val_features, val_annotation, val_selected = load_features_and_annotations( - val_embeddings, val_annotations, filter_fn=filter_val_fovs -) - -model = LogisticRegression(class_weight="balanced", random_state=42, solver="liblinear") -model = model.fit(train_features, train_annotation) -train_prediction = model.predict(train_features) -val_prediction = model.predict(val_features) - -print("Training\n", classification_report(train_annotation, train_prediction)) -print("Validation\n", classification_report(val_annotation, val_prediction)) - -val_selected["label"] = val_selected["infection_state"].cat.codes -val_selected["prediction_binary"] = val_prediction - -# %% -prediction = val_selected -prediction["HPI"] = prediction["t"] / 2 + 3 -bins = [3, 6, 9, 12, 15, 18, 21, 24] -labels = [f"{start}-{end}" for start, end in zip(bins[:-1], bins[1:])] -prediction["stage"] = pd.cut(prediction["HPI"], bins=bins, labels=labels, right=True) -comparison = prediction.melt( - id_vars=["fov_name", "id", "HPI"], - value_vars=["label", "prediction_binary"], - var_name="source", - value_name="value", -) -with sns.axes_style("whitegrid"): - ax = sns.lineplot( - data=comparison, - x="HPI", - y="value", - style="source", - errorbar=None, - color="gray", - ) - ax.set_ylabel("Infection ratio") - -# %% -accuracy_by_t = prediction.groupby(["stage"]).apply( - lambda x: float(accuracy_score(x["label"], x["prediction_binary"])) -) -f1_by_t = prediction.groupby(["stage"]).apply( - lambda x: float(f1_score(x["label"], x["prediction_binary"])) -) - -metrics_df = pd.DataFrame( - data={ - "accuracy": accuracy_by_t.values, - "F1": f1_by_t.values, - }, - index=f1_by_t.index, -).reset_index() - -metrics_long = metrics_df.melt( - id_vars=["stage"], - value_vars=["accuracy"], - var_name="metric", - value_name="score", -) -with sns.axes_style("ticks"): - plt.style.use("../figures/figure.mplstyle") - g = sns.catplot( - data=metrics_long, - x="stage", - y="score", - kind="point", - linewidth=1.5, - linestyles="--", - legend=False, - color="gray", - ) - g.set_axis_labels("HPI", "accuracy") - g.figure.set_size_inches(3.5, 0.75) - g.set(xlim=(-1, 7), ylim=(0.9, 1.0)) - plt.show() - -# %% -g.savefig( - Path.home() - / "gdrive/publications/dynaCLR/2025_dynaCLR_paper/fig_manuscript_svg/figure_knowledge_distillation/figure_parts/teacher_accuracy.pdf", - dpi=300, - bbox_inches="tight", -) - -# %% diff --git a/applications/contrastive_phenotyping/evaluation/pc_vs_computed_features/PC_vs_computed_features.py b/applications/contrastive_phenotyping/evaluation/pc_vs_computed_features/PC_vs_computed_features.py deleted file mode 100644 index 0e7e0dc33..000000000 --- a/applications/contrastive_phenotyping/evaluation/pc_vs_computed_features/PC_vs_computed_features.py +++ /dev/null @@ -1,161 +0,0 @@ -"""Script to compute the correlation between PCA and UMAP features and computed features -* finds the computed features best representing the PCA and UMAP components -* outputs a heatmap of the correlation between PCA and UMAP features and computed features -""" - -# %% -from pathlib import Path - -import matplotlib.pyplot as plt -import seaborn as sns -from compute_pca_features import compute_correlation_and_save_png, compute_features - -# %% for sensor features - -features_path = Path( - "/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/trainng_logs/SEC61/rev6_NTXent_sensorPhase_infection/2chan_160patch_94ckpt_rev6_2.zarr" -) -data_path = Path( - "/hpc/projects/intracellular_dashboard/organelle_dynamics/2024_02_04_A549_DENV_ZIKV_timelapse/8-train-test-split/registered_test.zarr" -) -tracks_path = Path( - "/hpc/projects/intracellular_dashboard/organelle_dynamics/2024_02_04_A549_DENV_ZIKV_timelapse/8-train-test-split/track_test.zarr" -) - -source_channel = ["Phase3D", "RFP"] -seg_channel = ["Nuclei_prediction_labels"] -z_range = (28, 43) -fov_list = ["/A/3", "/B/3", "/B/4"] - -features_sensor = compute_features( - features_path, - data_path, - tracks_path, - source_channel, - seg_channel, - z_range, - fov_list, -) - -features_sensor.to_csv( - "/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/Figure_panels/cell_division/features_allset_sensor.csv", - index=False, -) - -# features_sensor = pd.read_csv("/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/Figure_panels/cell_division/features_allset_sensor.csv") - -# take a subset without the 768 features -feature_columns = [f"feature_{i + 1}" for i in range(768)] -features_subset_sensor = features_sensor.drop(columns=feature_columns) -correlation_sensor = compute_correlation_and_save_png( - features_subset_sensor, - "/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/Figure_panels/cell_division/PC_vs_CF_2chan_pca_sensor_allset.svg", -) - -# %% plot PCA vs set of computed features for sensor features - -set_features = [ - "Fluor Radial Intensity Gradient", - "Phase Interquartile Range", - "Perimeter area ratio", - "Fluor Interquartile Range", - "Phase Entropy", - "Fluor Zernike Moment Mean", -] - -plt.figure(figsize=(10, 8)) -sns.heatmap( - correlation_sensor.loc[set_features, "PCA1":"PCA6"], - annot=True, - cmap="coolwarm", - fmt=".2f", - annot_kws={"size": 24}, - vmin=-1, - vmax=1, -) -plt.xlabel("Computed Features", fontsize=24) -plt.ylabel("PCA Features", fontsize=24) -plt.xticks(fontsize=24) # Increase x-axis tick labels -plt.yticks(fontsize=24) # Increase y-axis tick labels - -plt.savefig( - "/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/Figure_panels/cell_division/PC_vs_CF_2chan_pca_allset_sensor_6features.svg" -) - -# plot the PCA1 vs PCA2 map for sensor features - -plt.figure(figsize=(10, 10)) -sns.scatterplot( - x="PCA1", - y="PCA2", - data=features_sensor, -) - - -# .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. -# / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ -# '-' '-'-' '-'-' '-'-' '-'-' '-'-' '-'-' '-'-' '-'-' '-'-' '-'-' '-'-' '-'-' '-'-' '-'-' '-'-' '-'-' '-'-' '-'-' '-'-' '-'-' '-' - - -# %% for organelle features - -features_path = Path( - "/hpc/projects/intracellular_dashboard/organelle_dynamics/2024_11_07_A549_SEC61_ZIKV_DENV/4-phenotyping/predictions/Soorya/timeAware_2chan_ntxent_192patch_91ckpt_rev7_GT.zarr" -) -data_path = Path( - "/hpc/projects/intracellular_dashboard/organelle_dynamics/2024_11_07_A549_SEC61_ZIKV_DENV/2-assemble/2024_11_07_A549_SEC61_ZIKV_DENV.zarr" -) -tracks_path = Path( - "/hpc/projects/intracellular_dashboard/organelle_dynamics/2024_11_07_A549_SEC61_ZIKV_DENV/1-preprocess/label-free/4-track-gt/2024_11_07_A549_SEC61_ZIKV_DENV_2_cropped.zarr" -) - -source_channel = ["Phase3D", "raw GFP EX488 EM525-45"] -seg_channel = ["nuclei_prediction_labels_labels"] -z_range = (16, 21) -normalizations = None -fov_list = ["/B/2/000000", "/B/3/000000", "/C/2/000000"] - -features_organelle = compute_features( - features_path, - data_path, - tracks_path, - source_channel, - seg_channel, - z_range, - fov_list, -) - -# Save the features dataframe to a CSV file -features_organelle.to_csv( - "/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/Figure_panels/cell_division/features_twoChan_organelle_multiwell.csv", - index=False, -) - -correlation_organelle = compute_correlation_and_save_png( - features_organelle, - "/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/Figure_panels/cell_division/PC_vs_CF_2chan_pca_organelle_multiwell.svg", -) - -# features_organelle = pd.read_csv("/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/Figure_panels/cell_division/features_twoChan_organelle_multiwell_refinedPCA.csv") - -# %% plot PCA vs set of computed features for organelle features - -plt.figure(figsize=(10, 8)) -sns.heatmap( - correlation_organelle.loc[set_features, "PCA1":"PCA6"], - annot=True, - cmap="coolwarm", - fmt=".2f", - annot_kws={"size": 24}, - vmin=-1, - vmax=1, -) -plt.xlabel("Computed Features", fontsize=24) -plt.ylabel("PCA Features", fontsize=24) -plt.xticks(fontsize=24) # Increase x-axis tick labels -plt.yticks(fontsize=24) # Increase y-axis tick labels -plt.savefig( - "/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/Figure_panels/cell_division/PC_vs_CF_2chan_pca_setfeatures_organelle_6features.svg" -) - -# %% diff --git a/applications/contrastive_phenotyping/evaluation/pc_vs_computed_features/compute_pca_features.py b/applications/contrastive_phenotyping/evaluation/pc_vs_computed_features/compute_pca_features.py deleted file mode 100644 index eca2adaf6..000000000 --- a/applications/contrastive_phenotyping/evaluation/pc_vs_computed_features/compute_pca_features.py +++ /dev/null @@ -1,379 +0,0 @@ -from pathlib import Path - -import matplotlib.pyplot as plt -import numpy as np -import pandas as pd -import seaborn as sns -from sklearn.decomposition import PCA -from sklearn.preprocessing import StandardScaler - -from viscy.representation.embedding_writer import read_embedding_dataset -from viscy.representation.evaluation import dataset_of_tracks -from viscy.representation.evaluation.feature import CellFeatures - - -## function to read the embedding dataset and return the features -def compute_PCA(features_path: Path): - """Compute PCA components from embedding features and combine with original features. - - This function reads an embedding dataset, standardizes the features, and computes - 8 principal components. The PCA components are then combined with the original - features in an xarray dataset structure. - - Parameters - ---------- - features_path : Path - Path to the embedding dataset containing the feature vectors. - - Returns - ------- - features: xarray dataset with PCA components as new coordinates - - """ - embedding_dataset = read_embedding_dataset(features_path) - embedding_dataset - - # load all unprojected features: - features = embedding_dataset["features"] - scaled_features = StandardScaler().fit_transform(features.values) - # PCA analysis of the features - - pca = PCA(n_components=8) - pca_features = pca.fit_transform(scaled_features) - features = ( - features.assign_coords(PCA1=("sample", pca_features[:, 0])) - .assign_coords(PCA2=("sample", pca_features[:, 1])) - .assign_coords(PCA3=("sample", pca_features[:, 2])) - .assign_coords(PCA4=("sample", pca_features[:, 3])) - .assign_coords(PCA5=("sample", pca_features[:, 4])) - .assign_coords(PCA6=("sample", pca_features[:, 5])) - .assign_coords(PCA7=("sample", pca_features[:, 6])) - .assign_coords(PCA8=("sample", pca_features[:, 7])) - .set_index( - sample=["PCA1", "PCA2", "PCA3", "PCA4", "PCA5", "PCA6", "PCA7", "PCA8"], - append=True, - ) - ) - - return features - - -def compute_features( - features_path: Path, - data_path: Path, - tracks_path: Path, - source_channel: list, - seg_channel: list, - z_range: tuple, - fov_list: list, -): - """Compute various cell features and combine them with PCA features. - - This function processes cell tracking data to compute various morphological and - intensity-based features for both phase and fluorescence channels, and combines - them with PCA features from an embedding dataset. - - Parameters - ---------- - features_path : Path - Path to the embedding dataset containing PCA features. - data_path : Path - Path to the raw data directory containing image data. - tracks_path : Path - Path to the directory containing tracking data in CSV format. - source_channel : list - List of source channels to process from the data. - seg_channel : list - List of segmentation channels to process from the data. - z_range : tuple - Tuple specifying the z-range to process (min_z, max_z). - fov_list : list - List of field of view names to process. - - Returns - ------- - pandas.DataFrame - DataFrame containing all computed features including: - - Basic features (mean intensity, std dev, kurtosis, etc.) for both Phase and Fluor channels - - Organelle features (area, masked intensity) - - Nuclear features (area, perimeter, eccentricity) - - PCA components (PCA1-PCA8) - - Original tracking information (fov_name, track_id, time points) - """ - - embedding_dataset = compute_PCA(features_path) - features_npy = embedding_dataset["features"].values - - # convert the xarray to dataframe structure and add columns for computed features - embedding_df = embedding_dataset["sample"].to_dataframe().reset_index(drop=True) - feature_columns = pd.DataFrame( - features_npy, columns=[f"feature_{i + 1}" for i in range(768)] - ) - - embedding_df = pd.concat([embedding_df, feature_columns], axis=1) - embedding_df = embedding_df.drop(columns=["sample", "UMAP1", "UMAP2"]) - - # Filter features based on FOV names that start with any of the items in fov_list - embedding_df = embedding_df[ - embedding_df["fov_name"].apply( - lambda x: any(x.startswith(fov) for fov in fov_list) - ) - ] - - # Define feature categories and their corresponding column names - feature_columns = { - "basic_features": [ - ("Mean Intensity", ["Phase", "Fluor"]), - ("Std Dev", ["Phase", "Fluor"]), - ("Kurtosis", ["Phase", "Fluor"]), - ("Skewness", ["Phase", "Fluor"]), - ("Entropy", ["Phase", "Fluor"]), - ("Interquartile Range", ["Phase", "Fluor"]), - ("Dissimilarity", ["Phase", "Fluor"]), - ("Contrast", ["Phase", "Fluor"]), - ("Texture", ["Phase", "Fluor"]), - ("Weighted Intensity Gradient", ["Phase", "Fluor"]), - ("Radial Intensity Gradient", ["Phase", "Fluor"]), - ("Zernike Moment Std", ["Phase", "Fluor"]), - ("Zernike Moment Mean", ["Phase", "Fluor"]), - ("Intensity Localization", ["Phase", "Fluor"]), - ], - "organelle_features": [ - "Fluor Area", - "Fluor Masked Intensity", - ], - "nuclear_features": [ - "Nuclear area", - "Perimeter", - "Perimeter area ratio", - "Nucleus eccentricity", - ], - } - - # Initialize all feature columns - for category, feature_list in feature_columns.items(): - if isinstance(feature_list[0], tuple): # Handle features with multiple channels - for feature, channels in feature_list: - for channel in channels: - col_name = f"{channel} {feature}" - embedding_df[col_name] = np.nan - else: # Handle single features - for feature in feature_list: - embedding_df[feature] = np.nan - - # compute the computed features and add them to the dataset - - fov_names_list = embedding_df["fov_name"].unique() - unique_fov_names = sorted(list(set(fov_names_list))) - - for fov_name in unique_fov_names: - unique_track_ids = embedding_df[embedding_df["fov_name"] == fov_name][ - "track_id" - ].unique() - unique_track_ids = list(set(unique_track_ids)) - - # iteration_count = 0 - - for track_id in unique_track_ids: - if not embedding_df[ - (embedding_df["fov_name"] == fov_name) - & (embedding_df["track_id"] == track_id) - ].empty: - prediction_dataset = dataset_of_tracks( - data_path, - tracks_path, - [fov_name], - [track_id], - z_range=z_range, - source_channel=source_channel, - ) - track_channel = dataset_of_tracks( - tracks_path, - tracks_path, - [fov_name], - [track_id], - z_range=(0, 1), - source_channel=seg_channel, - ) - - whole = np.stack([p["anchor"] for p in prediction_dataset]) - seg_mask = np.stack([p["anchor"] for p in track_channel]) - phase = whole[:, 0, 2] - # Normalize phase image to 0-255 range - # phase = ((phase - phase.min()) / (phase.max() - phase.min()) * 255).astype(np.uint8) - # Normalize fluorescence image to 0-255 range - fluor = np.max(whole[:, 1], axis=1) - # fluor = ((fluor - fluor.min()) / (fluor.max() - fluor.min()) * 255).astype(np.uint8) - nucl_mask = seg_mask[:, 0, 0] - - for i, t in enumerate( - embedding_df[ - (embedding_df["fov_name"] == fov_name) - & (embedding_df["track_id"] == track_id) - ]["t"] - ): - # Basic statistical features for both channels - phase_features = CellFeatures(phase[i], nucl_mask[i]) - PF = phase_features.compute_all_features() - - # Get all basic statistical measures at once - phase_stats = { - "Mean Intensity": PF["mean_intensity"], - "Std Dev": PF["std_dev"], - "Kurtosis": PF["kurtosis"], - "Skewness": PF["skewness"], - "Interquartile Range": PF["iqr"], - "Entropy": PF["spectral_entropy"], - "Dissimilarity": PF["dissimilarity"], - "Contrast": PF["contrast"], - "Texture": PF["texture"], - "Zernike Moment Std": PF["zernike_std"], - "Zernike Moment Mean": PF["zernike_mean"], - "Radial Intensity Gradient": PF["radial_intensity_gradient"], - "Weighted Intensity Gradient": PF[ - "weighted_intensity_gradient" - ], - "Intensity Localization": PF["intensity_localization"], - } - - fluor_cell_features = CellFeatures(fluor[i], nucl_mask[i]) - - FF = fluor_cell_features.compute_all_features() - - fluor_stats = { - "Mean Intensity": FF["mean_intensity"], - "Std Dev": FF["std_dev"], - "Kurtosis": FF["kurtosis"], - "Skewness": FF["skewness"], - "Interquartile Range": FF["iqr"], - "Entropy": FF["spectral_entropy"], - "Contrast": FF["contrast"], - "Dissimilarity": FF["dissimilarity"], - "Texture": FF["texture"], - "Masked Area": FF["masked_area"], - "Masked Intensity": FF["masked_intensity"], - "Weighted Intensity Gradient": FF[ - "weighted_intensity_gradient" - ], - "Radial Intensity Gradient": FF["radial_intensity_gradient"], - "Zernike Moment Std": FF["zernike_std"], - "Zernike Moment Mean": FF["zernike_mean"], - "Intensity Localization": FF["intensity_localization"], - "Area": FF["area"], - } - - mask_features = CellFeatures(nucl_mask[i], nucl_mask[i]) - MF = mask_features.compute_all_features() - - mask_stats = { - "perimeter": MF["perimeter"], - "area": MF["area"], - "eccentricity": MF["eccentricity"], - "perimeter_area_ratio": MF["perimeter_area_ratio"], - } - - # Create dictionaries for each feature category - phase_feature_mapping = { - f"Phase {k.replace('_', ' ').title()}": v - for k, v in phase_stats.items() - } - - fluor_feature_mapping = { - f"Fluor {k.replace('_', ' ').title()}": v - for k, v in fluor_stats.items() - } - - mask_feature_mapping = { - "Nuclear area": mask_stats["area"], - "Perimeter": mask_stats["perimeter"], - "Perimeter area ratio": mask_stats["perimeter_area_ratio"], - "Nucleus eccentricity": mask_stats["eccentricity"], - } - - # Combine all feature dictionaries - feature_values = { - **phase_feature_mapping, - **fluor_feature_mapping, - **mask_feature_mapping, - } - - # update the features dataframe - for feature_name, value in feature_values.items(): - embedding_df.loc[ - (embedding_df["fov_name"] == fov_name) - & (embedding_df["track_id"] == track_id) - & (embedding_df["t"] == t), - feature_name, - ] = value[0] - - # iteration_count += 1 - print(f"Processed {fov_name}+{track_id}") - - return embedding_df - - -## save all feature dataframe to png file -def compute_correlation_and_save_png(features: pd.DataFrame, filename: str): - """Compute correlation between PCA features and computed features, and save as heatmap. - - This function calculates the Spearman correlation between PCA components and all - computed features, then visualizes the results as a heatmap. The heatmap focuses - on the correlation between PCA components (PCA1-PCA8) and all other computed features. - - Parameters - ---------- - features : pandas.DataFrame - DataFrame containing all features including: - - PCA components (PCA1-PCA8) - - Computed features (morphological, intensity-based, etc.) - - Tracking metadata (fov_name, track_id, t, etc.) - filename : str - Path where the correlation heatmap will be saved as a PNG or SVG file. - - Returns - ------- - pandas.DataFrame - The correlation matrix between all features. - """ - # remove the rows with missing values - features = features.dropna() - - # sub_features = features[features["Time"] == 20] - feature_df_removed = features.drop( - columns=["fov_name", "track_id", "t", "id", "parent_track_id", "parent_id"] - ) - - # Compute correlation between PCA features and computed features - correlation = feature_df_removed.corr(method="spearman") - - # display PCA correlation as a heatmap - - plt.figure(figsize=(30, 10)) - sns.heatmap( - correlation.drop( - columns=["PCA1", "PCA2", "PCA3", "PCA4", "PCA5", "PCA6", "PCA7", "PCA8"] - ).loc["PCA1":"PCA8", :], - annot=True, - cmap="coolwarm", - fmt=".2f", - annot_kws={"size": 18}, - cbar=False, - ) - plt.title("Correlation between PCA features and computed features", fontsize=12) - plt.xlabel("Computed Features", fontsize=18) - plt.ylabel("PCA Features", fontsize=18) - plt.xticks(fontsize=18, rotation=45, ha="right") # Rotate labels and align them - plt.yticks(fontsize=18) - - # Adjust layout to prevent label cutoff - plt.tight_layout() - - plt.savefig( - filename, - dpi=300, - bbox_inches="tight", - pad_inches=0.5, # Add padding around the figure - ) - plt.close() - - return correlation diff --git a/applications/contrastive_phenotyping/evaluation/plot_embeddings.py b/applications/contrastive_phenotyping/evaluation/plot_embeddings.py deleted file mode 100644 index 01d60180d..000000000 --- a/applications/contrastive_phenotyping/evaluation/plot_embeddings.py +++ /dev/null @@ -1,283 +0,0 @@ -# %% -from pathlib import Path - -import matplotlib.pyplot as plt -import numpy as np -import pandas as pd -import plotly.express as px -import seaborn as sns -from sklearn.decomposition import PCA -from sklearn.preprocessing import StandardScaler -from umap import UMAP - -from viscy.representation.embedding_writer import read_embedding_dataset -from viscy.representation.evaluation import ( - dataset_of_tracks, - load_annotation, -) - -# %% Paths and parameters. - -features_path = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/contrastive_tune_augmentations/predict/2024_06_13/l2_projection_batchnorm-128p.zarr" -) -data_path = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/2024_06_13_SEC61_TOMM20_ZIKV_DENGUE_1/2-register/registered_chunked.zarr" -) -tracks_path = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/2024_06_13_SEC61_TOMM20_ZIKV_DENGUE_1/4.1-tracking/test_tracking_4.zarr" -) - -# %% -embedding_dataset = read_embedding_dataset(features_path) -embedding_dataset - -# %% -# Compute PCA of the features and projections to estimate the number of components to keep. -PCA_features = PCA(n_components=100).fit(embedding_dataset["features"].values) -PCA_projection = PCA(n_components=100).fit(embedding_dataset["projections"].values) - -plt.plot(PCA_features.explained_variance_ratio_, label="features") -plt.plot(PCA_projection.explained_variance_ratio_, label="projections") -plt.legend() -plt.xlabel("n_components") -plt.show() - -# TODO: Include the followiing in the standard report. -# * Explained variance of the features and projections. -# * The UMAPs of the features and projections. -# * 2D image of the embeddings of features and projections of test tracks (e.g., infected, uninfected, dividing, non-dividing). -# * Heatmaps of annotations over UMAPs. - - -# %% -print(np.linalg.matrix_rank(embedding_dataset["features"].values)) -print(np.linalg.matrix_rank(embedding_dataset["projections"].values)) - -# %% -# Extract a track from the dataset and visualize its features. - -fov_name = "/0/1/000000" # "/B/4/4" FOV names can change between datasets. -track_id = 21 -all_tracks_FOV = embedding_dataset.sel(fov_name=fov_name) -a_track_in_FOV = all_tracks_FOV.sel(track_id=track_id) -# Why is sample dimension ~22000 long after the dataset is sliced by FOV and by track_id? -indices = np.arange(a_track_in_FOV.sizes["sample"]) -features_track = a_track_in_FOV["features"] -time_stamp = features_track["t"][indices].astype(str) - -px.imshow( - features_track.values[indices], - labels={ - "x": "feature", - "y": "t", - "color": "value", - }, # change labels to match our metadata - y=time_stamp, - # show fov_name as y-axis -) -# normalize individual features. - -scaled_features_track = StandardScaler().fit_transform(features_track.values) -px.imshow( - scaled_features_track, - labels={ - "x": "feature", - "y": "t", - "color": "value", - }, # change labels to match our metadata - y=time_stamp, - # show fov_name as y-axis -) -# Scaled features are centered around 0 with a standard deviation of 1. -# Each feature is individually normalized along the time dimension. - -plt.plot(np.mean(scaled_features_track, axis=1), label="scaled_mean") -plt.plot(np.std(scaled_features_track, axis=1), label="scaled_std") -plt.plot(np.mean(features_track.values, axis=1), label="mean") -plt.plot(np.std(features_track.values, axis=1), label="std") -plt.legend() -plt.xlabel("t") -plt.show() - -# %% -# Create the montage of the images of the cells in the track. - -source_channel = ["Phase3D", "RFP"] -z_range = (28, 43) -predict_dataset = dataset_of_tracks( - data_path, - tracks_path, - [fov_name], - [track_id], - z_range=z_range, - source_channel=source_channel, -) - -phase = np.stack([p["anchor"][0, 7].numpy() for p in predict_dataset]) -fluor = np.stack([np.max(p["anchor"][1].numpy(), axis=0) for p in predict_dataset]) - -# %% Naive loop to iterate over the images and display - -for t in range(len(predict_dataset)): - fig, axes = plt.subplots(1, 2, figsize=(10, 5)) - axes[0].imshow(phase[t].squeeze(), cmap="gray") - axes[0].set_title("Phase") - axes[0].axis("off") - axes[1].imshow(fluor[t].squeeze(), cmap="gray") - axes[1].set_title("Fluor") - axes[1].axis("off") - plt.title(f"t={t}") - plt.show() - -# %% display the track in napari -# import os - -# import napari - -# os.environ["DISPLAY"] = ":1" -# viewer = napari.Viewer() -# viewer.add_image(phase, name="Phase", colormap="gray") -# viewer.add_image(fluor, name="Fluor", colormap="magenta") - -# %% -# Compute UMAP over all features -features = embedding_dataset["features"] -# or select a well: -# features = features[features["fov_name"].str.contains("B/4")] - -scaled_features = StandardScaler().fit_transform(features.values) -umap = UMAP() -# Fit UMAP on all features -embedding = umap.fit_transform(scaled_features) - - -# %% -# Add UMAP coordinates to the dataset - -features = ( - features.assign_coords(UMAP1=("sample", embedding[:, 0])) - .assign_coords(UMAP2=("sample", embedding[:, 1])) - .set_index(sample=["UMAP1", "UMAP2"], append=True) -) -features - - -sns.scatterplot( - x=features["UMAP1"], y=features["UMAP2"], hue=features["t"], s=7, alpha=0.8 -) - -# %% -# Transform the track features -scaled_features_track_umap = umap.transform(scaled_features_track) -plt.plot(scaled_features_track_umap[:, 0], scaled_features_track_umap[:, 1]) -plt.plot(scaled_features_track_umap[0, 0], scaled_features_track_umap[0, 1], marker="o") -plt.plot( - scaled_features_track_umap[-1, 0], scaled_features_track_umap[-1, 1], marker="x" -) -for i in range(1, len(scaled_features_track_umap) - 1): - plt.plot( - scaled_features_track_umap[i, 0], - scaled_features_track_umap[i, 1], - marker=".", - color="blue", - ) -plt.show() - -# %% -# examine random features -random_samples = np.random.randint(0, embedding_dataset.sizes["sample"], 700) -# concatenate fov_name, track_id, and t to create a unique sample identifier -sample_id = ( - features["fov_name"][random_samples] - + "-" - + features["track_id"][random_samples].astype(str) - + "-" - + features["t"][random_samples].astype(str) -) -px.imshow( - scaled_features[random_samples], - labels={ - "x": "feature", - "y": "sample", - "color": "value", - }, # change labels to match our metadata - y=sample_id, - # show fov_name as y-axis -) -# %% -ann_root = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/2024_06_13_SEC61_TOMM20_ZIKV_DENGUE_1/4.1-tracking" -) - -infection = load_annotation( - features, - ann_root / "tracking_v1_infection.csv", - "infection class", - {0.0: "background", 1.0: "uninfected", 2.0: "infected"}, -) -division = load_annotation( - features, - ann_root / "cell_division_state.csv", - "division", - {0: "non-dividing", 2: "dividing"}, -) - - -# %% -sns.scatterplot(x=features["UMAP1"], y=features["UMAP2"], hue=division, s=7, alpha=0.8) - -# %% -sns.scatterplot(x=features["UMAP1"], y=features["UMAP2"], hue=infection, s=7, alpha=0.8) - -# %% -ax = sns.histplot(x=features["UMAP1"], y=features["UMAP2"], hue=infection, bins=64) -sns.move_legend(ax, loc="lower left") - -# %% -sns.displot( - x=features["UMAP1"], - y=features["UMAP2"], - kind="hist", - col=infection, - bins=64, - cmap="inferno", -) - -# %% -# interactive scatter plot to associate clusters with specific cells -df = pd.DataFrame({k: v for k, v in features.coords.items() if k != "features"}) -df["infection"] = infection.values -df["division"] = division.values -df["well"] = df["fov_name"].str.rsplit("/", n=1).str[0] -df["fov_track_id"] = df["fov_name"] + "-" + df["track_id"].astype(str) -# select row B (DENV) -df = df[df["fov_name"].str.contains("B")] -df.sort_values("t", inplace=True) - -g = px.scatter( - data_frame=df, - x="UMAP1", - y="UMAP2", - symbol="infection", - color="well", - hover_name="fov_name", - hover_data=["id", "t", "track_id"], - animation_frame="t", - animation_group="fov_track_id", -) -g.update_layout(width=800, height=600) - - -# %% -# cluster features in heatmap directly -# this is very slow for large datasets even with fastcluster installed -inf_codes = pd.Series(infection.values.codes, name="infection") -lut = dict(zip(inf_codes.unique(), "brw")) -row_colors = inf_codes.map(lut) - -g = sns.clustermap( - scaled_features, row_colors=row_colors.to_numpy(), col_cluster=False, cbar_pos=None -) -g.yaxis.set_ticks([]) -# %% diff --git a/applications/contrastive_phenotyping/evaluation/rpe1_fucci/linear_classifier.py b/applications/contrastive_phenotyping/evaluation/rpe1_fucci/linear_classifier.py deleted file mode 100644 index 89d8bb6fc..000000000 --- a/applications/contrastive_phenotyping/evaluation/rpe1_fucci/linear_classifier.py +++ /dev/null @@ -1,142 +0,0 @@ -# %% -from pathlib import Path - -import numpy as np -import pandas as pd -from sklearn.linear_model import LogisticRegression -from sklearn.metrics import accuracy_score, classification_report -from sklearn.model_selection import train_test_split - -from viscy.representation.embedding_writer import read_embedding_dataset - -test_data_features_path = Path( - "/hpc/projects/intracellular_dashboard/organelle_dynamics/2025_rpe_fucci_leger_weigert/0-phenotyping/bf_only_timeaware_ntxent_lr2e-5_temp_7e-2_tau1_w_augmentations_2_ckpt306.zarr" -) -cell_cycle_labels_path = "/hpc/projects/organelle_phenotyping/models/rpe_fucci/dynaclr/pseudolabels/cell_cycle_labels_w_mitosis.csv" - -# %% -# Load the data -cell_cycle_labels_df = pd.read_csv(cell_cycle_labels_path, dtype={"dataset_name": str}) -test_embeddings = read_embedding_dataset(test_data_features_path) - -# Extract features (768-dimensional embeddings) -features = test_embeddings.features.values - -# %% -sample_coords = test_embeddings.coords["sample"].values -fov_names = [coord[0] for coord in sample_coords] -ids = [coord[1] for coord in sample_coords] - -# Create DataFrame with embeddings and identifiers -embedding_df = pd.DataFrame( - { - "dataset_name": fov_names, - "timepoint": ids, - } -) - -# Merge with cell cycle labels -merged_data = embedding_df.merge( - cell_cycle_labels_df, on=["dataset_name", "timepoint"], how="inner" -) - -print(f"Original embeddings: {len(embedding_df)}") -print(f"Cell cycle labels: {len(cell_cycle_labels_df)}") -print(f"Merged data: {len(merged_data)}") -print(f"Cell cycle distribution:\n{merged_data['cell_cycle_state'].value_counts()}") - -# Get corresponding features for merged samples -merged_indices = merged_data.index.values -X = features[merged_indices] -y = merged_data["cell_cycle_state"].values - -# %% -# First split: 80% train+val, 20% test -X_train, X_test, y_train, y_test = train_test_split( - X, y, test_size=0.2, random_state=42, stratify=y -) -print(f"Training set: {X_train.shape[0]} samples") -print(f"Test set: {X_test.shape[0]} samples") - -# %% -# Train logistic regression model -clf = LogisticRegression(random_state=42, max_iter=1000) -clf.fit(X_train, y_train) - -y_test_pred = clf.predict(X_test) -test_accuracy = accuracy_score(y_test, y_test_pred) -print(f"Test accuracy: {test_accuracy:.4f}") - -print("\nTest set classification report:") -print(classification_report(y_test, y_test_pred)) - -# %% -# Enhanced evaluation and visualization -import matplotlib.pyplot as plt -from sklearn.metrics import ConfusionMatrixDisplay, confusion_matrix - -# 1. Confusion Matrix - shows which classes are confused with each other -cm = confusion_matrix(y_test, y_test_pred) -plt.figure(figsize=(8, 6)) -ConfusionMatrixDisplay(cm, display_labels=["G1", "G2", "S", "M"]).plot(cmap="Blues") -plt.title("Confusion Matrix") -plt.show() - -# 2. Per-class errors breakdown -print("\nDetailed per-class analysis:") -for class_name in ["G1", "G2", "S", "M"]: - mask = y_test == class_name - correct = (y_test_pred[mask] == class_name).sum() - total = mask.sum() - print(f"{class_name}: {correct}/{total} correct ({correct / total:.3f})") - - # Show what this class was misclassified as - if total > correct: - wrong_preds = y_test_pred[mask & (y_test_pred != class_name)] - unique, counts = np.unique(wrong_preds, return_counts=True) - print(f" Misclassified as: {dict(zip(unique, counts))}") - -# 3. Prediction confidence (probabilities) -y_test_proba = clf.predict_proba(X_test) -class_names = clf.classes_ - -plt.figure(figsize=(12, 4)) -for i, class_name in enumerate(class_names): - plt.subplot(1, 4, i + 1) - plt.hist( - y_test_proba[:, i], - bins=20, - alpha=0.7, - color=["blue", "orange", "green", "red"][i], - ) - plt.title(f"Confidence for {class_name}") - plt.xlabel("Probability") - plt.ylabel("Count") -plt.tight_layout() -plt.show() - -# 4. Most confident correct and incorrect predictions -print("\nMost confident predictions:") -max_proba = np.max(y_test_proba, axis=1) -pred_correct = y_test == y_test_pred - -# Most confident correct predictions -correct_idx = np.where(pred_correct)[0] -most_confident_correct = correct_idx[np.argsort(max_proba[correct_idx])[-5:]] -print("Top 5 most confident CORRECT predictions:") -for idx in most_confident_correct: - print( - f" True: {y_test[idx]}, Pred: {y_test_pred[idx]}, Confidence: {max_proba[idx]:.3f}" - ) - -# Most confident incorrect predictions -incorrect_idx = np.where(~pred_correct)[0] -if len(incorrect_idx) > 0: - most_confident_wrong = incorrect_idx[np.argsort(max_proba[incorrect_idx])[-5:]] - print("\nTop 5 most confident WRONG predictions:") - for idx in most_confident_wrong: - print( - f" True: {y_test[idx]}, Pred: {y_test_pred[idx]}, Confidence: {max_proba[idx]:.3f}" - ) - -# %% diff --git a/applications/contrastive_phenotyping/evaluation/rpe1_fucci/phate_plot.py b/applications/contrastive_phenotyping/evaluation/rpe1_fucci/phate_plot.py deleted file mode 100644 index e4a56ab88..000000000 --- a/applications/contrastive_phenotyping/evaluation/rpe1_fucci/phate_plot.py +++ /dev/null @@ -1,171 +0,0 @@ -# %% Imports -from pathlib import Path - -import matplotlib.pyplot as plt -import pandas as pd -import seaborn as sns - -from viscy.representation.embedding_writer import read_embedding_dataset -from viscy.representation.evaluation.dimensionality_reduction import compute_phate - -# %% -test_data_features_path = Path( - "/hpc/projects/intracellular_dashboard/organelle_dynamics/2025_rpe_fucci_leger_weigert/0-phenotyping/rpe_fucci_test_data_ckpt264.zarr" -) -test_drugs_path = Path( - "/hpc/projects/intracellular_dashboard/organelle_dynamics/2025_rpe_fucci_leger_weigert/0-phenotyping/rpe_fucci_test_drugs_ckpt264.zarr" -) -cell_cycle_labels_path = "/hpc/projects/organelle_phenotyping/models/rpe_fucci/pseudolabels/cell_cycle_labels.csv" - -# %% Load embeddings and annotations. - -test_features = read_embedding_dataset(test_data_features_path) -# test_drugs = read_embedding_dataset(test_drugs_path) - -# Load cell cycle labels -cell_cycle_labels_df = pd.read_csv(cell_cycle_labels_path, dtype={"dataset_name": str}) - -# Create a combined identifier for matching -sample_coords = test_features.coords["sample"].values -fov_names = [coord[0] for coord in sample_coords] -ids = [coord[1] for coord in sample_coords] - -# Create DataFrame with embeddings and identifiers -embedding_df = pd.DataFrame( - { - "dataset_name": fov_names, - "timepoint": ids, - } -) - -# Merge with cell cycle labels -merged_data = embedding_df.merge( - cell_cycle_labels_df, on=["dataset_name", "timepoint"], how="inner" -) - -print(f"Original embeddings: {len(embedding_df)}") -print(f"Cell cycle labels: {len(cell_cycle_labels_df)}") -print(f"Merged data: {len(merged_data)}") -print(f"Cell cycle distribution:\n{merged_data['cell_cycle_state'].value_counts()}") - -# Get corresponding features for merged samples -merged_indices = merged_data.index.values -cell_cycle_states = merged_data["cell_cycle_state"].values - -# %% -# compute phate -phate_kwargs = { - "knn": 10, - "decay": 20, - "n_components": 2, - "gamma": 1, - "t": "auto", - "n_jobs": -1, -} - -phate_model, phate_embedding = compute_phate(test_features, **phate_kwargs) -# %% - -# Define colorblind-friendly palette for cell cycle states (blue/orange as requested) -cycle_colors = {"G1": "#1f77b4", "G2": "#ff7f0e", "S": "#9467bd"} - -plt.figure(figsize=(10, 10)) -sns.scatterplot( - x=phate_embedding[merged_indices, 0], - y=phate_embedding[merged_indices, 1], - hue=cell_cycle_states, - palette=cycle_colors, - alpha=0.6, -) -plt.title("PHATE Embedding Colored by Cell Cycle State") -plt.legend(bbox_to_anchor=(1.05, 1), loc="upper left") - - -# %% -# Plot the PHATE embedding from the xarray - -plt.figure(figsize=(10, 10)) -sns.scatterplot( - x=test_features["PHATE1"][merged_indices], - y=test_features["PHATE2"][merged_indices], - hue=cell_cycle_states, - palette=cycle_colors, - alpha=0.6, -) -plt.title("PHATE1 vs PHATE2 Colored by Cell Cycle State") -plt.legend(bbox_to_anchor=(1.05, 1), loc="upper left") -# %% -# plot the 3D PHATE embedding (Note: seaborn scatterplot doesn't support 3D, using matplotlib) - -fig = plt.figure(figsize=(10, 10)) -ax = fig.add_subplot(111, projection="3d") - -for state in ["G1", "G2", "S"]: - mask = cell_cycle_states == state - ax.scatter( - test_features["PHATE1"][merged_indices][mask], - test_features["PHATE2"][merged_indices][mask], - test_features["PHATE3"][merged_indices][mask], - c=cycle_colors[state], - alpha=0.6, - label=state, - ) - -ax.set_xlabel("PHATE1") -ax.set_ylabel("PHATE2") -ax.set_zlabel("PHATE3") -ax.set_title("3D PHATE Embedding Colored by Cell Cycle State") -ax.legend() - -# %% -# Plot the PHATE embedding from test_drugs (commented out since not loaded) -# plt.figure(figsize=(10, 10)) -# sns.scatterplot( -# x=test_drugs["PHATE1"], -# y=test_drugs["PHATE2"], -# # hue=test_drugs["t"], -# alpha=0.5, -# ) -# plt.legend(bbox_to_anchor=(1.05, 1), loc="upper left") -# %% -fig, axes = plt.subplots(1, 3, figsize=(18, 6)) - -# PHATE1 vs PHATE2 -sns.scatterplot( - x=test_features["PHATE1"][merged_indices], - y=test_features["PHATE2"][merged_indices], - hue=cell_cycle_states, - palette=cycle_colors, - alpha=0.6, - ax=axes[0], -) -axes[0].set_title("PHATE1 vs PHATE2") -axes[0].legend(bbox_to_anchor=(1.05, 1), loc="upper left") - -# PHATE1 vs PHATE3 -sns.scatterplot( - x=test_features["PHATE1"][merged_indices], - y=test_features["PHATE3"][merged_indices], - hue=cell_cycle_states, - palette=cycle_colors, - alpha=0.6, - ax=axes[1], -) -axes[1].set_title("PHATE1 vs PHATE3") -axes[1].legend(bbox_to_anchor=(1.05, 1), loc="upper left") - -# PHATE2 vs PHATE3 -sns.scatterplot( - x=test_features["PHATE2"][merged_indices], - y=test_features["PHATE3"][merged_indices], - hue=cell_cycle_states, - palette=cycle_colors, - alpha=0.6, - ax=axes[2], -) -axes[2].set_title("PHATE2 vs PHATE3") -axes[2].legend(bbox_to_anchor=(1.05, 1), loc="upper left") - -plt.tight_layout() -plt.show() -# %% diff --git a/applications/contrastive_phenotyping/evaluation/smoothness/compute_smoothness.py b/applications/contrastive_phenotyping/evaluation/smoothness/compute_smoothness.py deleted file mode 100644 index 8047c3d63..000000000 --- a/applications/contrastive_phenotyping/evaluation/smoothness/compute_smoothness.py +++ /dev/null @@ -1,103 +0,0 @@ -# %% -from pathlib import Path - -import matplotlib.pyplot as plt -import pandas as pd -import seaborn as sns - -from viscy.representation.embedding_writer import read_embedding_dataset -from viscy.representation.evaluation.smoothness import compute_embeddings_smoothness - -# %% -# FEATURES - -# openphenom_features_path = Path("/home/jason/projects/contrastive_phenotyping/data/open_phenom/features/open_phenom_features.csv") -# imagenet_features_path = Path("/home/jason/projects/contrastive_phenotyping/data/imagenet/features/imagenet_features.csv") -dynaclr_features_path = Path( - "/hpc/projects/intracellular_dashboard/organelle_dynamics/rerun/2024_11_07_A549_SEC61_DENV/4-phenotyping/dtw_evaluation/SAM2/sam2_sensor_only.zarr" -) -dinov3_features_path = Path( - "/home/eduardo.hirata/repos/viscy/applications/benchmarking/DynaCLR/DINOV3/embeddings_convnext_tiny_phase_only_2.zarr" -) - -# LOADING DATASETS -# openphenom_features = read_embedding_dataset(openphenom_features_path) -# imagenet_features = read_embedding_dataset(imagenet_features_path) -dynaclr_embedding_dataset = read_embedding_dataset(dynaclr_features_path) -dinov3_embedding_dataset = read_embedding_dataset(dinov3_features_path) -# %% -# Compute the smoothness of the features -DISTANCE_METRIC = "cosine" -feature_paths = { - # "dynaclr": dynaclr_features_path, - "dinov3": dinov3_features_path, -} -cmap = plt.get_cmap("tab10") # or use "Set2", "tab20", etc. -labels = list(feature_paths.keys()) -interval_colors = {label: cmap(i % cmap.N) for i, label in enumerate(labels)} -# Print and check each path -for label, path in feature_paths.items(): - print(f"{label} color: {interval_colors[label]}") - assert Path(path).exists(), f"Path {path} does not exist" - -output_dir = Path("./smoothness_metrics") -output_dir.mkdir(parents=True, exist_ok=True) - -results = {} -for label, path in feature_paths.items(): - results[label] = {} - print(f"\nProcessing - {label}") - embedding_dataset = read_embedding_dataset(Path(path)) - - # Compute displacements - stats, distributions, _ = compute_embeddings_smoothness( - embedding_dataset=embedding_dataset, - distance_metric=DISTANCE_METRIC, - verbose=True, - ) - - # Plot the piecewise distances - plt.figure() - sns.histplot( - distributions["adjacent_frame_distribution"], - bins=30, - kde=True, - color="cyan", - alpha=0.5, - stat="density", - ) - sns.histplot( - distributions["random_frame_distribution"], - bins=30, - kde=True, - color="red", - alpha=0.5, - stat="density", - ) - plt.xlabel(f"{DISTANCE_METRIC} Distance") - plt.ylabel("Density") - # Add vertical lines for the peaks - plt.axvline(x=stats["adjacent_frame_peak"], color="cyan", linestyle="--", alpha=0.8) - plt.axvline(x=stats["random_frame_peak"], color="red", linestyle="--", alpha=0.8) - plt.tight_layout() - plt.legend(["Adjacent Frame", "Random Sample", "Adjacent Peak", "Random Peak"]) - plt.savefig(output_dir / f"{label}_smoothness.pdf", dpi=300) - plt.savefig(output_dir / f"{label}_smoothness.png", dpi=300) - plt.close() - - # metrics to csv - scalar_metrics = { - "adjacent_frame_mean": stats["adjacent_frame_mean"], - "adjacent_frame_std": stats["adjacent_frame_std"], - "adjacent_frame_median": stats["adjacent_frame_median"], - "adjacent_frame_peak": stats["adjacent_frame_peak"], - "random_frame_mean": stats["random_frame_mean"], - "random_frame_std": stats["random_frame_std"], - "random_frame_median": stats["random_frame_median"], - "random_frame_peak": stats["random_frame_peak"], - "smoothness_score": stats["smoothness_score"], - "dynamic_range": stats["dynamic_range"], - } - # Create DataFrame with single row - stats_df = pd.DataFrame(scalar_metrics, index=[0]) - stats_df.to_csv(output_dir / f"{label}_smoothness_stats.csv", index=False) diff --git a/applications/contrastive_phenotyping/figures/cell_division.py b/applications/contrastive_phenotyping/figures/cell_division.py deleted file mode 100644 index 096e7dba5..000000000 --- a/applications/contrastive_phenotyping/figures/cell_division.py +++ /dev/null @@ -1,284 +0,0 @@ -# %% figures for visualizing the results of cell division -import sys - -sys.path.append("/hpc/mydata/soorya.pradeep/scratch/viscy_infection_phenotyping/VisCy") -from pathlib import Path - -import matplotlib.pyplot as plt -import pandas as pd -import seaborn as sns -from sklearn.preprocessing import StandardScaler -from umap import UMAP - -from viscy.representation.embedding_writer import read_embedding_dataset - -# %% -# single channel. with temporal regularizations -# dataset = read_embedding_dataset( -# "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/time_sampling_strategies/time_interval_phase/predictions/epoch_186/1chan_128patch_186ckpt_Febtest.zarr" -# ) -# dataset - -# single cahnnel, without temporal regularizations -# dataset = read_embedding_dataset( -# "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/time_sampling_strategies/negpair_difcell_randomtime_sampling/Ver2_updateTracking_refineModel/predictions/Feb_1chan_128patch_32projDim/1chan_128patch_63ckpt_FebTest_divGT.zarr" -# ) -# dataset - -# two channel, with temporal regularizations -# dataset = read_embedding_dataset( -# "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/time_sampling_strategies/time_interval/predict/feb_test_time_interval_1_epoch_178_gt_tracks.zarr" -# ) -# dataset - -# two channel, without temporal regularizations -dataset = read_embedding_dataset( - "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/time_sampling_strategies/negpair_difcell_randomtime_sampling/Ver2_updateTracking_refineModel/predictions/Feb_2chan_128patch_32projDim/2chan_128patch_56ckpt_FebTest_divGT.zarr" -) -dataset - -# %% -# load all unprojected features: -features = dataset["features"] -# or select a well: -# features - features[features["fov_name"].str.contains("B/4")] -features - -# %% umap with 2 components -scaled_features = StandardScaler().fit_transform(features.values) - -umap = UMAP() - -embedding = umap.fit_transform(features.values) -features = ( - features.assign_coords(UMAP1=("sample", embedding[:, 0])) - .assign_coords(UMAP2=("sample", embedding[:, 1])) - .set_index(sample=["UMAP1", "UMAP2"], append=True) -) -features - -# %% - - -def load_annotation(da, path, name, categories: dict | None = None): - annotation = pd.read_csv(path) - # annotation_columns = annotation.columns.tolist() - # print(annotation_columns) - annotation["fov_name"] = "/" + annotation["fov ID"] - annotation = annotation.set_index(["fov_name", "id"]) - mi = pd.MultiIndex.from_arrays( - [da["fov_name"].values, da["id"].values], names=["fov_name", "id"] - ) - selected = annotation.loc[mi][name] - if categories: - selected = selected.astype("category").cat.rename_categories(categories) - return selected - - -# %% - -ann_root = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/2024_02_04_A549_DENV_ZIKV_timelapse/9-lineage-cell-division/lineages_gt" -) - -division = load_annotation( - features, - ann_root / "cell_division_state_test_set.csv", - "division", - {0: "interphase", 2: "mitosis"}, -) - -# %% -sns.scatterplot( - x=features["UMAP1"], - y=features["UMAP2"], - hue=division, - palette={"interphase": "steelblue", 1: "green", "mitosis": "orangered"}, - s=7, - alpha=0.8, -) -plt.show() -# plt.savefig( -# "/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/Figure_panels/cell_division/UMAP_cellDiv_GTtracking_sc_woT.svg" -# ) - -# %% -no_inter = division[division == "interphase"].count() -no_div = division[division == "mitosis"].count() - -# %% plot the trajectory quiver of one cell on top of the UMAP - -from matplotlib.patches import FancyArrowPatch - -cell_parent = features[ - (features["fov_name"].str.contains("A/3/7")) & (features["track_id"].isin([13])) -] -cell_daughter1 = features[ - (features["fov_name"].str.contains("A/3/7")) & (features["track_id"].isin([14])) -] -cell_daughter2 = features[ - (features["fov_name"].str.contains("A/3/7")) & (features["track_id"].isin([15])) -] - - -# %% Plot: Adding arrows to indicate trajectory direction -def add_arrows(df, color): - for i in range(df.shape[0] - 1): - start = df.iloc[i] - end = df.iloc[i + 1] - arrow = FancyArrowPatch( - (start["UMAP1"], start["UMAP2"]), - (end["UMAP1"], end["UMAP2"]), - color=color, - arrowstyle="->", - mutation_scale=20, # reduce the size of arrowhead by half - lw=2, - shrinkA=0, - shrinkB=0, - ) - plt.gca().add_patch(arrow) - - -# tried A/3/7, 8 to 9 & 10 -# tried A/3/7, 13 to 14 & 15 -# tried A/3/7, 18 to 19 & 20 -# tried A/3/8, 23 to 24 & 25 - -sns.scatterplot( - x=features["UMAP1"], - y=features["UMAP2"], - hue=division, - palette={"interphase": "steelblue", 1: "green", "mitosis": "orangered"}, - s=7, - alpha=0.5, -) - -# Apply arrows to the trajectories -add_arrows(cell_parent.to_dataframe(), color="black") -add_arrows(cell_daughter1.to_dataframe(), color="red") -add_arrows(cell_daughter2.to_dataframe(), color="blue") - -plt.xlabel("UMAP1") -plt.ylabel("UMAP2") -# plt.title('UMAP with Trajectory Direction') -# plt.legend(title='Division Phase') -plt.xlim(-5, 10) -plt.ylim(-5, 10) -plt.legend([], [], frameon=False) -# plt.show() - -# single channel, with temporal regularizations -plt.savefig( - "/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/Figure_panels/cell_division/cellDiv_trajectory_singelChannel.png", - dpi=300, -) - -# single channel, without temporal regularizations -# plt.savefig( -# "/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/Figure_panels/cell_division/cellDiv_trajectory_singelChannel_woT.png", -# dpi=300 -# ) - -# two channel, with temporal regularizations -# plt.savefig( -# "/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/Figure_panels/cell_division/cellDiv_trajectory_2Channel.png", -# dpi=300 -# ) - -# two channel, without temporal regularizations -# plt.savefig( -# "/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/Figure_panels/cell_division/cellDiv_trajectory_2Channel_woT.png", -# dpi=300 -# ) - -# %% Plot: display one arrow at end of trajectory of cell overlayed on UMAP - -sns.scatterplot( - x=features["UMAP1"], - y=features["UMAP2"], - hue=division, - palette={"interphase": "steelblue", 1: "green", "mitosis": "orangered"}, - s=27, - alpha=0.5, -) - -sns.lineplot(x=cell_parent["UMAP1"], y=cell_parent["UMAP2"], color="black", linewidth=2) -sns.lineplot( - x=cell_daughter1["UMAP1"], y=cell_daughter1["UMAP2"], color="blue", linewidth=2 -) -sns.lineplot( - x=cell_daughter2["UMAP1"], y=cell_daughter2["UMAP2"], color="red", linewidth=2 -) - -parent_arrow = FancyArrowPatch( - (cell_parent["UMAP1"].values[-2], cell_parent["UMAP2"].values[-2]), - (cell_parent["UMAP1"].values[-1], cell_parent["UMAP2"].values[-1]), - color="black", - arrowstyle="->", - mutation_scale=20, # reduce the size of arrowhead by half - lw=2, - shrinkA=0, - shrinkB=0, -) -plt.gca().add_patch(parent_arrow) -daughter1_arrow = FancyArrowPatch( - (cell_daughter1["UMAP1"].values[0], cell_daughter1["UMAP2"].values[0]), - (cell_daughter1["UMAP1"].values[1], cell_daughter1["UMAP2"].values[1]), - color="blue", - arrowstyle="->", - mutation_scale=20, # reduce the size of arrowhead by half - lw=2, - shrinkA=0, - shrinkB=0, -) -plt.gca().add_patch(daughter1_arrow) -daughter2_arrow = FancyArrowPatch( - (cell_daughter2["UMAP1"].values[0], cell_daughter2["UMAP2"].values[0]), - (cell_daughter2["UMAP1"].values[1], cell_daughter2["UMAP2"].values[1]), - color="red", - arrowstyle="->", - mutation_scale=20, # reduce the size of arrowhead by half - lw=2, - shrinkA=0, - shrinkB=0, -) -plt.gca().add_patch(daughter2_arrow) - - -# single channel, with temporal regularizations -# plt.xlim(-5, 8) -# plt.ylim(-6, 8) -# plt.legend([], [], frameon=False) -# plt.savefig( -# "/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/Figure_panels/cell_division/cellDiv_trajectory_singelChannel_arrow.png", -# dpi=300, -# ) - -# single channel, without temporal regularizations -# plt.xlim(0, 13) -# plt.ylim(-2, 6) -# plt.legend([], [], frameon=False) -# plt.savefig( -# "/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/Figure_panels/cell_division/cellDiv_trajectory_singelChannel_woT_arrow.png", -# dpi=300 -# ) - -# two channel, with temporal regularizations -# plt.xlim(-2, 15) -# plt.ylim(-5, 5) -# plt.legend([], [], frameon=False) -# plt.savefig( -# "/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/Figure_panels/cell_division/cellDiv_trajectory_2Channel_arrow.png", -# dpi=300 -# ) - -# two channel, without temporal regularizations -plt.xlim(-3, 12) -plt.ylim(1, 10) -plt.legend([], [], frameon=False) -plt.savefig( - "/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/Figure_panels/cell_division/cellDiv_trajectory_2Channel_woT_arrow.png", - dpi=300, -) - -# %% diff --git a/applications/contrastive_phenotyping/figures/figure.mplstyle b/applications/contrastive_phenotyping/figures/figure.mplstyle deleted file mode 100644 index 7e6095681..000000000 --- a/applications/contrastive_phenotyping/figures/figure.mplstyle +++ /dev/null @@ -1,8 +0,0 @@ -font.family: sans-serif -font.sans-serif: Arial -font.size: 10 -figure.titlesize: 12 -axes.titlesize: 10 -xtick.labelsize: 8 -ytick.labelsize: 8 -text.usetex: True diff --git a/applications/contrastive_phenotyping/figures/figure_cell_infection.py b/applications/contrastive_phenotyping/figures/figure_cell_infection.py deleted file mode 100644 index aeec26bc0..000000000 --- a/applications/contrastive_phenotyping/figures/figure_cell_infection.py +++ /dev/null @@ -1,646 +0,0 @@ -# %% -import sys -from pathlib import Path - -sys.path.append("/hpc/mydata/soorya.pradeep/scratch/viscy_infection_phenotyping/VisCy") - -import matplotlib.pyplot as plt -import numpy as np -import pandas as pd -import seaborn as sns -from sklearn.decomposition import PCA -from sklearn.preprocessing import StandardScaler -from umap import UMAP - -from viscy.representation.embedding_writer import read_embedding_dataset -from viscy.representation.evaluation import load_annotation - -# %% Paths and parameters. - - -features_path = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/time_sampling_strategies/time_interval/predict/feb_test_time_interval_1_epoch_178.zarr" -) -data_path = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/2024_02_04_A549_DENV_ZIKV_timelapse/8-train-test-split/registered_test.zarr" -) -tracks_path = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/2024_02_04_A549_DENV_ZIKV_timelapse/8-train-test-split/track_test.zarr" -) - - -# %% -embedding_dataset = read_embedding_dataset(features_path) -embedding_dataset - -# %% -# Compute UMAP over all features -features = embedding_dataset["features"] -# or select a well: -# features = features[features["fov_name"].str.contains("B/4")] - - -scaled_features = StandardScaler().fit_transform(features.values) -umap = UMAP() -# Fit UMAP on all features -embedding = umap.fit_transform(scaled_features) - -features = ( - features.assign_coords(UMAP1=("sample", embedding[:, 0])) - .assign_coords(UMAP2=("sample", embedding[:, 1])) - .set_index(sample=["UMAP1", "UMAP2"], append=True) -) -features - -pca = PCA(n_components=4) -# scaled_features = StandardScaler().fit_transform(features.values) -# pca_features = pca.fit_transform(scaled_features) -pca_features = pca.fit_transform(features.values) - - -features = ( - features.assign_coords(PCA1=("sample", pca_features[:, 0])) - .assign_coords(PCA2=("sample", pca_features[:, 1])) - .assign_coords(PCA3=("sample", pca_features[:, 2])) - .assign_coords(PCA4=("sample", pca_features[:, 3])) - .set_index(sample=["PCA1", "PCA2", "PCA3", "PCA4"], append=True) -) - -# %% OVERLAY INFECTION ANNOTATION -ann_root = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/2024_02_04_A549_DENV_ZIKV_timelapse/8-train-test-split/supervised_inf_pred" -) - -infection = load_annotation( - features, - ann_root / "extracted_inf_state.csv", - "infection_state", - {0.0: "background", 1.0: "uninfected", 2.0: "infected"}, -) - -# %% plot the umap - -# remove the rows in umap and annotation for background class -# Convert UMAP coordinates to a DataFrame -umap_npy = embedding.copy() -infection_npy = infection.cat.codes.values - -# Filter out the background class -umap_npy_filtered = umap_npy[infection_npy != 0] -infection_npy_filtered = infection_npy[infection_npy != 0] - -feature_npy = features.values -feature_npy_filtered = feature_npy[infection_npy != 0] - -sns.scatterplot( - x=umap_npy_filtered[:, 0], - y=umap_npy_filtered[:, 1], - hue=infection_npy_filtered, - palette={1: "steelblue", 2: "orangered"}, - hue_order=[1, 2], - s=7, - alpha=0.8, -) -plt.legend([], [], frameon=False) -plt.savefig( - "/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/Figure_panels/infection/umap_infection.png", - format="png", - dpi=300, -) - -# %% plot PCA components with infection hue - -pca_npy = pca_features.copy() -pca_npy_filtered = pca_npy[infection_npy != 0] - -sns.scatterplot( - x=pca_npy_filtered[:, 0], - y=pca_npy_filtered[:, 1], - hue=infection_npy_filtered, - palette={1: "steelblue", 2: "orangered"}, - hue_order=[1, 2], - s=7, - alpha=0.8, -) -plt.legend([], [], frameon=False) -plt.savefig( - "/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/Figure_panels/infection/pca_infection.png", - format="png", - dpi=300, -) - -# %% combine the umap, pca and infection annotation in one dataframe - -data = pd.DataFrame( - { - "UMAP1": umap_npy_filtered[:, 0], - "UMAP2": umap_npy_filtered[:, 1], - "PCA1": pca_npy_filtered[:, 0], - "PCA2": pca_npy_filtered[:, 1], - "PCA3": pca_npy_filtered[:, 2], - "PCA4": pca_npy_filtered[:, 3], - "infection": infection_npy_filtered, - } -) - -# add time and well info into dataframe -time_npy = features["t"].values -time_npy_filtered = time_npy[infection_npy != 0] -data["time"] = time_npy_filtered - -fov_name_list = features["fov_name"].values -fov_name_list_filtered = fov_name_list[infection_npy != 0] -data["fov_name"] = fov_name_list_filtered - -# Add all 768 features to the dataframe -for i in range(768): - data[f"feature_{i + 1}"] = feature_npy_filtered[:, i] - -# %% manually split the dataset into training and testing set by well name - -# dataframe for training set, fov names starts with "/B/4/6" or "/B/4/7" or "/A/3/" -data_train_val = data[ - data["fov_name"].str.contains("/B/4/6") - | data["fov_name"].str.contains("/B/4/7") - | data["fov_name"].str.contains("/A/3/") -] - -# dataframe for testing set, fov names starts with "/B/4/8" or "/B/4/9" or "/A/4/" -data_test = data[ - data["fov_name"].str.contains("/B/4/8") - | data["fov_name"].str.contains("/B/4/9") - | data["fov_name"].str.contains("/B/3/") -] - -# %% train a linear classifier to predict infection state from PCA components - -from sklearn.linear_model import LogisticRegression # noqa: E402 - -x_train = data_train_val.drop( - columns=[ - "infection", - "fov_name", - "time", - "UMAP1", - "UMAP2", - "PCA1", - "PCA2", - "PCA3", - "PCA4", - ] -) -y_train = data_train_val["infection"] - -# train a logistic regression model -clf = LogisticRegression(random_state=0).fit(x_train, y_train) - -x_test = data_test.drop( - columns=[ - "infection", - "fov_name", - "time", - "UMAP1", - "UMAP2", - "PCA1", - "PCA2", - "PCA3", - "PCA4", - ] -) -y_test = data_test["infection"] - -# predict the infection state for the testing set -y_pred = clf.predict(x_test) - -# %% construct confusion matrix to compare the true and predicted infection state - -import seaborn as sns # noqa: E402 -from sklearn.metrics import confusion_matrix # noqa: E402 - -cm = confusion_matrix(y_test, y_pred) -cm_percentage = cm.astype("float") / cm.sum(axis=1)[:, np.newaxis] * 100 -sns.heatmap(cm_percentage, annot=True, fmt=".2f", cmap="viridis") -plt.xlabel("Predicted") -plt.ylabel("True") -plt.title("Confusion Matrix (Percentage)") -plt.xticks(ticks=[0.5, 1.5], labels=["uninfected", "infected"]) -plt.yticks(ticks=[0.5, 1.5], labels=["uninfected", "infected"]) -plt.savefig( - "/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/Figure_panels/infection/confusion_matrix.svg", - format="svg", -) - -# %% use the trained classifier to perform prediction on the entire dataset - -data_test["predicted_infection"] = y_pred - -# plot the predicted infection state over time for /B/3 well and /B/4 well -time_points_test = np.unique(data_test["time"]) - -infected_test_cntrl = [] -infected_test_infected = [] - -for time in time_points_test: - infected_cell = data_test[ - (data_test["fov_name"].str.startswith("/B/3")) - & (data_test["time"] == time) - & (data_test["predicted_infection"] == 2) - ].shape[0] - total_cell = data_test[ - (data_test["fov_name"].str.startswith("/B/3")) & (data_test["time"] == time) - ].shape[0] - infected_test_cntrl.append(infected_cell * 100 / total_cell) - infected_cell = data_test[ - (data_test["fov_name"].str.startswith("/B/4")) - & (data_test["time"] == time) - & (data_test["predicted_infection"] == 2) - ].shape[0] - total_cell = data_test[ - (data_test["fov_name"].str.startswith("/B/4")) & (data_test["time"] == time) - ].shape[0] - infected_test_infected.append(infected_cell * 100 / total_cell) - - -infected_true_cntrl = [] -infected_true_infected = [] - -for time in time_points_test: - infected_cell = data_test[ - (data_test["fov_name"].str.startswith("/B/3")) - & (data_test["time"] == time) - & (data_test["infection"] == 2) - ].shape[0] - total_cell = data_test[ - (data_test["fov_name"].str.startswith("/B/3")) & (data_test["time"] == time) - ].shape[0] - infected_true_cntrl.append(infected_cell * 100 / total_cell) - infected_cell = data_test[ - (data_test["fov_name"].str.startswith("/B/4")) - & (data_test["time"] == time) - & (data_test["infection"] == 2) - ].shape[0] - total_cell = data_test[ - (data_test["fov_name"].str.startswith("/B/4")) & (data_test["time"] == time) - ].shape[0] - infected_true_infected.append(infected_cell * 100 / total_cell) - - -# %% perform prediction on the june dataset - -# Paths and parameters. -features_path = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/time_sampling_strategies/time_interval/predict/jun_time_interval_1_epoch_178.zarr" -) -data_path = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/2024_06_13_SEC61_TOMM20_ZIKV_DENGUE_1/2-register/registered_chunked.zarr" -) -tracks_path = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/2024_06_13_SEC61_TOMM20_ZIKV_DENGUE_1/4.2-tracking/track.zarr" -) - -# %% -embedding_dataset = read_embedding_dataset(features_path) -embedding_dataset - -# %% -june_features = embedding_dataset["features"] - -scaled_features = StandardScaler().fit_transform(june_features.values) -umap = UMAP() -# Fit UMAP on all features -embedding = umap.fit_transform(scaled_features) - -june_features = ( - june_features.assign_coords(UMAP1=("sample", embedding[:, 0])) - .assign_coords(UMAP2=("sample", embedding[:, 1])) - .set_index(sample=["UMAP1", "UMAP2"], append=True) -) -june_features - -pca = PCA(n_components=4) -pca_features = pca.fit_transform(june_features.values) - -# %% - -# sns.scatterplot( -# x=june_features["UMAP1"], -# y=june_features["UMAP2"], -# hue=june_pred, -# palette={1: 'blue', 2: 'red'}, -# hue_order=[1, 2], -# s=7, -# alpha=0.8, -# ) -# plt.legend([], [], frameon=False) -# plt.xlim(0, 15) -# plt.savefig('/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/Figure_panels/infection/june_umap_infection.png', format='png', dpi=300) - -# %% plot June and Feb test combined UMAP - -june_umap_npy = embedding.copy() -june_pca_npy = pca_features.copy() -june_data = pd.DataFrame( - { - "UMAP1": june_umap_npy[:, 0], - "UMAP2": june_umap_npy[:, 1], - "PCA1": june_pca_npy[:, 0], - "PCA2": june_pca_npy[:, 1], - "PCA3": june_pca_npy[:, 2], - "PCA4": june_pca_npy[:, 3], - "infection": np.nan, - } -) - -# add time and well info into dataframe -june_data["time"] = june_features["t"].values - -june_data["fov_name"] = june_features["fov_name"].values - -# Add all 768 features to the dataframe -june_features_npy = june_features.values -for i in range(768): - june_data[f"feature_{i + 1}"] = june_features_npy[:, i] - -# use one mock and one dengue infecected well only -june_data = june_data[ - june_data["fov_name"].str.contains("/0/6") - | june_data["fov_name"].str.contains("/0/2") -] - -# add the predicted infection state -june_pred = clf.predict( - june_data.drop( - columns=[ - "infection", - "fov_name", - "time", - "UMAP1", - "UMAP2", - "PCA1", - "PCA2", - "PCA3", - "PCA4", - ] - ) -) -june_data["predicted_infection"] = june_pred - -# %% combine the june and feb data - -combined_data = pd.concat([data_test, june_data]) - -# perform the umap analysis again with the 768 features -features = combined_data.drop( - columns=[ - "infection", - "predicted_infection", - "fov_name", - "time", - "UMAP1", - "UMAP2", - "PCA1", - "PCA2", - "PCA3", - "PCA4", - ] -) -scaled_features = StandardScaler().fit_transform(features.values) -umap = UMAP() -# Fit UMAP on all features -embedding = umap.fit_transform(scaled_features) - -# overwrite the umap coordinates on combined data -combined_data["UMAP1"] = embedding[:, 0] -combined_data["UMAP2"] = embedding[:, 1] - -# plot the combined data with 'fov_name' starting with '/A and '/B' hue 'infection' and '/0' hue 'predicted_infection' -Feb_split = combined_data[ - combined_data["fov_name"].str.contains("/A") - | combined_data["fov_name"].str.contains("/B") -] -June_split = combined_data[combined_data["fov_name"].str.contains("/0")] - -sns.scatterplot( - x=June_split["UMAP1"], - y=June_split["UMAP2"], - hue=June_split["predicted_infection"], - palette={1: "blue", 2: "red"}, - hue_order=[1, 2], - s=7, - alpha=0.8, -) -sns.scatterplot( - x=Feb_split["UMAP1"], - y=Feb_split["UMAP2"], - hue=Feb_split["infection"], - palette={1: "steelblue", 2: "orange"}, - hue_order=[1, 2], - s=7, - alpha=0.8, -) -plt.legend([], [], frameon=False) -# plt.savefig('/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/Figure_panels/infection/combined_umap_infection.png', format='png', dpi=300) - -# plot the scatterplot hue well name '/A' and '/B' are blue and '/0' are red -combined_data["color"] = combined_data["fov_name"].apply( - lambda x: "brown" if x.startswith("/0") else "green" -) - -sns.scatterplot( - x=combined_data["UMAP1"], - y=combined_data["UMAP2"], - hue="color", - palette={"green": "green", "brown": "brown"}, - data=combined_data, - s=7, - alpha=0.2, # Increased transparency -) -plt.xlim(-5, 5) -plt.ylim(-2, 20) -plt.legend([], [], frameon=False) -plt.savefig( - "/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/Figure_panels/infection/combined_umap_well.png", - format="png", - dpi=300, -) - -# plot the predicted infection state with combined data -sns.scatterplot( - x=combined_data["UMAP1"], - y=combined_data["UMAP2"], - hue=combined_data["predicted_infection"], - palette={1: "blue", 2: "red"}, - hue_order=[1, 2], - s=7, - alpha=0.8, -) -plt.xlim(-5, 5) -plt.ylim(-2, 20) -plt.legend([], [], frameon=False) -plt.savefig( - "/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/Figure_panels/infection/combined_umap_predicted_infection.png", - format="png", - dpi=300, -) - -# %% plot % infected over time - -time_points_june = np.unique(June_split["time"]) - -infected_june_cntrl = [] -infected_june_infected = [] - -for time in time_points_june: - infected_june = June_split[ - (June_split["fov_name"].str.startswith("/0/2")) - & (June_split["time"] == time) - & (June_split["predicted_infection"] == 2) - ].shape[0] - total_june = June_split[ - (June_split["fov_name"].str.startswith("/0/2")) & (June_split["time"] == time) - ].shape[0] - infected_june_cntrl.append(infected_june * 100 / total_june) - infected_june = June_split[ - (June_split["fov_name"].str.startswith("/0/6")) - & (June_split["time"] == time) - & (June_split["predicted_infection"] == 2) - ].shape[0] - total_june = June_split[ - (June_split["fov_name"].str.startswith("/0/6")) & (June_split["time"] == time) - ].shape[0] - infected_june_infected.append(infected_june * 100 / total_june) - - -# plot infected percentage over time for both wells -plt.plot( - time_points_test * 0.5 + 3, - infected_true_cntrl, - label="mock true", - color="steelblue", - linestyle="--", -) -plt.plot( - time_points_test * 0.5 + 3, - infected_test_cntrl, - label="mock predicted", - color="blue", - marker="+", -) -plt.plot( - time_points_test * 0.5 + 3, - infected_true_infected, - label="MOI true", - color="orange", - linestyle="--", -) -plt.plot( - time_points_test * 0.5 + 3, - infected_test_infected, - label="MOI predicted", - color="red", - marker="+", -) -plt.plot( - time_points_june * 2 + 3, - infected_june_cntrl, - label="mock new predicted", - color="blue", - marker="o", -) -plt.plot( - time_points_june * 2 + 3, - infected_june_infected, - label="MOI new predicted", - color="red", - marker="o", -) -plt.xlabel("HPI") -plt.ylabel("Infected percentage") -plt.legend() -plt.savefig( - "/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/Figure_panels/infection/infected_percentage_withJune.svg", - format="svg", -) - -# %% appendix video for infection dynamics umap, Feb test data, colored by human revised annotation - -for time in range(48): - plt.clf() - sns.scatterplot( - data=data_test[(data_test["time"] == time)], - x="UMAP1", - y="UMAP2", - hue="infection", - palette={1: "steelblue", 2: "orangered"}, - hue_order=[1, 2], - s=20, - alpha=0.8, - ) - handles, _ = plt.gca().get_legend_handles_labels() - plt.legend(handles=handles, labels=["uninfected", "infected"]) - plt.suptitle(f"Time: {time * 0.5 + 3} HPI") - plt.ylim(-10, 20) - plt.xlim(2, 18) - plt.savefig( - "/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/Figure_panels/infection/video_umap/umap_feb_true_infection_" - + str(time).zfill(3) - + ".png", - format="png", - dpi=300, - ) - -# %% appendix video for infection dynamics umap, Feb test data, colored by predicted infection - -for time in range(48): - plt.clf() - sns.scatterplot( - data=data_test[(data_test["time"] == time)], - x="UMAP1", - y="UMAP2", - hue="predicted_infection", - palette={1: "blue", 2: "red"}, - hue_order=[1, 2], - s=20, - alpha=0.8, - ) - handles, _ = plt.gca().get_legend_handles_labels() - plt.legend(handles=handles, labels=["uninfected", "infected"]) - plt.suptitle(f"Time: {time * 0.5 + 3} HPI") - plt.ylim(-10, 18) - plt.xlim(2, 18) - plt.savefig( - "/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/Figure_panels/infection/video_umap/umap_feb_predicted_infection_" - + str(time).zfill(3) - + ".png", - format="png", - dpi=300, - ) - -# %% appendix video for infection dynamics umap, June data, colored by predicted infection - -for time in range(12): - plt.clf() - sns.scatterplot( - data=June_split[(June_split["time"] == time)], - x="UMAP1", - y="UMAP2", - hue="predicted_infection", - palette={1: "blue", 2: "red"}, - hue_order=[1, 2], - s=20, - alpha=0.8, - ) - handles, _ = plt.gca().get_legend_handles_labels() - plt.legend(handles=handles, labels=["uninfected", "infected"]) - plt.suptitle(f"Time: {time * 2 + 3} HPI") - plt.ylim(-8, 10) - plt.xlim(-5, 5) - plt.savefig( - "/hpc/projects/comp.micro/infected_cell_imaging/Single_cell_phenotyping/ContrastiveLearning/Figure_panels/infection/video_umap/umap_june_predicted_infection_" - + str(time).zfill(3) - + ".png", - format="png", - dpi=300, - ) - -# %% diff --git a/applications/contrastive_phenotyping/figures/grad_attr.py b/applications/contrastive_phenotyping/figures/grad_attr.py deleted file mode 100644 index 038cc5c96..000000000 --- a/applications/contrastive_phenotyping/figures/grad_attr.py +++ /dev/null @@ -1,644 +0,0 @@ -# %% -import logging -import warnings -from pathlib import Path - -import matplotlib as mpl -import matplotlib.animation as animation -import matplotlib.pyplot as plt -import numpy as np -import pandas as pd -import torch -import xarray as xr -from cmap import Colormap -from lightning.pytorch import seed_everything -from skimage.exposure import rescale_intensity - -from viscy.data.triplet import TripletDataModule -from viscy.representation.embedding_writer import read_embedding_dataset -from viscy.representation.engine import ContrastiveEncoder, ContrastiveModule -from viscy.representation.evaluation import load_annotation -from viscy.representation.evaluation.lca import ( - AssembledClassifier, - fit_logistic_regression, - linear_from_binary_logistic_regression, -) -from viscy.transforms import ( - Decollated, - NormalizeSampled, - ScaleIntensityRangePercentilesd, -) - -seed_everything(42, workers=True) - -logger = logging.getLogger(__name__) -logger.setLevel(logging.DEBUG) - - -# %% -# Dataset for display and occlusion analysis -data_path = "/hpc/projects/intracellular_dashboard/organelle_dynamics/2024_02_04_A549_DENV_ZIKV_timelapse/8-train-test-split/registered_test.zarr" -tracks_path = "/hpc/projects/intracellular_dashboard/organelle_dynamics/2024_02_04_A549_DENV_ZIKV_timelapse/8-train-test-split/track_test.zarr" -annotation_occlusion_infection_path = "/hpc/projects/intracellular_dashboard/organelle_dynamics/2024_02_04_A549_DENV_ZIKV_timelapse/8-train-test-split/supervised_inf_pred/extracted_inf_state.csv" -annotation_occlusion_division_path = "/hpc/projects/intracellular_dashboard/organelle_dynamics/2024_02_04_A549_DENV_ZIKV_timelapse/9-lineage-cell-division/lineages_gt/cell_division_state_test_set.csv" -fov = "/B/4/8" -track = [44, 46] - -# %% -dm = TripletDataModule( - data_path=data_path, - tracks_path=tracks_path, - source_channel=["Phase3D", "RFP"], - z_range=[25, 40], - batch_size=1, - num_workers=0, - initial_yx_patch_size=(128, 128), - final_yx_patch_size=(128, 128), - normalizations=[ - NormalizeSampled( - keys=["Phase3D"], level="fov_statistics", subtrahend="mean", divisor="std" - ), - ScaleIntensityRangePercentilesd( - keys=["RFP"], lower=50, upper=99, b_min=0.0, b_max=1.0 - ), - Decollated( - keys=["Phase3D", "RFP"], - ), - ], - predict_cells=True, - include_fov_names=[fov] * len(track), - include_track_ids=track, -) -dm.setup("predict") -len(dm.predict_dataset) - -# %% -# load model -model = ContrastiveModule.load_from_checkpoint( - "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/time_sampling_strategies/time_interval/predict/epoch=178-step=16826.ckpt", - encoder=ContrastiveEncoder( - backbone="convnext_tiny", - in_channels=2, - in_stack_depth=15, - stem_kernel_size=(5, 4, 4), - stem_stride=(5, 4, 4), - embedding_dim=768, - projection_dim=32, - ), -).eval() - - -# %% -def load_and_combine_datasets( - datasets, - target_type="infection", - standardization_mapping=None, -): - """Load and combine multiple embedding datasets with their annotations. - - Parameters - ---------- - datasets : list of tuple - List of (embedding_path, annotation_path, train_fovs) tuples containing - paths to embedding files, annotation CSV files, and training FOVs. - target_type : str, default='infection' - Type of classification target. Either 'infection' or 'division' - determines - which column to look for in the annotation files. - standardization_mapping : dict, optional - Dictionary to standardize different annotation formats across datasets. - Maps original values to standardized values. - Example: {'infected': 2, 'uninfected': 1, 'background': 0, - 2.0: 2, 1.0: 1, 0.0: 0, 'mitosis': 2, 'interphase': 1, 'unknown': 0} - - Returns - ------- - combined_features : xarray.DataArray - Combined feature embeddings from all successfully loaded datasets. - combined_annotations : pandas.Series - Combined and standardized annotations from all datasets. - - Raises - ------ - ValueError - If no datasets were successfully loaded. - """ - - all_features = [] - all_annotations = [] - - # Default standardization mappings - if standardization_mapping is None: - if target_type == "infection": - standardization_mapping = { - # String formats - "infected": 2, - "uninfected": 1, - "background": 0, - "unknown": 0, - # Numeric formats - 2.0: 2, - 1.0: 1, - 0.0: 0, - 2: 2, - } - elif target_type == "division": - standardization_mapping = { - # String formats - "mitosis": 2, - "interphase": 1, - "unknown": 0, - # Numeric formats - 2.0: 2, - 1.0: 1, - 0.0: 0, - 2: 2, - } - - for emb_path, ann_path, train_fovs in datasets: - try: - logger.debug(f"Loading dataset: {emb_path}") - dataset = read_embedding_dataset(emb_path) - - # Read annotation CSV to detect column names - logger.debug(f"Reading annotation CSV: {ann_path}") - ann_df = pd.read_csv(ann_path) - # make sure the ann_fov_names start with '/' otherwise add it, and strip whitespace - ann_df["fov_name"] = ann_df["fov_name"].apply( - lambda x: ( - "/" + x.strip() if not x.strip().startswith("/") else x.strip() - ) - ) - - if train_fovs == "all": - train_fovs = np.unique(dataset["fov_name"]) - - # Auto-detect annotation column based on target_type - annotation_key = None - if target_type == "infection": - for col in [ - "infection_state", - "infection", - "infection_status", - ]: - if col in ann_df.columns: - annotation_key = col - break - - elif target_type == "division": - for col in ["division", "cell_division", "cell_state"]: - if col in ann_df.columns: - annotation_key = col - break - - if annotation_key is None: - print(f" No {target_type} column found, skipping...") - continue - - # Filter the dataset to only include the FOVs in the annotation - # Use xarray's native filtering methods - ann_fov_names = set(ann_df["fov_name"].unique()) - train_fovs = set(train_fovs) - - logger.debug(f"Dataset FOVs: {dataset['fov_name'].values}") - logger.debug(f"Annotation FOV names: {ann_fov_names}") - logger.debug(f"Train FOVs: {train_fovs}") - logger.debug(f"Dataset samples before filtering: {len(dataset.sample)}") - - # Filter and get only the intersection of train_fovs and ann_fov_names - common_fovs = train_fovs.intersection(ann_fov_names) - # missed out fovs in the dataset - missed_fovs = train_fovs - common_fovs - # missed out fovs in the annotations - missed_fovs_ann = ann_fov_names - common_fovs - - if len(common_fovs) == 0: - raise ValueError( - f"No common FOVs found between dataset and annotations: {train_fovs} not in {ann_fov_names}" - ) - elif len(missed_fovs) > 0: - warnings.warn( - f"No matching found for FOVs in the train dataset: {missed_fovs}" - ) - elif len(missed_fovs_ann) > 0: - warnings.warn( - f"No matching found for FOVs in the annotations: {missed_fovs_ann}" - ) - - logger.debug(f"Intersection of train_fovs and ann_fov_names: {common_fovs}") - - # Filter the dataset to only include the intersection of train_fovs and ann_fov_names - dataset = dataset.where( - dataset["fov_name"].isin(list(common_fovs)), drop=True - ) - - logger.debug(f"Dataset samples after filtering: {len(dataset.sample)}") - - # Load annotations without class mapping first - annotations = load_annotation(dataset, ann_path, annotation_key) - - # Check unique values before standardization - unique_vals = annotations.unique() - logger.debug(f"Original unique values: {unique_vals}") - - # Apply standardization mapping - standardized_annotations = annotations.copy() - if standardization_mapping: - for original_val, standard_val in standardization_mapping.items(): - mask = annotations == original_val - if mask.any(): - standardized_annotations[mask] = standard_val - logger.debug( - f"Mapped {original_val} -> {standard_val} ({mask.sum()} instances)" - ) - - # Check standardized values - std_unique_vals = standardized_annotations.unique() - logger.debug(f"Standardized unique values: {std_unique_vals}") - - # Convert to categorical for consistency - standardized_annotations = standardized_annotations.astype("category") - - # Keep features as xarray DataArray for compatibility with fit_logistic_regression - all_features.append(dataset["features"]) - all_annotations.append(standardized_annotations) - - logger.debug(f"Features shape: {dataset['features'].shape}") - logger.debug(f"Annotations shape: {standardized_annotations.shape}") - except Exception as e: - raise ValueError(f"Error loading dataset {emb_path}: {e}") - - # Combine all datasets - if all_features: - # Extract features and coordinates from each dataset - all_features_arrays = [] - all_coords = [] - - for dataset in all_features: - # Extract the features array - features_array = dataset["features"].values - all_features_arrays.append(features_array) - - # Extract coordinates - coords_dict = {} - for coord_name in dataset.coords: - if coord_name != "sample": # skip sample coordinate - coords_dict[coord_name] = dataset.coords[coord_name].values - all_coords.append(coords_dict) - - # Combine feature arrays - combined_features_array = np.concatenate(all_features_arrays, axis=0) - - # Combine coordinates (excluding 'features' from coordinates) - combined_coords = {} - for coord_name in all_coords[0].keys(): - if coord_name != "features": # Don't include 'features' in coordinates - coord_values = [] - for coords_dict in all_coords: - coord_values.extend(coords_dict[coord_name]) - combined_coords[coord_name] = coord_values - - # Create new combined dataset in the correct format - coords_dict = { - "sample": range(len(combined_features_array)), - } - - # Add each coordinate as a 1D coordinate along the sample dimension - for coord_name, coord_values in combined_coords.items(): - coords_dict[coord_name] = ("sample", coord_values) - - combined_dataset = xr.Dataset( - { - "features": (("sample", "features"), combined_features_array), - }, - coords=coords_dict, - ) - - # Set the index properly like the original datasets - if "fov_name" in combined_coords: - available_coords = [ - coord - for coord in combined_coords.keys() - if coord in ["fov_name", "track_id", "t"] - ] - combined_dataset = combined_dataset.set_index(sample=available_coords) - - combined_annotations = pd.concat(all_annotations, ignore_index=True) - - logger.debug(f"Combined features shape: {combined_dataset['features'].shape}") - logger.debug(f"Combined annotations shape: {combined_annotations.shape}") - - # Final check of combined annotations - final_unique = combined_annotations.unique() - logger.debug(f"Final combined unique values: {final_unique}") - - return combined_dataset["features"], combined_annotations - - -# %% -# train linear classifier -path_infection_embedding = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/time_sampling_strategies/time_interval/predict/feb_test_time_interval_1_epoch_178.zarr" -) -path_division_embedding = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/time_sampling_strategies/time_interval/predict/feb_test_time_interval_1_epoch_178_gt_tracks.zarr" -) -path_annotations_infection = Path( - "/hpc/projects/intracellular_dashboard/organelle_dynamics/2024_02_04_A549_DENV_ZIKV_timelapse/8-train-test-split/supervised_inf_pred/extracted_inf_state.csv" -) -path_annotations_division = Path( - "/hpc/projects/intracellular_dashboard/organelle_dynamics/2024_02_04_A549_DENV_ZIKV_timelapse/9-lineage-cell-division/lineages_gt/cell_division_state_test_set.csv" -) - -infection_dataset = read_embedding_dataset(path_infection_embedding) -infection_features = infection_dataset["features"] -infection = load_annotation( - infection_dataset, - path_annotations_infection, - "infection_state", - {0.0: "background", 1.0: "uninfected", 2.0: "infected"}, -) - -division_dataset = read_embedding_dataset(path_division_embedding) -division_features = division_dataset["features"] -division = load_annotation(division_dataset, path_annotations_division, "division") -# move the unknown class to the 0 label -division[division == 1] = -2 -division += 2 -division /= 2 -division = division.astype("category") - -# %% -train_fovs = ["/A/3/7", "/A/3/8", "/A/3/9", "/B/4/6", "/B/4/7"] - -# %% -logistic_regression_infection, _ = fit_logistic_regression( - infection_features.copy(), - infection.copy(), - train_fovs, - remove_background_class=True, - scale_features=False, - class_weight="balanced", - solver="liblinear", -) -# %% -logistic_regression_division, _ = fit_logistic_regression( - division_features.copy(), - division.copy(), - train_fovs, - remove_background_class=True, - scale_features=False, - class_weight="balanced", - solver="liblinear", -) - -# %% -linear_classifier_infection = linear_from_binary_logistic_regression( - logistic_regression_infection -) -assembled_classifier_infection = ( - AssembledClassifier(model.model, linear_classifier_infection) - .eval() - .to(model.device) -) - -# %% -linear_classifier_division = linear_from_binary_logistic_regression( - logistic_regression_division -) -assembled_classifier_division = ( - AssembledClassifier(model.model, linear_classifier_division).eval().to(model.device) -) - -# %% -# load infection annotations -infection = pd.read_csv( - "/hpc/projects/intracellular_dashboard/organelle_dynamics/2024_02_04_A549_DENV_ZIKV_timelapse/8-train-test-split/supervised_inf_pred/extracted_inf_state.csv", -) -track_classes_infection = infection[infection["fov_name"] == fov[1:]] -track_classes_infection = track_classes_infection[ - track_classes_infection["track_id"].isin(track) -]["infection_state"] - -# %% -# load division annotations -division = pd.read_csv( - "/hpc/projects/intracellular_dashboard/organelle_dynamics/2024_02_04_A549_DENV_ZIKV_timelapse/9-lineage-cell-division/lineages_gt/cell_division_state_test_set.csv", -) -track_classes_division = division[division["fov_name"] == fov[1:]] -track_classes_division = track_classes_division[ - track_classes_division["track_id"].isin(track) -]["division"] - - -# %% -# Loading the lineage images -img = [] -for sample in dm.predict_dataloader(): - img.append(sample["anchor"].numpy()) -img = np.concatenate(img, axis=0) -print(f"Loaded images with shape: {img.shape}") - -# %% -img_tensor = torch.from_numpy(img).to(model.device) - -with torch.inference_mode(): - infection_probs = assembled_classifier_infection(img_tensor).sigmoid() - division_probs = assembled_classifier_division(img_tensor).sigmoid() - -# %% -attr_kwargs = dict( - img=img_tensor, - sliding_window_shapes=(1, 15, 12, 12), - strides=(1, 15, 4, 4), - show_progress=True, -) - - -infection_attribution = ( - assembled_classifier_infection.attribute_occlusion(**attr_kwargs).cpu().numpy() -) -division_attribution = ( - assembled_classifier_division.attribute_occlusion(**attr_kwargs).cpu().numpy() -) - - -# %% -def clip_rescale(img, low, high): - return rescale_intensity(img.clip(low, high), out_range=(0, 1)) - - -def clim_percentile(heatmap, low=1, high=99): - lo, hi = np.percentile(heatmap, (low, high)) - return clip_rescale(heatmap, lo, hi) - - -g_lim = 1 -z_slice = 5 -phase = clim_percentile(img[:, 0, z_slice]) -rfp = clim_percentile(img[:, 1, z_slice]) -img_render = np.concatenate([phase, rfp], axis=2) -phase_heatmap_inf = infection_attribution[:, 0, z_slice] -rfp_heatmap_inf = infection_attribution[:, 1, z_slice] -inf_render = clip_rescale( - np.concatenate([phase_heatmap_inf, rfp_heatmap_inf], axis=2), -g_lim, g_lim -) -phase_heatmap_div = division_attribution[:, 0, z_slice] -rfp_heatmap_div = division_attribution[:, 1, z_slice] -div_render = clip_rescale( - np.concatenate([phase_heatmap_div, rfp_heatmap_div], axis=2), -g_lim, g_lim -) - -# %% -plt.style.use("./figure.mplstyle") - -selected_time_points = [3, 6, 15, 16] -selected_div_states = [False] * 3 + [True] - -icefire = Colormap("icefire").to_mpl() - -f, ax = plt.subplots( - 3, len(selected_time_points), figsize=(5.5, 3), layout="compressed" -) -for i, time in enumerate(selected_time_points): - hpi = 3 + 0.5 * time - prob = infection_probs[time].item() - inf_binary = str(bool(track_classes_infection.iloc[time] - 1)).lower() - div_binary = str(selected_div_states[i]).lower() - ax[0, i].imshow(img_render[time], cmap="gray") - ax[0, i].set_title(f"{hpi} HPI") - ax[1, i].imshow(inf_render[time], cmap=icefire, vmin=0, vmax=1) - ax[1, i].set_title( - f"infected: {prob:.3f}\nlabel: {inf_binary}", - ) - ax[2, i].imshow(div_render[time], cmap=icefire, vmin=0, vmax=1) - ax[2, i].set_title( - f"dividing: {division_probs[time].item():.3f}\nlabel: {div_binary}", - ) -for a in ax.ravel(): - a.axis("off") -norm = mpl.colors.Normalize(vmin=-g_lim, vmax=g_lim) -cbar = f.colorbar( - mpl.cm.ScalarMappable(norm=norm, cmap=icefire), - orientation="vertical", - ax=ax[1:].ravel().tolist(), - format=mpl.ticker.StrMethodFormatter("{x:.1f}"), -) -cbar.set_label("occlusion attribution") - -# %% -f.savefig( - Path.home() - / "mydata" - / "gdrive/publications/dynaCLR/2025_dynaCLR_paper/fig_manuscript_svg/figure_occlusion_analysis/figure_parts/fig_explanation_patch12_stride4.pdf", - dpi=300, -) - -# %% -# Create video animation of occlusion analysis -icefire = Colormap("icefire").to_mpl() -plt.style.use("./figure.mplstyle") - -fig, ax = plt.subplots(3, 1, figsize=(6, 8), layout="compressed") - -# Initialize plots -im1 = ax[0].imshow(img_render[0], cmap="gray") -ax[0].set_title("Original Image") -ax[0].axis("off") - -im2 = ax[1].imshow(inf_render[0], cmap=icefire, vmin=0, vmax=1) -ax[1].set_title("Infection Occlusion Attribution") -ax[1].axis("off") - -im3 = ax[2].imshow(div_render[0], cmap=icefire, vmin=0, vmax=1) -ax[2].set_title("Division Occlusion Attribution") -ax[2].axis("off") - -# Store initial border colors -for a in ax: - for spine in a.spines.values(): - spine.set_linewidth(3) - spine.set_color("black") - -# Add colorbar -norm = mpl.colors.Normalize(vmin=-g_lim, vmax=g_lim) -cbar = fig.colorbar( - mpl.cm.ScalarMappable(norm=norm, cmap=icefire), - ax=ax[1:], - orientation="horizontal", - shrink=0.8, - pad=0.1, -) -cbar.set_label("Occlusion Attribution") - - -# Animation function -def animate(frame): - time = frame - hpi = 3 + 0.5 * time - - # Update images - im1.set_array(img_render[time]) - im2.set_array(inf_render[time]) - im3.set_array(div_render[time]) - - # Update titles with probabilities - inf_prob = infection_probs[time].item() - div_prob = division_probs[time].item() - inf_binary = bool(track_classes_infection.iloc[time] - 1) - div_binary = bool(track_classes_division.iloc[time] - 1) - - # Color code labels - red for true, green for false - inf_color = "darkorange" if inf_binary else "blue" - div_color = "darkorange" if div_binary else "blue" - - # Make label text bold when true - inf_weight = "bold" if inf_binary else "normal" - div_weight = "bold" if div_binary else "normal" - - # Update border colors to highlight true labels - for spine in ax[1].spines.values(): - spine.set_color(inf_color) - spine.set_linewidth(4 if inf_binary else 2) - - for spine in ax[2].spines.values(): - spine.set_color(div_color) - spine.set_linewidth(4 if div_binary else 2) - - ax[0].set_title(f"Original Image - {hpi:.1f} HPI", fontsize=12, fontweight="bold") - ax[1].set_title( - f"Infection Attribution - Prob: {inf_prob:.3f} (Label: {str(inf_binary).lower()})", - fontsize=12, - fontweight=inf_weight, - color=inf_color, - ) - ax[2].set_title( - f"Division Attribution - Prob: {div_prob:.3f} (Label: {str(div_binary).lower()})", - fontsize=12, - fontweight=div_weight, - color=div_color, - ) - - return [im1, im2, im3] - - -# %% - -# Create animation -anim = animation.FuncAnimation( - fig, animate, frames=len(img_render), interval=200, blit=True, repeat=True -) - -# Save as video -video_path = ( - Path.home() - / "mydata" - / "gdrive/2025_dynaCLR_paper/fig_manuscript_svg/figure_occlusion_analysis/figure_parts/occlusion_analysis_video.mp4" -) -video_path.parent.mkdir(parents=True, exist_ok=True) - -# Save as MP4 -Writer = animation.writers["ffmpeg"] -writer = Writer(fps=5, metadata=dict(artist="VisCy"), bitrate=1800) -anim.save(str(video_path), writer=writer) - -print(f"Video saved to: {video_path}") diff --git a/applications/contrastive_phenotyping/figures/organelle_dynamics.py b/applications/contrastive_phenotyping/figures/organelle_dynamics.py deleted file mode 100644 index 4ee9980ac..000000000 --- a/applications/contrastive_phenotyping/figures/organelle_dynamics.py +++ /dev/null @@ -1,238 +0,0 @@ -# %% -from pathlib import Path - -import matplotlib as mpl -import matplotlib.pyplot as plt -import numpy as np -import pandas as pd -import seaborn as sns -import xarray as xr -from cmap import Colormap -from lightning.pytorch import seed_everything -from skimage.exposure import rescale_intensity -from sklearn.preprocessing import StandardScaler -from umap import UMAP - -from viscy.data.triplet import TripletDataModule -from viscy.representation.embedding_writer import read_embedding_dataset -from viscy.transforms import NormalizeSampled, ScaleIntensityRangePercentilesd - -plt.style.use("../evaluation/figure.mplstyle") -seed_everything(42, workers=True) - -# %% Paths and parameters. - -features_path = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/time_sampling_strategies/time_interval/predict/jun_time_interval_1_epoch_178.zarr" -) -data_path = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/2024_06_13_SEC61_TOMM20_ZIKV_DENGUE_1/2-register/registered_chunked.zarr" -) -tracks_path = Path( - "/hpc/projects/intracellular_dashboard/viral-sensor/2024_06_13_SEC61_TOMM20_ZIKV_DENGUE_1/4.2-tracking/track.zarr" -) - -# %% -embedding_dataset = read_embedding_dataset(features_path) -embedding_dataset - -# %% -# Compute UMAP over all features -features = embedding_dataset["features"] -# or select a well: -features = features[features["fov_name"].str.contains(r"/0/[36]")] -features - -# %% -scaled_features = StandardScaler().fit_transform(features.values) -umap = UMAP(random_state=42) -# Fit UMAP on all features -embedding = umap.fit_transform(scaled_features) - - -# %% -# Add UMAP coordinates to the dataset - -features = ( - features.assign_coords(UMAP1=("sample", embedding[:, 0])) - .assign_coords(UMAP2=("sample", embedding[:, 1])) - .set_index(sample=["UMAP1", "UMAP2"], append=True) -) -features - -# %% -ax = sns.scatterplot( - x=features["UMAP1"], y=features["UMAP2"], hue=features["t"], s=7, alpha=0.8 -) -fmt = mpl.ticker.StrMethodFormatter("{x}") -ax.xaxis.set_major_formatter(fmt) -ax.yaxis.set_major_formatter(fmt) - -# %% -fovs = ["/0/3/002000", "/0/6/000000", "/0/6/000002", "/0/6/001000"] -tracks = [24, 14, 34, 38] - - -track_features = xr.concat( - [features.sel(fov_name=fov, track_id=track) for fov, track in zip(fovs, tracks)], - dim="sample", -) - -# %% -dm = TripletDataModule( - data_path=data_path, - tracks_path=tracks_path, - source_channel=[ - "Phase3D", - "MultiCam_GFP_mCherry_BF-Prime BSI Express", - "MultiCam_GFP_mCherry_BF-Andor EMCCD", - ], - z_range=[10, 55], - batch_size=48, - num_workers=0, - initial_yx_patch_size=(128, 128), - final_yx_patch_size=(128, 128), - normalizations=[ - NormalizeSampled( - keys=["Phase3D"], level="fov_statistics", subtrahend="mean", divisor="std" - ), - ScaleIntensityRangePercentilesd( - keys=[ - "MultiCam_GFP_mCherry_BF-Prime BSI Express", - "MultiCam_GFP_mCherry_BF-Andor EMCCD", - ], - lower=50, - upper=99, - b_min=0.0, - b_max=1.0, - channel_wise=True, - ), - ], - predict_cells=True, - include_fov_names=fovs, - include_track_ids=tracks, -) -dm.setup("predict") -ds = dm.predict_dataset -len(ds) - - -# %% -def render(img, cmaps: list[str]): - channels = [] - for ch, cmap in zip(img, cmaps): - lo, hi = np.percentile(ch, [1, 99]) - rescaled = rescale_intensity(ch.clip(lo, hi), out_range=(0, 1)) - rendered = Colormap(cmap)(rescaled) - channels.append(rendered) - return np.sum(channels, axis=0).clip(0, 1) - - -renders = [] - -f, ax = plt.subplots(4, 12, figsize=(12, 4)) -for sample, a in zip(ds, ax.flatten()): - img = sample["anchor"][1:].numpy().max(1) - rend = render(img, ["magenta", "green"]) - renders.append(rend) - a.imshow(rend, cmap="gray") - idx = sample["index"] - name = "-".join([str(idx["track_id"]), str(idx["t"])]) - a.set_title(name) - a.axis("off") - -# %% -track_df = ds.tracks -selected_times = [2, 6, 8] -track_df = track_df[track_df["t"].isin(selected_times)] -selected_features = track_features[track_features["t"].isin(selected_times)] -selected_renders = [renders[i] for i in track_df.index] - - -# %% -fig = plt.figure(layout="constrained", figsize=(5.5, 2.7)) -subfigs = fig.subfigures(1, 2, wspace=0.02, width_ratios=[4, 7]) - -umap_fig = subfigs[0] -umap_fig.suptitle("a", horizontalalignment="left", x=0, y=1) -umap_ax = umap_fig.subplots(1, 1) -umap_ax.invert_xaxis() - -sns.scatterplot( - x=features["UMAP1"], y=features["UMAP2"], s=40, alpha=0.01, ax=umap_ax, color="k" -) - -sns.scatterplot( - x=track_features["UMAP1"], - y=track_features["UMAP2"], - ax=umap_ax, - hue=track_features["fov_name"], - s=5, - legend=False, -) - -sns.lineplot( - x=track_features["UMAP1"], - y=track_features["UMAP2"], - ax=umap_ax, - hue=track_features["fov_name"], - legend=False, - size=0.5, -) - -hpi = (track_df["t"].reset_index(0, drop=True) * 2 + 2.5).astype(str) + " HPI" -track_names = pd.Series( - np.concatenate([[t] * 3 for t in ["Track 1", "Track 2", "Track 3", "Track 4"]]), - name="track", -) -sns.scatterplot( - x=selected_features["UMAP1"], - y=selected_features["UMAP2"], - ax=umap_ax, - style=hpi, - markers=["P", "s", "D"], - s=20, - hue=track_names, - # legend=False, -) -handles, labels = umap_ax.get_legend_handles_labels() -umap_ax.legend( - handles=handles[1:5] + handles[6:], - labels=labels[1:5] + labels[6:], - loc="upper center", - ncol=2, - bbox_to_anchor=(0.5, -0.2), - labelspacing=0.2, - handletextpad=0, - fontsize=8, -) - -img_fig = subfigs[1] -img_fig.suptitle("b", horizontalalignment="left", x=-0, y=1) -img_axes = img_fig.subplots(3, 4, sharex=True, sharey=True) - -for i, (ax, rend, time, track_name) in enumerate( - zip(img_axes.T.flatten(), selected_renders, hpi.to_list(), track_names) -): - ax.imshow(rend) - if i % 3 == 0: - ax.set_title(track_name) - if i < 3: - ax.set_ylabel(f"{time}") - ax.set_xticks([]) - ax.set_yticks([]) - -for sf in subfigs: - for a in sf.get_axes(): - fmt = mpl.ticker.StrMethodFormatter("{x:.0f}") - a.xaxis.set_major_formatter(fmt) - a.yaxis.set_major_formatter(fmt) - -# %% -fig.savefig( - Path.home() - / "gdrive/publications/learning_impacts_of_infection/fig_manuscript/fig_organelle_dynamics/fig_organelle_dynamics.pdf", - dpi=300, -) - -# %% diff --git a/applications/contrastive_phenotyping/figures/track_smoothness.py b/applications/contrastive_phenotyping/figures/track_smoothness.py deleted file mode 100644 index 1ddc27d3a..000000000 --- a/applications/contrastive_phenotyping/figures/track_smoothness.py +++ /dev/null @@ -1,111 +0,0 @@ -# %% -from pathlib import Path - -import matplotlib as mpl -import matplotlib.pyplot as plt -import numpy as np -import seaborn as sns -from cmap import Colormap -from iohub import open_ome_zarr -from skimage.color import label2rgb -from skimage.exposure import rescale_intensity - -from viscy.representation.embedding_writer import read_embedding_dataset -from viscy.representation.evaluation.dimensionality_reduction import compute_umap - -# %% -t_slice = slice(18, 33) -y_slice = slice(16, 144) -x_slice = slice(0, 224) - -phase = open_ome_zarr( - "/hpc/projects/intracellular_dashboard/viral-sensor/2024_02_04_A549_DENV_ZIKV_timelapse/8-train-test-split/registered_test.zarr/B/4/8" -)["0"][t_slice, 3, 31, y_slice, x_slice] - -segments = open_ome_zarr( - "/hpc/projects/intracellular_dashboard/viral-sensor/2024_02_04_A549_DENV_ZIKV_timelapse/8-train-test-split/track_test.zarr/B/4/8" -)["0"][t_slice, 0, 0, y_slice, x_slice] - -# %% -features = read_embedding_dataset( - "/hpc/projects/intracellular_dashboard/viral-sensor/infection_classification/models/time_sampling_strategies/time_interval/predict/feb_test_time_interval_1_epoch_178.zarr" -) - -# %% -_, _, umap_df = compute_umap(features) -umap_df - -# %% -track_ids = np.unique(segments)[1:] -track_ids - -# %% -selected_umap = umap_df[ - (umap_df["fov_name"] == "/B/4/8") - & umap_df["track_id"].isin(track_ids) - & (umap_df["t"] >= t_slice.start) - & (umap_df["t"] < t_slice.stop) -] - -selected_umap["HPI"] = selected_umap["t"] * 0.5 + 3 - -# %% -plt.style.use("../evaluation/figure.mplstyle") -fig = plt.figure(figsize=(5.5, 4.5), layout="constrained") -subfigs = fig.subfigures(2, 1, wspace=0.02, height_ratios=[3, 2]) - -img_fig = subfigs[0] -img_fig.suptitle("a", horizontalalignment="left", x=0, y=1) -img_ax = img_fig.subplots(3, 5) - -clim = 0.03 -cmap = Colormap("tab10") - -labels = label2rgb( - segments, - image=rescale_intensity(phase, in_range=(-clim, clim), out_range=(0, 1)), - colors=cmap(range(10)), -) - -for t, (a, rgb) in enumerate(zip(img_ax.flatten(), labels)): - a.imshow(rgb) - a.set_title(f"{(t + t_slice.start) / 2 + 3} HPI") - a.axis("off") - -line_fig = subfigs[1] -line_fig.suptitle("b", horizontalalignment="left", x=0, y=1) -line_ax_1 = line_fig.subplots(1, 1) -line_ax_2 = line_ax_1.twinx() -sns.lineplot( - data=selected_umap, - x="HPI", - y="UMAP1", - hue="track_id", - palette=[c for c in cmap([2, 4, 6])], - ax=line_ax_1, -) -sns.move_legend(line_ax_1, "upper right", title="Track ID") -sns.lineplot( - data=selected_umap, - x="HPI", - y="UMAP2", - hue="track_id", - palette=[c for c in cmap([2, 4, 6])], - ax=line_ax_2, - linestyle="--", - legend=False, -) - -fmt = mpl.ticker.StrMethodFormatter("{x:.1f}") -for a in [line_ax_1, line_ax_2]: - a.xaxis.set_major_formatter(fmt) - a.yaxis.set_major_formatter(fmt) - -# %% -fig.savefig( - Path.home() - / "gdrive/publications/learning_impacts_of_infection/fig_manuscript/si/appendix_track_smoothness.pdf", - dpi=300, -) - -# %% diff --git a/applications/cytoland/README.md b/applications/cytoland/README.md new file mode 100644 index 000000000..e06fe0a6b --- /dev/null +++ b/applications/cytoland/README.md @@ -0,0 +1,86 @@ +# Cytoland + +Robust virtual staining of landmark organelles from label-free microscopy. + +Part of the [VisCy](https://github.com/mehta-lab/VisCy) monorepo. + +> **Paper:** [Robust virtual staining in Nature Machine Intelligence](https://www.nature.com/articles/s42256-025-01046-2) + +## Installation + +```bash +# From the VisCy monorepo root +uv pip install -e "applications/cytoland" +``` + +## Usage + +Training and prediction use the shared `viscy` CLI provided by `viscy-utils`: + +```bash +# Training (pick a model-specific config) +uv run --package cytoland viscy fit -c examples/configs/vscyto3d/finetune.yml + +# Training with Spotlight loss +uv run --package cytoland viscy fit -c examples/configs/vscyto3d/train_spotlight.yml + +# Prediction +uv run --package cytoland viscy predict -c examples/configs/vscyto3d/predict.yml +``` + +The YAML config determines which model and data module to use via `class_path`: + +```yaml +model: + class_path: cytoland.engine.VSUNet +data: + class_path: viscy_data.hcs.HCSDataModule +``` + +## Tutorials and demos + +Scripts and tutorials live under [`examples/`](./examples/): + +| Folder | What it demonstrates | +|--------|----------------------| +| [`examples/VS_model_inference/`](./examples/VS_model_inference/) | Python API inference demos for VSCyto2D, VSCyto3D, VSNeuromast, and TTA-augmented sliding-window prediction | +| [`examples/vcp_tutorials/`](./examples/vcp_tutorials/) | Virtual Cell Platform quick-start and organism-specific walkthroughs (HEK293T, neuromast) | +| [`examples/dl-course-exercise/`](./examples/dl-course-exercise/) | Image-translation course exercise (training from scratch + evaluation) — used at DL@MBL and DL@Janelia | +| [`examples/phase_contrast/`](./examples/phase_contrast/) | Phase-contrast tutorial and demo workflows | +| [`examples/configs/`](./examples/configs/) | YAML configs for `viscy fit` / `viscy predict` across models (VSCyto2D/3D, VSNeuromast, FNet3D, dynacell) | + +All demo scripts are written as jupytext-style percent-cell `.py` files. +Regenerate paired `.ipynb` notebooks with `jupytext --to ipynb solution.py` +if you prefer the notebook UI. + +## Models + +| Model | Input | Output | Architecture | +|-------|-------|--------|-------------| +| VSCyto3D | Phase3D | Nuclei + Membrane | FCMAE / UNeXt2 | +| VSCyto2D | Phase2D | Nuclei + Membrane | UNeXt2 | +| VSNeuromast | DIC | Multiple fluorescent markers | UNeXt2 | +| FNet3D | Transmitted light | Fluorescence | Unet3d (Ounkomol et al. 2018) | + +> **FNet3D note:** All spatial dimensions (Z, Y, X) must be divisible by `2^depth` +> (default depth=4 requires divisibility by 16). See `examples/configs/fnet3d/fit.yml`. + +> **Benchmark note:** FNet3D and SEC61B benchmarks now launch from +> [`applications/dynacell/`](../dynacell/README.md). Cytoland copies are +> transitional legacy — see `examples/configs/dynacell/` and `examples/configs/fnet3d/`. + +## References + +
+Liu, Hirata-Miyasaki et al., 2025 + +```bibtex +@article{liu2025robust, + title = {Robust virtual staining of landmark organelles}, + author = {Liu, Ziwen and Hirata-Miyasaki, Eduardo and Pradeep, Soorya and others}, + journal = {Nature Machine Intelligence}, + year = {2025}, + doi = {10.1038/s42256-025-01046-2}, +} +``` +
diff --git a/examples/virtual_staining/VS_model_inference/demo_vscyto2d.py b/applications/cytoland/examples/VS_model_inference/demo_vscyto2d.py similarity index 88% rename from examples/virtual_staining/VS_model_inference/demo_vscyto2d.py rename to applications/cytoland/examples/VS_model_inference/demo_vscyto2d.py index 14dcb4499..b3f99c8e8 100644 --- a/examples/virtual_staining/VS_model_inference/demo_vscyto2d.py +++ b/applications/cytoland/examples/VS_model_inference/demo_vscyto2d.py @@ -13,12 +13,12 @@ from iohub import open_ome_zarr from plot import plot_vs_n_fluor -# Viscy classes for the trainer and model -from viscy.data.hcs import HCSDataModule -from viscy.trainer import VisCyTrainer -from viscy.transforms import NormalizeSampled -from viscy.translation.engine import FcmaeUNet -from viscy.translation.predict_writer import HCSPredictionWriter +# Cytoland and VisCy modular classes for the trainer and model +from cytoland.engine import FcmaeUNet +from viscy_data.hcs import HCSDataModule +from viscy_transforms import NormalizeSampled +from viscy_utils.callbacks import HCSPredictionWriter +from viscy_utils.trainer import VisCyTrainer # %% [markdown] tags=[] # @@ -74,12 +74,11 @@ data_module = HCSDataModule( data_path=input_data_path, source_channel=phase_channel_name, - target_channel=["Membrane", "Nuclei"], + target_channel=["Nuclei", "Membrane"], z_window_size=1, split_ratio=0.8, batch_size=BATCH_SIZE, num_workers=NUM_WORKERS, - architecture="fcmae", normalizations=[ NormalizeSampled( [phase_channel_name], @@ -105,9 +104,7 @@ "pretraining": False, } -model_VSCyto2D = FcmaeUNet.load_from_checkpoint( - model_ckpt_path, model_config=config_VSCyto2D -) +model_VSCyto2D = FcmaeUNet.load_from_checkpoint(model_ckpt_path, model_config=config_VSCyto2D) model_VSCyto2D.eval() # %% @@ -138,7 +135,7 @@ # Open the experimental fluorescence fluor_store = open_ome_zarr(input_data_path, mode="r") # Get the 2D images -# NOTE: Channel indeces hardcoded for this dataset +# NOTE: Channel indices hardcoded for this dataset fluor_nucleus = fluor_store[0][0, 1, 0] # (t,c,z,y,x) fluor_membrane = fluor_store[0][0, 2, 0] # (t,c,z,y,x) diff --git a/examples/virtual_staining/VS_model_inference/demo_vscyto3d.py b/applications/cytoland/examples/VS_model_inference/demo_vscyto3d.py similarity index 88% rename from examples/virtual_staining/VS_model_inference/demo_vscyto3d.py rename to applications/cytoland/examples/VS_model_inference/demo_vscyto3d.py index 6afe54e7f..c0791afd8 100644 --- a/examples/virtual_staining/VS_model_inference/demo_vscyto3d.py +++ b/applications/cytoland/examples/VS_model_inference/demo_vscyto3d.py @@ -13,13 +13,12 @@ from iohub import open_ome_zarr from plot import plot_vs_n_fluor -from viscy.data.hcs import HCSDataModule -from viscy.trainer import VisCyTrainer -from viscy.transforms import NormalizeSampled - -# Viscy classes for the trainer and model -from viscy.translation.engine import VSUNet -from viscy.translation.predict_writer import HCSPredictionWriter +# Cytoland and VisCy modular classes for the trainer and model +from cytoland.engine import VSUNet +from viscy_data.hcs import HCSDataModule +from viscy_transforms import NormalizeSampled +from viscy_utils.callbacks import HCSPredictionWriter +from viscy_utils.trainer import VisCyTrainer # %% [markdown] """ @@ -83,12 +82,11 @@ data_module = HCSDataModule( data_path=input_data_path, source_channel=phase_channel_name, - target_channel=["Membrane", "Nuclei"], + target_channel=["Nuclei", "Membrane"], z_window_size=5, split_ratio=0.8, batch_size=BATCH_SIZE, num_workers=NUM_WORKERS, - architecture="UNeXt2", normalizations=[ NormalizeSampled( [phase_channel_name], @@ -114,9 +112,7 @@ "head_pool": True, } -model_VSCyto3D = VSUNet.load_from_checkpoint( - model_ckpt_path, architecture="UNeXt2", model_config=config_VSCyto3D -) +model_VSCyto3D = VSUNet.load_from_checkpoint(model_ckpt_path, architecture="UNeXt2", model_config=config_VSCyto3D) model_VSCyto3D.eval() # %% @@ -150,7 +146,7 @@ # Open the experimental fluorescence fluor_store = open_ome_zarr(input_data_path, mode="r") # Get the 2D images -# NOTE: Channel indeces hardcoded for this dataset +# NOTE: Channel indices hardcoded for this dataset fluor_nucleus = fluor_store[0][0, 2, z_slice] # (t,c,z,y,x) fluor_membrane = fluor_store[0][0, 1, z_slice] # (t,c,z,y,x) diff --git a/applications/cytoland/examples/VS_model_inference/demo_vscyto_w_ttas.py b/applications/cytoland/examples/VS_model_inference/demo_vscyto_w_ttas.py new file mode 100644 index 000000000..02e3fef72 --- /dev/null +++ b/applications/cytoland/examples/VS_model_inference/demo_vscyto_w_ttas.py @@ -0,0 +1,81 @@ +# %% +""" +Demo: In-memory volume prediction using predict_sliding_windows. + +This API provides the same results as the `viscy predict` CLI (HCSPredictionWriter) +since both use the same linear feathering blending algorithm for overlapping windows. +""" + +from pathlib import Path + +import napari +import numpy as np +import torch +from iohub import open_ome_zarr + +from cytoland.engine import AugmentedPredictionVSUNet, VSUNet + +DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu") + +# Instantiate model manually +model = ( + VSUNet( + architecture="fcmae", + model_config={ + "in_channels": 1, + "out_channels": 2, + "in_stack_depth": 21, + "encoder_blocks": [3, 3, 9, 3], + "dims": [96, 192, 384, 768], + "decoder_conv_blocks": 2, + "stem_kernel_size": [7, 4, 4], + "pretraining": False, + "head_conv": True, + "head_conv_expansion_ratio": 4, + "head_conv_pool": False, + }, + ckpt_path="/path/to/checkpoint.ckpt", + ) + .to(DEVICE) + .eval() +) + +vs = ( + AugmentedPredictionVSUNet( + model=model.model, + forward_transforms=[lambda t: t], + inverse_transforms=[lambda t: t], + ) + .to(DEVICE) + .eval() +) + +# Load data and apply the same precomputed FOV statistics (median / IQR) +# normalization that ``viscy predict`` performs via ``NormalizeSampled``. +# Without this step the in-memory path is not comparable to the CLI output. +path = Path("/path/to/your.zarr/0/1/000000") +source_channel = "Phase3D" +with open_ome_zarr(path) as ds: + channel_index = ds.channel_names.index(source_channel) + vol_np = np.asarray(ds.data[0:1, channel_index : channel_index + 1]) # (1, 1, Z, Y, X) + fov_stats = ds.zattrs["normalization"][source_channel]["fov_statistics"] + median = float(fov_stats["median"]) + iqr = float(fov_stats["iqr"]) + +vol_np = (vol_np - median) / iqr +vol = torch.from_numpy(vol_np).float().to(DEVICE) + +# Run inference with sliding windows and linear feathering blending +# step=1 gives maximum overlap; increase step for faster inference +with torch.inference_mode(): + pred = vs.predict_sliding_windows(vol, out_channel=2, step=1) + +# Visualize +pred_np = pred.cpu().numpy() +nuc, mem = pred_np[0, 0], pred_np[0, 1] + +viewer = napari.Viewer() +viewer.add_image(vol_np, name="phase_input", colormap="gray") +viewer.add_image(nuc, name="virt_nuclei", colormap="magenta") +viewer.add_image(mem, name="virt_membrane", colormap="cyan") +napari.run() diff --git a/examples/virtual_staining/VS_model_inference/demo_vsneuromast.py b/applications/cytoland/examples/VS_model_inference/demo_vsneuromast.py similarity index 88% rename from examples/virtual_staining/VS_model_inference/demo_vsneuromast.py rename to applications/cytoland/examples/VS_model_inference/demo_vsneuromast.py index 78a046893..b33b2ee85 100644 --- a/examples/virtual_staining/VS_model_inference/demo_vsneuromast.py +++ b/applications/cytoland/examples/VS_model_inference/demo_vsneuromast.py @@ -13,13 +13,12 @@ from iohub import open_ome_zarr from plot import plot_vs_n_fluor -from viscy.data.hcs import HCSDataModule -from viscy.trainer import VisCyTrainer -from viscy.transforms import NormalizeSampled - -# Viscy classes for the trainer and model -from viscy.translation.engine import VSUNet -from viscy.translation.predict_writer import HCSPredictionWriter +# Cytoland and VisCy modular classes for the trainer and model +from cytoland.engine import VSUNet +from viscy_data.hcs import HCSDataModule +from viscy_transforms import NormalizeSampled +from viscy_utils.callbacks import HCSPredictionWriter +from viscy_utils.trainer import VisCyTrainer # %% [markdown] """ @@ -80,12 +79,11 @@ data_module = HCSDataModule( data_path=input_data_path, source_channel=phase_channel_name, - target_channel=["Membrane", "Nuclei"], + target_channel=["Nuclei", "Membrane"], z_window_size=21, split_ratio=0.8, batch_size=BATCH_SIZE, num_workers=NUM_WORKERS, - architecture="UNeXt2", normalizations=[ NormalizeSampled( [phase_channel_name], @@ -111,9 +109,7 @@ "head_pool": True, } -model_VSNeuromast = VSUNet.load_from_checkpoint( - model_ckpt_path, architecture="UNeXt2", model_config=config_VSNeuromast -) +model_VSNeuromast = VSUNet.load_from_checkpoint(model_ckpt_path, architecture="UNeXt2", model_config=config_VSNeuromast) model_VSNeuromast.eval() # %% @@ -147,7 +143,7 @@ # Open the experimental fluorescence fluor_store = open_ome_zarr(input_data_path, mode="r") # Get the 2D images -# NOTE: Channel indeces hardcoded for this dataset +# NOTE: Channel indices hardcoded for this dataset fluor_nucleus = fluor_store[0][0, 1, z_slice] # (t,c,z,y,x) fluor_membrane = fluor_store[0][0, 2, z_slice] # (t,c,z,y,x) diff --git a/examples/virtual_staining/VS_model_inference/plot.py b/applications/cytoland/examples/VS_model_inference/plot.py similarity index 89% rename from examples/virtual_staining/VS_model_inference/plot.py rename to applications/cytoland/examples/VS_model_inference/plot.py index 796f15864..eb7f4ce2a 100644 --- a/examples/virtual_staining/VS_model_inference/plot.py +++ b/applications/cytoland/examples/VS_model_inference/plot.py @@ -43,15 +43,9 @@ def plot_vs_n_fluor(vs_nucleus, vs_membrane, fluor_nucleus, fluor_membrane): fluor_membrane_rgb[:, :, 2] = fluor_membrane * colormap_4[2] # Merge the two channels merged_fluor = np.zeros((*fluor_nucleus.shape[-2:], 3)) - merged_fluor[:, :, 0] = ( - fluor_nucleus * colormap_3[0] + fluor_membrane * colormap_4[0] - ) - merged_fluor[:, :, 1] = ( - fluor_nucleus * colormap_3[1] + fluor_membrane * colormap_4[1] - ) - merged_fluor[:, :, 2] = ( - fluor_nucleus * colormap_3[2] + fluor_membrane * colormap_4[2] - ) + merged_fluor[:, :, 0] = fluor_nucleus * colormap_3[0] + fluor_membrane * colormap_4[0] + merged_fluor[:, :, 1] = fluor_nucleus * colormap_3[1] + fluor_membrane * colormap_4[1] + merged_fluor[:, :, 2] = fluor_nucleus * colormap_3[2] + fluor_membrane * colormap_4[2] # %% # Plot @@ -73,7 +67,7 @@ def plot_vs_n_fluor(vs_nucleus, vs_membrane, fluor_nucleus, fluor_membrane): ax[1, 2].imshow(merged_fluor) ax[1, 2].set_title("Experimental Fluorescence Nuclei+Membrane") - # turnoff axis + # turn off axis for a in ax.flatten(): a.axis("off") plt.margins(0, 0) diff --git a/applications/cytoland/examples/configs/dynacell/fit_fnet3d_sec61b.yml b/applications/cytoland/examples/configs/dynacell/fit_fnet3d_sec61b.yml new file mode 100644 index 000000000..c3b8ff259 --- /dev/null +++ b/applications/cytoland/examples/configs/dynacell/fit_fnet3d_sec61b.yml @@ -0,0 +1,46 @@ +# Legacy transitional config; new benchmark launches should use Dynacell. +# See: applications/dynacell/examples/configs/sec61b/ +# FNet3D on AICS iPSC SEC61B (ER) — dynacell benchmark. +# Usage: uv run python -m cytoland fit --config dynacell/fit_fnet3d_sec61b.yml +# Batch related launches with: +# export VISCY_WANDB_LAUNCH=20260401-augfix-r1 +base: + - ../recipes/trainer/fit.yml + - ../recipes/topology/single_gpu.yml + - ../recipes/data/hcs_sec61b_3d.yml + - ../recipes/models/fnet3d_z8.yml + +model: + init_args: + loss_function: + class_path: viscy_utils.losses.MixedLoss + init_args: + l1_alpha: 0.5 + ms_dssim_alpha: 0.5 + lr: 0.001 + schedule: WarmupCosine + +trainer: + precision: bf16-mixed + max_epochs: 100 + logger: + init_args: + # Override cytoland's default project: this bridge trains on a dynacell dataset (iPSC SEC61B). + project: dynacell + name: FNet3D_iPSC_SEC61B + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell_cytoland/ipsc/sec61b/fnet3d + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell_cytoland/ipsc/sec61b/fnet3d/checkpoints + +data: + init_args: + batch_size: 64 diff --git a/applications/cytoland/examples/configs/dynacell/fit_vscyto3d_sec61b.yml b/applications/cytoland/examples/configs/dynacell/fit_vscyto3d_sec61b.yml new file mode 100644 index 000000000..2e5b2e129 --- /dev/null +++ b/applications/cytoland/examples/configs/dynacell/fit_vscyto3d_sec61b.yml @@ -0,0 +1,46 @@ +# Legacy transitional config; new benchmark launches should use Dynacell. +# See: applications/dynacell/examples/configs/sec61b/ +# VSCyto3D (UNeXt2) on AICS iPSC SEC61B (ER) — dynacell benchmark. +# Usage: uv run python -m cytoland fit --config dynacell/fit_vscyto3d_sec61b.yml +# Batch related launches with: +# export VISCY_WANDB_LAUNCH=20260401-augfix-r1 +base: + - ../recipes/trainer/fit.yml + - ../recipes/topology/single_gpu.yml + - ../recipes/data/hcs_sec61b_3d.yml + - ../recipes/models/unext2_3d_z8.yml + +model: + init_args: + loss_function: + class_path: viscy_utils.losses.MixedLoss + init_args: + l1_alpha: 0.5 + ms_dssim_alpha: 0.5 + lr: 0.0002 + schedule: WarmupCosine + +trainer: + precision: bf16-mixed + max_epochs: 100 + logger: + init_args: + # Override cytoland's default project: this bridge trains on a dynacell dataset (iPSC SEC61B). + project: dynacell + name: VSCyto3D_iPSC_SEC61B + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell_cytoland/ipsc/sec61b/vscyto3d + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell_cytoland/ipsc/sec61b/vscyto3d/checkpoints + +data: + init_args: + batch_size: 64 diff --git a/applications/cytoland/examples/configs/dynacell/run_fnet3d_sec61b.slurm b/applications/cytoland/examples/configs/dynacell/run_fnet3d_sec61b.slurm new file mode 100644 index 000000000..09723f2de --- /dev/null +++ b/applications/cytoland/examples/configs/dynacell/run_fnet3d_sec61b.slurm @@ -0,0 +1,25 @@ +#!/bin/bash +# Legacy transitional config; new benchmark launches should use Dynacell. +# See: applications/dynacell/examples/configs/sec61b/ + +#SBATCH --job-name=FNet3D_SEC61B +#SBATCH --time=20-00:00:00 +#SBATCH --nodes=1 +#SBATCH --ntasks=1 +#SBATCH --partition=gpu +#SBATCH --cpus-per-task=32 +#SBATCH --gpus=1 +#SBATCH --mem=256G +#SBATCH --constraint=h200 +#SBATCH --output=/hpc/projects/comp.micro/virtual_staining/models/dynacell_cytoland/ipsc/sec61b/fnet3d/slurm/%j.out +#SBATCH --error=/hpc/projects/comp.micro/virtual_staining/models/dynacell_cytoland/ipsc/sec61b/fnet3d/slurm/%j.err + +mkdir -p -m 775 /hpc/projects/comp.micro/virtual_staining/models/dynacell_cytoland/ipsc/sec61b/fnet3d/slurm +mkdir -p -m 775 /hpc/projects/comp.micro/virtual_staining/models/dynacell_cytoland/ipsc/sec61b/fnet3d/checkpoints + +ml uv + +export PYTHONUNBUFFERED=1 + +nvidia-smi +uv run python -m cytoland fit --config applications/cytoland/examples/configs/dynacell/fit_fnet3d_sec61b.yml diff --git a/applications/cytoland/examples/configs/dynacell/run_vscyto3d_sec61b.slurm b/applications/cytoland/examples/configs/dynacell/run_vscyto3d_sec61b.slurm new file mode 100644 index 000000000..95b6f7c45 --- /dev/null +++ b/applications/cytoland/examples/configs/dynacell/run_vscyto3d_sec61b.slurm @@ -0,0 +1,25 @@ +#!/bin/bash +# Legacy transitional config; new benchmark launches should use Dynacell. +# See: applications/dynacell/examples/configs/sec61b/ + +#SBATCH --job-name=VSCyto3D_SEC61B +#SBATCH --time=20-00:00:00 +#SBATCH --nodes=1 +#SBATCH --ntasks=1 +#SBATCH --partition=gpu +#SBATCH --cpus-per-task=32 +#SBATCH --gpus=1 +#SBATCH --mem=256G +#SBATCH --constraint=h200 +#SBATCH --output=/hpc/projects/comp.micro/virtual_staining/models/dynacell_cytoland/ipsc/sec61b/vscyto3d/slurm/%j.out +#SBATCH --error=/hpc/projects/comp.micro/virtual_staining/models/dynacell_cytoland/ipsc/sec61b/vscyto3d/slurm/%j.err + +mkdir -p -m 775 /hpc/projects/comp.micro/virtual_staining/models/dynacell_cytoland/ipsc/sec61b/vscyto3d/slurm +mkdir -p -m 775 /hpc/projects/comp.micro/virtual_staining/models/dynacell_cytoland/ipsc/sec61b/vscyto3d/checkpoints + +ml uv + +export PYTHONUNBUFFERED=1 + +nvidia-smi +uv run python -m cytoland fit --config applications/cytoland/examples/configs/dynacell/fit_vscyto3d_sec61b.yml diff --git a/applications/cytoland/examples/configs/fnet3d/fit.yml b/applications/cytoland/examples/configs/fnet3d/fit.yml new file mode 100644 index 000000000..61df4e08b --- /dev/null +++ b/applications/cytoland/examples/configs/fnet3d/fit.yml @@ -0,0 +1,26 @@ +# FNet3D benchmark ownership has moved to Dynacell. +# See: applications/dynacell/examples/configs/fnet3d/ +# FNet3D: supervised training (Ounkomol et al. 2018). +# Usage: python -m cytoland fit --config fnet3d/fit.yml +base: + - ../recipes/trainer/fit.yml + - ../recipes/topology/ddp_4gpu.yml + - ../recipes/data/hcs_nuc_mem_3d.yml + - ../recipes/models/fnet3d.yml + +model: + init_args: + lr: 0.001 + schedule: Constant + +trainer: + precision: 16-mixed + max_epochs: 200 + max_steps: 50000 + +data: + init_args: + data_path: #TODO HCS OME-Zarr data + z_window_size: 32 + batch_size: 24 + yx_patch_size: [64, 64] diff --git a/applications/cytoland/examples/configs/fnet3d/predict.yml b/applications/cytoland/examples/configs/fnet3d/predict.yml new file mode 100644 index 000000000..05466f236 --- /dev/null +++ b/applications/cytoland/examples/configs/fnet3d/predict.yml @@ -0,0 +1,14 @@ +# FNet3D benchmark ownership has moved to Dynacell. +# See: applications/dynacell/examples/configs/fnet3d/ +# FNet3D: inference. +# Usage: python -m cytoland predict --config fnet3d/predict.yml +base: + - ../recipes/trainer/predict.yml + - ../recipes/topology/single_gpu.yml + - ../recipes/data/hcs_nuc_mem_3d.yml + - ../recipes/models/fnet3d.yml + +data: + init_args: + data_path: #TODO input OME-Zarr + z_window_size: 32 diff --git a/applications/cytoland/examples/configs/recipes/data/cached_pretrain.yml b/applications/cytoland/examples/configs/recipes/data/cached_pretrain.yml new file mode 100644 index 000000000..3ee0891f6 --- /dev/null +++ b/applications/cytoland/examples/configs/recipes/data/cached_pretrain.yml @@ -0,0 +1,73 @@ +# Data recipe: CachedOmeZarrDataModule for FCMAE self-supervised pretraining. +# Wraps in CombinedDataModule as required by FcmaeUNet. +# Uses source channel only (no target for reconstruction objective). +data: + class_path: viscy_data.combined.CombinedDataModule + init_args: + data_modules: + - class_path: viscy_data.gpu_aug.CachedOmeZarrDataModule + init_args: + data_path: #TODO + channels: Phase3D + batch_size: 4 + num_workers: 6 + split_ratio: 0.8 + train_cpu_transforms: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + remove_meta: true + val_cpu_transforms: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + remove_meta: true + train_gpu_transforms: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [Phase3D] + prob: 0.8 + rotate_range: [3.14159, 0, 0] + scale_range: [[0.8, 1.2], [0.7, 1.3], [0.7, 1.3]] + - class_path: viscy_transforms.BatchedRandInvertIntensityd + init_args: + keys: [Phase3D] + prob: 0.5 + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [Phase3D] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [Phase3D] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [Phase3D] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [Phase3D] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + - class_path: viscy_transforms.BatchedStackChannelsd + init_args: + channel_map: + source: [Phase3D] + val_gpu_transforms: + - class_path: viscy_transforms.BatchedStackChannelsd + init_args: + channel_map: + source: [Phase3D] diff --git a/applications/cytoland/examples/configs/recipes/data/hcs_a549_infected_d1_hummingbird.yml b/applications/cytoland/examples/configs/recipes/data/hcs_a549_infected_d1_hummingbird.yml new file mode 100644 index 000000000..d376cfc93 --- /dev/null +++ b/applications/cytoland/examples/configs/recipes/data/hcs_a549_infected_d1_hummingbird.yml @@ -0,0 +1,78 @@ +# Data recipe: A549 infection finetune — Hummingbird, 2026-01-29 (D1). +# Phase3D -> DAPI (nucleus) + TXR (membrane). Per-timepoint normalization. +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: + data_path: /hpc/projects/virtual_staining/training/a549/2026_05_infected_cell/2026_01_29_A549_H2B_CAAX_DAPI_DENV_ZIKV.zarr + source_channel: Phase3D + target_channel: [DAPI_Density3D, TXR_Density3D] + z_window_size: 20 + split_ratio: 0.8 + batch_size: 16 + num_workers: 8 + persistent_workers: true + mmap_preload: true + yx_patch_size: [384, 384] + # CPU: normalize, then weighted-sample 4 crops per FOV at full Z depth + # (20, 600, 600). Weighting by the DAPI (nucleus) channel biases crops + # to cell-dense regions. Matches the dynacell fcmae_vscyto3d recipe. + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, DAPI_Density3D, TXR_Density3D] + w_key: DAPI_Density3D + spatial_size: [20, 600, 600] + num_samples: 4 + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [DAPI_Density3D, TXR_Density3D] + level: timepoint_statistics + subtrahend: median + divisor: iqr + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] diff --git a/applications/cytoland/examples/configs/recipes/data/hcs_a549_infected_d2_hummingbird.yml b/applications/cytoland/examples/configs/recipes/data/hcs_a549_infected_d2_hummingbird.yml new file mode 100644 index 000000000..0cd2c5f7e --- /dev/null +++ b/applications/cytoland/examples/configs/recipes/data/hcs_a549_infected_d2_hummingbird.yml @@ -0,0 +1,75 @@ +# Data recipe: A549 infection finetune — Hummingbird, 2026-03-10 (D2). +# Phase3D -> DAPI (nucleus) + TXR (membrane). Per-timepoint normalization. +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: + data_path: /hpc/projects/virtual_staining/training/a549/2026_05_infected_cell/2026_03_10_A549_H2B_CAXX_DAPI_DENV_ZIKV.zarr + source_channel: Phase3D + target_channel: [DAPI_Density3D, TXR_Density3D] + z_window_size: 20 + split_ratio: 0.8 + batch_size: 16 + num_workers: 8 + persistent_workers: true + mmap_preload: true + yx_patch_size: [384, 384] + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, DAPI_Density3D, TXR_Density3D] + w_key: DAPI_Density3D + spatial_size: [20, 600, 600] + num_samples: 4 + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [DAPI_Density3D, TXR_Density3D] + level: timepoint_statistics + subtrahend: median + divisor: iqr + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] diff --git a/applications/cytoland/examples/configs/recipes/data/hcs_a549_infected_d3_mantis.yml b/applications/cytoland/examples/configs/recipes/data/hcs_a549_infected_d3_mantis.yml new file mode 100644 index 000000000..c2f8bdea8 --- /dev/null +++ b/applications/cytoland/examples/configs/recipes/data/hcs_a549_infected_d3_mantis.yml @@ -0,0 +1,112 @@ +# Data recipe: A549 infection finetune — Mantis, 2026-03-26 (D3). +# Phase3D -> raw mCherry (nucleus H2B) + raw Cy5 (membrane CAAX). Per-timepoint normalization. +# Excludes 27 FOVs held out as the Mantis test set (see 2026-02 Viral infection wiki). +# Run `uv run viscy preprocess` on the zarr first to populate normalization_metadata. +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: + data_path: /hpc/projects/virtual_staining/training/a549/2026_05_infected_cell/2026_03_26_A549_CAAX_H2B_DENV_ZIKV.zarr + source_channel: Phase3D + target_channel: + - "raw mCherry EX561 EM600-37" + - "raw Cy5 EX639 EM698-70" + z_window_size: 20 + split_ratio: 0.8 + batch_size: 16 + num_workers: 8 + persistent_workers: true + mmap_preload: true + yx_patch_size: [384, 384] + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: + - Phase3D + - "raw mCherry EX561 EM600-37" + - "raw Cy5 EX639 EM698-70" + w_key: "raw mCherry EX561 EM600-37" + spatial_size: [20, 600, 600] + num_samples: 4 + exclude_fov_names: + - B/2/000000 + - B/2/000001 + - B/2/000002 + - B/2/000003 + - B/2/000004 + - B/2/000005 + - B/2/000006 + - B/2/000007 + - B/2/000008 + - B/3/000004 + - B/3/000005 + - B/3/000006 + - B/3/000007 + - B/3/000008 + - B/3/001000 + - B/3/001001 + - B/3/001002 + - B/3/001003 + - B/4/000000 + - B/4/000001 + - B/4/000002 + - B/4/000003 + - B/4/000006 + - B/4/000007 + - B/4/000008 + - B/4/001000 + - B/4/002000 + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: + - "raw mCherry EX561 EM600-37" + - "raw Cy5 EX639 EM698-70" + level: timepoint_statistics + subtrahend: median + divisor: iqr + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] diff --git a/applications/cytoland/examples/configs/recipes/data/hcs_nuc_mem_2d.yml b/applications/cytoland/examples/configs/recipes/data/hcs_nuc_mem_2d.yml new file mode 100644 index 000000000..3dc8675ac --- /dev/null +++ b/applications/cytoland/examples/configs/recipes/data/hcs_nuc_mem_2d.yml @@ -0,0 +1,19 @@ +# Data recipe: HCSDataModule for Phase3D → Nuclei + Membrane, 2D (z=1). +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: + data_path: #TODO + source_channel: Phase3D + target_channel: [Nuclei, Membrane] + z_window_size: 1 + split_ratio: 0.8 + batch_size: 32 + num_workers: 8 + yx_patch_size: [256, 256] + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D, Nuclei, Membrane] + level: fov_statistics + subtrahend: mean + divisor: std diff --git a/applications/cytoland/examples/configs/recipes/data/hcs_nuc_mem_3d.yml b/applications/cytoland/examples/configs/recipes/data/hcs_nuc_mem_3d.yml new file mode 100644 index 000000000..2a9ff12b4 --- /dev/null +++ b/applications/cytoland/examples/configs/recipes/data/hcs_nuc_mem_3d.yml @@ -0,0 +1,19 @@ +# Data recipe: HCSDataModule for Phase3D → Nuclei + Membrane, 3D (z=5). +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: + data_path: #TODO + source_channel: Phase3D + target_channel: [Nuclei, Membrane] + z_window_size: 5 + split_ratio: 0.8 + batch_size: 32 + num_workers: 8 + yx_patch_size: [256, 256] + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D, Nuclei, Membrane] + level: fov_statistics + subtrahend: mean + divisor: std diff --git a/applications/cytoland/examples/configs/recipes/data/hcs_nuc_mem_neuromast.yml b/applications/cytoland/examples/configs/recipes/data/hcs_nuc_mem_neuromast.yml new file mode 100644 index 000000000..132d7e050 --- /dev/null +++ b/applications/cytoland/examples/configs/recipes/data/hcs_nuc_mem_neuromast.yml @@ -0,0 +1,19 @@ +# Data recipe: HCSDataModule for Phase3D → Nuclei + Membrane, neuromast (z=21). +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: + data_path: #TODO + source_channel: Phase3D + target_channel: [Nuclei, Membrane] + z_window_size: 21 + split_ratio: 0.8 + batch_size: 2 + num_workers: 4 + yx_patch_size: [256, 256] + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D, Nuclei, Membrane] + level: fov_statistics + subtrahend: mean + divisor: std diff --git a/applications/cytoland/examples/configs/recipes/data/hcs_sec61b_3d.yml b/applications/cytoland/examples/configs/recipes/data/hcs_sec61b_3d.yml new file mode 100644 index 000000000..8cf4c5d23 --- /dev/null +++ b/applications/cytoland/examples/configs/recipes/data/hcs_sec61b_3d.yml @@ -0,0 +1,66 @@ +# Legacy transitional config; new benchmark launches should use Dynacell. +# See: applications/dynacell/examples/configs/sec61b/ +# Data recipe: HCSDataModule for Phase3D -> Structure (SEC61B), 3D (z=8). +# Uses mean/std (source) and median/iqr (target) normalization with GPU-side Batched* augmentations. +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/SEC61B.zarr + source_channel: Phase3D + target_channel: Structure + z_window_size: 8 + num_workers: 8 + yx_patch_size: [512, 512] + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Structure] + level: fov_statistics + subtrahend: median + divisor: iqr + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandWeightedCropd + init_args: + keys: [source, target] + w_key: target + spatial_size: [8, 384, 384] + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.5 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 3.0, 3.0] + scale_range: [[0.8, 1.2], [0.7, 1.3], [0.7, 1.3]] + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 256, 256] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.3 + gamma: [0.75, 1.5] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + factors: 0.5 + prob: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 1.0 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 1.5] + sigma_y: [0.25, 1.5] + sigma_z: [0.25, 1.5] diff --git a/applications/cytoland/examples/configs/recipes/models/fcmae_2d.yml b/applications/cytoland/examples/configs/recipes/models/fcmae_2d.yml new file mode 100644 index 000000000..b1a4a62e2 --- /dev/null +++ b/applications/cytoland/examples/configs/recipes/models/fcmae_2d.yml @@ -0,0 +1,14 @@ +# Model recipe: VSCyto2D — FcmaeUNet with FCMAE encoder, 2D (in_stack_depth=1). +# Published model: compmicro-czb/VSCyto2D +model: + class_path: cytoland.engine.FcmaeUNet + init_args: + model_config: + in_channels: 1 + out_channels: 2 + encoder_blocks: [3, 3, 9, 3] + dims: [96, 192, 384, 768] + decoder_conv_blocks: 2 + stem_kernel_size: [1, 2, 2] + in_stack_depth: 1 + pretraining: false diff --git a/applications/cytoland/examples/configs/recipes/models/fcmae_3d.yml b/applications/cytoland/examples/configs/recipes/models/fcmae_3d.yml new file mode 100644 index 000000000..593a93bd5 --- /dev/null +++ b/applications/cytoland/examples/configs/recipes/models/fcmae_3d.yml @@ -0,0 +1,14 @@ +# Model recipe: FcmaeUNet with FCMAE encoder, 3D (in_stack_depth=5). +# Used for VSCyto3D pretraining and fine-tuning. +model: + class_path: cytoland.engine.FcmaeUNet + init_args: + model_config: + in_channels: 1 + out_channels: 2 + encoder_blocks: [3, 3, 9, 3] + dims: [96, 192, 384, 768] + decoder_conv_blocks: 2 + stem_kernel_size: [5, 4, 4] + in_stack_depth: 5 + pretraining: false diff --git a/applications/cytoland/examples/configs/recipes/models/fnet3d.yml b/applications/cytoland/examples/configs/recipes/models/fnet3d.yml new file mode 100644 index 000000000..1f5a545d2 --- /dev/null +++ b/applications/cytoland/examples/configs/recipes/models/fnet3d.yml @@ -0,0 +1,11 @@ +# Model recipe: FNet3D — recursive encoder-decoder (Ounkomol et al. 2018). +model: + class_path: cytoland.engine.VSUNet + init_args: + architecture: FNet3D + model_config: + in_channels: 1 + out_channels: 1 + depth: 4 + mult_chan: 32 + in_stack_depth: 32 diff --git a/applications/cytoland/examples/configs/recipes/models/fnet3d_z8.yml b/applications/cytoland/examples/configs/recipes/models/fnet3d_z8.yml new file mode 100644 index 000000000..4ea0af16f --- /dev/null +++ b/applications/cytoland/examples/configs/recipes/models/fnet3d_z8.yml @@ -0,0 +1,13 @@ +# Legacy transitional config; new benchmark launches should use Dynacell. +# See: applications/dynacell/examples/configs/sec61b/ +# Model recipe: FNet3D for z=8 input (depth=3, divisor=8). +model: + class_path: cytoland.engine.VSUNet + init_args: + architecture: FNet3D + model_config: + in_channels: 1 + out_channels: 1 + depth: 3 + mult_chan: 32 + in_stack_depth: 8 diff --git a/applications/cytoland/examples/configs/recipes/models/unext2_3d.yml b/applications/cytoland/examples/configs/recipes/models/unext2_3d.yml new file mode 100644 index 000000000..eeb0a62ad --- /dev/null +++ b/applications/cytoland/examples/configs/recipes/models/unext2_3d.yml @@ -0,0 +1,15 @@ +# Model recipe: VSCyto3D — UNeXt2 with ConvNeXt-V2 encoder, 3D (in_stack_depth=5). +# Published model: compmicro-czb/VSCyto3D +model: + class_path: cytoland.engine.VSUNet + init_args: + architecture: UNeXt2 + model_config: + in_channels: 1 + out_channels: 2 + in_stack_depth: 5 + backbone: convnextv2_tiny + stem_kernel_size: [5, 4, 4] + decoder_mode: pixelshuffle + head_expansion_ratio: 4 + head_pool: true diff --git a/applications/cytoland/examples/configs/recipes/models/unext2_3d_z8.yml b/applications/cytoland/examples/configs/recipes/models/unext2_3d_z8.yml new file mode 100644 index 000000000..86240bbab --- /dev/null +++ b/applications/cytoland/examples/configs/recipes/models/unext2_3d_z8.yml @@ -0,0 +1,16 @@ +# Legacy transitional config; new benchmark launches should use Dynacell. +# See: applications/dynacell/examples/configs/sec61b/ +# Model recipe: UNeXt2 (VSCyto3D) for z=8 input (stem=[8,4,4]). +model: + class_path: cytoland.engine.VSUNet + init_args: + architecture: UNeXt2 + model_config: + in_channels: 1 + out_channels: 1 + in_stack_depth: 8 + backbone: convnextv2_tiny + stem_kernel_size: [8, 4, 4] + decoder_mode: pixelshuffle + head_expansion_ratio: 4 + head_pool: true diff --git a/applications/cytoland/examples/configs/recipes/models/unext2_neuromast.yml b/applications/cytoland/examples/configs/recipes/models/unext2_neuromast.yml new file mode 100644 index 000000000..3adfbcfd2 --- /dev/null +++ b/applications/cytoland/examples/configs/recipes/models/unext2_neuromast.yml @@ -0,0 +1,15 @@ +# Model recipe: VSNeuromast — UNeXt2 for zebrafish neuromast (in_stack_depth=21). +# Published model: compmicro-czb/VSNeuromast +model: + class_path: cytoland.engine.VSUNet + init_args: + architecture: UNeXt2 + model_config: + in_channels: 1 + out_channels: 2 + in_stack_depth: 21 + backbone: convnextv2_tiny + stem_kernel_size: [7, 4, 4] + decoder_mode: pixelshuffle + head_expansion_ratio: 4 + head_pool: true diff --git a/applications/cytoland/examples/configs/recipes/modes/spotlight.yml b/applications/cytoland/examples/configs/recipes/modes/spotlight.yml new file mode 100644 index 000000000..be9b5d295 --- /dev/null +++ b/applications/cytoland/examples/configs/recipes/modes/spotlight.yml @@ -0,0 +1,29 @@ +# Mode recipe: Spotlight foreground-aware loss (Kalinin et al. 2025). +# Cross-cutting: sets loss, fg_mask_key, nonzero filtering, AND +# Otsu-centered normalization. fg_threshold: 0.0 is valid ONLY +# because normalizations use subtrahend: otsu_threshold. +# +# Requires: viscy preprocess --compute_otsu --compute_fg_masks +# +# Base ordering: this mode must come AFTER the data recipe in the +# base: list so that its normalizations replace the data default. +model: + init_args: + loss_function: + class_path: viscy_utils.losses.SpotlightLoss + init_args: + lambda_mse: 0.5 + sigmoid_k: -0.95 + fg_threshold: 0.0 +data: + init_args: + fg_mask_key: fg_mask + min_nonzero_fraction: 0.001 + nonzero_threshold: 0.0 + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D, Nuclei, Membrane] + level: fov_statistics + subtrahend: otsu_threshold + divisor: std diff --git a/applications/cytoland/examples/configs/recipes/topology/ddp_4gpu.yml b/applications/cytoland/examples/configs/recipes/topology/ddp_4gpu.yml new file mode 100644 index 000000000..6ecdb4ad8 --- /dev/null +++ b/applications/cytoland/examples/configs/recipes/topology/ddp_4gpu.yml @@ -0,0 +1,6 @@ +# Topology recipe: 4-GPU DDP training on a single node. +trainer: + accelerator: gpu + strategy: ddp + devices: 4 + num_nodes: 1 diff --git a/applications/cytoland/examples/configs/recipes/topology/single_gpu.yml b/applications/cytoland/examples/configs/recipes/topology/single_gpu.yml new file mode 100644 index 000000000..a05fa451a --- /dev/null +++ b/applications/cytoland/examples/configs/recipes/topology/single_gpu.yml @@ -0,0 +1,7 @@ +# Single-GPU training. strategy=auto lets Lightning pick single_device; +# plain ddp at devices=1 would add pointless process-group overhead. +trainer: + accelerator: gpu + strategy: auto + devices: 1 + num_nodes: 1 diff --git a/applications/cytoland/examples/configs/recipes/trainer/fit.yml b/applications/cytoland/examples/configs/recipes/trainer/fit.yml new file mode 100644 index 000000000..ab3554025 --- /dev/null +++ b/applications/cytoland/examples/configs/recipes/trainer/fit.yml @@ -0,0 +1,24 @@ +# Topology (accelerator / devices / strategy / num_nodes) lives in +# recipes/topology/*.yml. Precision lives in model overlays. +# max_epochs and max_steps also live in model overlays or leaves. +seed_everything: 42 +trainer: + log_every_n_steps: 10 + enable_checkpointing: true + inference_mode: true + logger: + class_path: lightning.pytorch.loggers.WandbLogger + init_args: + project: cytoland + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + filename: "epoch={epoch}-step={step}-loss={loss/validate:.3f}" + auto_insert_metric_name: false diff --git a/applications/cytoland/examples/configs/recipes/trainer/predict.yml b/applications/cytoland/examples/configs/recipes/trainer/predict.yml new file mode 100644 index 000000000..52a1c6036 --- /dev/null +++ b/applications/cytoland/examples/configs/recipes/trainer/predict.yml @@ -0,0 +1,10 @@ +# Unified predict trainer recipe. +# Topology lives in recipes/topology/single_gpu.yml. +trainer: + precision: 32-true + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: #TODO output zarr path +return_predictions: false +ckpt_path: #TODO checkpoint path diff --git a/applications/cytoland/examples/configs/vscyto2d/finetune.yml b/applications/cytoland/examples/configs/vscyto2d/finetune.yml new file mode 100644 index 000000000..d9838635b --- /dev/null +++ b/applications/cytoland/examples/configs/vscyto2d/finetune.yml @@ -0,0 +1,28 @@ +# VSCyto2D: supervised fine-tuning from FCMAE-pretrained encoder. +# Usage: python -m cytoland fit --config vscyto2d/finetune.yml +base: + - ../recipes/trainer/fit.yml + - ../recipes/topology/ddp_4gpu.yml + - ../recipes/data/hcs_nuc_mem_2d.yml + - ../recipes/models/fcmae_2d.yml + +model: + init_args: + loss_function: + class_path: viscy_utils.losses.MixedLoss + init_args: + l1_alpha: 0.5 + l2_alpha: 0.0 + ms_dssim_alpha: 0.5 + encoder_only: true + ckpt_path: #TODO pretrained FCMAE checkpoint + lr: 0.0002 + schedule: WarmupCosine + +trainer: + precision: 16-mixed + max_epochs: 200 + +data: + init_args: + data_path: #TODO HCS OME-Zarr data diff --git a/applications/cytoland/examples/configs/vscyto2d/predict.yml b/applications/cytoland/examples/configs/vscyto2d/predict.yml new file mode 100644 index 000000000..b633b2243 --- /dev/null +++ b/applications/cytoland/examples/configs/vscyto2d/predict.yml @@ -0,0 +1,20 @@ +# VSCyto2D: inference with published checkpoint. +# Checkpoint: https://public.czbiohub.org/comp.micro/viscy/VS_models/VSCyto2D/VSCyto2D/epoch=399-step=23200.ckpt +# Usage: python -m cytoland predict --config vscyto2d/predict.yml +base: + - ../recipes/trainer/predict.yml + - ../recipes/topology/single_gpu.yml + - ../recipes/data/hcs_nuc_mem_2d.yml + - ../recipes/models/fcmae_2d.yml + +data: + init_args: + data_path: #TODO input OME-Zarr + batch_size: 8 + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: median + divisor: iqr diff --git a/applications/cytoland/examples/configs/vscyto2d/pretrain.yml b/applications/cytoland/examples/configs/vscyto2d/pretrain.yml new file mode 100644 index 000000000..c0b2c1d92 --- /dev/null +++ b/applications/cytoland/examples/configs/vscyto2d/pretrain.yml @@ -0,0 +1,42 @@ +# VSCyto2D: FCMAE self-supervised pretraining (2D, in_stack_depth=1). +# Usage: python -m cytoland fit --config vscyto2d/pretrain.yml +base: + - ../recipes/trainer/fit.yml + - ../recipes/topology/ddp_4gpu.yml + - ../recipes/data/cached_pretrain.yml + +model: + class_path: cytoland.engine.FcmaeUNet + init_args: + fit_mask_ratio: 0.5 + model_config: + in_channels: 1 + out_channels: 1 + encoder_blocks: [3, 3, 9, 3] + dims: [96, 192, 384, 768] + decoder_conv_blocks: 1 + stem_kernel_size: [1, 2, 2] + in_stack_depth: 1 + loss_function: + class_path: cytoland.engine.MaskedMSELoss + lr: 0.0002 + schedule: WarmupCosine + log_batches_per_epoch: 3 + log_samples_per_batch: 1 + +trainer: + # FCMAE pretraining requires find_unused_parameters=True (masked decoder). + strategy: ddp_find_unused_parameters_true + precision: 16-mixed + max_epochs: 400 + use_distributed_sampler: false + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/val + every_n_epochs: 1 + save_top_k: 5 + save_last: true diff --git a/applications/cytoland/examples/configs/vscyto3d/finetune.yml b/applications/cytoland/examples/configs/vscyto3d/finetune.yml new file mode 100644 index 000000000..d547f176d --- /dev/null +++ b/applications/cytoland/examples/configs/vscyto3d/finetune.yml @@ -0,0 +1,26 @@ +# VSCyto3D: supervised fine-tuning from FCMAE-pretrained encoder. +# Usage: python -m cytoland fit --config vscyto3d/finetune.yml +base: + - ../recipes/trainer/fit.yml + - ../recipes/topology/ddp_4gpu.yml + - ../recipes/data/hcs_nuc_mem_3d.yml + - ../recipes/models/unext2_3d.yml + +model: + init_args: + loss_function: + class_path: viscy_utils.losses.MixedLoss + init_args: + l1_alpha: 0.5 + l2_alpha: 0.0 + ms_dssim_alpha: 0.5 + lr: 0.0002 + schedule: WarmupCosine + +trainer: + precision: bf16-mixed + max_epochs: 200 + +data: + init_args: + data_path: #TODO HCS OME-Zarr data diff --git a/applications/cytoland/examples/configs/vscyto3d/finetune_a549_infected.yml b/applications/cytoland/examples/configs/vscyto3d/finetune_a549_infected.yml new file mode 100644 index 000000000..fad85d814 --- /dev/null +++ b/applications/cytoland/examples/configs/vscyto3d/finetune_a549_infected.yml @@ -0,0 +1,353 @@ +# VSCyto3D: warm-start finetune on A549 infected-cell data from three microscopes. +# +# Sources: +# D1 = Hummingbird 2026-01-29, DAPI_Density3D + TXR_Density3D +# D2 = Hummingbird 2026-03-10, DAPI_Density3D + TXR_Density3D +# D3 = Mantis 2026-03-26, raw mCherry + raw Cy5 (27 FOVs held out for test) +# +# The three sub-DMs keep their native channel names; CombinedDataModule +# (MAX_SIZE_CYCLE) pulls one sub-batch from each per step so every +# microscope contributes equally to each gradient step regardless of +# dataset size. +# +# Run D3 preprocess first to populate normalization_metadata: +# uv run viscy preprocess --data_path \ +# --channel_names+ "Phase3D" \ +# --channel_names+ "raw mCherry EX561 EM600-37" \ +# --channel_names+ "raw Cy5 EX639 EM698-70" +# +# Usage: +# uv run python -m cytoland fit --config vscyto3d/finetune_a549_infected.yml +base: + - ../recipes/trainer/fit.yml + - ../recipes/topology/ddp_4gpu.yml + +# The published VSCyto3D is FullyConvolutionalMAE (architecture='fcmae') in +# supervised mode (pretraining=False), not UNeXt2 despite the dataset/model +# card nickname. VSUNet(architecture='fcmae') lets us warm-start from the +# published ckpt without the extra FCMAE pretraining validators on +# FcmaeUNet (which require a GPUTransformDataModule that HCSDataModule +# does not subclass). +model: + class_path: cytoland.engine.VSUNet + init_args: + architecture: fcmae + model_config: + in_channels: 1 + out_channels: 2 + encoder_blocks: [3, 3, 9, 3] + encoder_drop_path_rate: 0.1 + dims: [96, 192, 384, 768] + decoder_conv_blocks: 2 + stem_kernel_size: [5, 4, 4] + in_stack_depth: 15 + pretraining: false + loss_function: + class_path: viscy_utils.losses.MixedLoss + init_args: + l1_alpha: 0.5 + l2_alpha: 0.0 + ms_dssim_alpha: 0.5 + # lr=2e-4 matches the canonical vs_test/finetune_3d.py recipe that + # produced the published VSCyto3D ckpt we warm-start from. + # (Dynacell's fcmae_vscyto3d_fit.yml uses lr=4e-4 as a retune vs. a + # UNeXt2 throughput baseline — not the right anchor for this finetune.) + lr: 0.0002 + schedule: WarmupCosine + # D3 drives step count in MAX_SIZE_CYCLE: ~ (243-27) * 0.8 * 11 T / batch_size + # ~= 120 steps/epoch at batch_size=16 across 4 GPUs. One-epoch warmup. + warmup_steps: 120 + warmup_multiplier: 1e-3 + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/fcmae-cyto3d-sensor/vscyto3d-logs/hek-a549-ipsc-finetune/checkpoints/epoch=83-step=14532-loss=0.492.ckpt + +trainer: + # FullyConvolutionalMAE(pretraining=False) has decoder/head params that + # only receive gradients on some forward paths; default ddp with + # find_unused_parameters=False errors at step 1. Matches dynacell + # fcmae_vscyto3d_fit.yml and vs_test/finetune_3d.py:215. + strategy: ddp_find_unused_parameters_true + # bf16-mixed avoids the Hopper fp16 cuDNN slowdown documented in + # applications/dynacell/configs/examples/fcmae_hopper_slowdown.md. + precision: bf16-mixed + # Matches the canonical vs_test finetune budget that produced the + # published VSCyto3D ckpt (stopped at epoch 83 on val loss). + max_epochs: 100 + logger: + init_args: + project: cytoland + name: VSCyto3D_ft_A549_infected + save_dir: /hpc/projects/comp.micro/virtual_staining/models/cytoland/a549_infected/vscyto3d + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/cytoland/a549_infected/vscyto3d/checkpoints + +data: + class_path: viscy_data.combined.CombinedDataModule + init_args: + train_mode: MAX_SIZE_CYCLE + val_mode: SEQUENTIAL + data_modules: + # D1 — Hummingbird 2026-01-29 + - class_path: viscy_data.hcs.HCSDataModule + init_args: + data_path: /hpc/projects/virtual_staining/training/a549/2026_05_infected_cell/2026_01_29_A549_H2B_CAAX_DAPI_DENV_ZIKV.zarr + source_channel: Phase3D + target_channel: [DAPI_Density3D, TXR_Density3D] + z_window_size: 20 + split_ratio: 0.8 + batch_size: 16 + num_workers: 8 + persistent_workers: true + mmap_preload: true + yx_patch_size: [384, 384] + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, DAPI_Density3D, TXR_Density3D] + w_key: DAPI_Density3D + spatial_size: [20, 600, 600] + num_samples: 4 + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [DAPI_Density3D, TXR_Density3D] + level: timepoint_statistics + subtrahend: median + divisor: iqr + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + + # D2 — Hummingbird 2026-03-10 + - class_path: viscy_data.hcs.HCSDataModule + init_args: + data_path: /hpc/projects/virtual_staining/training/a549/2026_05_infected_cell/2026_03_10_A549_H2B_CAXX_DAPI_DENV_ZIKV.zarr + source_channel: Phase3D + target_channel: [DAPI_Density3D, TXR_Density3D] + z_window_size: 20 + split_ratio: 0.8 + batch_size: 16 + num_workers: 8 + persistent_workers: true + mmap_preload: true + yx_patch_size: [384, 384] + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, DAPI_Density3D, TXR_Density3D] + w_key: DAPI_Density3D + spatial_size: [20, 600, 600] + num_samples: 4 + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [DAPI_Density3D, TXR_Density3D] + level: timepoint_statistics + subtrahend: median + divisor: iqr + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + + # D3 — Mantis 2026-03-26 (27 FOVs held out for test) + - class_path: viscy_data.hcs.HCSDataModule + init_args: + data_path: /hpc/projects/virtual_staining/training/a549/2026_05_infected_cell/2026_03_26_A549_CAAX_H2B_DENV_ZIKV.zarr + source_channel: Phase3D + target_channel: + - "raw mCherry EX561 EM600-37" + - "raw Cy5 EX639 EM698-70" + z_window_size: 20 + split_ratio: 0.8 + batch_size: 16 + num_workers: 8 + persistent_workers: true + mmap_preload: true + yx_patch_size: [384, 384] + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: + - Phase3D + - "raw mCherry EX561 EM600-37" + - "raw Cy5 EX639 EM698-70" + w_key: "raw mCherry EX561 EM600-37" + spatial_size: [20, 600, 600] + num_samples: 4 + exclude_fov_names: + - B/2/000000 + - B/2/000001 + - B/2/000002 + - B/2/000003 + - B/2/000004 + - B/2/000005 + - B/2/000006 + - B/2/000007 + - B/2/000008 + - B/3/000004 + - B/3/000005 + - B/3/000006 + - B/3/000007 + - B/3/000008 + - B/3/001000 + - B/3/001001 + - B/3/001002 + - B/3/001003 + - B/4/000000 + - B/4/000001 + - B/4/000002 + - B/4/000003 + - B/4/000006 + - B/4/000007 + - B/4/000008 + - B/4/001000 + - B/4/002000 + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: + - "raw mCherry EX561 EM600-37" + - "raw Cy5 EX639 EM698-70" + level: timepoint_statistics + subtrahend: median + divisor: iqr + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] diff --git a/applications/cytoland/examples/configs/vscyto3d/finetune_a549_infected_4gpu_batched.yml b/applications/cytoland/examples/configs/vscyto3d/finetune_a549_infected_4gpu_batched.yml new file mode 100644 index 000000000..d3efd9f82 --- /dev/null +++ b/applications/cytoland/examples/configs/vscyto3d/finetune_a549_infected_4gpu_batched.yml @@ -0,0 +1,237 @@ +# Production 4-GPU finetune of VSCyto3D on A549 infected-cell data. +# Mirrors the dynacell joint training pattern (see +# applications/dynacell/configs/benchmarks/virtual_staining/nucleus/ +# fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/train.yml). +# +# Architecture: BatchedConcatDataModule pools D1+D2+D3 cropped zarrs +# (~323 GB total) into a single shuffled dataset with one +# ShardedDistributedSampler. Per dynacell joint convention, batch_size is +# NOT divided by num_samples in joint mode — bs=16 indices × num_samples=4 +# = 64 patches/step/rank × 4 ranks = 256 patches/step total. +# +# Backend: zarr-python with mmap_preload to /tmp. Stages all FOVs to a +# MemoryMappedTensor on local /tmp (28 TB) during prepare_data, then +# closes the zarr handles. DataLoader workers fork from a parent with no +# live zarr asyncio loop and read from the mmap'd tensor — fork-safe. +# /dev/shm (~126 GB) is too small even for the cropped 323 GB dataset. +# +# Cropped zarrs (zarrv3_cropped/) shrink each FOV from +# (T, 3ch, Z=126, 2048, 2048) full-tile down to (T, 3ch, Z=50, ~1500-2048, +# ~1300-2048) so total staging fits well under /tmp. +# +# Standalone (does NOT inherit finetune_a549_infected.yml) because the +# parent's data: block authors a CombinedDataModule with +# train_mode/val_mode init_args that BatchedConcatDataModule rejects. +base: + - ../recipes/trainer/fit.yml + - ../recipes/topology/ddp_4gpu.yml + +model: + class_path: cytoland.engine.VSUNet + init_args: + architecture: fcmae + model_config: + in_channels: 1 + out_channels: 2 + encoder_blocks: [3, 3, 9, 3] + encoder_drop_path_rate: 0.1 + dims: [96, 192, 384, 768] + decoder_conv_blocks: 2 + stem_kernel_size: [5, 4, 4] + in_stack_depth: 15 + pretraining: false + loss_function: + class_path: viscy_utils.losses.MixedLoss + init_args: + l1_alpha: 0.5 + l2_alpha: 0.0 + ms_dssim_alpha: 0.5 + # Smaller lr than the published VSCyto3D recipe because we're + # finetuning from a strong ckpt onto a smaller dataset (~50 FOVs) + # for a focused domain shift (A549 infected-cell phenotype). + lr: 2.0e-5 + schedule: WarmupCosine + # ~50 train FOVs × ~5 T (avg) × (50-19)=31 Z windows = ~7700 patches + # per epoch dataset-wide. At 256 patches/step (joint, 4 ranks) → + # ~30 steps/epoch. ~1 epoch warmup = 30. + warmup_steps: 30 + warmup_multiplier: 1e-3 + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/fcmae-cyto3d-sensor/vscyto3d-logs/hek-a549-ipsc-finetune/checkpoints/epoch=83-step=14532-loss=0.492.ckpt + +trainer: + strategy: ddp_find_unused_parameters_true + precision: bf16-mixed + # Finetune budget per user spec: 30 epochs is plenty for adapting from + # a converged ckpt onto this small domain-shifted set. + max_epochs: 30 + logger: + init_args: + project: cytoland + name: VSCyto3D_ft_A549_infected_4gpu_batched + save_dir: /hpc/mydata/eduardo.hirata/cytoland/a549_infected_4gpu_batched + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/mydata/eduardo.hirata/cytoland/a549_infected_4gpu_batched/checkpoints + +# Shared HCS init args — mirrors dynacell joint config defaults. +_hcs_init_args: &hcs_init_args + z_window_size: 20 + split_ratio: 0.8 + # Joint mode: batch_size is NOT divided by num_samples (see + # BatchedConcatDataModule.train_dataloader). 16 indices × num_samples=4 + # = 64 patches/step/rank × 4 ranks = 256 patches/step. + batch_size: 16 + # OOM-tuned for the cropped 459 GB mmap working set: each worker pulls + # full-FOV slabs (~660 MB read per index) before cropping. 16 indices + # × num_workers × prefetch_factor batches in-flight = the binding + # constraint, not the mmap virtual size. Halving each cuts ~4× per + # rank. + num_workers: 2 + prefetch_factor: 1 + persistent_workers: true + mmap_preload: true + scratch_dir: /tmp + pin_memory: false + yx_patch_size: [384, 384] + +# CPU-side weighted crop — dynacell pattern. RandWeightedCropd with +# num_samples=4 yields 4 patches per stack, all weighted by the nuclear +# marker channel. Runs in DataLoader workers (fork-safe because mmap_ +# preload closed zarr handles before fork). +_d1_d2_normalizations: &d1_d2_normalizations + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [DAPI_Density3D, TXR_Density3D] + level: timepoint_statistics + subtrahend: median + divisor: iqr + +_d1_d2_augmentations: &d1_d2_augmentations + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, DAPI_Density3D, TXR_Density3D] + w_key: DAPI_Density3D + spatial_size: [20, 600, 600] + num_samples: 4 + +_gpu_augmentations: &gpu_augmentations + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + +_val_gpu_augmentations: &val_gpu_augmentations + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + +data: + class_path: viscy_data.combined.BatchedConcatDataModule + init_args: + data_modules: + # D1 — Hummingbird 2026-01-29 (cropped: 15 FOVs, T=3, Z=50, 2048×2048) + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/a549/2026_05_infected_cell/zarrv3_cropped/2026_01_29_A549_H2B_CAAX_DAPI_DENV_ZIKV.zarr + source_channel: Phase3D + target_channel: [DAPI_Density3D, TXR_Density3D] + normalizations: *d1_d2_normalizations + augmentations: *d1_d2_augmentations + gpu_augmentations: *gpu_augmentations + val_gpu_augmentations: *val_gpu_augmentations + + # D2 — Hummingbird 2026-03-10 (cropped: 15 FOVs, T=3, Z=50, 2025×1998) + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/a549/2026_05_infected_cell/zarrv3_cropped/2026_03_10_A549_H2B_CAXX_DAPI_DENV_ZIKV.zarr + source_channel: Phase3D + target_channel: [DAPI_Density3D, TXR_Density3D] + normalizations: *d1_d2_normalizations + augmentations: *d1_d2_augmentations + gpu_augmentations: *gpu_augmentations + val_gpu_augmentations: *val_gpu_augmentations + + # D3 — Mantis 2026-03-26 (cropped: 17 FOVs, T=11, Z=50, 1600×1332). + # 27 FOVs held out for test on the un-cropped store. + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/a549/2026_05_infected_cell/zarrv3_cropped/2026_03_26_A549_CAAX_H2B_DENV_ZIKV.zarr + source_channel: Phase3D + target_channel: + - "raw Cy5 EX639 EM698-70" + - "raw mCherry EX561 EM600-37" + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: + - "raw mCherry EX561 EM600-37" + - "raw Cy5 EX639 EM698-70" + level: timepoint_statistics + subtrahend: median + divisor: iqr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: + - Phase3D + - "raw mCherry EX561 EM600-37" + - "raw Cy5 EX639 EM698-70" + w_key: "raw Cy5 EX639 EM698-70" # H2B nuclear marker; mCherry is membrane on D3 + spatial_size: [20, 600, 600] + num_samples: 4 + gpu_augmentations: *gpu_augmentations + val_gpu_augmentations: *val_gpu_augmentations diff --git a/applications/cytoland/examples/configs/vscyto3d/finetune_a549_infected_d2_smoke.yml b/applications/cytoland/examples/configs/vscyto3d/finetune_a549_infected_d2_smoke.yml new file mode 100644 index 000000000..ca7c5b620 --- /dev/null +++ b/applications/cytoland/examples/configs/vscyto3d/finetune_a549_infected_d2_smoke.yml @@ -0,0 +1,61 @@ +# Smoke-test leaf: warm-start VSCyto3D on D2 alone (Hummingbird 2026-03-10) +# — single HCSDataModule, tiny batch, limited steps. Purpose: verify model +# load / forward / backward / val on the simplest subset of the full +# finetune_a549_infected.yml stack before scaling up. +# +# Model + the D2 recipe's gpu_augmentations mirror the dynacell +# FCMAE-VSCyto3D warm-start recipe +# (applications/dynacell/configs/benchmarks/.../fcmae_vscyto3d_fit.yml) +# so this smoke exercises the same code paths the main leaf runs at scale. +# +# Usage: +# uv run python -m cytoland fit --config vscyto3d/finetune_a549_infected_d2_smoke.yml +base: + - ../recipes/trainer/fit.yml + - ../recipes/data/hcs_a549_infected_d2_hummingbird.yml + +model: + class_path: cytoland.engine.VSUNet + init_args: + architecture: fcmae + model_config: + in_channels: 1 + out_channels: 2 + encoder_blocks: [3, 3, 9, 3] + encoder_drop_path_rate: 0.1 + dims: [96, 192, 384, 768] + decoder_conv_blocks: 2 + stem_kernel_size: [5, 4, 4] + in_stack_depth: 15 + pretraining: false + loss_function: + class_path: viscy_utils.losses.MixedLoss + init_args: + l1_alpha: 0.5 + l2_alpha: 0.0 + ms_dssim_alpha: 0.5 + lr: 0.0002 + schedule: Constant + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/fcmae-cyto3d-sensor/vscyto3d-logs/hek-a549-ipsc-finetune/checkpoints/epoch=83-step=14532-loss=0.492.ckpt + +trainer: + accelerator: gpu + strategy: auto + devices: 1 + precision: bf16-mixed + max_epochs: 1 + limit_train_batches: 2 + limit_val_batches: 2 + num_sanity_val_steps: 0 + logger: null + callbacks: [] + +data: + init_args: + # batch_size must be divisible by augmentations.RandWeightedCropd.num_samples (4). + batch_size: 4 + num_workers: 2 + # Disable mmap_preload for the smoke — building the full D2 cache would + # dominate smoke walltime. Prod leaves keep mmap_preload: true. + mmap_preload: false + persistent_workers: false diff --git a/applications/cytoland/examples/configs/vscyto3d/predict.yml b/applications/cytoland/examples/configs/vscyto3d/predict.yml new file mode 100644 index 000000000..7728eb18a --- /dev/null +++ b/applications/cytoland/examples/configs/vscyto3d/predict.yml @@ -0,0 +1,20 @@ +# VSCyto3D: inference with published checkpoint. +# Checkpoint: https://public.czbiohub.org/comp.micro/viscy/VS_models/VSCyto3D/epoch=48-step=18130.ckpt +# Usage: python -m cytoland predict --config vscyto3d/predict.yml +base: + - ../recipes/trainer/predict.yml + - ../recipes/topology/single_gpu.yml + - ../recipes/data/hcs_nuc_mem_3d.yml + - ../recipes/models/unext2_3d.yml + +data: + init_args: + data_path: #TODO input OME-Zarr + batch_size: 2 + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: median + divisor: iqr diff --git a/applications/cytoland/examples/configs/vscyto3d/preprocess_a549_infected_d3.sh b/applications/cytoland/examples/configs/vscyto3d/preprocess_a549_infected_d3.sh new file mode 100755 index 000000000..4018dd3be --- /dev/null +++ b/applications/cytoland/examples/configs/vscyto3d/preprocess_a549_infected_d3.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# One-shot preprocess for the A549 Mantis infection dataset (D3). +# +# Writes per-FOV and per-timepoint normalization statistics into the +# zarr's .zattrs for the three channels actually used by the VSCyto3D +# finetune (Phase3D source + raw mCherry / raw Cy5 targets). D1 and D2 +# already have normalization_metadata and do not need preprocessing. +# +# Usage: +# bash applications/cytoland/examples/configs/vscyto3d/preprocess_a549_infected_d3.sh +set -euo pipefail + +REPO_ROOT="$(git -C "$(dirname "${BASH_SOURCE[0]}")" rev-parse --show-toplevel)" +cd "$REPO_ROOT" +mkdir -p .tmp/preprocess_logs + +uv run viscy preprocess \ + --data_path /hpc/projects/virtual_staining/training/a549/2026_05_infected_cell/2026_03_26_A549_CAAX_H2B_DENV_ZIKV.zarr \ + --channel_names+ "Phase3D" \ + --channel_names+ "raw mCherry EX561 EM600-37" \ + --channel_names+ "raw Cy5 EX639 EM698-70" \ + --num_workers 16 \ + 2>&1 | tee .tmp/preprocess_logs/d3_preprocess.log diff --git a/applications/cytoland/examples/configs/vscyto3d/pretrain.yml b/applications/cytoland/examples/configs/vscyto3d/pretrain.yml new file mode 100644 index 000000000..18e673362 --- /dev/null +++ b/applications/cytoland/examples/configs/vscyto3d/pretrain.yml @@ -0,0 +1,42 @@ +# VSCyto3D: FCMAE self-supervised pretraining. +# Usage: python -m cytoland fit --config vscyto3d/pretrain.yml +base: + - ../recipes/trainer/fit.yml + - ../recipes/topology/ddp_4gpu.yml + - ../recipes/data/cached_pretrain.yml + +model: + class_path: cytoland.engine.FcmaeUNet + init_args: + fit_mask_ratio: 0.5 + model_config: + in_channels: 1 + out_channels: 1 + encoder_blocks: [3, 3, 9, 3] + dims: [96, 192, 384, 768] + decoder_conv_blocks: 1 + stem_kernel_size: [5, 4, 4] + in_stack_depth: 5 + loss_function: + class_path: cytoland.engine.MaskedMSELoss + lr: 0.0002 + schedule: WarmupCosine + log_batches_per_epoch: 3 + log_samples_per_batch: 1 + +trainer: + # FCMAE pretraining requires find_unused_parameters=True (masked decoder). + strategy: ddp_find_unused_parameters_true + precision: 16-mixed + max_epochs: 400 + use_distributed_sampler: false + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/val + every_n_epochs: 1 + save_top_k: 5 + save_last: true diff --git a/applications/cytoland/examples/configs/vscyto3d/run_a549_4gpu_batched.slurm b/applications/cytoland/examples/configs/vscyto3d/run_a549_4gpu_batched.slurm new file mode 100644 index 000000000..97f3a1398 --- /dev/null +++ b/applications/cytoland/examples/configs/vscyto3d/run_a549_4gpu_batched.slurm @@ -0,0 +1,47 @@ +#!/bin/bash +# 4-GPU production training for VSCyto3D A549 infected-cell finetune. +# Uses cropped zarrs (~323 GB total) so mmap_preload stages to /tmp +# without OOM. Mirrors dynacell joint training pattern. +# +# sbatch applications/cytoland/examples/configs/vscyto3d/run_a549_4gpu_batched.slurm +# +# Architecture: BatchedConcatDataModule + zarr-python + mmap_preload + fork. +# - mmap_preload stages cropped FOVs to /tmp (~323 GB; node /tmp is 28 TB) +# - During training, DataLoader workers (fork) read from MemoryMappedTensor +# instead of zarr — no fork-after-asyncio issue. + +#SBATCH --job-name=VSCyto3D_A549_4gpu_batched +#SBATCH --time=22:00:00 +#SBATCH --nodes=1 +#SBATCH --ntasks-per-node=4 +#SBATCH --partition=gpu +#SBATCH --cpus-per-task=8 +#SBATCH --gpus=4 +#SBATCH --mem=1024G +#SBATCH --constraint='h200|h100' +#SBATCH --output=/home/eduardo.hirata/repos/viscy/slurm_logs/cytoland_4gpu_batched/run_%j.out +#SBATCH --error=/home/eduardo.hirata/repos/viscy/slurm_logs/cytoland_4gpu_batched/run_%j.err + +set -euo pipefail + +mkdir -p /home/eduardo.hirata/repos/viscy/slurm_logs/cytoland_4gpu_batched +mkdir -p /hpc/mydata/eduardo.hirata/cytoland/a549_infected_4gpu_batched/checkpoints + +ml uv + +export PYTHONUNBUFFERED=1 +export PYTHONNOUSERSITE=1 +# Limit threading libs so DataLoader workers don't oversubscribe the +# 8-core/rank allocation. +export OMP_NUM_THREADS=1 +export MKL_NUM_THREADS=1 +export NUMEXPR_NUM_THREADS=1 + +REPO=/hpc/mydata/eduardo.hirata/repos/viscy +cd "${REPO}" + +nvidia-smi +df -h /tmp + +srun uv run python -m cytoland fit \ + --config applications/cytoland/examples/configs/vscyto3d/finetune_a549_infected_4gpu_batched.yml diff --git a/applications/cytoland/examples/configs/vscyto3d/run_a549_infected.slurm b/applications/cytoland/examples/configs/vscyto3d/run_a549_infected.slurm new file mode 100755 index 000000000..bbc43182b --- /dev/null +++ b/applications/cytoland/examples/configs/vscyto3d/run_a549_infected.slurm @@ -0,0 +1,27 @@ +#!/bin/bash +# Full finetune: VSCyto3D warm-started on A549 infection data from three +# microscopes (D1/D2/D3). Run from repo root: sbatch applications/cytoland/examples/configs/vscyto3d/run_a549_infected.slurm + +#SBATCH --job-name=VSCyto3D_A549_infected +#SBATCH --time=5-00:00:00 +#SBATCH --nodes=1 +#SBATCH --ntasks-per-node=4 +#SBATCH --partition=gpu +#SBATCH --cpus-per-task=16 +#SBATCH --gpus=4 +#SBATCH --mem=1024G +# Need >=80 GB VRAM so the 3-DM cycled batch (16x4x3 = 192 crops/step/GPU) +# fits with bf16 + 35M-param FCMAE. Excludes 40 GB A100 / 48 GB A40/A6000. +#SBATCH --constraint='h200|h100_80|a100_80' +#SBATCH --output=/hpc/projects/comp.micro/virtual_staining/models/cytoland/a549_infected/vscyto3d/slurm/%j.out +#SBATCH --error=/hpc/projects/comp.micro/virtual_staining/models/cytoland/a549_infected/vscyto3d/slurm/%j.err + +mkdir -p -m 775 /hpc/projects/comp.micro/virtual_staining/models/cytoland/a549_infected/vscyto3d/slurm +mkdir -p -m 775 /hpc/projects/comp.micro/virtual_staining/models/cytoland/a549_infected/vscyto3d/checkpoints + +ml uv + +export PYTHONUNBUFFERED=1 + +nvidia-smi +srun uv run python -m cytoland fit --config applications/cytoland/examples/configs/vscyto3d/finetune_a549_infected.yml diff --git a/applications/cytoland/examples/configs/vscyto3d/train_spotlight.yml b/applications/cytoland/examples/configs/vscyto3d/train_spotlight.yml new file mode 100644 index 000000000..a5cbdd25c --- /dev/null +++ b/applications/cytoland/examples/configs/vscyto3d/train_spotlight.yml @@ -0,0 +1,22 @@ +# VSCyto3D: supervised training with Spotlight foreground-aware loss. +# Requires: viscy preprocess --compute_otsu --compute_fg_masks +# Usage: python -m cytoland fit --config vscyto3d/train_spotlight.yml +base: + - ../recipes/trainer/fit.yml + - ../recipes/topology/ddp_4gpu.yml + - ../recipes/data/hcs_nuc_mem_3d.yml + - ../recipes/modes/spotlight.yml + - ../recipes/models/unext2_3d.yml + +model: + init_args: + lr: 0.0002 + schedule: WarmupCosine + +trainer: + precision: 16-mixed + max_epochs: 200 + +data: + init_args: + data_path: #TODO HCS OME-Zarr data diff --git a/applications/cytoland/examples/configs/vsneuromast/fit.yml b/applications/cytoland/examples/configs/vsneuromast/fit.yml new file mode 100644 index 000000000..371c61904 --- /dev/null +++ b/applications/cytoland/examples/configs/vsneuromast/fit.yml @@ -0,0 +1,26 @@ +# VSNeuromast: supervised training from scratch (no pretraining). +# Usage: python -m cytoland fit --config vsneuromast/fit.yml +base: + - ../recipes/trainer/fit.yml + - ../recipes/topology/ddp_4gpu.yml + - ../recipes/data/hcs_nuc_mem_neuromast.yml + - ../recipes/models/unext2_neuromast.yml + +model: + init_args: + loss_function: + class_path: viscy_utils.losses.MixedLoss + init_args: + l1_alpha: 0.5 + l2_alpha: 0.0 + ms_dssim_alpha: 0.5 + lr: 0.001 + schedule: Constant + +trainer: + precision: 16-mixed + max_epochs: 200 + +data: + init_args: + data_path: #TODO HCS OME-Zarr data diff --git a/applications/cytoland/examples/configs/vsneuromast/predict.yml b/applications/cytoland/examples/configs/vsneuromast/predict.yml new file mode 100644 index 000000000..2f56a67e9 --- /dev/null +++ b/applications/cytoland/examples/configs/vsneuromast/predict.yml @@ -0,0 +1,19 @@ +# VSNeuromast: inference with published checkpoint. +# Checkpoint: https://public.czbiohub.org/comp.micro/viscy/VS_models/VSNeuromast/epoch=64-step=24960.ckpt +# Usage: python -m cytoland predict --config vsneuromast/predict.yml +base: + - ../recipes/trainer/predict.yml + - ../recipes/topology/single_gpu.yml + - ../recipes/data/hcs_nuc_mem_neuromast.yml + - ../recipes/models/unext2_neuromast.yml + +data: + init_args: + data_path: #TODO input OME-Zarr + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: median + divisor: iqr diff --git a/applications/cytoland/examples/dl-course-exercise/README.md b/applications/cytoland/examples/dl-course-exercise/README.md new file mode 100644 index 000000000..f48a2c31d --- /dev/null +++ b/applications/cytoland/examples/dl-course-exercise/README.md @@ -0,0 +1,136 @@ +# Exercise 6: Image translation - Part 1 + +This demo script was developed for the DL@MBL 2024 course by Eduardo Hirata-Miyasaki, Ziwen Liu and Shalin Mehta, with many inputs and bugfixes by [Morgan Schwartz](https://github.com/msschwartz21), [Caroline Malin-Mayor](https://github.com/cmalinmayor), and [Peter Park](https://github.com/peterhpark). + + +# Image translation (Virtual Staining) + +Written by Eduardo Hirata-Miyasaki, Ziwen Liu, and Shalin Mehta, CZ Biohub San Francisco. + +## Overview + +In this exercise, we will predict fluorescence images of nuclei and plasma membrane markers from quantitative phase images of cells, i.e., we will _virtually stain_ the nuclei and plasma membrane visible in the phase image. +This is an example of an image translation task. We will apply spatial and intensity augmentations to train robust models and evaluate their performance. Finally, we will explore the opposite process of predicting a phase image from a fluorescence membrane label. + +[![HEK293T](https://raw.githubusercontent.com/mehta-lab/VisCy/main/docs/figures/svideo_1.png)](https://github.com/mehta-lab/VisCy/assets/67518483/d53a81eb-eb37-44f3-b522-8bd7bddc7755) +(Click on image to play video) + +## Goals + +### Part 1: Learn to use iohub (I/O library), VisCy dataloaders, and TensorBoard. + + - Use an OME-Zarr dataset of 34 FOVs of adenocarcinomic human alveolar basal epithelial cells (A549), + each FOV has 3 channels (phase, nuclei, and cell membrane). + The nuclei were stained with DAPI and the cell membrane with Cellmask. + - Explore OME-Zarr using [iohub](https://czbiohub-sf.github.io/iohub/main/index.html) + and the high-content-screen (HCS) format. + - Use [MONAI](https://monai.io/) to implement data augmentations. + +### Part 2: Train and evaluate the model to translate phase into fluorescence, and vice versa. + - Train a 2D UNeXt2 model to predict nuclei and membrane from phase images. + - Compare the performance of the trained model and a pre-trained model. + - Evaluate the model using pixel-level and instance-level metrics. + + +Checkout [VisCy](https://github.com/mehta-lab/VisCy/tree/main/examples/demos), +our deep learning pipeline for training and deploying computer vision models +for image-based phenotyping including the robust virtual staining of landmark organelles. +VisCy exploits recent advances in data and metadata formats +([OME-zarr](https://www.nature.com/articles/s41592-021-01326-w)) and DL frameworks, +[PyTorch Lightning](https://lightning.ai/) and [MONAI](https://monai.io/). + +## Setup + +There are two setup scripts depending on your role: + +- **Students:** run [`setup_student.sh`](setup_student.sh) — creates a per-user + Python venv, registers a Jupyter kernel, and downloads the data only if it + isn't already on disk. +- **TAs / course operators:** run [`setup_TA.sh`](setup_TA.sh) before the + course to pre-stage the ~14 GB of data + checkpoint onto a shared + filesystem so each student doesn't have to re-download it. + +### Student + +From the exercise folder: + +```bash +cd applications/cytoland/examples/dl-course-exercise +bash setup_student.sh +``` + +If your TA pre-staged the data on a shared mount, point `DATA_ROOT` at it to +skip the download: + +```bash +DATA_ROOT=/mnt/shared/image_translation bash setup_student.sh +``` + +The script will: + +- Install [`uv`](https://docs.astral.sh/uv/) if it isn't already on your PATH. +- Create a Python 3.13 virtual environment at `./.venv`. +- Install `cytoland` + `viscy` (`>=0.5.0a0`) plus the tutorial extras: + `cellpose`, `torchview`, `microssim`, `jupyter`, `ipykernel`, + `ipywidgets`, `jupytext`. If you ran the script from inside a clone of + the [VisCy monorepo](https://github.com/mehta-lab/VisCy), it installs + `cytoland` editable from the local workspace; otherwise it installs from + PyPI. +- Register the venv as a Jupyter kernel named **`06_image_translation`** + (display name: *Python (06_image_translation)*). +- Download the training / test OME-Zarr datasets and the VSCyto2D + pretrained checkpoint into `$DATA_ROOT` (default `~/data/06_image_translation/`), + unless the data is already present. + +Everything is self-contained inside this folder — no conda required. + +### TA / course operator + +Run once before the course, ideally targeting a shared mount: + +```bash +cd applications/cytoland/examples/dl-course-exercise +DATA_ROOT=/mnt/shared/image_translation bash setup_TA.sh +``` + +This downloads the OME-Zarr datasets (~14 GB) and the pretrained checkpoint +into `$DATA_ROOT`. Typical runtime is 20–40 min. It does **not** create a +Python environment — students do that themselves with `setup_student.sh`. + +## Run the exercise + +The recommended workflow is to generate the notebook from `solution.py` and +run it in Jupyter — the notebook strips the `tags=["task"]` placeholder cells +so it executes top-to-bottom without `NameError`s. + +### Generate and launch the notebook + +```bash +./.venv/bin/jupytext --to ipynb solution.py +./.venv/bin/jupyter notebook solution.ipynb +``` + +Pick **Python (06_image_translation)** as the kernel. + +### (Advanced) Run `solution.py` directly in VSCode + +If you prefer to step through the raw script in VSCode with the Python + +Jupyter extensions, open [`solution.py`](solution.py) and pick the +**Python (06_image_translation)** kernel from the top-right selector. The +script uses [cell mode](https://code.visualstudio.com/docs/python/jupyter-support-py). +Do **not** run the file strictly top-to-bottom — **skip any cell tagged +`tags=["task"]`** (they contain `TODO` / `...` placeholders that raise +`NameError`). The generated `solution.ipynb` does this stripping for you. + +If the kernel is missing (e.g. you reinstalled the venv), re-register it: + +```bash +./.venv/bin/python -m ipykernel install --user \ + --name 06_image_translation \ + --display-name "Python (06_image_translation)" +``` + +### References + +- [Liu, Z. and Hirata-Miyasaki, E. et al. (2024) Robust Virtual Staining of Cellular Landmarks](https://www.biorxiv.org/content/10.1101/2024.05.31.596901v2.full.pdf) +- [Guo et al. (2020) Revealing architectural order with quantitative label-free imaging and deep learning. eLife](https://elifesciences.org/articles/55502) diff --git a/examples/virtual_staining/dlmbl_exercise/prepare-exercise.sh b/applications/cytoland/examples/dl-course-exercise/prepare-exercise.sh similarity index 100% rename from examples/virtual_staining/dlmbl_exercise/prepare-exercise.sh rename to applications/cytoland/examples/dl-course-exercise/prepare-exercise.sh diff --git a/applications/cytoland/examples/dl-course-exercise/setup_TA.sh b/applications/cytoland/examples/dl-course-exercise/setup_TA.sh new file mode 100644 index 000000000..3cb23d380 --- /dev/null +++ b/applications/cytoland/examples/dl-course-exercise/setup_TA.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env -S bash -i +# +# Image-translation exercise — TA / course-operator setup. +# +# Pre-stage the OME-Zarr datasets and pretrained VSCyto2D checkpoint onto a +# shared filesystem BEFORE the course starts so each student doesn't have to +# re-download ~14 GB. This typically takes 20–40 min depending on link speed +# and storage backend. +# +# Usage: +# +# # Default: stage to ~/data/06_image_translation/ +# bash setup_TA.sh +# +# # Stage to a shared mount (recommended for courses): +# DATA_ROOT=/mnt/efs/image_translation bash setup_TA.sh +# +# Once this finishes, students point setup_student.sh at the same DATA_ROOT +# and skip the download: +# +# DATA_ROOT=/mnt/efs/image_translation bash setup_student.sh +# +# This script does NOT create a Python environment. Run setup_student.sh for +# that (it can be run before, after, or instead of this script). + +set -euo pipefail + +START_DIR=$(pwd) +KERNEL_NAME="${KERNEL_NAME:-06_image_translation}" +DATA_ROOT="${DATA_ROOT:-$HOME/data/$KERNEL_NAME}" + +mkdir -p "$DATA_ROOT/training" "$DATA_ROOT/test" "$DATA_ROOT/pretrained_models" + +echo "Staging data + checkpoint into $DATA_ROOT ..." +echo "(this typically takes 20-40 min)" + +cd "$DATA_ROOT/training" +wget -m -np -nH --cut-dirs=6 -R "index.html*" "https://public.czbiohub.org/comp.micro/viscy/VS_datasets/VSCyto2D/training/zarrv3/a549_hoechst_cellmask_train_val.zarr/" + +cd "$DATA_ROOT/test" +wget -m -np -nH --cut-dirs=6 -R "index.html*" "https://public.czbiohub.org/comp.micro/viscy/VS_datasets/VSCyto2D/test/zarrv3/a549_hoechst_cellmask_test.zarr/" + +cd "$DATA_ROOT/pretrained_models" +wget -m -np -nH --cut-dirs=4 -R "index.html*" "https://public.czbiohub.org/comp.micro/viscy/VS_models/VSCyto2D/VSCyto2D/epoch=399-step=23200.ckpt" +# Second checkpoint used in Task 2.5 (fluorescence -> phase reverse model). +wget -m -np -nH --cut-dirs=4 -R "index.html*" "https://public.czbiohub.org/comp.micro/viscy/VS_models/VSCyto2D/AIMBL_Demo/fluor2phase_step668.ckpt" + +cd "$START_DIR" + +cat <=0.5.0a0) plus the tutorial extras: +# cellpose, torchview, microssim, jupyter, ipywidgets, jupytext. +# If run from inside a checkout of the VisCy monorepo, installs +# the local cytoland workspace package in editable mode (pulls +# viscy-data, viscy-models, viscy-transforms, viscy-utils from +# the workspace). Otherwise installs from PyPI. +# 4. Registers the venv as a Jupyter kernel named "06_image_translation" +# so students see it in VSCode / JupyterLab. +# 5. Downloads the training / test OME-Zarr datasets and the VSCyto2D +# pretrained checkpoint into $DATA_ROOT (default ~/data/06_image_translation), +# ONLY IF the data is not already there. If a TA has pre-staged data +# on a shared filesystem, point DATA_ROOT at it to skip the download: +# +# DATA_ROOT=/mnt/shared/image_translation bash setup_student.sh +# +# Run this from the exercise folder: +# cd applications/cytoland/examples/dl-course-exercise +# bash setup_student.sh + +set -euo pipefail + +START_DIR=$(pwd) +KERNEL_NAME="${KERNEL_NAME:-06_image_translation}" +PYTHON_VERSION="${PYTHON_VERSION:-3.13}" + +# --- Detect optional VisCy monorepo root (four levels up from this script) - +# When this exercise lives inside a viscy clone, install cytoland in editable +# mode against the local workspace. Otherwise fall back to PyPI. +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +MONOREPO_ROOT="$(cd "$SCRIPT_DIR/../../../.." 2>/dev/null && pwd || true)" +if [[ -n "${MONOREPO_ROOT:-}" && -f "$MONOREPO_ROOT/pyproject.toml" ]] \ + && grep -q '^name = "viscy"' "$MONOREPO_ROOT/pyproject.toml"; then + INSTALL_MODE="workspace" +else + INSTALL_MODE="pypi" + MONOREPO_ROOT="" +fi +echo "Install mode: $INSTALL_MODE" + +# --- 1. Install uv if missing ---------------------------------------------- +if ! command -v uv >/dev/null 2>&1; then + echo "uv not found — installing to ~/.local/bin ..." + curl -LsSf https://astral.sh/uv/install.sh | sh + # The installer updates shell profiles but not the current shell + export PATH="$HOME/.local/bin:$PATH" +fi +echo "Using uv: $(uv --version)" + +# --- 2. Create a venv under this exercise folder --------------------------- +VENV_DIR="$SCRIPT_DIR/.venv" +uv venv --python "$PYTHON_VERSION" "$VENV_DIR" +PY="$VENV_DIR/bin/python" + +# --- 3. Install cytoland + viscy + tutorial extras ------------------------- +if [[ "$INSTALL_MODE" == "workspace" ]]; then + echo "Installing cytoland (editable) from $MONOREPO_ROOT ..." + uv pip install --python "$PY" -e "$MONOREPO_ROOT/applications/cytoland[metrics]" +else + echo "Installing cytoland + viscy from PyPI (>=0.5.0a0) ..." + uv pip install --python "$PY" --prerelease=allow \ + "viscy>=0.5.0a0" \ + "cytoland[metrics]>=0.5.0a0" +fi +uv pip install --python "$PY" \ + cellpose \ + torchview \ + microssim \ + jupyter \ + ipykernel \ + ipywidgets \ + jupytext \ + nbformat \ + scikit-learn \ + nbconvert + +# --- 4. Register the venv as a Jupyter kernel ------------------------------ +"$PY" -m ipykernel install --user \ + --name "$KERNEL_NAME" \ + --display-name "Python ($KERNEL_NAME)" +echo "Registered Jupyter kernel: $KERNEL_NAME" + +# --- 5. Download data + pretrained checkpoints (skip if already present) ---- +DATA_ROOT="${DATA_ROOT:-$HOME/data/$KERNEL_NAME}" +TRAINING_ZARR="$DATA_ROOT/training/a549_hoechst_cellmask_train_val.zarr" +TEST_ZARR="$DATA_ROOT/test/a549_hoechst_cellmask_test.zarr" +CHECKPOINT="$DATA_ROOT/pretrained_models/VSCyto2D/epoch=399-step=23200.ckpt" +FLUOR2PHASE_CKPT="$DATA_ROOT/pretrained_models/AIMBL_Demo/fluor2phase_step668.ckpt" + +mkdir -p "$DATA_ROOT/training" "$DATA_ROOT/test" "$DATA_ROOT/pretrained_models" + +if [[ -d "$TRAINING_ZARR" && -d "$TEST_ZARR" && -f "$CHECKPOINT" && -f "$FLUOR2PHASE_CKPT" ]]; then + echo "Data already present at $DATA_ROOT — skipping download." +else + echo "Downloading data + checkpoints to $DATA_ROOT ..." + cd "$DATA_ROOT/training" + wget -m -np -nH --cut-dirs=6 -R "index.html*" "https://public.czbiohub.org/comp.micro/viscy/VS_datasets/VSCyto2D/training/zarrv3/a549_hoechst_cellmask_train_val.zarr/" + + cd "$DATA_ROOT/test" + wget -m -np -nH --cut-dirs=6 -R "index.html*" "https://public.czbiohub.org/comp.micro/viscy/VS_datasets/VSCyto2D/test/zarrv3/a549_hoechst_cellmask_test.zarr/" + + cd "$DATA_ROOT/pretrained_models" + wget -m -np -nH --cut-dirs=4 -R "index.html*" "https://public.czbiohub.org/comp.micro/viscy/VS_models/VSCyto2D/VSCyto2D/epoch=399-step=23200.ckpt" + # Second checkpoint used in Task 2.5 (fluorescence -> phase reverse model). + wget -m -np -nH --cut-dirs=4 -R "index.html*" "https://public.czbiohub.org/comp.micro/viscy/VS_models/VSCyto2D/AIMBL_Demo/fluor2phase_step668.ckpt" +fi + +cd "$START_DIR" + +cat < +# The exercise is organized in 3 parts: + +#
    +#
  • Part 1 - Train a virtual staining model using iohub (I/O library), VisCy dataloaders, and tensorboard
  • +#
  • Part 2 - Evaluate the model to translate phase into fluorescence.
  • +#
  • Part 3 - Visualize the image transforms learned by the model and explore the model's regime of validity.
  • +#
+ +# + +# %% [markdown] tags=[] +#
+# Set your python kernel to 06_image_translation +#
+ +# %% [markdown] tags=[] +# ## PyTorch Lightning in one minute +# +# If you've used plain PyTorch you already know the pattern: write a model, write a +# `for batch in dataloader` loop, move tensors to `cuda`, call `loss.backward()`, step +# the optimizer, remember to `zero_grad()`, log every N steps, save a checkpoint, and +# repeat for validation. That boilerplate is the same in every project — so +# [PyTorch Lightning](https://lightning.ai) factors it out into **three objects** and +# owns the training loop for you. +# +# | Lightning object | What it holds | In this exercise | +# | --- | --- | --- | +# | `LightningDataModule` | How to load, split, augment, and batch your data (`train/val/test/predict_dataloader`) | `HCSDataModule` — reads OME-Zarr and yields `{"source": ..., "target": ...}` dicts | +# | `LightningModule` | The network, the loss, and what happens in `training_step` / `validation_step` (one batch at a time) | `VSUNet` — wraps the UNeXt2 architecture and the virtual-staining loss | +# | `Trainer` | The loop: device placement, mixed precision, logging, checkpointing, multi-GPU | `VisCyTrainer` — a thin subclass with VisCy-friendly defaults | +# +# You don't write a `for` loop. You call **`trainer.fit(model, datamodule)`** and +# Lightning drives everything. The trainer handles: +# +# - moving batches to the right device (`accelerator="gpu"`, `devices=[0]`) +# - mixed-precision training (`precision="16-mixed"`) so you use less GPU memory +# - when to log metrics / images (`log_every_n_steps`) and where (`logger=TensorBoardLogger(...)`) +# - saving checkpoints automatically under the logger's directory +# - running a sanity check on a single batch before real training (`fast_dev_run=True`) +# +# VisCy builds on top of Lightning and provides the `HCSDataModule` and `VSUNet` +# classes so you don't have to subclass `LightningDataModule` / `LightningModule` +# yourself — you configure them via constructor arguments and let Lightning run. +# When you see `trainer.fit(...)` below, that single call replaces a ~50-line hand- +# written training loop. + +# %% [markdown] +# # Part 1: Log training data to tensorboard, start training a model. +# --------- +# Learning goals: + +# - Load the OME-zarr dataset and examine the channels (A549). +# - Configure and understand the data loader. +# - Log some patches to tensorboard. +# - Initialize a 2D UNeXt2 model for virtual staining of nuclei and membrane from phase. +# - Start training the model to predict nuclei and membrane from phase. + +# %% Imports +import os +from glob import glob +from pathlib import Path +from typing import Tuple + +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd +import torch +import torchview +import torchvision +from cellpose import models +from iohub import open_ome_zarr +from iohub.reader import print_info +from lightning.pytorch import seed_everything +from lightning.pytorch.loggers import TensorBoardLogger + +# microSSIM: SSIM variant designed for fluorescence microscopy. +from microssim import micro_structural_similarity +from natsort import natsorted +from numpy.typing import ArrayLike + +# pytorch lightning wrapper for Tensorboard. +from skimage.color import label2rgb +from torch.utils.tensorboard import SummaryWriter # for logging to tensorboard +from torchmetrics.functional import accuracy, jaccard_index +from torchmetrics.functional.segmentation import dice_score +from tqdm import tqdm + +# Trainer class and UNet from the cytoland package. +from cytoland.engine import VSUNet + +# HCSDataModule makes it easy to load data during training. +from viscy_data.hcs import HCSDataModule + +# training augmentations +from viscy_transforms import ( + NormalizeSampled, + RandAdjustContrastd, + RandAffined, + RandGaussianNoised, + RandGaussianSmoothd, + RandScaleIntensityd, + RandWeightedCropd, +) +from viscy_utils.evaluation.metrics import mean_average_precision +from viscy_utils.losses import MixedLoss +from viscy_utils.trainer import VisCyTrainer + +# %% +# seed random number generators for reproducibility. +seed_everything(42, workers=True) + +# Paths to data and log directory. +# DATA_ROOT (set by setup_student.sh / setup_TA.sh) points directly at the +# folder containing training/, test/, pretrained_models/. When unset, fall +# back to the default ~/data/06_image_translation layout. +top_dir = Path(os.environ.get("DATA_ROOT", "~/data/06_image_translation")).expanduser() + +# Path to the training data +data_path = top_dir / "training/a549_hoechst_cellmask_train_val.zarr" + +# Path where we will save our training logs +training_top_dir = Path(f"{os.getcwd()}/data/") +# Create top_training_dir directory if needed, and launch tensorboard +training_top_dir.mkdir(parents=True, exist_ok=True) +log_dir = training_top_dir / "06_image_translation/logs/" +# Create log directory if needed, and launch tensorboard +log_dir.mkdir(parents=True, exist_ok=True) + +if not data_path.exists(): + raise FileNotFoundError(f"Data not found at {data_path}. Please check the top_dir and data_path variables.") + +# %% [markdown] tags=[] +# The next cell starts tensorboard. + +#
+# If you launched jupyter lab from ssh terminal, add --host <your-server-name> to the tensorboard command below. <your-server-name> is the address of your compute node that ends in amazonaws.com. + +#
+ + +# %% tags=[] +# Imports and paths +# Function to find an available port +def find_free_port(): + import socket + + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.bind(("", 0)) + return s.getsockname()[1] + + +# Launch TensorBoard on the browser +def launch_tensorboard(log_dir): + import subprocess + + port = find_free_port() + tensorboard_cmd = f"tensorboard --logdir={log_dir} --port={port}" + process = subprocess.Popen(tensorboard_cmd, shell=True) + print( + f"TensorBoard started at http://localhost:{port}. \n" + "If you are using VSCode remote session, forward the port using the PORTS tab next to TERMINAL." + ) + return process + + +# Launch tensorboard and click on the link to view the logs. +tensorboard_process = launch_tensorboard(log_dir) +# %% [markdown] tags = [] +#
+# If you are using VSCode and a remote server, you will need to forward the port to view the tensorboard.
+# Take note of the port number was assigned in the previous cell.(i.e http://localhost:{port_number_assigned})
+ +# Locate the your VSCode terminal and select the Ports tab
+#
    +#
  • Add a new port with the port_number_assigned +#
+# Click on the link to view the tensorboard and it should open in your browser. +#
+ + +# %% [markdown] tags=[] +# ## Load OME-Zarr Dataset +# +# **OME-Zarr** is a chunked, cloud-friendly microscopy format; **HCS layout** +# nests the zarr store like a physical plate — `row/col/field/level/T/C/Z/Y/X` — +# so each FOV is addressable by `dataset[f"{row}/{col}/{field}/{level}"]` and +# returns an `(T, C, Z, Y, X)` array. +# +# This dataset has 34 FOVs of 2048×2048 images across 3 channels (QPI, nuclei +# stained with DAPI, membrane stained with Cellmask), a single pyramid level +# `0`, and a single time point. + +# %% [markdown] tags=[] +#
+# You can inspect the tree structure by using your terminal: +# iohub info -v "path-to-ome-zarr" + +#
+# More info on the CLI: +# iohub info --help to see the help menu. +#
+# %% +# This is the python function called by `iohub info` CLI command +print_info(data_path, verbose=True) + +# Open and inspect the dataset. +dataset = open_ome_zarr(data_path) + +# %% [markdown] tags=[] +#
+# +# ### Task 1.1 +# Look at a couple different fields of view (FOVs) by changing the `field` variable. +# Check the cell density, the cell morphologies, and fluorescence signal. +# HINT: look at the HCS Plate format to see what your options are. +#
+# %% tags=[] +# Use the field and pyramid_level below to visualize data. +row = 0 +col = 0 +field = 9 # TODO: Change this to explore data. + +pyramid_level = 0 + +# `channel_names` is the metadata that is stored with data according to the OME-NGFF spec. +n_channels = len(dataset.channel_names) + +image = dataset[f"{row}/{col}/{field}/{pyramid_level}"].numpy() +print(f"data shape: {image.shape}, FOV: {field}, pyramid level: {pyramid_level}") + +figure, axes = plt.subplots(1, n_channels, figsize=(9, 3)) + +for i in range(n_channels): + channel_image = image[0, i, 0] + # Adjust contrast to 0.5th and 99.5th percentile of pixel values. + p_low, p_high = np.percentile(channel_image, (0.5, 99.5)) + channel_image = np.clip(channel_image, p_low, p_high) + axes[i].imshow(channel_image, cmap="gray") + axes[i].axis("off") + axes[i].set_title(dataset.channel_names[i]) +plt.tight_layout() + +# %% [markdown] tags=[] +# ## Explore the effects of augmentation on batch. +# +# Time to meet the first of the three Lightning objects from the primer above: the +# **DataModule**. `HCSDataModule` is VisCy's `LightningDataModule` — it knows how +# to read an OME-Zarr store, split FOVs into train/val, apply normalization and +# augmentations, and hand the Trainer a PyTorch `DataLoader`. You configure it +# once; Lightning calls the right method (`train_dataloader()`, +# `val_dataloader()`, etc.) at the right time. +# +# Every sample `HCSDataModule` yields is a Python `dict` (not a tuple) with: +# +# - `source`: the input image, a tensor of shape `(1, 1, Y, X)` → `(C, Z, Y, X)` +# - `target`: the target image, a tensor of shape `(2, 1, Y, X)` → `(C, Z, Y, X)` +# - `index` : the tuple `(HCS location, time, z-slice)` identifying the sample +# +# A `batch` is a dict of the same keys with an extra leading batch dimension, e.g. +# `batch["source"].shape == (B, 1, 1, Y, X)`. The `training_step` method inside +# `VSUNet` receives this dict directly — no unpacking required. + +# %% [markdown] tags=[] +#
+# +# ### Task 1.2 +# - Run the next cell to setup a logger for your augmentations. +# - Setup the `HCSDataloader()` in for training. +# - Configure the dataloader for the `"UNeXt2_2D"` +# - Configure the dataloader for the phase (source) to fluorescence cell nuclei and membrane (targets) regression task. +# - Configure the dataloader for training. Hint: use the `HCSDataloader.setup()` +# - Open your tensorboard and look at the `IMAGES tab`. +# +# Note: If tensorboard is not showing images or the plots, try refreshing and using the "Images" tab. +#
+ + +# %% +# Define a function to write a batch to tensorboard log. +def log_batch_tensorboard(batch, batchno, writer, card_name): + """ + Logs a batch of images to TensorBoard. + + Args: + batch (dict): A dictionary containing the batch of images to be logged. + writer (SummaryWriter): A TensorBoard SummaryWriter object. + card_name (str): The name of the card to be displayed in TensorBoard. + + Returns: + None + """ + batch_phase = batch["source"][:, :, 0, :, :] # batch_size x z_size x Y x X tensor. + batch_membrane = batch["target"][:, 1, 0, :, :].unsqueeze(1) # batch_size x 1 x Y x X tensor. + batch_nuclei = batch["target"][:, 0, 0, :, :].unsqueeze(1) # batch_size x 1 x Y x X tensor. + + p1, p99 = np.percentile(batch_membrane, (0.1, 99.9)) + batch_membrane = np.clip((batch_membrane - p1) / (p99 - p1), 0, 1) + + p1, p99 = np.percentile(batch_nuclei, (0.1, 99.9)) + batch_nuclei = np.clip((batch_nuclei - p1) / (p99 - p1), 0, 1) + + p1, p99 = np.percentile(batch_phase, (0.1, 99.9)) + batch_phase = np.clip((batch_phase - p1) / (p99 - p1), 0, 1) + + [N, C, H, W] = batch_phase.shape + interleaved_images = torch.zeros((3 * N, C, H, W), dtype=batch_phase.dtype) + interleaved_images[0::3, :] = batch_phase + interleaved_images[1::3, :] = batch_nuclei + interleaved_images[2::3, :] = batch_membrane + + grid = torchvision.utils.make_grid(interleaved_images, nrow=3) + + # add the grid to tensorboard + writer.add_image(card_name, grid, batchno) + + +# Define a function to visualize a batch on jupyter, in case tensorboard is finicky +def log_batch_jupyter(batch): + """ + Logs a batch of images on jupyter using ipywidget. + + Args: + batch (dict): A dictionary containing the batch of images to be logged. + + Returns: + None + """ + batch_phase = batch["source"][:, :, 0, :, :] # batch_size x z_size x Y x X tensor. + batch_size = batch_phase.shape[0] + batch_membrane = batch["target"][:, 1, 0, :, :].unsqueeze(1) # batch_size x 1 x Y x X tensor. + batch_nuclei = batch["target"][:, 0, 0, :, :].unsqueeze(1) # batch_size x 1 x Y x X tensor. + + p1, p99 = np.percentile(batch_membrane, (0.1, 99.9)) + batch_membrane = np.clip((batch_membrane - p1) / (p99 - p1), 0, 1) + + p1, p99 = np.percentile(batch_nuclei, (0.1, 99.9)) + batch_nuclei = np.clip((batch_nuclei - p1) / (p99 - p1), 0, 1) + + p1, p99 = np.percentile(batch_phase, (0.1, 99.9)) + batch_phase = np.clip((batch_phase - p1) / (p99 - p1), 0, 1) + + n_channels = batch["target"].shape[1] + batch["source"].shape[1] + plt.figure() + fig, axes = plt.subplots(batch_size, n_channels, figsize=(n_channels * 2, batch_size * 2)) + [N, C, H, W] = batch_phase.shape + for sample_id in range(batch_size): + axes[sample_id, 0].imshow(batch_phase[sample_id, 0]) + axes[sample_id, 1].imshow(batch_nuclei[sample_id, 0]) + axes[sample_id, 2].imshow(batch_membrane[sample_id, 0]) + + for i in range(n_channels): + axes[sample_id, i].axis("off") + axes[sample_id, i].set_title(dataset.channel_names[i]) + plt.tight_layout() + plt.show() + + +# %% tags=["task"] +# Initialize the data module. + +BATCH_SIZE = 4 + +# 4 is a perfectly reasonable batch size +# (batch size does not have to be a power of 2) +# See: https://sebastianraschka.com/blog/2022/batch-size-2.html + +# ####################### +# ##### TODO ######## +# ####################### +# HINT: Run dataset.channel_names +source_channel = ["TODO"] +target_channel = ["TODO", "TODO"] + +# ####################### +# ##### TODO ######## +# ####################### +data_module = HCSDataModule( + data_path, + z_window_size=1, + source_channel=source_channel, + target_channel=target_channel, + split_ratio=0.8, + batch_size=BATCH_SIZE, + num_workers=8, + yx_patch_size=(256, 256), # larger patch size makes it easy to see augmentations. + augmentations=[], # Turn off augmentation for now. + normalizations=[], # Turn off normalization for now. +) +# ####################### +# ##### TODO ######## +# ####################### +# Setup the data_module to fit. HINT: data_module.setup() + + +# Evaluate the data module +print( + f"Samples in training set: {len(data_module.train_dataset)}, " + f"samples in validation set:{len(data_module.val_dataset)}" +) +train_dataloader = data_module.train_dataloader() +# Instantiate the tensorboard SummaryWriter, logs the first batch and then iterates through all the batches and logs them to tensorboard. +writer = SummaryWriter(log_dir=f"{log_dir}/view_batch") +# Draw a batch and write to tensorboard. +batch = next(iter(train_dataloader)) +log_batch_tensorboard(batch, 0, writer, "augmentation/none") +writer.close() +# %% tags=["solution"] +# ####################### +# ##### SOLUTION ######## +# ####################### + +BATCH_SIZE = 4 +# 4 is a perfectly reasonable batch size +# (batch size does not have to be a power of 2) +# See: https://sebastianraschka.com/blog/2022/batch-size-2.html + +source_channel = ["Phase3D"] +target_channel = ["Nucl", "Mem"] + +data_module = HCSDataModule( + data_path, + z_window_size=1, + source_channel=source_channel, + target_channel=target_channel, + split_ratio=0.8, + batch_size=BATCH_SIZE, + num_workers=8, + yx_patch_size=(256, 256), # larger patch size makes it easy to see augmentations. + augmentations=[], # Turn off augmentation for now. + normalizations=[], # Turn off normalization for now. +) + +# Setup the data_module to fit. HINT: data_module.setup() +data_module.setup("fit") + +# Evaluate the data module +print( + f"Samples in training set: {len(data_module.train_dataset)}, " + f"samples in validation set:{len(data_module.val_dataset)}" +) +train_dataloader = data_module.train_dataloader() +# Instantiate the tensorboard SummaryWriter, logs the first batch and then iterates through all the batches and logs them to tensorboard. +writer = SummaryWriter(log_dir=f"{log_dir}/view_batch") +# Draw a batch and write to tensorboard. +batch = next(iter(train_dataloader)) +log_batch_tensorboard(batch, 0, writer, "augmentation/none") +writer.close() +# %% [markdown] tags=[] +#
+# +# ### Questions +# 1. What are the two channels in the target image? +# 2. How many samples are in the training and validation set? What determined that split? +# +# Note: If tensorboard is not showing images, try refreshing and using the "Images" tab. +#
+ +# %% [markdown] tags=[] +# If your tensorboard is causing issues, you can visualize directly on Jupyter /VSCode +# %% +# Visualize in Jupyter +log_batch_jupyter(batch) + +# %% [markdown] tags=[] +#
+#

Question for Task 1.3

+# 1. How do they make the model more robust to imaging parameters or conditions +# without having to acquire data for every possible condition?
+#
+# %% [markdown] tags=[] +# Each augmentation simulates a real-world source of microscope-to-microscope +# variation so the model doesn't overfit to the training conditions: +# +# | Transform | Simulates | +# | --- | --- | +# | `RandWeightedCropd` | random crops biased toward signal-dense regions (foreground oversampling) | +# | `RandAffined` | stage rotation, scale drift, slight shear between acquisitions | +# | `RandAdjustContrastd` | illumination / exposure differences | +# | `RandScaleIntensityd` | gain / brightness differences between cameras | +# | `RandGaussianNoised` | shot and read noise at different detector settings | +# | `RandGaussianSmoothd` | small focus drift / defocus | +# %% [markdown] tags=[] +#
+# +# ### Task 1.3 +# Add the following augmentations: +# - Add augmentations to rotate about $\pi$ around z-axis, 30% scale in (y,x), +# shearing of 1% in (y,x), and no padding with zeros with a probability of 80%. +# - Add a Gaussian noise with a mean of 0.0 and standard deviation of 0.3 with a probability of 50%. +# +# HINT: `RandAffined()` and `RandGaussianNoised()` are MONAI dictionary +# transforms re-exported from `viscy_transforms`. See the MONAI docs for +# arguments and probability semantics: +# [RandAffined](https://docs.monai.io/en/stable/transforms.html#randaffined), +# [RandGaussianNoised](https://docs.monai.io/en/stable/transforms.html#randgaussiannoised). +# You can also inspect any transform in a cell with `RandAffined?`.

+# [Compare your choice of augmentations against the pretrained models and config files](https://github.com/mehta-lab/VisCy/releases/download/v0.1.0/VisCy-0.1.0-VS-models.zip). +#
+# %% tags=["task"] +# Here we turn on data augmentation and rerun setup +# ####################### +# ##### TODO ######## +# ####################### +# HINT: Run dataset.channel_names +source_channel = ["TODO"] +target_channel = ["TODO", "TODO"] + +augmentations = [ + RandWeightedCropd( + keys=source_channel + target_channel, + spatial_size=(1, 384, 384), + num_samples=2, + w_key=target_channel[0], + ), + # ####################### + # ##### TODO ######## + # ####################### + ## TODO: Add Random Affine Transorms + ## Write code below + # ####################### + RandAdjustContrastd(keys=source_channel, prob=0.5, gamma=(0.8, 1.2)), + RandScaleIntensityd(keys=source_channel, factors=0.5, prob=0.5), + # ####################### + # ##### TODO ######## + # ####################### + ## TODO: Add Random Gaussian Noise + ## Write code below + # ####################### + RandGaussianSmoothd( + keys=source_channel, + sigma_x=(0.25, 0.75), + sigma_y=(0.25, 0.75), + sigma_z=(0.0, 0.0), + prob=0.5, + ), +] + +normalizations = [ + NormalizeSampled( + keys=source_channel, + level="fov_statistics", + subtrahend="mean", + divisor="std", + ), + NormalizeSampled( + keys=target_channel, + level="fov_statistics", + subtrahend="median", + divisor="iqr", + ), +] + +data_module.augmentations = augmentations +data_module.normalizations = normalizations + +data_module.setup("fit") + +# get the new data loader with augmentation turned on +augmented_train_dataloader = data_module.train_dataloader() + +# Draw batches and write to tensorboard +writer = SummaryWriter(log_dir=f"{log_dir}/view_batch") +augmented_batch = next(iter(augmented_train_dataloader)) +log_batch_tensorboard(augmented_batch, 0, writer, "augmentation/some") +writer.close() + +# %% tags=["solution"] +# ####################### +# ##### SOLUTION ######## +# ####################### +source_channel = ["Phase3D"] +target_channel = ["Nucl", "Mem"] + +augmentations = [ + RandWeightedCropd( + keys=source_channel + target_channel, + spatial_size=(1, 384, 384), + num_samples=2, + w_key=target_channel[0], + ), + RandAffined( + keys=source_channel + target_channel, + rotate_range=[3.14, 0.0, 0.0], + scale_range=[0.0, 0.3, 0.3], + prob=0.8, + padding_mode="zeros", + shear_range=[0.0, 0.01, 0.01], + ), + RandAdjustContrastd(keys=source_channel, prob=0.5, gamma=(0.8, 1.2)), + RandScaleIntensityd(keys=source_channel, factors=0.5, prob=0.5), + RandGaussianNoised(keys=source_channel, prob=0.5, mean=0.0, std=0.3), + RandGaussianSmoothd( + keys=source_channel, + sigma_x=(0.25, 0.75), + sigma_y=(0.25, 0.75), + sigma_z=(0.0, 0.0), + prob=0.5, + ), +] + +normalizations = [ + NormalizeSampled( + keys=source_channel, + level="fov_statistics", + subtrahend="mean", + divisor="std", + ), + NormalizeSampled( + keys=target_channel, + level="fov_statistics", + subtrahend="median", + divisor="iqr", + ), +] + +data_module.augmentations = augmentations + +# Setup the data_module to fit. HINT: data_module.setup() +data_module.setup("fit") + +# get the new data loader with augmentation turned on +augmented_train_dataloader = data_module.train_dataloader() + +# Draw batches and write to tensorboard +writer = SummaryWriter(log_dir=f"{log_dir}/view_batch") +augmented_batch = next(iter(augmented_train_dataloader)) +log_batch_tensorboard(augmented_batch, 0, writer, "augmentation/some") +writer.close() + +# %% [markdown] tags=[] +#
+#

Question for Task 1.3

+# 1. Look at your tensorboard. Can you tell the agumentations were applied to the sample batch? Compare the batch with and without augmentations.
+# 2. Are these augmentations good enough? What else would you add? +#
+ +# %% [markdown] +# Visualize directly on Jupyter + +# %% +log_batch_jupyter(augmented_batch) + +# %% [markdown] tags=[] +# ## Train a 2D U-Net model to predict nuclei and membrane from phase. +# ### Constructing a 2D UNeXt2 using VisCy +# +# Now we meet the second Lightning object: the **`LightningModule`**. `VSUNet` is +# VisCy's `LightningModule` and it bundles three things that plain PyTorch keeps +# separate: +# +# 1. **The network** — a UNeXt2 architecture, configured through `model_config`. +# 2. **The loss** — passed in as `loss_function=MixedLoss(...)`. +# 3. **The per-batch logic** — `training_step` and `validation_step` methods that +# take one `{"source", "target"}` batch, run the forward pass, compute the +# loss, and return it. You don't see these methods here because they're +# defined once inside `VSUNet`; Lightning calls them for you. +# +# Other constructor arguments you'll recognize from plain PyTorch training: +# `lr` is the learning rate, `schedule="WarmupCosine"` picks the LR schedule, +# and `freeze_encoder=False` lets gradients flow through the whole network. +# `log_batches_per_epoch` is a VisCy extra — it tells the module how many image +# samples to push to TensorBoard each epoch. +# %% [markdown] +# **Architecture config** — UNeXt2 is a U-Net with ConvNeXt-style blocks: +# +# - `encoder_blocks=[3, 3, 9, 3]` and `dims=[96, 192, 384, 768]` — 4 downsampling +# stages with that many blocks and feature channels per stage (last stage is +# the bottleneck). More blocks / dims = more capacity and more compute. +# - `decoder_conv_blocks=2` — conv blocks after each upsampling step. +# - `stem_kernel_size=(1, 2, 2)` and `in_stack_depth=1` — this is a 2D model, +# so we use 1 z-slice and a stem that doesn't convolve across z. +# +# **Loss** — `MixedLoss(l1_alpha=0.5, ms_dssim_alpha=0.5)` combines per-pixel +# L1 (penalizes intensity error) with multi-scale SSIM (penalizes structural +# error — edges, texture, shape). L1 alone produces blurry outputs; MS-SSIM +# alone ignores absolute intensity. The 0.5/0.5 mix balances both. +# +# **Schedule** — `schedule="WarmupCosine"`, `lr=6e-4`: the learning rate ramps +# up from 0 over the first few epochs (warmup), then follows a cosine decay +# toward 0. Warmup avoids early gradient blow-up with AdamW; cosine decay is a +# strong default for vision transformer / ConvNeXt-style encoders. + +# %% [markdown] +#
+# +# ### Task 1.4 +# - Run the next cell to instantiate the `UNeXt2_2D` model +# - Configure the network for the phase (source) to fluorescence cell nuclei and membrane (targets) regression task. +# - Call the VSUNet with the `"UNeXt2_2D"` architecture. +# - Run the next cells to instantiate data module and trainer. +# - Add the source channel name and the target channel names +# - Start the training
+# +# Note
+# See ``viscy.translation.engine.VSUNet`` ([source code](https://github.com/mehta-lab/VisCy/blob/main/viscy/translation/engine.py)) and ``viscy.unet.networks.fcmae`` ([source code](https://github.com/mehta-lab/VisCy/blob/main/viscy/unet/networks/fcmae.py)) to learn more about the configuration parameters and FCMAE architecture. +#
+ +# %% tags=["task"] +# Create a 2D UNet. +GPU_ID = 0 + +BATCH_SIZE = 16 +YX_PATCH_SIZE = (256, 256) + +# ####################### +# ##### TODO ######## +# ####################### +# Dictionary that specifies key parameters of the model. +phase2fluor_config = dict( + in_channels=..., # TODO how many input channels are we feeding Hint: int?, + out_channels=..., # TODO how many output channels are we solving for? Hint: int, + encoder_blocks=[3, 3, 9, 3], + dims=[96, 192, 384, 768], + decoder_conv_blocks=2, + stem_kernel_size=(1, 2, 2), + in_stack_depth=..., # TODO: was this a 2D or 3D input? HINT: int, + pretraining=False, +) + +# ####################### +# ##### TODO ######## +# ####################### +phase2fluor_model = VSUNet( + architecture=..., # TODO: 2D UNeXt2 architecture + model_config=phase2fluor_config.copy(), + loss_function=MixedLoss(l1_alpha=0.5, l2_alpha=0.0, ms_dssim_alpha=0.5), + schedule="WarmupCosine", + lr=6e-4, + log_batches_per_epoch=5, # Number of samples from each batch to log to tensorboard. + freeze_encoder=False, +) + +# ####################### +# ##### TODO ######## +# ####################### +# HINT: Run dataset.channel_names +source_channel = ["TODO"] +target_channel = ["TODO", "TODO"] + +# Setup the data module. +phase2fluor_2D_data = HCSDataModule( + data_path, + source_channel=source_channel, + target_channel=target_channel, + z_window_size=1, + split_ratio=0.8, + batch_size=BATCH_SIZE, + num_workers=8, + yx_patch_size=YX_PATCH_SIZE, + augmentations=augmentations, + normalizations=normalizations, +) +phase2fluor_2D_data.setup("fit") +# fast_dev_run runs a single batch of data through the model to check for errors. +trainer = VisCyTrainer(accelerator="gpu", devices=[GPU_ID], precision="16-mixed", fast_dev_run=True) + +# trainer class takes the model and the data module as inputs. +trainer.fit(phase2fluor_model, datamodule=phase2fluor_2D_data) + + +# %% tags=["solution"] + +# Here we are creating a 2D UNet. +GPU_ID = 0 + +BATCH_SIZE = 16 +YX_PATCH_SIZE = (256, 256) + +# Dictionary that specifies key parameters of the model. +# ####################### +# ##### SOLUTION ######## +# ####################### +phase2fluor_config = dict( + in_channels=1, + out_channels=2, + encoder_blocks=[3, 3, 9, 3], + dims=[96, 192, 384, 768], + decoder_conv_blocks=2, + stem_kernel_size=(1, 2, 2), + in_stack_depth=1, + pretraining=False, +) + +phase2fluor_model = VSUNet( + architecture="UNeXt2_2D", # 2D UNeXt2 architecture + model_config=phase2fluor_config.copy(), + loss_function=MixedLoss(l1_alpha=0.5, l2_alpha=0.0, ms_dssim_alpha=0.5), + schedule="WarmupCosine", + lr=6e-4, + log_batches_per_epoch=5, # Number of samples from each batch to log to tensorboard. + freeze_encoder=False, +) + +# ### Instantiate data module and trainer, test that we are setup to launch training. +# ####################### +# ##### SOLUTION ######## +# ####################### +# Selecting the source and target channel names from the dataset. +source_channel = ["Phase3D"] +target_channel = ["Nucl", "Mem"] +# Setup the data module. +phase2fluor_2D_data = HCSDataModule( + data_path, + source_channel=source_channel, + target_channel=target_channel, + z_window_size=1, + split_ratio=0.8, + batch_size=BATCH_SIZE, + num_workers=8, + yx_patch_size=YX_PATCH_SIZE, + augmentations=augmentations, + normalizations=normalizations, +) +# ####################### +# ##### SOLUTION ######## +# ####################### +phase2fluor_2D_data.setup("fit") + +# --- The third Lightning object: the Trainer --- +# +# This is the object that replaces the hand-written training loop. Each kwarg +# controls one piece of the boilerplate Lightning is handling for you: +# +# - accelerator="gpu", devices=[GPU_ID] +# Pick the device. No more ".to(device)" sprinkled through your code — +# Lightning moves model + every batch for you. +# - precision="16-mixed" +# Automatic mixed-precision training (fp16 activations, fp32 master +# weights). Cuts GPU memory roughly in half and speeds up matmuls on +# modern GPUs — no autocast() context managers needed. +# - fast_dev_run=True +# Sanity check: run ONE training batch + ONE validation batch and exit. +# Use this on every new pipeline to catch shape bugs, NaN losses, or +# bad paths *before* you commit to a multi-hour training job. +# +# trainer.fit(model, datamodule=...) then drives the whole thing: it calls +# datamodule.setup(), pulls batches from train_dataloader(), invokes +# model.training_step(batch), runs loss.backward() + optimizer.step() + +# zero_grad(), runs validation, logs to TensorBoard, and saves checkpoints. +trainer = VisCyTrainer(accelerator="gpu", devices=[GPU_ID], precision="16-mixed", fast_dev_run=True) +trainer.fit(phase2fluor_model, datamodule=phase2fluor_2D_data) + +# %% [markdown] tags=[] +# ## View model graph. +# +# PyTorch uses dynamic graphs under the hood. +# The graphs are constructed on the fly. +# This is in contrast to TensorFlow, +# where the graph is constructed before the training loop and remains static. +# In other words, the graph of the network can change with every forward pass. +# Therefore, we need to supply an input tensor to construct the graph. +# The input tensor can be a random tensor of the correct shape and type. +# We can also supply a real image from the dataset. +# The latter is more useful for debugging. + +# %% [markdown] +#
+# +# ### Task 1.5 +# Run the next cell to generate a graph representation of the model architecture. +#
+ +# %% +# visualize graph of phase2fluor model as image. +model_graph_phase2fluor = torchview.draw_graph( + phase2fluor_model, + phase2fluor_2D_data.train_dataset[0]["source"].unsqueeze(dim=0), + roll=True, + depth=3, # adjust depth to zoom in. + device="cpu", + # expand_nested=True, +) +# Print the image of the model. +model_graph_phase2fluor.visual_graph + +# %% [markdown] tags=[] +#
+# +# ### Question: +# Can you recognize the UNet structure and skip connections in this graph visualization? +#
+ +# %% [markdown] +#
+ +#

Task 1.6

+# Start training by running the following cell. Check the new logs on the tensorboard. +#
+ +# %% [markdown] +#
+# Before re-running training: if a previous training cell is still +# holding the GPU (you'll see CUDA out of memory), restart the +# Jupyter kernel (Kernel → Restart in Jupyter, or Restart in +# VSCode) to release the previous model and optimizer state. The dataset and +# augmentations will rebuild quickly; only the trained weights need to be +# re-loaded via load_from_checkpoint if you want to resume. +#
+ +# %% [markdown] +# Now that `fast_dev_run` confirmed the pipeline works end-to-end, we switch +# to a "real" Trainer configured for an actual multi-epoch run. New Lightning +# knobs appearing here: +# +# - `max_epochs=n_epochs` — run this many passes over the training set, then stop. +# - `log_every_n_steps=steps_per_epoch // 2` — how often Lightning flushes +# scalars (loss, learning rate) to the logger. Setting it to half an epoch +# gives us two data points per epoch without spamming TensorBoard. +# - `logger=TensorBoardLogger(save_dir=log_dir, name="phase2fluor", log_graph=True)` +# — Lightning writes TensorBoard event files *and* model checkpoints under +# `{save_dir}/{name}/version_N/`. You don't call `torch.save` yourself; the +# trainer persists checkpoints automatically, and `log_graph=True` adds the +# network architecture to the Graphs tab. +# +# Calling `trainer.fit` again below runs the full training loop — forward, +# loss, backward, optimizer step, validation every epoch, checkpoint at the +# end — across `max_epochs` epochs. + +# %% +# Check if GPU is available +# You can check by typing `nvidia-smi` +GPU_ID = 0 + +n_samples = len(phase2fluor_2D_data.train_dataset) +steps_per_epoch = n_samples // BATCH_SIZE # steps per epoch. +n_epochs = 80 # Set this to 80-100 or the number of epochs you want to train for. + +trainer = VisCyTrainer( + accelerator="gpu", + devices=[GPU_ID], + max_epochs=n_epochs, + precision="16-mixed", + log_every_n_steps=steps_per_epoch // 2, + # log losses and image samples 2 times per epoch. + logger=TensorBoardLogger( + save_dir=log_dir, + # lightning trainer transparently saves logs and model checkpoints in this directory. + name="phase2fluor", + log_graph=True, + ), +) +# Launch training and check that loss and images are being logged on tensorboard. +trainer.fit(phase2fluor_model, datamodule=phase2fluor_2D_data) + +# Move the model to the GPU. +device = torch.device("cuda" if torch.cuda.is_available() else "cpu") +phase2fluor_model.to(device) +# %% [markdown] tags=[] +#
+ +#

Checkpoint 1

+ +# While your model is training, let's think about the following questions:
+#
    +#
  • What is the information content of each channel in the dataset?
  • +#
  • How would you use image translation models?
  • +#
  • What can you try to improve the performance of each model?
  • +#
+ +# Now the training has started, +# we can come back after a while and evaluate the performance! + +#
+# %% [markdown] tags=[] +# # Part 2: Assess your trained model +# +# We evaluate on a held-out test set using two complementary families of metrics: +# +# - **Regression / pixel-level** (Pearson, microSSIM): are predicted +# intensities close to ground truth, per pixel? Cheap, but can hide +# topological errors — a model that merges two nuclei may still score well +# pixel-wise. +# - **Segmentation / instance-level** (Jaccard/IoU, Dice, mAP over IoU +# thresholds): run Cellpose on both predicted and measured fluorescence, +# then compare instance masks. This is what ultimately matters for +# downstream analysis (counting cells, tracking, phenotyping). +# +# Also inspect the validation samples on TensorBoard — the experimental +# nuclei channel is noisy, so "ground truth" is itself imperfect. + +# %% [markdown] +#
+ +#

Task 2.1 Define metrics

+ +# For each of the above metrics, write a brief definition of what they are and what they mean +# for this image translation task. Use your favorite search engine and/or resources. + +#
+ +# %% [markdown] tags=[] +# ``` +# ####################### +# ##### Solution ######## +# ####################### +# ``` +# +# - **Pearson Correlation**: linear correlation between predicted and target +# intensities across all pixels, in `[-1, 1]`. `1` means the prediction is a +# perfect affine rescaling of the target; invariant to mean / contrast +# offsets. Good at flagging "the pattern is right" but blind to structural +# errors that preserve correlation (e.g. a uniformly blurred prediction). +# +# - **microSSIM**: a microscopy-aware variant of +# [Structural Similarity (SSIM)](https://en.wikipedia.org/wiki/Structural_similarity). +# Classic SSIM patch-wise compares local mean, variance, and covariance and +# captures structure Pearson misses (blurring, contrast loss) — but it +# assumes the natural-image dynamic range. Fluorescence microscopy images +# are sparse, dim, and noisy: with the default SSIM parameters the scores +# collapse into a narrow band that barely separates good and bad +# predictions. [microSSIM](https://github.com/juglab/MicroSSIM) +# ([Ashesh et al., 2024](https://arxiv.org/abs/2408.08747)) fixes this by +# subtracting the image background and fitting a per-image rescaling factor +# before computing SSIM, so the metric becomes sensitive over the range of +# intensities microscopy predictions actually live in. We use it as a +# drop-in replacement for `skimage.metrics.structural_similarity`. + +# %% [markdown] tags=[] +# ### Let's compute metrics directly and plot below. +# %% [markdown] tags=[] +#
+# If you weren't able to train or training didn't complete please run the following lines to load the latest checkpoint
+# +# ```python +# phase2fluor_model_ckpt = natsorted(glob( +# str(top_dir / "06_image_translation/logs/phase2fluor/version*/checkpoints/*.ckpt") +# ))[-1] +# ``` +#
+# NOTE: if their model didn't go past epoch 5, lost their checkpoint, or didnt train anything. +# Run the following: +# +# ```python +# phase2fluor_model_ckpt = natsorted(glob( +# str(top_dir/"06_image_translation/backup/phase2fluor/version_0/checkpoints/*.ckpt") +# ))[-1] +# ``` + +# ```python +# phase2fluor_config = dict( +# in_channels=1, +# out_channels=2, +# encoder_blocks=[3, 3, 9, 3], +# dims=[96, 192, 384, 768], +# decoder_conv_blocks=2, +# stem_kernel_size=(1, 2, 2), +# in_stack_depth=1, +# pretraining=False, +# ) +# Load the model checkpoint +# phase2fluor_model = VSUNet.load_from_checkpoint( +# phase2fluor_model_ckpt, +# architecture="UNeXt2_2D", +# model_config = phase2fluor_config, +# accelerator='gpu' +# ) +# ```` +#
+# %% +# Setup the test data module. +test_data_path = top_dir / "test/a549_hoechst_cellmask_test.zarr" +source_channel = ["Phase3D"] +target_channel = ["Nucl", "Mem"] + +test_data = HCSDataModule( + test_data_path, + source_channel=source_channel, + target_channel=target_channel, + z_window_size=1, + batch_size=1, + num_workers=8, +) +test_data.setup("test") + +test_metrics = pd.DataFrame(columns=["pearson_nuc", "microSSIM_nuc", "pearson_mem", "microSSIM_mem"]) + + +# %% +# Compute metrics directly and plot here. +def normalize_fov(input: ArrayLike): + "Normalizing the fov with zero mean and unit variance" + mean = np.mean(input) + std = np.std(input) + return (input - mean) / std + + +for i, sample in enumerate(tqdm(test_data.test_dataloader(), desc="Computing metrics per sample")): + phase_image = sample["source"].to(phase2fluor_model.device) + with torch.inference_mode(): # turn off gradient computation. + predicted_image = phase2fluor_model(phase_image) + + target_image = sample["target"].cpu().numpy().squeeze(0) # Squeezing batch dimension. + predicted_image = predicted_image.cpu().numpy().squeeze(0) + phase_image = phase_image.cpu().numpy().squeeze(0) + target_mem = normalize_fov(target_image[1, 0, :, :]) + target_nuc = normalize_fov(target_image[0, 0, :, :]) + # slicing channel dimension, squeezing z-dimension. + predicted_mem = normalize_fov(predicted_image[1, :, :, :].squeeze(0)) + predicted_nuc = normalize_fov(predicted_image[0, :, :, :].squeeze(0)) + + # Compute microSSIM and pearson correlation. + ssim_nuc = micro_structural_similarity(target_nuc, predicted_nuc) + ssim_mem = micro_structural_similarity(target_mem, predicted_mem) + pearson_nuc = np.corrcoef(target_nuc.flatten(), predicted_nuc.flatten())[0, 1] + pearson_mem = np.corrcoef(target_mem.flatten(), predicted_mem.flatten())[0, 1] + + test_metrics.loc[i] = { + "pearson_nuc": pearson_nuc, + "microSSIM_nuc": ssim_nuc, + "pearson_mem": pearson_mem, + "microSSIM_mem": ssim_mem, + } + +# Plot the following metrics +test_metrics.boxplot( + column=["pearson_nuc", "microSSIM_nuc", "pearson_mem", "microSSIM_mem"], + rot=30, +) + + +# %% +# Adjust the image to the 0.5-99.5 percentile range. +def process_image(image): + p_low, p_high = np.percentile(image, (0.5, 99.5)) + return np.clip(image, p_low, p_high) + + +# Plot the predicted image vs target image. +channel_titles = [ + "Phase", + "Target Nuclei", + "Target Membrane", + "Predicted Nuclei", + "Predicted Membrane", +] +fig, axes = plt.subplots(5, 1, figsize=(20, 20)) + +# Get a writer to output the images into tensorboard and plot the source, predictions and target images +for i, sample in enumerate(test_data.test_dataloader()): + # Plot the phase image + phase_image = sample["source"] + channel_image = phase_image[0, 0, 0] + p_low, p_high = np.percentile(channel_image, (0.5, 99.5)) + channel_image = np.clip(channel_image, p_low, p_high) + axes[0].imshow(channel_image, cmap="gray") + axes[0].axis("off") + axes[0].set_title(channel_titles[0]) + + with torch.inference_mode(): # turn off gradient computation. + predicted_image = phase2fluor_model(phase_image.to(phase2fluor_model.device)).cpu().numpy().squeeze(0) + + target_image = sample["target"].cpu().numpy().squeeze(0) + phase_raw = process_image(phase_image[0, 0, 0]) + predicted_nuclei = process_image(predicted_image[0, 0]) + predicted_membrane = process_image(predicted_image[1, 0]) + target_nuclei = process_image(target_image[0, 0]) + target_membrane = process_image(target_image[1, 0]) + # Concatenate all images side by side + combined_image = np.concatenate( + ( + phase_raw, + predicted_nuclei, + predicted_membrane, + target_nuclei, + target_membrane, + ), + axis=1, + ) + + # Plot the phase,target nuclei, target membrane, predicted nuclei, predicted membrane + axes[1].imshow(target_nuclei, cmap="gray") + axes[2].imshow(target_membrane, cmap="gray") + axes[3].imshow(predicted_nuclei, cmap="gray") + axes[4].imshow(predicted_membrane, cmap="gray") + + for ax in axes: + ax.axis("off") + plt.tight_layout() + plt.show() + break +# %% [markdown] tags=[] +#
+ +#

Task 2.2 Loading the pretrained model VSCyto2D

+# Here we will compare your model with the VSCyto2D pretrained model by computing the pixel-based metrics and segmentation-based metrics. +# +#
    +#
  • The pretrained checkpoint was downloaded by setup.sh to +# ~/data/06_image_translation/pretrained_models/VSCyto2D/epoch=399-step=23200.ckpt +# — if missing, download it directly from +# public.czbiohub.org. +# Check with ls ~/data/06_image_translation/pretrained_models/VSCyto2D/.
  • +#
  • Load the VSCyto2D model checkpoint and the configuration file
  • +#
  • Compute the pixel-based metrics and segmentation-based metrics between the model you trained and the pretrained model
  • +#
+#
+ +#
+ + +# %% tags=["task"] +################# +##### TODO ###### +################# +# Let's load the pretrained model checkpoint +pretrained_model_ckpt = top_dir / ... ## Add the path to the "VSCyto2D/epoch=399-step=23200.ckpt" + +# TODO: Load the phase2fluor_config just like the model you trained +phase2fluor_config = dict() ## + +# TODO: Load the checkpoint. Write the architecture name. HINT: look at the previous config. +pretrained_phase2fluor = VSUNet.load_from_checkpoint( + pretrained_model_ckpt, + architecture=..., + model_config=phase2fluor_config, +) +# Move the loaded model to GPU (VSUNet does not take an accelerator kwarg; +# Lightning's trainer handles that for fit/predict, but for manual inference +# we move the module ourselves). +pretrained_phase2fluor = pretrained_phase2fluor.to( + torch.device(f"cuda:{GPU_ID}" if torch.cuda.is_available() else "cpu") +) +# TODO: Setup the dataloader in evaluation/predict mode +# + +# %% tags=["solution"] +# ####################### +# ##### SOLUTION ######## +# ####################### + +pretrained_model_ckpt = top_dir / "pretrained_models/VSCyto2D/epoch=399-step=23200.ckpt" + +phase2fluor_config = dict( + in_channels=1, + out_channels=2, + encoder_blocks=[3, 3, 9, 3], + dims=[96, 192, 384, 768], + decoder_conv_blocks=2, + stem_kernel_size=(1, 2, 2), + in_stack_depth=1, + pretraining=False, +) +# Load the model checkpoint +device = torch.device("cuda" if torch.cuda.is_available() else "cpu") +pretrained_phase2fluor = VSUNet.load_from_checkpoint( + pretrained_model_ckpt, + architecture="UNeXt2_2D", + model_config=phase2fluor_config, + map_location=device, +) +pretrained_phase2fluor = pretrained_phase2fluor.to(device) +pretrained_phase2fluor.eval() + +### Re-load your trained model +# NOTE: assuming the latest checkpoint it your latest training and model +phase2fluor_model_ckpt = natsorted( + glob(str(training_top_dir / "06_image_translation/logs/phase2fluor/version*/checkpoints/*.ckpt")) +)[-1] + +# NOTE: if their model didn't go past epoch 5, lost their checkpoint, or didnt train anything. +# Uncomment the next lines +# phase2fluor_model_ckpt = natsorted(glob( +# str(top_dir/"06_image_translation/backup/phase2fluor/version_0/checkpoints/*.ckpt") +# ))[-1] + +phase2fluor_config = dict( + in_channels=1, + out_channels=2, + encoder_blocks=[3, 3, 9, 3], + dims=[96, 192, 384, 768], + decoder_conv_blocks=2, + stem_kernel_size=(1, 2, 2), + in_stack_depth=1, + pretraining=False, +) +# Load the model checkpoint, then move it to GPU for manual inference. +# (VSUNet does not accept an accelerator kwarg — Lightning's Trainer handles +# device placement automatically for fit/predict, but here we call the model +# directly, so we move it explicitly.) +phase2fluor_model = VSUNet.load_from_checkpoint( + phase2fluor_model_ckpt, + architecture="UNeXt2_2D", + model_config=phase2fluor_config, +).to(torch.device(f"cuda:{GPU_ID}" if torch.cuda.is_available() else "cpu")) +phase2fluor_model.eval() +# %% [markdown] tags=[] +#
+#

Question

+# 1. Can we evaluate a model's performance based on their segmentations?
+# 2. Look up IoU or Jaccard index, dice coefficient, and AP metrics. LINK:https://metrics-reloaded.dkfz.de/metric-library
+# We will evaluate the performance of your trained model with a pre-trained model using pixel based metrics as above and +# segmantation based metrics including (mAP@0.5, dice, accuracy and jaccard index).
+#
+# %% [markdown] tags=["solution"] +# +# - IoU (Intersection over Union): Also referred to as the Jaccard index, is essentially a method to quantify the percent overlap between the target and predicted masks. +# It is calculated as the intersection of the target and predicted masks divided by the union of the target and predicted masks.
+# - Dice Coefficient: Metric used to evaluate the similarity between two sets.
+# It is calculated as twice the intersection of the target and predicted masks divided by the sum of the target and predicted masks.
+# - mAP (mean Average Precision): The mean Average Precision (mAP) is a metric used to evaluate the performance of object detection models. +# It is calculated as the average precision across all classes and is used to measure the accuracy of the model in localizing objects. +# +# %% [markdown] tags=[] +# ### Let's compute the metrics for the test dataset +# Before you run the following code, make sure you have the pretrained model loaded and the test data is ready. + +# The following code will compute the following: +# - the pixel-based metrics (pearson correlation, SSIM) +# - segmentation-based metrics (mAP@0.5, dice, accuracy, jaccard index) + + +# #### Note: +# - The segmentation-based metrics are computed using the cellpose stock `nuclei` model +# - The metrics will be store in the `test_pixel_metrics` and `test_segmentation_metrics` dataframes +# - The segmentations will be stored in the `segmentation_store` zarr file +# - Analyze the code while it runs. +# %% +# Create cellpose model once for reuse +device = torch.device("cuda" if torch.cuda.is_available() else "cpu") +cellpose_model = models.CellposeModel(gpu=True if device.type == "cuda" else False, device=device) + + +# Define the function to compute the cellpose segmentation +def cellpose_segmentation(prediction: ArrayLike, target: ArrayLike) -> Tuple[torch.ShortTensor]: + # NOTE these are hardcoded for this notebook and A549 dataset + + # Convert 2D arrays to 3D format expected by cellpose v4.0.1+ + # Add channel dimension and replicate to 3 channels (RGB format) + if prediction.ndim == 2: + prediction = np.tile(prediction, (3, 1, 1)) # Shape: (3, H, W) + if target.ndim == 2: + target = np.tile(target, (3, 1, 1)) # Shape: (3, H, W) + + cp_nuc_kwargs = { + "diameter": 65, + "cellprob_threshold": 0.0, + } + + pred_label, _, _ = cellpose_model.eval(prediction, **cp_nuc_kwargs) + target_label, _, _ = cellpose_model.eval(target, **cp_nuc_kwargs) + + pred_label = pred_label.astype(np.int32) + target_label = target_label.astype(np.int32) + pred_label = torch.ShortTensor(pred_label) + target_label = torch.ShortTensor(target_label) + + return (pred_label, target_label) + + +# %% +# Setting the paths for the test data and the output segmentation +test_data_path = top_dir / "test/a549_hoechst_cellmask_test.zarr" +output_segmentation_path = training_top_dir / "06_image_translation/pretrained_model_segmentations.zarr" + +# Creating the dataframes to store the pixel and segmentation metrics +test_pixel_metrics = pd.DataFrame( + columns=["model", "fov", "pearson_nuc", "microSSIM_nuc", "pearson_mem", "microSSIM_mem"] +) +test_segmentation_metrics = pd.DataFrame( + columns=[ + "model", + "fov", + "masks_per_fov", + "accuracy", + "dice", + "jaccard", + "mAP", + "mAP_50", + "mAP_75", + "mAR_100", + ] +) +# Opening the test dataset +test_dataset = open_ome_zarr(test_data_path) + +# Creating an output store for the predictions and segmentations +segmentation_store = open_ome_zarr( + output_segmentation_path, + channel_names=["nuc_pred", "mem_pred", "nuc_labels"], + mode="w", + layout="hcs", +) + +# Looking at the test dataset +print("Test dataset:") +test_dataset.print_tree() +channel_names = test_dataset.channel_names +print(f"Channel names: {channel_names}") + +# Finding the channel indices for the corresponding channel names +phase_cidx = channel_names.index("Phase3D") +nuc_cidx = channel_names.index("Nucl") +mem_cidx = channel_names.index("Mem") +nuc_label_cidx = channel_names.index("nuclei_segmentation") + + +# %% +def min_max_scale(image: ArrayLike) -> ArrayLike: + "Normalizing the image using min-max scaling" + min_val = image.min() + max_val = image.max() + return (image - min_val) / (max_val - min_val) + + +# %% [markdown] +# ## Visualize segmentation comparison: Fluorescence vs Virtual Staining vs Pretrained +# Let's compare nucleus and membrane segmentation across all three models + +# %% +# Get a sample FOV for visualization +positions = list(test_dataset.positions()) +sample_fov, sample_pos = positions[0] # Use first FOV as example + +T, C, Z, Y, X = sample_pos.data.shape +Z_slice = slice(Z // 2, Z // 2 + 1) + +# Get the data +sample_phase = sample_pos.data[:, phase_cidx : phase_cidx + 1, Z_slice] +sample_nucleus = sample_pos.data[0, nuc_cidx : nuc_cidx + 1, Z_slice] +sample_membrane = sample_pos.data[0, mem_cidx : mem_cidx + 1, Z_slice] + +# Crop 300x300 pixels from center +center_y, center_x = sample_nucleus.shape[2] // 2, sample_nucleus.shape[3] // 2 +crop_size = 300 +y_start = max(0, center_y - crop_size // 2) +y_end = min(sample_nucleus.shape[2], center_y + crop_size // 2) +x_start = max(0, center_x - crop_size // 2) +x_end = min(sample_nucleus.shape[3], center_x + crop_size // 2) + +# Crop fluorescence data +sample_nucleus_crop = min_max_scale(sample_nucleus[0, 0, y_start:y_end, x_start:x_end]) +sample_membrane_crop = min_max_scale(sample_membrane[0, 0, y_start:y_end, x_start:x_end]) + +# Generate virtual stained data from phase (trained model) +sample_phase_tensor = torch.tensor(sample_phase, dtype=torch.float32).to(device) +with torch.inference_mode(): + predicted_image = phase2fluor_model(sample_phase_tensor) +predicted_nuc_crop = min_max_scale(predicted_image.cpu().numpy()[0, 0, 0, y_start:y_end, x_start:x_end]) +predicted_mem_crop = min_max_scale(predicted_image.cpu().numpy()[0, 1, 0, y_start:y_end, x_start:x_end]) + +# Generate virtual stained data from pretrained model +with torch.inference_mode(): + predicted_image_pretrained = pretrained_phase2fluor(sample_phase_tensor) +predicted_nuc_pretrained_crop = min_max_scale( + predicted_image_pretrained.cpu().numpy()[0, 0, 0, y_start:y_end, x_start:x_end] +) +predicted_mem_pretrained_crop = min_max_scale( + predicted_image_pretrained.cpu().numpy()[0, 1, 0, y_start:y_end, x_start:x_end] +) + +# Run segmentation on all nuclei +fluor_nuc_seg, _ = cellpose_segmentation(sample_nucleus_crop, sample_nucleus_crop) +virtual_nuc_seg, _ = cellpose_segmentation(predicted_nuc_crop, predicted_nuc_crop) +pretrained_nuc_seg, _ = cellpose_segmentation(predicted_nuc_pretrained_crop, predicted_nuc_pretrained_crop) + +# Run segmentation on all membranes (using nucleus parameters for consistency) +fluor_mem_seg, _ = cellpose_segmentation(sample_membrane_crop, sample_membrane_crop) +virtual_mem_seg, _ = cellpose_segmentation(predicted_mem_crop, predicted_mem_crop) +pretrained_mem_seg, _ = cellpose_segmentation(predicted_mem_pretrained_crop, predicted_mem_pretrained_crop) + +# Convert to numpy +fluor_nuc_seg = fluor_nuc_seg.numpy() +virtual_nuc_seg = virtual_nuc_seg.numpy() +pretrained_nuc_seg = pretrained_nuc_seg.numpy() +fluor_mem_seg = fluor_mem_seg.numpy() +virtual_mem_seg = virtual_mem_seg.numpy() +pretrained_mem_seg = pretrained_mem_seg.numpy() + +# Create 3x4 visualization +fig, axes = plt.subplots(3, 4, figsize=(16, 12)) + +# Row 1: Fluorescence data +axes[0, 0].imshow(sample_nucleus_crop, cmap="gray") +axes[0, 0].set_title("Fluorescence Nucleus") +axes[0, 0].axis("off") + +fluor_nuc_overlay = label2rgb(fluor_nuc_seg, sample_nucleus_crop, bg_label=0) +axes[0, 1].imshow(fluor_nuc_overlay) +axes[0, 1].set_title("Nucleus Segmentation") +axes[0, 1].axis("off") + +axes[0, 2].imshow(sample_membrane_crop, cmap="gray") +axes[0, 2].set_title("Fluorescence Membrane") +axes[0, 2].axis("off") + +fluor_mem_overlay = label2rgb(fluor_mem_seg, sample_membrane_crop, bg_label=0) +axes[0, 3].imshow(fluor_mem_overlay) +axes[0, 3].set_title("Membrane Segmentation") +axes[0, 3].axis("off") + +# Row 2: Virtual stained data (trained) +axes[1, 0].imshow(predicted_nuc_crop, cmap="gray") +axes[1, 0].set_title("Virtual Nucleus (Trained)") +axes[1, 0].axis("off") + +virtual_nuc_overlay = label2rgb(virtual_nuc_seg, predicted_nuc_crop, bg_label=0) +axes[1, 1].imshow(virtual_nuc_overlay) +axes[1, 1].set_title("Nucleus Segmentation") +axes[1, 1].axis("off") + +axes[1, 2].imshow(predicted_mem_crop, cmap="gray") +axes[1, 2].set_title("Virtual Membrane (Trained)") +axes[1, 2].axis("off") + +virtual_mem_overlay = label2rgb(virtual_mem_seg, predicted_mem_crop, bg_label=0) +axes[1, 3].imshow(virtual_mem_overlay) +axes[1, 3].set_title("Membrane Segmentation") +axes[1, 3].axis("off") + +# Row 3: Virtual stained data (pretrained) +axes[2, 0].imshow(predicted_nuc_pretrained_crop, cmap="gray") +axes[2, 0].set_title("Virtual Nucleus (Pretrained)") +axes[2, 0].axis("off") + +pretrained_nuc_overlay = label2rgb(pretrained_nuc_seg, predicted_nuc_pretrained_crop, bg_label=0) +axes[2, 1].imshow(pretrained_nuc_overlay) +axes[2, 1].set_title("Nucleus Segmentation") +axes[2, 1].axis("off") + +axes[2, 2].imshow(predicted_mem_pretrained_crop, cmap="gray") +axes[2, 2].set_title("Virtual Membrane (Pretrained)") +axes[2, 2].axis("off") + +pretrained_mem_overlay = label2rgb(pretrained_mem_seg, predicted_mem_pretrained_crop, bg_label=0) +axes[2, 3].imshow(pretrained_mem_overlay) +axes[2, 3].set_title("Membrane Segmentation") +axes[2, 3].axis("off") + +plt.suptitle(f"Complete Segmentation Comparison - FOV: {sample_fov}", fontsize=16) +plt.tight_layout() +plt.show() + +print("Nucleus segmentation counts:") +print(f" Fluorescence: {len(np.unique(fluor_nuc_seg)) - 1} nuclei") +print(f" Virtual (trained): {len(np.unique(virtual_nuc_seg)) - 1} nuclei") +print(f" Virtual (pretrained): {len(np.unique(pretrained_nuc_seg)) - 1} nuclei") + +print("\nMembrane segmentation counts:") +print(f" Fluorescence: {len(np.unique(fluor_mem_seg)) - 1} objects") +print(f" Virtual (trained): {len(np.unique(virtual_mem_seg)) - 1} objects") +print(f" Virtual (pretrained): {len(np.unique(pretrained_mem_seg)) - 1} objects") + +# %% [markdown] +# Now let's compute metrics across all FOVs + +# %% +# Iterating through the test dataset positions to: +total_positions = len(positions) + +# Initializing the progress bar with the total number of positions +with tqdm(total=total_positions, desc="Processing FOVs") as pbar: + # Iterating through the test dataset positions + for fov, pos in positions: + T, C, Z, Y, X = pos.data.shape + Z_slice = slice(Z // 2, Z // 2 + 1) + # Getting the arrays and the center slices + phase_image = pos.data[:, phase_cidx : phase_cidx + 1, Z_slice] + target_nucleus = pos.data[0, nuc_cidx : nuc_cidx + 1, Z_slice] + target_membrane = pos.data[0, mem_cidx : mem_cidx + 1, Z_slice] + target_nuc_label = pos.data[0, nuc_label_cidx : nuc_label_cidx + 1, Z_slice] + + # normalize the phase + phase_image = normalize_fov(phase_image) + + # Running the prediction for both models + phase_image = torch.from_numpy(phase_image).type(torch.float32) + phase_image = phase_image.to(phase2fluor_model.device) + with torch.inference_mode(): # turn off gradient computation. + predicted_image_phase2fluor = phase2fluor_model(phase_image) + predicted_image_pretrained = pretrained_phase2fluor(phase_image) + + # Loading and Normalizing the target and predictions for both models + predicted_image_phase2fluor = predicted_image_phase2fluor.cpu().numpy().squeeze(0) + predicted_image_pretrained = predicted_image_pretrained.cpu().numpy().squeeze(0) + phase_image = phase_image.cpu().numpy().squeeze(0) + + target_mem = min_max_scale(target_membrane[0, 0]) + target_nuc = min_max_scale(target_nucleus[0, 0]) + + # Normalizing the dataset using min-max scaling + predicted_mem_phase2fluor = min_max_scale(predicted_image_phase2fluor[1, :, :, :].squeeze(0)) + predicted_nuc_phase2fluor = min_max_scale(predicted_image_phase2fluor[0, :, :, :].squeeze(0)) + + predicted_mem_pretrained = min_max_scale(predicted_image_pretrained[1, :, :, :].squeeze(0)) + predicted_nuc_pretrained = min_max_scale(predicted_image_pretrained[0, :, :, :].squeeze(0)) + + ####### Pixel-based Metrics ############ + # Compute microSSIM and Pearson correlation for phase2fluor_model + pbar.set_description(f"Processing FOV {fov} - Computing Pixel Metrics") + pbar.refresh() + ssim_nuc_phase2fluor = micro_structural_similarity(target_nuc, predicted_nuc_phase2fluor) + ssim_mem_phase2fluor = micro_structural_similarity(target_mem, predicted_mem_phase2fluor) + pearson_nuc_phase2fluor = np.corrcoef(target_nuc.flatten(), predicted_nuc_phase2fluor.flatten())[0, 1] + pearson_mem_phase2fluor = np.corrcoef(target_mem.flatten(), predicted_mem_phase2fluor.flatten())[0, 1] + + test_pixel_metrics.loc[len(test_pixel_metrics)] = { + "model": "phase2fluor", + "fov": fov, + "pearson_nuc": pearson_nuc_phase2fluor, + "microSSIM_nuc": ssim_nuc_phase2fluor, + "pearson_mem": pearson_mem_phase2fluor, + "microSSIM_mem": ssim_mem_phase2fluor, + } + # Compute microSSIM and Pearson correlation for pretrained_model + ssim_nuc_pretrained = micro_structural_similarity(target_nuc, predicted_nuc_pretrained) + ssim_mem_pretrained = micro_structural_similarity(target_mem, predicted_mem_pretrained) + pearson_nuc_pretrained = np.corrcoef(target_nuc.flatten(), predicted_nuc_pretrained.flatten())[0, 1] + pearson_mem_pretrained = np.corrcoef(target_mem.flatten(), predicted_mem_pretrained.flatten())[0, 1] + + test_pixel_metrics.loc[len(test_pixel_metrics)] = { + "model": "pretrained_phase2fluor", + "fov": fov, + "pearson_nuc": pearson_nuc_pretrained, + "microSSIM_nuc": ssim_nuc_pretrained, + "pearson_mem": pearson_mem_pretrained, + "microSSIM_mem": ssim_mem_pretrained, + } + + ###### Segmentation based metrics ######### + # Load the manually curated nuclei target label + pbar.set_description(f"Processing FOV {fov} - Computing Segmentation Metrics") + pbar.refresh() + pred_label, target_label = cellpose_segmentation(predicted_nuc_phase2fluor, target_nuc) + # Binary labels + pred_label_binary = pred_label > 0 + target_label_binary = target_label > 0 + + # Use Coco metrics to get mean average precision + coco_metrics = mean_average_precision(pred_label, target_label) + # Find unique number of labels + num_masks_fov = len(np.unique(pred_label)) + + test_segmentation_metrics.loc[len(test_segmentation_metrics)] = { + "model": "phase2fluor", + "fov": fov, + "masks_per_fov": num_masks_fov, + "accuracy": accuracy(pred_label_binary, target_label_binary, task="binary").item(), + "dice": dice_score( + pred_label_binary.long()[None], + target_label_binary.long()[None], + num_classes=2, + input_format="index", + average="micro", + ).item(), + "jaccard": jaccard_index(pred_label_binary, target_label_binary, task="binary").item(), + "mAP": coco_metrics["map"].item(), + "mAP_50": coco_metrics["map_50"].item(), + "mAP_75": coco_metrics["map_75"].item(), + "mAR_100": coco_metrics["mar_100"].item(), + } + + pred_label, target_label = cellpose_segmentation(predicted_nuc_pretrained, target_nuc) + + # Binary labels + pred_label_binary = pred_label > 0 + target_label_binary = target_label > 0 + + # Use Coco metrics to get mean average precision + coco_metrics = mean_average_precision(pred_label, target_label) + # Find unique number of labels + num_masks_fov = len(np.unique(pred_label)) + + test_segmentation_metrics.loc[len(test_segmentation_metrics)] = { + "model": "phase2fluor_pretrained", + "fov": fov, + "masks_per_fov": num_masks_fov, + "accuracy": accuracy(pred_label_binary, target_label_binary, task="binary").item(), + "dice": dice_score( + pred_label_binary.long()[None], + target_label_binary.long()[None], + num_classes=2, + input_format="index", + average="micro", + ).item(), + "jaccard": jaccard_index(pred_label_binary, target_label_binary, task="binary").item(), + "mAP": coco_metrics["map"].item(), + "mAP_50": coco_metrics["map_50"].item(), + "mAP_75": coco_metrics["map_75"].item(), + "mAR_100": coco_metrics["mar_100"].item(), + } + + # Save the predictions and segmentations + position = segmentation_store.create_position(*Path(fov).parts[-3:]) + output_array = np.zeros((T, 3, 1, Y, X), dtype=np.float32) + output_array[0, 0, 0] = predicted_nuc_pretrained + output_array[0, 1, 0] = predicted_mem_pretrained + output_array[0, 2, 0] = np.array(pred_label) + position.create_image("0", output_array) + + # Update the progress bar + pbar.set_description("Processing FOVs") + pbar.update(1) + +# Close the OME-Zarr files +test_dataset.close() +segmentation_store.close() +# %% +# Save the test metrics into a dataframe +pixel_metrics_path = training_top_dir / "06_image_translation/VS_metrics_pixel.csv" +segmentation_metrics_path = training_top_dir / "06_image_translation/VS_metrics_segments.csv" +test_pixel_metrics.to_csv(pixel_metrics_path) +test_segmentation_metrics.to_csv(segmentation_metrics_path) + +# %% [markdown] tags=[] +#
+ +#

Task 2.3 Compare the model's metrics

+# In the previous section, we computed the pixel-based metrics and segmentation-based metrics. +# Now we will compare the performance of the model you trained with the pretrained model by plotting the boxplots. + +# After you plot the metrics answer the following: +#
    +#
  • What do these metrics tells us about the performance of the model?
  • +#
  • How do you interpret the differences in the metrics between the models?
  • +#
  • How is your model compared to the pretrained model? How can you improve it?
  • +#
+#
+ +# %% +# Show boxplot of the metrics +# Boxplot of the metrics +test_pixel_metrics.boxplot( + by="model", + column=["pearson_nuc", "microSSIM_nuc", "pearson_mem", "microSSIM_mem"], + rot=30, + figsize=(8, 8), +) +plt.suptitle("Model Pixel Metrics") +plt.show() +# Show boxplot of the metrics +# Boxplot of the metrics +test_segmentation_metrics.boxplot( + by="model", + column=["jaccard", "accuracy", "mAP_75", "mAP_50"], + rot=30, + figsize=(8, 8), +) +plt.suptitle("Model Segmentation Metrics") +plt.show() + +# %% [markdown] tags=["task"] +#
+#

Questions

+#
    +#
  • What do these metrics tells us about the performance of the model?
  • +#
  • How do you interpret the differences in the metrics between the models?
  • +#
  • How is your model compared to the pretrained model? How can you improve it?
  • +#
+#
+ +# %% [markdown] +# ### Plotting the predictions and segmentations +#
+# +#

Task 2.4: Visualize the predictions and segmentations

+# Here we will plot the predictions and segmentations side by side for the pretrained and trained models.
+#
    +#
  • How does your model, the pretrained model and the ground truth compare?
  • +#
  • How do the segmentations compare?
  • +#
+# Feel free to modify the crop size and Y,X slicing to view different areas of the FOV +#
+# %% tags=["task"] + +# Get the shape of the 2D image +Y, X = phase_image.shape[-2:] +######## TODO ########## +# Modify the crop size and Y,X slicing to view different areas of the FOV + +crop = 256 +y_slice = slice(Y // 2 - crop // 2, Y // 2 + crop // 2) +x_slice = slice(X // 2 - crop // 2, X // 2 + crop // 2) +####################### +# Plotting side by side comparisons +fig, axs = plt.subplots(4, 3, figsize=(15, 20)) + +# First row: phase_image, target_nuc, target_mem +axs[0, 0].imshow(phase_image[0, 0, y_slice, x_slice], cmap="gray") +axs[0, 0].set_title("Phase Image") +axs[0, 1].imshow(target_nuc[y_slice, x_slice], cmap="gray") +axs[0, 1].set_title("Target Nucleus") +axs[0, 2].imshow(target_mem[y_slice, x_slice], cmap="gray") +axs[0, 2].set_title("Target Membrane") + +# Second row: target_nuc, pred_nuc_phase2fluor, pred_nuc_pretrained +axs[1, 0].imshow(target_nuc[y_slice, x_slice], cmap="gray") +axs[1, 0].set_title("Target Nucleus") +axs[1, 1].imshow(predicted_nuc_phase2fluor[y_slice, x_slice], cmap="gray") +axs[1, 1].set_title("Pred Nucleus Phase2Fluor") +axs[1, 2].imshow(predicted_nuc_pretrained[y_slice, x_slice], cmap="gray") +axs[1, 2].set_title("Pred Nucleus Pretrained") + +# Third row: target_mem, pred_mem_phase2fluor, pred_mem_pretrained +axs[2, 0].imshow(target_mem[y_slice, x_slice], cmap="gray") +axs[2, 0].set_title("Target Membrane") +axs[2, 1].imshow(predicted_mem_phase2fluor[y_slice, x_slice], cmap="gray") +axs[2, 1].set_title("Pred Membrane Phase2Fluor") +axs[2, 2].imshow(predicted_mem_pretrained[y_slice, x_slice], cmap="gray") +axs[2, 2].set_title("Pred Membrane Pretrained") + +# Fourth row: target_nuc, segment_nuc, segment_nuc2 +axs[3, 0].imshow(target_nuc[y_slice, x_slice], cmap="gray") +axs[3, 0].set_title("Target Nucleus") +axs[3, 1].imshow(label2rgb(np.array(target_label[y_slice, x_slice], dtype="int")), cmap="gray") +axs[3, 1].set_title("Segmented Nucleus (Target)") +axs[3, 2].imshow(label2rgb(np.array(pred_label[y_slice, x_slice], dtype="int")), cmap="gray") +axs[3, 2].set_title("Segmented Nucleus") + +# Hide axes ticks +for ax in axs.flat: + ax.set_xticks([]) + ax.set_yticks([]) + +plt.tight_layout() +plt.show() + + +# %% [markdown] tags=[] +#
+ +#

Checkpoint 2

+# +# Congratulations! You have completed the second checkpoint. You have: +# - Visualized the predictions and segmentations of the model.
+# - Evaluated the performance of the model using pixel-based metrics and segmentation-based metrics.
+# - Compared the performance of the model you trained with the pretrained model.
+# +#
+ +# %% [markdown] tags=[] +#
+# +# ### Task 2.5: Evaluate a fluorescence to phase model +# In this section, we will explore the inverse transformation using fluorescence images +# (nuclei + membrane) to predict the phase image. +# +#

Learning Goals:

+#
    +#
  • Understand the concept of fluorescence to phase transformations in image translation
  • +#
  • Load a pretrained model for the reverse task (fluor → phase)
  • +#
  • Compare input fluorescence channels with predicted phase
  • +#
  • Analyze why the phase prediction is not perfect
  • +#
+# We'll use a pretrained model that was trained to predict phase from fluorescence channels. +#
+ +# %% [markdown] tags=[] +#
+# +#

Questions

+#
    +#
  • How much information is lost in the phase to fluorescence transformation?
  • +#
  • Why might perfect reconstruction not be possible?
  • +#
  • Can multiple phase patterns produce similar fluorescence signals?
  • +#
+#
+ +# %% +# Path to the pretrained fluorescence to phase model checkpoint +fluor2phase_model_path = top_dir / "pretrained_models/AIMBL_Demo/fluor2phase_step668.ckpt" + + +# %% tags=["task"] +# Load a pretrained model for fluorescence to phase translation +from pathlib import Path + +import torch + +# ####################### +# ##### TODO ######## +# ####################### +# TODO: Load the pretrained fluorescence to phase model +# HINT: Look for pretrained models in the VisCy repository or use a model checkpoint +# HINT: The model should take 2 input channels (nuclei + membrane) and output 1 channel (phase) +# HINT: Use similar architecture as before but with different input/output channels + +# For now, we'll create a placeholder - replace with actual model loading +print("Loading pretrained fluorescence-to-phase model...") + +# TODO: Replace this with actual model loading code +fluor2phase_config = dict( + in_channels=..., # Nuclei + Membrane channels + out_channels=..., # Phase channel + encoder_blocks=[3, 3, 9, 3], + dims=[96, 192, 384, 768], + decoder_conv_blocks=2, + stem_kernel_size=(1, 2, 2), + in_stack_depth=1, +) +fluor2phase_model = VSUNet.load_from_checkpoint( + fluor2phase_model_path, model_config=fluor2phase_config, architecture="fcmae" +) +assert fluor2phase_model is not None, ( + "Fluorescence to phase model not loaded. Check the model config,and the path to the model checkpoint." +) +fluor2phase_model.eval() + +# %% tags=["task"] +# Test the fluorescence to phase model on our test data + +source_channel_fluor = ["TODO", "TODO"] +target_channel_labelfree = ["TODO"] + +test_data_fluor2phase = HCSDataModule( + test_data_path, + source_channel=source_channel_fluor, + target_channel=target_channel_labelfree, + z_window_size=1, + batch_size=1, + num_workers=8, +) +test_data_fluor2phase.setup("test") + + +# Get a test sample +sample = next(iter(test_data_fluor2phase.test_dataloader())) + +# ####################### +# ##### TODO ######## +# ####################### +# TODO: Extract the input channels (fluorescence) and target (phase) +# HINT: Print the keys of the `sample` dictionary +# HINT: Input should be nuclei and membrane channels concatenated +# HINT: Target should be the original phase image + +fluor_input = ... # TODO: Source +target_phase = ... # TODO: Target + +# TODO: Make prediction with the fluorescence to phase model +# NOTE: The `fluor2phase_model`, returns a tuple. Select the first item with `[0]` +with torch.inference_mode(): + predicted_phase = ... + +# ####################### +# ##### TODO ######## +# ####################### +# Calculate metrics between predicted and target phase +# HINT: Use SSIM and Pearson correlation as before + +# TODO: Normalize data range to 0-1 +###### YOUR CODE HERE ###### + +# TODO: Calculate SSIM and Pearson correlation +###### YOUR CODE HERE ###### + +# TODO: Print metrics +print("Phase Reconstruction Metrics:") +print(f"SSIM: {ssim_phase:.3f}") +print(f"Pearson Correlation: {pearson_phase:.3f}") + + +# %% tags=["solution"] +# Load a pretrained model for fluorescence to phase translation +from pathlib import Path + +import torch + +# Load the pretrained fluorescence to phase model +print("Loading pretrained fluorescence-to-phase model...") + +# Note: This assumes a pretrained model is available. In practice, you would: +# 1. Download from VisCy releases or train your own +# 2. Adjust the path accordingly + +# For demonstration, we'll create a model with the correct architecture +fluor2phase_config = dict( + in_channels=2, # Nuclei + Membrane channels + out_channels=1, # Phase channel + encoder_blocks=[3, 3, 9, 3], + dims=[96, 192, 384, 768], + decoder_conv_blocks=2, + stem_kernel_size=(1, 2, 2), + in_stack_depth=1, +) + +# Create the fluorescence to phase model architecture +print("Fluorescence-to-phase model created (note: using untrained model for demonstration)") +print("In practice, load a pretrained checkpoint for meaningful results") + +print("\nLoading pretrained fluorescence-to-phase model...") +fluor2phase_model_path = top_dir / "pretrained_models/AIMBL_Demo/fluor2phase_step668.ckpt" +assert fluor2phase_model_path.exists(), "Fluorescence-to-phase model checkpoint not found. Please check the path." +fluor2phase_model = VSUNet.load_from_checkpoint( + fluor2phase_model_path, model_config=fluor2phase_config, architecture="fcmae" +) +fluor2phase_model.eval() + +# %% tags=["solution"] +# Test the fluorescence to phase model on our test data + +# First-use imports for this cell (kept here so the solution notebook works +# top-to-bottom even when earlier task-tagged cells are stripped). +from skimage import metrics # noqa: E402 +from skimage.exposure import rescale_intensity # noqa: E402 + +source_channel_fluor = ["Nucl", "Mem"] +target_channel_labelfree = ["Phase3D"] + +test_data_fluor2phase = HCSDataModule( + test_data_path, + source_channel=source_channel_fluor, + target_channel=target_channel_labelfree, + z_window_size=1, + batch_size=1, + num_workers=8, +) +test_data_fluor2phase.setup("test") + +# Get a test sample +sample = next(iter(test_data_fluor2phase.test_dataloader())) + +# Extract input channels (fluorescence nuclei and membrane) and target (phase) +fluor_input = sample["source"].to(fluor2phase_model.device) +target_image = sample["target"].cpu().numpy().squeeze(0) + +# Run inference +with torch.inference_mode(): + predicted_phase = fluor2phase_model(fluor_input)[0] + +fluor_input = fluor_input.cpu().numpy() +predicted_image = predicted_phase.cpu().numpy().squeeze(0) +target_phase = rescale_intensity(target_image[0, 0], out_range=(0, 1)) +predicted_phase = rescale_intensity(predicted_image[0, 0], out_range=(0, 1)) +ssim_phase = metrics.structural_similarity(target_phase, predicted_phase, data_range=1) +pearson_phase = np.corrcoef(target_phase.flatten(), predicted_phase.flatten())[0, 1] + +print("Phase Reconstruction Metrics:") +print(f"SSIM: {ssim_phase:.3f}") +print(f"Pearson Correlation: {pearson_phase:.3f}") + +# %% +# Visualize the fluorescence to phase transformation results +# TODO: Visualize the fluorescence to phase transformation results. Modify is as you see fit. + +fig, axs = plt.subplots(2, 3, figsize=(15, 10)) + +axs[0, 0].imshow(fluor_input[0, 0, 0], cmap="gray") +axs[0, 0].set_title("Input: Nuclei Channel") +axs[0, 1].imshow(fluor_input[0, 1, 0], cmap="gray") +axs[0, 1].set_title("Input: Membrane Channel") +axs[0, 2].imshow(fluor_input[0, 0, 0] + fluor_input[0, 1, 0], cmap="gray") +axs[0, 2].set_title("Combined Fluorescence\n(Nuclei + Membrane)") + +axs[1, 0].imshow(target_phase, cmap="gray") +axs[1, 0].set_title("Target Phase Image") +axs[1, 1].imshow(predicted_phase, cmap="gray") +axs[1, 1].set_title(f"Predicted Phase\nSSIM: {ssim_phase:.3f}") +axs[1, 2].imshow(np.abs(target_phase - predicted_phase), cmap="magma") +axs[1, 2].set_title("Absolute Difference\n|Target - Predicted|") + +for ax in axs.flat: + ax.set_xticks([]) + ax.set_yticks([]) + +plt.tight_layout() +plt.show() + +# %% [markdown] tags=[] +#
+#

Analysis Questions: Why is Phase Reconstruction Imperfect?

+# +# Looking at your results, consider these questions: +# +#
    +#
  • Does the fluorescence image contain all the information needed to reconstruct the phase?
  • +#
  • What structures are visible in phase but not in fluorescence channels?
  • +#
  • Which has higher information content: phase or fluorescence images?
  • +#
  • What does the reconstruction error map tell you about what's difficult to predict?
  • +#
+#
+ +# %% [markdown] tags=[] +#
+#

Key Insights from Fluorescence to Phase Model

+# +# This exploration reveals fundamental limitations in image-to-image translation: +#
    +#
  • Phase images contain rich structural information about unlabeled cellular components
  • +#
  • Fluorescence only captures specific labeled structures (nuclei, membranes,etc.)
  • +#
  • The fluorescence to phase model is an ill-posed problem - multiple phase images could produce similar fluorescence patterns
  • +#
  • Models can only predict based on correlations learned during training
  • +#
  • Structural details not correlated with fluorescence signals cannot be recovered
  • +#
+# +# #### Now, let's return to the `phase2fluor` model! +# +#
+ +# %% [markdown] tags=[] +#
+#

Bonus: Test Time Augmentation (TTA)

+# +# Test Time Augmentation is a technique where you apply multiple augmentations to a single test image, +# make predictions on each augmented version, and then combine the results (usually by averaging). +# +# **In this section we will:** +#
    +#
  • Use `Rotate90d` and `Flipd` for deterministic transformations
  • +#
  • Apply transforms, make predictions, then apply inverse transforms
  • +#
  • Average all predictions to get the final TTA result that is more robust to geometric variations.
  • +#
+# +# Reference: N.Moshkov (2020) https://www.nature.com/articles/s41598-020-61808-3 +# +# Hint: You can use the `Rotate90` and `Flip` transforms from MONAI. +# Example forward transform: `Rotate90(k=1, spatial_axes=(-1, -2))` +# Example inverse transform: `Rotate90(k=3, spatial_axes=(-1, -2))` +# +#
+ +# %% tags=["task"] +from monai.transforms import ( + Flip, + Rotate90, +) + +# Get a test sample +sample = next(iter(test_data.test_dataloader())) +source_tensor = sample["source"].to(phase2fluor_model.device) +target_tensor = sample["target"] +target_nuc = target_tensor[0, 0].cpu().numpy() +target_mem = target_tensor[0, 1].cpu().numpy() + +# Saving the single prediction without TTA for later comparison +with torch.inference_mode(): + single_pred = phase2fluor_model(source_tensor) + single_pred_nuc = single_pred[0, 0].cpu().numpy() + single_pred_mem = single_pred[0, 1].cpu().numpy() + +# TODO: Define TTA transforms using MONAI as a list of tuples (forward, inverse) +###### YOUR CODE HERE ###### +transform_list = [("TODO", "TODO")] + +# TODO: Apply test-time augmentation +# 1. Get original prediction (no augmentation) +# 2. For each transform: +# - Apply transform to input +# - Run inference +# - De-apply transform to prediction +# 3. Average all predictions + +predictions = [] + +for forward_transform, inverse_transform in transform_list: + # Apply transform to each sample in batch + augmented_batch = [] + for i in range(source_tensor.shape[0]): + # Apply the forward and store them + ###### YOUR CODE HERE ###### + aug_img = ... + augmented_batch.append(aug_img) + augmented_source = torch.stack(augmented_batch).to(source_tensor.device) + + # TODO: Run inference on augmented input + with torch.inference_mode(): + ###### YOUR CODE HERE ###### + augmented_pred = ... + + # TODO: De-apply transform to prediction + deaugmented_batch = [] + for i in range(augmented_pred.shape[0]): + ###### YOUR CODE HERE ###### + deaug_pred = ... + deaugmented_pred = torch.stack(deaugmented_batch) + + predictions.append(deaugmented_pred.cpu().numpy()) + +# TODO: Average all predictions or take the median +###### YOUR CODE HERE ###### +averaged_pred = ... + +# TODO: Extract nucleus and membrane predictions +###### YOUR CODE HERE ###### +tta_pred_nuc = ... +tta_pred_mem = ... + +# %% tags=["task"] +# TODO: Compare TTA results with single prediction +# Calculate metrics (SSIM, Pearson correlation) for both approaches. Do not forget to normalize the data range to 0-1. + +# TODO Normalize data range to 0-1 +###### YOUR CODE HERE ###### + +# TODO Calculate metrics +###### YOUR CODE HERE ###### + +# TODO # TTA prediction metrics +###### YOUR CODE HERE ###### + +# Print comparison +print("\nMetrics Comparison:") +print(f"{'Metric':<20} {'Single':<10} {'TTA':<10} {'Improvement':<12}") +print("-" * 55) +print(f"{'SSIM Nucleus':<20} {ssim_nuc_single:.3f} {ssim_nuc_tta:.3f} {ssim_nuc_tta - ssim_nuc_single:+.3f}") +print(f"{'SSIM Membrane':<20} {ssim_mem_single:.3f} {ssim_mem_tta:.3f} {ssim_mem_tta - ssim_mem_single:+.3f}") +print( + f"{'Pearson Nucleus':<20} {pearson_nuc_single:.3f} {pearson_nuc_tta:.3f} {pearson_nuc_tta - pearson_nuc_single:+.3f}" +) +print( + f"{'Pearson Membrane':<20} {pearson_mem_single:.3f} {pearson_mem_tta:.3f} {pearson_mem_tta - pearson_mem_single:+.3f}" +) + +# %% tags=["solution"] +# TTA implementation + metrics in one cell: the metrics below reference +# tta_pred_nuc/tta_pred_mem, so we run the TTA prediction first within the +# same solution cell. (When the notebook is generated, task-tagged cells are +# stripped, so we cannot rely on a later cell to populate these variables.) + +from monai.transforms import ( # noqa: E402, F811 + Flip, + Rotate90, +) + +# Get a test sample +sample = next(iter(test_data.test_dataloader())) +source_tensor = sample["source"].to(phase2fluor_model.device) +target_tensor = sample["target"] +target_nuc = target_tensor[0, 0].cpu().numpy() +target_mem = target_tensor[0, 1].cpu().numpy() + +predictions = [] + +# Original prediction without augmentation +with torch.inference_mode(): + original_pred = phase2fluor_model(source_tensor) + predictions.append(original_pred.cpu().numpy()) + +# Define the TTA transforms and the inverse transforms as a list of tuples (forward, inverse) +transform_list = [ + (Rotate90(k=1, spatial_axes=(-1, -2)), Rotate90(k=3, spatial_axes=(-1, -2))), + (Rotate90(k=2, spatial_axes=(-1, -2)), Rotate90(k=2, spatial_axes=(-1, -2))), + (Rotate90(k=3, spatial_axes=(-1, -2)), Rotate90(k=1, spatial_axes=(-1, -2))), + (Flip(spatial_axis=-2), Flip(spatial_axis=-2)), + (Flip(spatial_axis=-1), Flip(spatial_axis=-1)), +] + +for forward_transform, inverse_transform in transform_list: + # Apply transform to each sample in batch + augmented_batch = [] + for i in range(source_tensor.shape[0]): + img = source_tensor[i].cpu().numpy() + aug_img = forward_transform(img) + augmented_batch.append(aug_img) + augmented_source = torch.stack(augmented_batch).to(source_tensor.device) + + # Run inference on augmented input + with torch.inference_mode(): + augmented_pred = phase2fluor_model(augmented_source) + + # De-apply transform to prediction + deaugmented_batch = [] + for i in range(augmented_pred.shape[0]): + pred = augmented_pred[i].cpu().numpy() + deaug_pred = inverse_transform(pred) + deaugmented_batch.append(deaug_pred) + deaugmented_pred = torch.stack(deaugmented_batch) + + predictions.append(deaugmented_pred.cpu().numpy()) + +# Average all predictions +averaged_pred = np.stack(predictions).mean(axis=0) + +# Extract nucleus and membrane predictions +tta_pred_nuc = averaged_pred[0, 0] +tta_pred_mem = averaged_pred[0, 1] + +# Compare with single prediction (no TTA) +with torch.inference_mode(): + single_pred = phase2fluor_model(source_tensor) + single_pred_nuc = single_pred[0, 0].cpu().numpy() + single_pred_mem = single_pred[0, 1].cpu().numpy() + +# Normalize data range to 0-1 before computing metrics +target_nuc[0] = rescale_intensity(target_nuc[0], in_range="image", out_range=(0, 1)) +single_pred_nuc[0] = rescale_intensity(single_pred_nuc[0], in_range="image", out_range=(0, 1)) +target_mem[0] = rescale_intensity(target_mem[0], in_range="image", out_range=(0, 1)) +single_pred_mem[0] = rescale_intensity(single_pred_mem[0], in_range="image", out_range=(0, 1)) +tta_pred_nuc[0] = rescale_intensity(tta_pred_nuc[0], in_range="image", out_range=(0, 1)) +tta_pred_mem[0] = rescale_intensity(tta_pred_mem[0], in_range="image", out_range=(0, 1)) + +# Calculate metrics +ssim_nuc_single = metrics.structural_similarity(target_nuc[0], single_pred_nuc[0], data_range=1) +ssim_mem_single = metrics.structural_similarity(target_mem[0], single_pred_mem[0], data_range=1) +pearson_nuc_single = np.corrcoef(target_nuc[0].flatten(), single_pred_nuc[0].flatten())[0, 1] +pearson_mem_single = np.corrcoef(target_mem[0].flatten(), single_pred_mem[0].flatten())[0, 1] + +# TTA prediction metrics +ssim_nuc_tta = metrics.structural_similarity(target_nuc[0], tta_pred_nuc[0], data_range=1) +ssim_mem_tta = metrics.structural_similarity(target_mem[0], tta_pred_mem[0], data_range=1) +pearson_nuc_tta = np.corrcoef(target_nuc[0].flatten(), tta_pred_nuc[0].flatten())[0, 1] +pearson_mem_tta = np.corrcoef(target_mem[0].flatten(), tta_pred_mem[0].flatten())[0, 1] + +# Print comparison +print("\nMetrics Comparison:") +print(f"{'Metric':<20} {'Single':<10} {'TTA':<10} {'Improvement':<12}") +print("-" * 55) +print(f"{'SSIM Nucleus':<20} {ssim_nuc_single:.3f} {ssim_nuc_tta:.3f} {ssim_nuc_tta - ssim_nuc_single:+.3f}") +print(f"{'SSIM Membrane':<20} {ssim_mem_single:.3f} {ssim_mem_tta:.3f} {ssim_mem_tta - ssim_mem_single:+.3f}") +print( + f"{'Pearson Nucleus':<20} {pearson_nuc_single:.3f} {pearson_nuc_tta:.3f} {pearson_nuc_tta - pearson_nuc_single:+.3f}" +) +print( + f"{'Pearson Membrane':<20} {pearson_mem_single:.3f} {pearson_mem_tta:.3f} {pearson_mem_tta - pearson_mem_single:+.3f}" +) + +# %% +# TODO: Modify as you see fit to compute the metrics on the full FOV. +# Visualize the comparison +# Modify as you see fit to visualize the results + +fig, axs = plt.subplots(3, 3, figsize=(15, 15)) + +# First row: Input phase and targets +axs[0, 0].imshow(source_tensor[0, 0, 0].cpu().numpy(), cmap="gray") +axs[0, 0].set_title("Input Phase") +axs[0, 1].imshow(target_nuc[0], cmap="gray") +axs[0, 1].set_title("Target Nucleus") +axs[0, 2].imshow(target_mem[0], cmap="gray") +axs[0, 2].set_title("Target Membrane") + +# Second row: Single predictions +axs[1, 0].imshow(source_tensor[0, 0, 0].cpu().numpy(), cmap="gray") +axs[1, 0].set_title("Input Phase") +axs[1, 1].imshow(single_pred_nuc[0], cmap="gray") +axs[1, 1].set_title(f"Single Pred Nucleus\nSSIM: {ssim_nuc_single:.3f}") +axs[1, 2].imshow(single_pred_mem[0], cmap="gray") +axs[1, 2].set_title(f"Single Pred Membrane\nSSIM: {ssim_mem_single:.3f}") + +# Third row: TTA predictions +axs[2, 0].imshow(source_tensor[0, 0, 0].cpu().numpy(), cmap="gray") +axs[2, 0].set_title("Input Phase") +axs[2, 1].imshow(tta_pred_nuc[0], cmap="gray") +axs[2, 1].set_title(f"TTA Pred Nucleus\nSSIM: {ssim_nuc_tta:.3f}") +axs[2, 2].imshow(tta_pred_mem[0], cmap="gray") +axs[2, 2].set_title(f"TTA Pred Membrane\nSSIM: {ssim_mem_tta:.3f}") + +# Remove ticks +for ax in axs.flat: + ax.set_xticks([]) + ax.set_yticks([]) + +plt.tight_layout() +plt.show() + +# %% [markdown] tags=[] +#
+# +#

Discussion Questions for Test Time Augmentation

+# +#
    +#
  • Did TTA improve the metrics? By how much?
  • +#
  • What are the trade-offs of using TTA? (hint: think about computation time vs. accuracy)
  • +#
  • When would TTA be most beneficial in fluorescence microscopy?
  • +#
  • How could you modify the TTA strategy to be more effective for this specific virtual staining task?
  • +#
  • What other MONAI transforms could be useful for TTA in this context? (e.g., slight rotations, scaling)
  • +#
  • Is there any hallucinations that are removed with TTA?
  • +#
+#
+ +# %% [markdown] tags=[] +#
+#

Bonus Section Complete!

+# +# You have successfully implemented Test Time Augmentation using MONAI transforms! +# +# Key takeaways: +#
    +#
  • TTA is particularly useful when prediction quality is critical and computational budget allows
  • +#
  • Multiple geometric augmentations can reduce prediction variance and improve robustness
  • +#
  • TTA leverages deterministic transforms (`Rotate90d`, `Flipd`) instead of random ones
  • +#
  • The computational cost increases linearly with the number of TTA transforms
  • +#
+#
+ +# %% [markdown] tags=[] +# # Part 3: Visualizing the encoder and decoder features & exploring the model's range of validity +# +# - In this section, we will visualize the encoder and decoder features of the model you trained. +# - We will also explore the model's range of validity by looking at the feature maps of the encoder and decoder. +# +# %% [markdown] tags=[] +#
+#

Task 3.1: Let's look at what the model is learning

+# +# - If you are unfamiliar with Principal Component Analysis (PCA), you can read up here
+# - Run the next cells. We will visualize the encoder feature maps of the trained model. +# We will use PCA to visualize the feature maps by mapping the first 3 principal components to a colormap `Color`
+# +# +#
+ +# %% +""" +Script to visualize the encoder feature maps of a trained model. +Using PCA to visualize feature maps is inspired by +https://doi.org/10.48550/arXiv.2304.07193 (Oquab et al., 2023). +""" +from typing import NamedTuple # noqa: E402 + +from monai.networks.layers import GaussianFilter # noqa: E402 +from skimage.exposure import rescale_intensity # noqa: E402 +from sklearn.decomposition import PCA # noqa: E402 + + +def feature_map_pca(feature_map: np.array, n_components: int = 8) -> PCA: + """ + Compute PCA on a feature map. + :param np.array feature_map: (C, H, W) feature map + :param int n_components: number of components to keep + :return: PCA: fit sklearn PCA object + """ + # (C, H, W) -> (C, H*W) + feat = feature_map.reshape(feature_map.shape[0], -1) + pca = PCA(n_components=n_components) + pca.fit(feat) + return pca + + +def pcs_to_rgb(feat: np.ndarray, n_components: int = 8) -> np.ndarray: + pca = feature_map_pca(feat[0], n_components=n_components) + pc_first_3 = pca.components_[:3].reshape(3, *feat.shape[-2:]) + return np.stack([rescale_intensity(pc, out_range=(0, 1)) for pc in pc_first_3], axis=-1) + + +# %% +# Load the test dataset +test_data_path = top_dir / "test/a549_hoechst_cellmask_test.zarr" +test_dataset = open_ome_zarr(test_data_path) + +# Looking at the test dataset +print("Test dataset:") +test_dataset.print_tree() + +# %% [markdown] tags=[] +#
+# +# - Change the `fov` and `crop` size to visualize the feature maps of the encoder and decoder
+# Note: the crop should be a multiple of 384 +#
+# %% +# Load one position +row = 0 +col = 0 +center_index = 2 +n = 1 +crop = 384 * n +fov = 10 + +# normalize phase +norm_meta = test_dataset.zattrs["normalization"]["Phase3D"]["dataset_statistics"] + +# Get the OME-Zarr metadata +Y, X = test_dataset[f"0/0/{fov}"].data.shape[-2:] +test_dataset.channel_names +phase_idx = test_dataset.channel_names.index("Phase3D") +assert crop // 2 < Y and crop // 2 < Y, "Crop size larger than the image. Check the image shape" + +phase_img = test_dataset[f"0/0/{fov}/0"][ + :, + phase_idx : phase_idx + 1, + 0:1, + Y // 2 - crop // 2 : Y // 2 + crop // 2, + X // 2 - crop // 2 : X // 2 + crop // 2, +] +fluo = test_dataset[f"0/0/{fov}/0"][ + 0, + 1:3, + 0, + Y // 2 - crop // 2 : Y // 2 + crop // 2, + X // 2 - crop // 2 : X // 2 + crop // 2, +] + +phase_img = (phase_img - norm_meta["median"]) / norm_meta["iqr"] +plt.imshow(phase_img[0, 0, 0], cmap="gray") + +# %% [markdown] tags=[] +#
+# For the following tasks we will use the pretrained model to extract the encoder and decoder features
+# Extra: If you are done with the whole checkpoint, you can try to look at what your trained model learned. +#
+# %% + +# Loading the pretrained model +pretrained_model_ckpt = top_dir / "pretrained_models/VSCyto2D/epoch=399-step=23200.ckpt" +# model config as before +phase2fluor_config = dict( + in_channels=1, + out_channels=2, + encoder_blocks=[3, 3, 9, 3], + dims=[96, 192, 384, 768], + decoder_conv_blocks=2, + stem_kernel_size=(1, 2, 2), + in_stack_depth=1, + pretraining=False, +) + +# load model +model = VSUNet.load_from_checkpoint( + pretrained_model_ckpt, + architecture="UNeXt2_2D", + model_config=phase2fluor_config.copy(), + accelerator="gpu", +) + +# %% tags=[] +# Extract features +with torch.inference_mode(): + # encoder + encoder_features = model.model.encoder(torch.from_numpy(phase_img.astype(np.float32)).to(model.device))[0] + encoder_features_np = [f.detach().cpu().numpy() for f in encoder_features] + + # Print the encoder features shapes + for f in encoder_features_np: + print(f.shape) + + # decoder + features = encoder_features.copy() + features.reverse() + feat = features[0] + features.append(None) + decoder_features_np = [] + for skip, stage in zip(features[1:], model.model.decoder.decoder_stages): + feat = stage(feat, skip) + decoder_features_np.append(feat.detach().cpu().numpy()) + for f in decoder_features_np: + print(f.shape) + prediction = model.model.head(feat).detach().cpu().numpy() + + +# Defining the colors for plotting +class Color(NamedTuple): + r: float + g: float + b: float + + +# Defining the colors for plottting the PCA +BOP_ORANGE = Color(0.972549, 0.6784314, 0.1254902) +BOP_BLUE = Color(BOP_ORANGE.b, BOP_ORANGE.g, BOP_ORANGE.r) +GREEN = Color(0.0, 1.0, 0.0) +MAGENTA = Color(1.0, 0.0, 1.0) + + +# Defining the functions to rescale the image and composite the nuclear and membrane images +def rescale_clip(image: torch.Tensor) -> np.ndarray: + return rescale_intensity(image, out_range=(0, 1))[..., None].repeat(3, axis=-1) + + +def composite_nuc_mem(image: torch.Tensor, nuc_color: Color, mem_color: Color) -> np.ndarray: + c_nuc = rescale_clip(image[0]) * nuc_color + c_mem = rescale_clip(image[1]) * mem_color + return rescale_intensity(c_nuc + c_mem, out_range=(0, 1)) + + +def clip_p(image: np.ndarray) -> np.ndarray: + return rescale_intensity(image.clip(*np.percentile(image, [1, 99]))) + + +def clip_highlight(image: np.ndarray) -> np.ndarray: + return rescale_intensity(image.clip(0, np.percentile(image, 99.5))) + + +# Plot the PCA to RGB of the feature maps +f, ax = plt.subplots(10, 1, figsize=(5, 25)) +n_components = 4 +ax[0].imshow(phase_img[0, 0, 0], cmap="gray") +ax[0].set_title(f"Phase {phase_img.shape[1:]}") +ax[-1].imshow(clip_p(composite_nuc_mem(fluo, GREEN, MAGENTA))) +ax[-1].set_title("Fluorescence") + +for level, feat in enumerate(encoder_features_np): + ax[level + 1].imshow(pcs_to_rgb(feat, n_components=n_components)) + ax[level + 1].set_title(f"Encoder stage {level + 1} {feat.shape[1:]}") + +for level, feat in enumerate(decoder_features_np): + ax[5 + level].imshow(pcs_to_rgb(feat, n_components=n_components)) + ax[5 + level].set_title(f"Decoder stage {level + 1} {feat.shape[1:]}") + +pred_comp = composite_nuc_mem(prediction[0, :, 0], BOP_BLUE, BOP_ORANGE) +ax[-2].imshow(clip_p(pred_comp)) +ax[-2].set_title(f"Prediction {prediction.shape[1:]}") + +for a in ax.ravel(): + a.axis("off") +plt.tight_layout() + +# %% [markdown] tags=["task"] +#
+# +# ### Task 3.2: Select a sample batch to test the range of validty of the model +# - Run the next cell to setup the your dataloader for `test`
+# - Select a test batch from the `test_dataloader` by changing the `batch_number`
+# - Examine the plot of the source and target images of the batch
+# +# Note the 2D images have different focus
+#
+ +# %% +YX_PATCH_SIZE = (256 * 2, 256 * 2) +source_channel = ["Phase3D"] +target_channel = ["Nucl", "Mem"] + +normalizations = [ + NormalizeSampled( + keys=source_channel, + level="fov_statistics", + subtrahend="mean", + divisor="std", + ), + NormalizeSampled( + keys=target_channel, + level="fov_statistics", + subtrahend="median", + divisor="iqr", + ), +] + +# Re-load the dataloader +phase2fluor_2D_data = HCSDataModule( + data_path, + source_channel=source_channel, + target_channel=target_channel, + z_window_size=1, + split_ratio=0.8, + batch_size=1, + num_workers=8, + yx_patch_size=YX_PATCH_SIZE, + augmentations=[], + normalizations=normalizations, +) +phase2fluor_2D_data.setup("test") +# %% tags=[] +# ########## TODO ############## +batch_number = 3 # Change this to see different batches of data +# ####################### +y_slice = slice(Y // 2 - 256 * n // 2, Y // 2 + 256 * n // 2) +x_slice = slice(X // 2 - 256 * n // 2, X // 2 + 256 * n // 2) + +# Iterate through the test dataloader to get the desired batch +i = 0 +for batch in phase2fluor_2D_data.test_dataloader(): + # break if we reach the desired batch + if i == batch_number - 1: + break + i += 1 + +# Plot the batch source and target images +f, ax = plt.subplots(1, 2, figsize=(8, 12)) +target_composite = composite_nuc_mem(batch["target"][0].cpu().numpy(), GREEN, MAGENTA) +ax[0].imshow( + batch["source"][0, 0, 0, y_slice, x_slice].cpu().numpy(), + cmap="gray", + vmin=-15, + vmax=15, +) +ax[1].imshow(clip_highlight(target_composite[0, y_slice, x_slice])) +for a in ax.ravel(): + a.axis("off") +f.tight_layout() +plt.show() + +# %% [markdown] tags=[] +#
+# +# ### Task 3.3: Using the selected batch to test the model's range of validity +# +# - Given the selected batch use `monai.networks.layers.GaussianFilter` to blur the images with different sigmas. +# Check the documentation here
+# - Plot the source and predicted images comparing the source, target and added perturbations
+# - How is the model's predictions given the perturbations?
+#
+# %% tags=["task"] +# ########## TODO ############## +# Try out different multiples of 256 to visualize larger/smaller crops +n = 3 +# ############################## +# Center cropping the image +y_slice = slice(Y // 2 - 256 * n // 2, Y // 2 + 256 * n // 2) +x_slice = slice(X // 2 - 256 * n // 2, X // 2 + 256 * n // 2) + +f, ax = plt.subplots(3, 2, figsize=(8, 12)) + +target_composite = composite_nuc_mem(batch["target"][0].cpu().numpy(), GREEN, MAGENTA) +ax[0, 0].imshow( + batch["source"][0, 0, 0, y_slice, x_slice].cpu().numpy(), + cmap="gray", + vmin=-15, + vmax=15, +) +ax[0, 1].imshow(clip_highlight(target_composite[0, y_slice, x_slice])) +ax[0, 0].set_title("Source and target") + +# no perturbation +with torch.inference_mode(): + phase = batch["source"].to(model.device)[:, :, :, y_slice, x_slice] + pred = model(phase).cpu().numpy() +pred_composite = composite_nuc_mem(pred[0], BOP_BLUE, BOP_ORANGE) +ax[1, 0].imshow(phase[0, 0, 0].cpu().numpy(), cmap="gray", vmin=-15, vmax=15) +ax[1, 1].imshow(pred_composite[0]) +ax[1, 0].set_title("No perturbation") + +# Select a sigma for the Gaussian filtering +# ########## TODO ############## +# Tensor dimensions (B, C, Z, Y, X). +# Hint: Use the GaussianFilter layer to blur the phase image. Provide the num spatial dimensions and sigmas +# Hint: Spatial (Z, Y, X) +gaussian_blur = GaussianFilter(...) +# ############################# +with torch.inference_mode(): + phase = batch["source"].to(model.device)[:, :, :, y_slice, x_slice] + phase = gaussian_blur(phase) + pred = model(phase).cpu().numpy() +pred_composite = composite_nuc_mem(pred[0], BOP_BLUE, BOP_ORANGE) +ax[2, 0].imshow(phase[0, 0, 0].cpu().numpy(), cmap="gray", vmin=-15, vmax=15) +ax[2, 1].imshow(pred_composite[0]) + +# %% tags=["solution"] +# ########## SOLUTION ############## +# Try out different multiples of 256 to visualize larger/smaller crops +n = 3 +# ############################## +# Center cropping the image +y_slice = slice(Y // 2 - 256 * n // 2, Y // 2 + 256 * n // 2) +x_slice = slice(X // 2 - 256 * n // 2, X // 2 + 256 * n // 2) + +f, ax = plt.subplots(3, 2, figsize=(8, 12)) + +target_composite = composite_nuc_mem(batch["target"][0].cpu().numpy(), GREEN, MAGENTA) +ax[0, 0].imshow( + batch["source"][0, 0, 0, y_slice, x_slice].cpu().numpy(), + cmap="gray", + vmin=-15, + vmax=15, +) +ax[0, 1].imshow(clip_highlight(target_composite[0, y_slice, x_slice])) +ax[0, 0].set_title("Source and target") + +# no perturbation +with torch.inference_mode(): + phase = batch["source"].to(model.device)[:, :, :, y_slice, x_slice] + pred = model(phase).cpu().numpy() +pred_composite = composite_nuc_mem(pred[0], BOP_BLUE, BOP_ORANGE) +ax[1, 0].imshow(phase[0, 0, 0].cpu().numpy(), cmap="gray", vmin=-15, vmax=15) +ax[1, 1].imshow(pred_composite[0]) +ax[1, 0].set_title("No perturbation") + + +# Select a sigma for the Gaussian filtering +# ########## SOLUTION ############## +# Tensor dimensions (B, C, Z, Y, X). +# Hint: Use the GaussianFilter layer to blur the phase image. Provide the num spatial dimensions and sigma +# Hint: Spatial (Z, Y, X). Apply the same sigma to Y, X +gaussian_blur = GaussianFilter(spatial_dims=3, sigma=(0, 2, 2)) +# ############################# +with torch.inference_mode(): + phase = batch["source"].to(model.device)[:, :, :, y_slice, x_slice] + phase = gaussian_blur(phase) + pred = model(phase).cpu().numpy() +pred_composite = composite_nuc_mem(pred[0], BOP_BLUE, BOP_ORANGE) +ax[2, 0].imshow(phase[0, 0, 0].cpu().numpy(), cmap="gray", vmin=-15, vmax=15) +ax[2, 1].imshow(pred_composite[0]) + +# %% [markdown] tags=[] +#
+# +# ### Task 3.3: Using the selected batch to test the model's range of validity +# +# - Scale the pixel values up/down of the phase image
+# - Plot the source and predicted images comparing the source, target and added perturbations
+# - How is the model's predictions given the perturbations?
+#
+ +# %% tags=["task"] +n = 3 +y_slice = slice(Y // 2, Y // 2 + 256 * n) +x_slice = slice(X // 2, X // 2 + 256 * n) +f, ax = plt.subplots(3, 2, figsize=(8, 12)) + +target_composite = composite_nuc_mem(batch["target"][0].cpu().numpy(), GREEN, MAGENTA) +ax[0, 0].imshow( + batch["source"][0, 0, 0, y_slice, x_slice].cpu().numpy(), + cmap="gray", + vmin=-15, + vmax=15, +) +ax[0, 1].imshow(clip_highlight(target_composite[0, y_slice, x_slice])) +ax[0, 0].set_title("Source and target") + +# no perturbation +with torch.inference_mode(): + phase = batch["source"].to(model.device)[:, :, :, y_slice, x_slice] + pred = model(phase).cpu().numpy() +pred_composite = composite_nuc_mem(pred[0], BOP_BLUE, BOP_ORANGE) +ax[1, 0].imshow(phase[0, 0, 0].cpu().numpy(), cmap="gray", vmin=-15, vmax=15) +ax[1, 1].imshow(pred_composite[0]) +ax[1, 0].set_title("No perturbation") + + +# Rescale the pixel value up/down +with torch.inference_mode(): + phase = batch["source"].to(model.device)[:, :, :, y_slice, x_slice] + # ########## TODO ############## + # Hint: Scale the phase intensity up/down until the model breaks + phase = phase * ... + # ####################### + pred = model(phase).cpu().numpy() +pred_composite = composite_nuc_mem(pred[0], BOP_BLUE, BOP_ORANGE) +ax[2, 0].imshow(phase[0, 0, 0].cpu().numpy(), cmap="gray", vmin=-15, vmax=15) +ax[2, 1].imshow(pred_composite[0]) + +# %% tags=["solution"] +n = 3 +y_slice = slice(Y // 2, Y // 2 + 256 * n) +x_slice = slice(X // 2, X // 2 + 256 * n) +f, ax = plt.subplots(3, 2, figsize=(8, 12)) + +target_composite = composite_nuc_mem(batch["target"][0].cpu().numpy(), GREEN, MAGENTA) +ax[0, 0].imshow( + batch["source"][0, 0, 0, y_slice, x_slice].cpu().numpy(), + cmap="gray", + vmin=-15, + vmax=15, +) +ax[0, 1].imshow(clip_highlight(target_composite[0, y_slice, x_slice])) +ax[0, 0].set_title("Source and target") + +# no perturbation +with torch.inference_mode(): + phase = batch["source"].to(model.device)[:, :, :, y_slice, x_slice] + pred = model(phase).cpu().numpy() +pred_composite = composite_nuc_mem(pred[0], BOP_BLUE, BOP_ORANGE) +ax[1, 0].imshow(phase[0, 0, 0].cpu().numpy(), cmap="gray", vmin=-15, vmax=15) +ax[1, 1].imshow(pred_composite[0]) +ax[1, 0].set_title("No perturbation") + + +# Rescale the pixel value up/down +with torch.inference_mode(): + phase = batch["source"].to(model.device)[:, :, :, y_slice, x_slice] + # ########## SOLUTION ############## + # Hint: Scale the phase intensity up/down until the model breaks + phase = phase * 10 + # ####################### + pred = model(phase).cpu().numpy() +pred_composite = composite_nuc_mem(pred[0], BOP_BLUE, BOP_ORANGE) +ax[2, 0].imshow(phase[0, 0, 0].cpu().numpy(), cmap="gray", vmin=-15, vmax=15) +ax[2, 1].imshow(pred_composite[0]) + +# %% [markdown] +#
+#

Questions

+# How is the model's predictions given the blurring and scaling perturbations?
+#
+ +# %% tags=["solution"] +# ########## SOLUTIONS FOR ALL POSSIBLE PLOTTINGS ############## +# This plots all perturbations + +n = 3 +y_slice = slice(Y // 2, Y // 2 + 256 * n) +x_slice = slice(X // 2, X // 2 + 256 * n) +f, ax = plt.subplots(6, 2, figsize=(8, 12)) + +target_composite = composite_nuc_mem(batch["target"][0].cpu().numpy(), GREEN, MAGENTA) +ax[0, 0].imshow( + batch["source"][0, 0, 0, y_slice, x_slice].cpu().numpy(), + cmap="gray", + vmin=-15, + vmax=15, +) +ax[0, 1].imshow(clip_highlight(target_composite[0, y_slice, x_slice])) +ax[0, 0].set_title("Source and target") + +# no perturbation +with torch.inference_mode(): + phase = batch["source"].to(model.device)[:, :, :, y_slice, x_slice] + pred = model(phase).cpu().numpy() +pred_composite = composite_nuc_mem(pred[0], BOP_BLUE, BOP_ORANGE) +ax[1, 0].imshow(phase[0, 0, 0].cpu().numpy(), cmap="gray", vmin=-15, vmax=15) +ax[1, 1].imshow(pred_composite[0]) +ax[1, 0].set_title("No perturbation") + + +# 2-sigma gaussian blur +gaussian_blur = GaussianFilter(spatial_dims=3, sigma=(0, 2, 2)) +with torch.inference_mode(): + phase = batch["source"].to(model.device)[:, :, :, y_slice, x_slice] + phase = gaussian_blur(phase) + pred = model(phase).cpu().numpy() +pred_composite = composite_nuc_mem(pred[0], BOP_BLUE, BOP_ORANGE) +ax[2, 0].imshow(phase[0, 0, 0].cpu().numpy(), cmap="gray", vmin=-15, vmax=15) +ax[2, 1].imshow(pred_composite[0]) +ax[2, 0].set_title("Gaussian Blur Sigma=2") + + +# 5-sigma gaussian blur +gaussian_blur = GaussianFilter(spatial_dims=3, sigma=(0, 5, 5)) +with torch.inference_mode(): + phase = batch["source"].to(model.device)[:, :, :, y_slice, x_slice] + phase = gaussian_blur(phase) + pred = model(phase).cpu().numpy() +pred_composite = composite_nuc_mem(pred[0], BOP_BLUE, BOP_ORANGE) +ax[3, 0].imshow(phase[0, 0, 0].cpu().numpy(), cmap="gray", vmin=-15, vmax=15) +ax[3, 1].imshow(pred_composite[0]) +ax[3, 0].set_title("Gaussian Blur Sigma=5") + + +# 0.1x scaling +with torch.inference_mode(): + phase = batch["source"].to(model.device)[:, :, :, y_slice, x_slice] + phase = phase * 0.1 + pred = model(phase).cpu().numpy() +pred_composite = composite_nuc_mem(pred[0], BOP_BLUE, BOP_ORANGE) +ax[4, 0].imshow(phase[0, 0, 0].cpu().numpy(), cmap="gray", vmin=-15, vmax=15) +ax[4, 1].imshow(pred_composite[0]) +ax[4, 0].set_title("0.1x scaling") + +# 10x scaling +with torch.inference_mode(): + phase = batch["source"].to(model.device)[:, :, :, y_slice, x_slice] + phase = phase * 10 + pred = model(phase).cpu().numpy() +pred_composite = composite_nuc_mem(pred[0], BOP_BLUE, BOP_ORANGE) +ax[5, 0].imshow(phase[0, 0, 0].cpu().numpy(), cmap="gray", vmin=-15, vmax=15) +ax[5, 1].imshow(pred_composite[0]) +ax[5, 0].set_title("10x scaling") + +for a in ax.ravel(): + a.axis("off") + +f.tight_layout() +# %% [markdown] tags=[] +#
+ +#

+# 🎉 The end of the notebook 🎉 +#

+ +# Congratulations! You have trained an image translation model, evaluated its performance, and explored what the network has learned. + +#
diff --git a/applications/cytoland/examples/phase_contrast/README.md b/applications/cytoland/examples/phase_contrast/README.md new file mode 100644 index 000000000..2bb96eb71 --- /dev/null +++ b/applications/cytoland/examples/phase_contrast/README.md @@ -0,0 +1,39 @@ +# Demo: Virtual staining of phase contrast data + +# Overview: + +Generalization to Zernike phase contrast images. This demo showcases the use of VSCyto3D model with and without augmentations on Zernike phase contrast data. + +## Setup + +Run the setup script from this examples folder to create the environment and download the dataset: +```bash +cd applications/cytoland/examples/phase_contrast +source setup.sh +``` +The script resolves the cytoland package relative to its own location, so it works regardless of the current working directory. + +Activate your environment +```bash +conda activate vs_Phc +``` + +## Use vscode + +Install vscode, install jupyter extension inside vscode, and setup [cell mode](https://code.visualstudio.com/docs/python/jupyter-support-py). Open [solution.py](solution.py) and run the script interactively. + +## Use Jupyter Notebook + +Launch a jupyter environment + +``` +jupyter notebook +``` + +...and continue with the instructions in the notebook. + +If `vs_Phc` is not available as a kernel in jupyter, run: + +``` +python -m ipykernel install --user --name=vs_Phc +``` diff --git a/examples/virtual_staining/img2img_translation/prepare-exercise.sh b/applications/cytoland/examples/phase_contrast/prepare-exercise.sh similarity index 100% rename from examples/virtual_staining/img2img_translation/prepare-exercise.sh rename to applications/cytoland/examples/phase_contrast/prepare-exercise.sh diff --git a/applications/cytoland/examples/phase_contrast/setup.sh b/applications/cytoland/examples/phase_contrast/setup.sh new file mode 100644 index 000000000..853ac930c --- /dev/null +++ b/applications/cytoland/examples/phase_contrast/setup.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env -S bash -i + +START_DIR=$(pwd) + +# Resolve this script's directory so install paths work regardless of cwd. +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# Cytoland package lives two levels up from this examples folder +# (applications/cytoland/examples/phase_contrast -> applications/cytoland). +CYTOLAND_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)" + +conda deactivate +# Create conda environment +conda create -y --name vs_Phc python=3.12 + +# Install Jupyter kernel, notebook server, and related tooling in the environment. +conda install -y ipykernel notebook nbformat nbconvert ruff jupytext ipywidgets --name vs_Phc +# Specifying the environment explicitly. +# conda activate sometimes doesn't work from within shell scripts. + +# Install cytoland (pulls in viscy-data, viscy-models, viscy-transforms, viscy-utils). +# Find path to the environment - conda activate doesn't work from within shell scripts. +ENV_PATH=$(conda info --envs | grep vs_Phc | awk '{print $NF}') +$ENV_PATH/bin/pip install -e "${CYTOLAND_DIR}[metrics]" + +# Create the directory structure +mkdir -p ~/data/vs_PhC/test +mkdir -p ~/data/vs_PhC/models + +# Change to the target directory +# Download the OME-Zarr dataset recursively +cd ~/data/vs_PhC/test +wget -m -np -nH --cut-dirs=5 -R "index.html*" "https://public.czbiohub.org/comp.micro/viscy/VS_datasets/VSCyto3D/test/HEK_H2B_CAAX_PhC_40x_registered.zarr/" + +# Get the models +cd ~/data/vs_PhC/models +wget -m -np -nH --cut-dirs=4 -R "index.html*" "https://public.czbiohub.org/comp.micro/viscy/VS_models/VSCyto3D/no_augmentations/best_epoch=30-step=6076.ckpt" +wget -m -np -nH --cut-dirs=5 -R "index.html*" "https://public.czbiohub.org/comp.micro/viscy/VS_models/VSCyto3D/epoch=48-step=18130.ckpt" + + +# Change back to the starting directory +cd $START_DIR diff --git a/applications/cytoland/examples/phase_contrast/solution.py b/applications/cytoland/examples/phase_contrast/solution.py new file mode 100644 index 000000000..a579e7e97 --- /dev/null +++ b/applications/cytoland/examples/phase_contrast/solution.py @@ -0,0 +1,225 @@ +# %% [markdown] tags=[] +# # Virtual staining of phase contrast images using VSCyto3D with and without augmentations +# +# Written by Eduardo Hirata-Miyasaki, Ziwen Liu, and Shalin Mehta, CZ Biohub San Francisco +# +# ## Overview +# +# This notebook demonstrates how to use the VSCyto3D model to virtually stain phase contrast images. The phase contrast images were not part of the training. +# We will use the VSCyto3D model to predict the nuclei and cell membrane channels from a phase contrast image with two models: +# - One model trained without augmentations +# - One model trained with augmentations +# + +# %% tags=[] +# Imports +from pathlib import Path + +import matplotlib.pyplot as plt +import numpy as np +import torch +from iohub import open_ome_zarr +from lightning.pytorch import seed_everything + +# Cytoland and VisCy modular classes for the model and data module +from cytoland.engine import VSUNet +from viscy_data.hcs import HCSDataModule +from viscy_transforms import NormalizeSampled + +# seed random number generators for reproducibility. +seed_everything(42, workers=True) +# %% +# Paths to data and log directory +top_dir = ( + Path("~/data/vs_PhC").expanduser() +) # If this fails, make sure this to point to your data directory in the shared mounting point inside /dlmbl/data + +# Path to the training data +data_path = top_dir / "test/HEK_H2B_CAAX_PhC_40x_registered.zarr" + +# %% [markdown] tags=[] +# ## Load OME-Zarr Dataset + +# There should be 34 FOVs in the dataset. +# +# Each FOV consists of 3 channels of 2048x2048 images, +# saved in the [High-Content Screening (HCS) layout](https://ngff.openmicroscopy.org/latest/#hcs-layout) +# specified by the Open Microscopy Environment Next Generation File Format +# (OME-NGFF). +# +# The 3 channels correspond to the QPI, nuclei, and cell membrane. The nuclei were stained with DAPI and the cell membrane with Cellmask. +# +# - The layout on the disk is: `row/col/field/pyramid_level/timepoint/channel/z/y/x.` +# - These datasets only have 1 level in the pyramid (highest resolution) which is '0'. +# %% +# Open dataset and look at its structure +dataset = open_ome_zarr(data_path) +dataset.print_tree() +# %% +row = 0 +col = 3 +field = "000000" # TODO Change this for a different FOV + +# NOTE: this dataset only has one level +pyramid_level = 0 + +fov_path = f"{row}/{col}/{field}" +input_data_path = Path(data_path) / fov_path +image = dataset[fov_path][pyramid_level].numpy() + +n_channels = len(dataset.channel_names) +Z, Y, X = image.shape[-3:] +figure, axes = plt.subplots(1, n_channels, figsize=(9, 3)) +title_names = ["PhC", "TXR", "Y5"] +for i in range(n_channels): + channel_image = image[0, i, Z // 2] + # Invert the phase contrast channel + if i == 0: + channel_image = channel_image * -1 + # Adjust contrast to 0.5th and 99.5th percentile of pixel values. + p_low, p_high = np.percentile(channel_image, (0.5, 99.5)) + channel_image = np.clip(channel_image, p_low, p_high) + axes[i].imshow(channel_image, cmap="gray") + axes[i].axis("off") + axes[i].set_title(title_names[i]) +plt.tight_layout() + +# %% [markdown] tags=[] +# ## Create the VSCyto3D model +# Here we will instantiate the `HCSDataModule` that reads the ome-zarr dataset and prepares the data for inference. +# %% +# Reduce the batch size if encountering out-of-memory errors +BATCH_SIZE = 5 +# NOTE: Set the number of workers to 0 for Windows and macOS +# since multiprocessing only works with a +# `if __name__ == '__main__':` guard. +# On Linux, set it to the number of CPU cores to maximize performance. +NUM_WORKERS = 0 +source_channel_name = "BF" + +# %%[markdown] +""" +For this example we will use the following parameters: +### For more information on the VSCyto3D model: +See ``viscy.unet.networks.fcmae`` +([source code](https://github.com/mehta-lab/VisCy/blob/6a3457ec8f43ecdc51b1760092f1a678ed73244d/viscy/unet/networks/unext2.py#L252)) +for configuration details. +""" +# %% +# Setup the data module. +data_module = HCSDataModule( + data_path=input_data_path, + source_channel=source_channel_name, + target_channel=["Nuclei", "Membrane"], + z_window_size=5, + split_ratio=0.8, + batch_size=BATCH_SIZE, + num_workers=NUM_WORKERS, + normalizations=[ + NormalizeSampled( + [source_channel_name], + level="fov_statistics", + subtrahend="median", + divisor="iqr", + ) + ], +) +data_module.prepare_data() +data_module.setup(stage="predict") + +# %% [markdown] tags=[] +# ## Setup the _VSCyto3D_ model with and without augmentations +# We will load the model checkpoints and run inference on the phase contrast image.abs +# The model that utilizes augmentations shows better performance in the prediction of the nuclei and cell membrane channels. +# The phase contrast images were not part of the training for the `VSCyto3D`` model. +# %% + +# TODO: change if you want to use a different GPU +GPU_ID = 0 + +# TODO: point to the downloaded model checkpoints +no_augmentation_model_ckpt = top_dir / "models/no_augmentations/best_epoch=30-step=6076.ckpt" +VSCyto3D_model_ckpt = top_dir / "models/epoch=48-step=18130.ckpt" + +# Dictionary that specifies key parameters of the model. +config_VSCyto3D = { + "in_channels": 1, + "out_channels": 2, + "in_stack_depth": 5, + "backbone": "convnextv2_tiny", + "stem_kernel_size": (5, 4, 4), + "decoder_mode": "pixelshuffle", + "head_expansion_ratio": 4, + "head_pool": True, +} + +# Select the device used for direct model inference. +inference_device = torch.device(f"cuda:{GPU_ID}" if torch.cuda.is_available() else "cpu") +print(f"Running inference on {inference_device}") + +# Model without augmentation +model_VSCyto3D_no_augmentation = ( + VSUNet.load_from_checkpoint(no_augmentation_model_ckpt, architecture="UNeXt2", model_config=config_VSCyto3D) + .to(inference_device) + .eval() +) +# Model with augmentation +model_VSCyto3D_w_augmentation = ( + VSUNet.load_from_checkpoint(VSCyto3D_model_ckpt, architecture="UNeXt2", model_config=config_VSCyto3D) + .to(inference_device) + .eval() +) + +n = 5 +patch_size = 256 +y_slice = slice(Y // 2 - patch_size * n // 2, Y // 2 + patch_size * n // 2) +x_slice = slice(X // 2 - patch_size * n // 2, X // 2 + patch_size * n // 2) + +# Get the Phase Contrast channel +c_idx = dataset.channel_names.index(source_channel_name) +phase_image = image[0:1, c_idx : c_idx + 1, Z // 2 - 2 : Z // 2 + 3, y_slice, x_slice] +# Normalize the image +median = dataset[fov_path].zattrs["normalization"][source_channel_name]["fov_statistics"]["median"] +iqr = dataset[fov_path].zattrs["normalization"][source_channel_name]["fov_statistics"]["iqr"] +phase_image = ((phase_image - median) / iqr) * -1 + +# Load the image to device +device = model_VSCyto3D_no_augmentation.device +phase_image = torch.tensor(phase_image).to(device) + +# Run inference on the given volume +with torch.inference_mode(): # turn off gradient computation. + pred_no_augmentation = model_VSCyto3D_no_augmentation(phase_image) + pred_w_augmentation = model_VSCyto3D_w_augmentation(phase_image) + +pred_no_augmentation = pred_no_augmentation.cpu().detach().numpy() +pred_w_augmentation = pred_w_augmentation.cpu().detach().numpy() +phase_image = phase_image.cpu().detach().numpy() +clim_max = 30 +clim_min = -20 + +# Plot the predicted images with model without augmentations +fig, ax = plt.subplots(2, 3, figsize=(12, 12)) +ax[0, 0].imshow(phase_image[0, 0, 2, :, :], cmap="gray", vmin=clim_min, vmax=clim_max) +ax[0, 0].axis("off") +ax[0, 0].set_title("Phase Contrast") +for i in range(2): + ax[0, i + 1].imshow(pred_no_augmentation[0, i, 2, :, :], cmap="gray") + ax[0, i + 1].axis("off") +ax[0, 1].set_title("VS_Nuclei without augmentations") +ax[0, 2].set_title("VS_Membrane without augmentations") + +# Plot the predicted images with VSCyto3D with augmentations +ax[1, 0].imshow(phase_image[0, 0, 2, :, :], cmap="gray", vmin=clim_min, vmax=clim_max) +ax[1, 0].axis("off") +ax[1, 0].set_title("Phase Contrast") +for i in range(2): + ax[1, i + 1].imshow( + pred_w_augmentation[0, i, 2, :, :], + cmap="gray", + ) + ax[1, i + 1].axis("off") +ax[1, 1].set_title("VS_Nuclei with augmentations") +ax[1, 2].set_title("VS_Membrane with augmentations") + +plt.tight_layout() diff --git a/applications/cytoland/examples/vcp_tutorials/README.md b/applications/cytoland/examples/vcp_tutorials/README.md new file mode 100644 index 000000000..a394a2d9c --- /dev/null +++ b/applications/cytoland/examples/vcp_tutorials/README.md @@ -0,0 +1,21 @@ +# Virtual Cell Platform Tutorials + +This directory contains tutorial scripts for the Virtual Cell Platform. +Jupyter notebooks can be generated from these Python scripts as described below. + +- [Quick Start](quick_start.py): +get started with model inference in Python with an A549 cell dataset. +- [CLI inference and visualization](hek293t.py): +run inference from CLI on a HEK293T cell dataset and visualize the results. +- [Virtual staining _in vivo_](neuromast.py): +compare virtual staining and fluorescence in a time-lapse dataset of the zebrafish neuromast. + +## Development + +The development happens on the Python scripts, +which are converted to Jupyter notebooks with: + +```sh +# TODO: change the file name at the end to be the script to convert +jupytext --to ipynb --update-metadata '{"jupytext":{"cell_metadata_filter":"all"}}' --update quick_start.py +``` diff --git a/examples/virtual_staining/vcp_tutorials/hek293t.py b/applications/cytoland/examples/vcp_tutorials/hek293t.py similarity index 98% rename from examples/virtual_staining/vcp_tutorials/hek293t.py rename to applications/cytoland/examples/vcp_tutorials/hek293t.py index 41decac02..d318ff895 100644 --- a/examples/virtual_staining/vcp_tutorials/hek293t.py +++ b/applications/cytoland/examples/vcp_tutorials/hek293t.py @@ -19,7 +19,7 @@ """ # Prerequisites -Python>=3.11 +Python>=3.12 """ # %% [markdown] @@ -55,10 +55,10 @@ """ # %% -# Install VisCy with the optional dependencies for this example +# Install the modular Cytoland/VisCy packages required for this example # See the [repository](https://github.com/mehta-lab/VisCy) for more details # Here stackview and ipycanvas are installed for visualization -# !pip install -U "viscy[metrics,visual]==0.4.0a3" stackview ipycanvas==0.11 +# !pip install -U cytoland viscy stackview ipycanvas==0.11 # %% # Restart kernel if running in Google Colab diff --git a/examples/virtual_staining/vcp_tutorials/neuromast.py b/applications/cytoland/examples/vcp_tutorials/neuromast.py similarity index 92% rename from examples/virtual_staining/vcp_tutorials/neuromast.py rename to applications/cytoland/examples/vcp_tutorials/neuromast.py index 33e097c79..a65d305ea 100644 --- a/examples/virtual_staining/vcp_tutorials/neuromast.py +++ b/applications/cytoland/examples/vcp_tutorials/neuromast.py @@ -19,7 +19,7 @@ """ # Prerequisites -Python>=3.11 +Python>=3.12 """ # %% [markdown] @@ -63,10 +63,10 @@ """ # %% -# Install VisCy with the optional dependencies for this example +# Install the modular Cytoland/VisCy packages required for this example # See the [repository](https://github.com/mehta-lab/VisCy) for more details -# Here stackview and ipycanvas are installed for visualization -# !pip install -U "viscy[metrics,visual]==0.4.0a3" +# Here stackview and ipycanvas are installed for visualization, and iohub provides OME-Zarr access +# !pip install -U cytoland viscy iohub stackview ipycanvas # %% # Restart kernel if running in Google Colab @@ -169,9 +169,7 @@ from skimage.exposure import rescale_intensity -def render_rgb( - image: np.ndarray, colormap: Colormap -) -> tuple[NDArray, plt.cm.ScalarMappable]: +def render_rgb(image: np.ndarray, colormap: Colormap) -> tuple[NDArray, plt.cm.ScalarMappable]: """Render a 2D grayscale image as RGB using a colormap. Parameters @@ -188,9 +186,7 @@ def render_rgb( """ image = rescale_intensity(image, out_range=(0, 1)) image = colormap(image) - mappable = plt.cm.ScalarMappable( - norm=plt.Normalize(0, 1), cmap=colormap.to_matplotlib() - ) + mappable = plt.cm.ScalarMappable(norm=plt.Normalize(0, 1), cmap=colormap.to_matplotlib()) return image, mappable @@ -213,9 +209,7 @@ def render_rgb( merged_vs = (vs_nucleus_rgb + vs_membrane_rgb).clip(0, 1) fluor_nucleus_rgb, fluor_nucleus_mappable = render_rgb(fluor_nucleus, Colormap("green")) -fluor_membrane_rgb, fluor_membrane_mappable = render_rgb( - fluor_membrane, Colormap("magenta") -) +fluor_membrane_rgb, fluor_membrane_mappable = render_rgb(fluor_membrane, Colormap("magenta")) merged_fluor = (fluor_nucleus_rgb + fluor_membrane_rgb).clip(0, 1) # Plot @@ -223,9 +217,7 @@ def render_rgb( images = {"fluorescence": merged_fluor, "virtual staining": merged_vs} -for row, (subfig, (name, img)) in enumerate( - zip(fig.subfigures(nrows=2, ncols=1), images.items()) -): +for row, (subfig, (name, img)) in enumerate(zip(fig.subfigures(nrows=2, ncols=1), images.items())): subfig.suptitle(name) cax_nuc = subfig.add_axes([1, 0.55, 0.02, 0.3]) cax_mem = subfig.add_axes([1, 0.15, 0.02, 0.3]) @@ -237,9 +229,7 @@ def render_rgb( ax.axis("off") if row == 0: subfig.colorbar(fluor_nucleus_mappable, cax=cax_nuc, label="Nuclei (GFP)") - subfig.colorbar( - fluor_membrane_mappable, cax=cax_mem, label="Membrane (mScarlett)" - ) + subfig.colorbar(fluor_membrane_mappable, cax=cax_mem, label="Membrane (mScarlett)") elif row == 1: subfig.colorbar(vs_nucleus_mappable, cax=cax_nuc, label="Nuclei (VS)") subfig.colorbar(vs_membrane_mappable, cax=cax_mem, label="Membrane (VS)") @@ -285,9 +275,7 @@ def highlight_intensity_normalized(fov_path: str, channel_name: str) -> list[flo # %% # Plot intensity over time mean_fl = highlight_intensity_normalized("input.ome.zarr/0/3/0", "mScarlett") -mean_vs = highlight_intensity_normalized( - "prediction.ome.zarr/0/3/0", "membrane_prediction" -) +mean_vs = highlight_intensity_normalized("prediction.ome.zarr/0/3/0", "membrane_prediction") time = np.arange(0, 100, 30) plt.plot(time, mean_fl, label="membrane fluorescence") diff --git a/examples/virtual_staining/vcp_tutorials/quick_start.py b/applications/cytoland/examples/vcp_tutorials/quick_start.py similarity index 95% rename from examples/virtual_staining/vcp_tutorials/quick_start.py rename to applications/cytoland/examples/vcp_tutorials/quick_start.py index cbb0bd457..d67a3545f 100644 --- a/examples/virtual_staining/vcp_tutorials/quick_start.py +++ b/applications/cytoland/examples/vcp_tutorials/quick_start.py @@ -17,7 +17,7 @@ # %% [markdown] """ # Prerequisites -Python>=3.11 +Python>=3.12 """ @@ -86,9 +86,9 @@ """ # %% -# Install VisCy with the optional dependencies for this example -# See the [repository](https://github.com/mehta-lab/VisCy) for more details -# !pip install "viscy[metrics,visual]==0.4.0a3" +# Install the modular packages required for this example +# See the repositories/package indexes for the latest compatibility details +# !pip install cytoland viscy_data viscy_transforms viscy_utils torchview cmap iohub # %% # restart kernel if running in Google Colab @@ -122,11 +122,11 @@ from iohub import open_ome_zarr # noqa: E402 from torchview import draw_graph # noqa: E402 -from viscy.data.hcs import HCSDataModule # noqa: E402 -from viscy.trainer import VisCyTrainer # noqa: E402 -from viscy.transforms import NormalizeSampled # noqa: E402 -from viscy.translation.engine import FcmaeUNet # noqa: E402 -from viscy.translation.predict_writer import HCSPredictionWriter # noqa: E402 +from cytoland.engine import FcmaeUNet # noqa: E402 +from viscy_data.hcs import HCSDataModule # noqa: E402 +from viscy_transforms import NormalizeSampled # noqa: E402 +from viscy_utils.callbacks import HCSPredictionWriter # noqa: E402 +from viscy_utils.trainer import VisCyTrainer # noqa: E402 # %% # NOTE: Nothing needs to be changed in this code block for the example to work. @@ -158,7 +158,7 @@ # Name of the input phase channel source_channel="Phase3D", # Desired name of the output channels - target_channel=["Membrane", "Nuclei"], + target_channel=["Nuclei", "Membrane"], # Axial input size, 1 for 2D models z_window_size=1, # Batch size diff --git a/applications/cytoland/pyproject.toml b/applications/cytoland/pyproject.toml new file mode 100644 index 000000000..66ba77117 --- /dev/null +++ b/applications/cytoland/pyproject.toml @@ -0,0 +1,67 @@ +[build-system] +build-backend = "hatchling.build" +requires = [ "hatchling", "uv-dynamic-versioning" ] + +[project] +name = "cytoland" +description = "Robust virtual staining of landmark organelles from label-free microscopy" +readme = "README.md" +keywords = [ + "deep learning", + "fluorescence prediction", + "microscopy", + "virtual staining", +] +license = "BSD-3-Clause" +authors = [ { name = "Biohub", email = "compmicro@czbiohub.org" } ] +requires-python = ">=3.12" +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Topic :: Scientific/Engineering :: Image Processing", +] +dynamic = [ "version" ] +dependencies = [ + "imageio", + "lightning>=2.3", + "monai", + "torchmetrics>=1", + "viscy-data", + "viscy-models", + "viscy-transforms", + "viscy-utils", +] + +optional-dependencies.metrics = [ + "cellpose", +] +urls.Homepage = "https://github.com/mehta-lab/VisCy" +urls.Issues = "https://github.com/mehta-lab/VisCy/issues" +urls.Repository = "https://github.com/mehta-lab/VisCy" + +[dependency-groups] +dev = [ { include-group = "test" } ] +test = [ + "pytest>=9.0.2", + "pytest-cov>=7", + "tensorboard", +] + +[tool.hatch.version] +source = "uv-dynamic-versioning" + +[tool.hatch.build.targets.wheel] +packages = [ "src/cytoland" ] + +[tool.uv-dynamic-versioning] +vcs = "git" +style = "pep440" +pattern-prefix = "cytoland-" +fallback-version = "0.0.0" diff --git a/applications/cytoland/src/cytoland/__init__.py b/applications/cytoland/src/cytoland/__init__.py new file mode 100644 index 000000000..6df466c9e --- /dev/null +++ b/applications/cytoland/src/cytoland/__init__.py @@ -0,0 +1,17 @@ +"""Cytoland virtual staining application using UNet architectures.""" + +from cytoland.engine import ( + AugmentedPredictionVSUNet, + FcmaeUNet, + MaskedMSELoss, + VSUNet, +) +from cytoland.evaluation import SegmentationMetrics2D + +__all__ = [ + "AugmentedPredictionVSUNet", + "FcmaeUNet", + "MaskedMSELoss", + "SegmentationMetrics2D", + "VSUNet", +] diff --git a/applications/cytoland/src/cytoland/__main__.py b/applications/cytoland/src/cytoland/__main__.py new file mode 100644 index 000000000..da9b08a27 --- /dev/null +++ b/applications/cytoland/src/cytoland/__main__.py @@ -0,0 +1,12 @@ +"""Lightning CLI entry point for the Cytoland application. + +Usage +----- +python -m cytoland fit --config vscyto3d/finetune.yml +python -m cytoland predict --config vscyto3d/predict.yml +""" + +from viscy_utils.cli import main + +if __name__ == "__main__": + main() diff --git a/applications/cytoland/src/cytoland/engine.py b/applications/cytoland/src/cytoland/engine.py new file mode 100644 index 000000000..03b272c08 --- /dev/null +++ b/applications/cytoland/src/cytoland/engine.py @@ -0,0 +1,987 @@ +"""Cytoland LightningModules for virtual staining.""" + +import inspect +import logging +import os +from typing import Callable, Literal, Sequence + +import numpy as np +import torch +import torch.nn.functional as F +from imageio import imwrite +from lightning.pytorch import LightningModule +from monai.transforms import DivisiblePad, Rotate90 +from torch import Tensor, nn +from torchmetrics.functional import ( + accuracy, + cosine_similarity, + jaccard_index, + mean_absolute_error, + mean_squared_error, + pearson_corrcoef, + r2_score, + structural_similarity_index_measure, +) +from torchmetrics.functional.segmentation import dice_score + +from viscy_data import CombinedDataModule, GPUTransformDataModule, Sample +from viscy_models import FullyConvolutionalMAE, Unet2d, Unet3d, Unet25d, UNeXt2 +from viscy_utils.callbacks.prediction_writer import _blend_in +from viscy_utils.evaluation.metrics import mean_average_precision +from viscy_utils.log_images import detach_sample, log_image_grid +from viscy_utils.optimizers import configure_adamw_scheduler +from viscy_utils.tensor_utils import to_numpy + +_UNET_ARCHITECTURE = { + "2D": Unet2d, + "UNeXt2": UNeXt2, + "2.5D": Unet25d, + "FNet3D": Unet3d, + "fcmae": FullyConvolutionalMAE, + "UNeXt2_2D": FullyConvolutionalMAE, +} + +_logger = logging.getLogger("lightning.pytorch") + + +def _make_divisible_pad(model: nn.Module) -> DivisiblePad: + """Build a DivisiblePad that matches the model's downsampling axes.""" + down_factor = 2**model.num_blocks + if getattr(model, "downsamples_z", False): + return DivisiblePad((0, down_factor, down_factor, down_factor)) + return DivisiblePad((0, 0, down_factor, down_factor)) + + +def _identity(x: Tensor) -> Tensor: + """Identity transform (no-op).""" + return x + + +def _center_crop_to_shape(tensor: Tensor, spatial_shape: tuple[int, ...]) -> Tensor: + """Center-crop trailing spatial dimensions to the requested shape.""" + slices = [slice(None)] * tensor.ndim + start_dim = tensor.ndim - len(spatial_shape) + for dim, size in enumerate(spatial_shape, start=start_dim): + current = tensor.shape[dim] + if current < size: + raise ValueError(f"Cannot crop dimension {dim} from {current} to {size}") + start = (current - size) // 2 + slices[dim] = slice(start, start + size) + return tensor[tuple(slices)] + + +class MaskedMSELoss(nn.Module): + """Masked MSE loss for FCMAE pre-training.""" + + def forward(self, preds, original, mask): + """Compute masked mean squared error loss. + + Parameters + ---------- + preds : Tensor + Predicted tensor. + original : Tensor + Original tensor. + mask : Tensor + Binary mask tensor. + + Returns + ------- + Tensor + Masked MSE loss value. + """ + loss = F.mse_loss(preds, original, reduction="none") + loss = (loss.mean(2) * mask).sum() / mask.sum() + return loss + + +class VSUNet(LightningModule): + """Regression U-Net module for virtual staining. + + Parameters + ---------- + architecture : Literal["2D", "UNeXt2", "2.5D", "FNet3D", "fcmae", "UNeXt2_2D"] + Architecture type to use. + model_config : dict + Model configuration dictionary. + loss_function : nn.Module | None + Loss function for training/validation. + Defaults to L2 (mean squared error). + lr : float + Learning rate, defaults to 1e-3. + schedule : Literal["WarmupCosine", "Constant"] + Learning rate scheduler, defaults to "Constant". + freeze_encoder : bool + Whether to freeze encoder weights. + ckpt_path : str | None + Path to checkpoint to load weights. + log_batches_per_epoch : int + Number of batches to log each epoch, defaults to 8. + log_samples_per_batch : int + Number of samples to log each batch, defaults to 1. + example_input_yx_shape : Sequence[int] + XY shape of example input for graph tracing, defaults to (256, 256). + test_cellpose_model_path : str | None + Path to CellPose model for testing segmentation. + test_cellpose_diameter : float | None + Diameter parameter for CellPose model. + test_evaluate_cellpose : bool | None + Evaluate CellPose model instead of trained model in test stage. + test_time_augmentations : bool | None + Apply test time augmentations in test stage. + tta_type : Literal["mean", "median", "product"] + Type of test time augmentations aggregation, defaults to "mean". + """ + + def __init__( + self, + architecture: Literal["2D", "UNeXt2", "2.5D", "FNet3D", "fcmae", "UNeXt2_2D"], + model_config: dict | None = None, + loss_function: nn.Module | None = None, + lr: float = 1e-3, + schedule: Literal["WarmupCosine", "Constant"] = "Constant", + warmup_steps: int = 3, + warmup_multiplier: float = 1e-3, + freeze_encoder: bool = False, + ckpt_path: str | None = None, + log_batches_per_epoch: int = 8, + log_samples_per_batch: int = 1, + example_input_yx_shape: Sequence[int] = (256, 256), + test_cellpose_model_path: str | None = None, + test_cellpose_diameter: float | None = None, + test_evaluate_cellpose: bool | None = False, + test_time_augmentations: bool | None = False, + tta_type: Literal["mean", "median", "product"] = "mean", + ) -> None: + super().__init__() + self.save_hyperparameters(ignore=["loss_function", "ckpt_path"]) + if model_config is None: + model_config = {} + net_class = _UNET_ARCHITECTURE.get(architecture) + if not net_class: + raise ValueError(f"Architecture {architecture} not in {_UNET_ARCHITECTURE.keys()}") + self.model = net_class(**model_config) + # TODO: handle num_outputs in metrics + # self.out_channels = self.model.terminal_block.out_filters + self.loss_function = loss_function if loss_function else nn.MSELoss() + self.lr = lr + self.schedule = schedule + self.warmup_steps = warmup_steps + self.warmup_multiplier = warmup_multiplier + self.log_batches_per_epoch = log_batches_per_epoch + self.log_samples_per_batch = log_samples_per_batch + self.training_step_outputs = [] + self.validation_losses = [] + self.validation_step_outputs = [] + # required to log the graph + if architecture == "2D": + example_depth = 1 + else: + example_depth = model_config.get("in_stack_depth") or 5 + self.example_input_array = torch.rand( + 1, + model_config.get("in_channels") or 1, + example_depth, + *example_input_yx_shape, + ) + self.test_cellpose_model_path = test_cellpose_model_path + self.test_cellpose_diameter = test_cellpose_diameter + self.test_evaluate_cellpose = test_evaluate_cellpose + # Cache loss function fg_mask compatibility to avoid per-batch inspect.signature(). + sig = inspect.signature(self.loss_function.forward) + self._loss_accepts_fg_mask = "fg_mask" in sig.parameters or any( + p.kind == inspect.Parameter.VAR_KEYWORD for p in sig.parameters.values() + ) + self.test_time_augmentations = test_time_augmentations + self.tta_type = tta_type + self.freeze_encoder = freeze_encoder + self._original_shape_yx = None + if ckpt_path is not None: + self.load_state_dict(torch.load(ckpt_path, weights_only=True, map_location="cpu")["state_dict"]) + + def forward(self, x: Tensor) -> Tensor: + """Run forward pass through the model. + + Parameters + ---------- + x : Tensor + Input tensor. + + Returns + ------- + Tensor + Model output. + """ + return self.model(x) + + def _compute_loss(self, pred: Tensor, target: Tensor, batch: Sample) -> Tensor: + """Compute loss, passing precomputed fg_mask to the loss if present. + + When ``fg_mask_key`` is set in the data config, ``batch["fg_mask"]`` + is forwarded as a keyword argument. The loss function must accept + ``fg_mask`` explicitly or via ``**kwargs``; standard losses like + ``nn.MSELoss`` will raise ``TypeError`` at training time. + """ + if "fg_mask" in batch: + if not self._loss_accepts_fg_mask: + raise TypeError( + f"{type(self.loss_function).__name__} does not accept 'fg_mask'. " + f"Use SpotlightLoss or remove fg_mask_key from the data config." + ) + return self.loss_function(pred, target, fg_mask=batch["fg_mask"]) + return self.loss_function(pred, target) + + def training_step(self, batch: Sample | Sequence[Sample], batch_idx: int): + """Execute a single training step. + + Parameters + ---------- + batch : Sample | Sequence[Sample] + Input batch. + batch_idx : int + Batch index. + + Returns + ------- + Tensor + Training loss. + """ + losses = [] + batch_size = 0 + if not isinstance(batch, Sequence): + batch = [batch] + for b in batch: + source = b["source"] + target = b["target"] + pred = self.forward(source) + loss = self._compute_loss(pred, target, b) + losses.append(loss) + batch_size += source.shape[0] + if batch_idx < self.log_batches_per_epoch: + self.training_step_outputs.extend(detach_sample((source, target, pred), self.log_samples_per_batch)) + loss_step = torch.stack(losses).mean() + self.log( + "loss/train", + loss_step.to(self.device), + on_step=True, + on_epoch=True, + prog_bar=True, + logger=True, + sync_dist=True, + batch_size=batch_size, + ) + return loss_step + + def validation_step(self, batch: Sample, batch_idx: int, dataloader_idx: int = 0): + """Execute a single validation step. + + Parameters + ---------- + batch : Sample + Input batch. + batch_idx : int + Batch index. + dataloader_idx : int + Dataloader index, defaults to 0. + """ + source: Tensor = batch["source"] + target: Tensor = batch["target"] + pred = self.forward(source) + loss = self._compute_loss(pred, target, batch) + if dataloader_idx + 1 > len(self.validation_losses): + self.validation_losses.append([]) + self.validation_losses[dataloader_idx].append(loss.detach()) + self.log( + f"loss/val/{dataloader_idx}", + loss.to(self.device), + sync_dist=True, + batch_size=source.shape[0], + ) + if batch_idx < self.log_batches_per_epoch: + self.validation_step_outputs.extend(detach_sample((source, target, pred), self.log_samples_per_batch)) + + def test_step(self, batch: Sample, batch_idx: int): + """Execute a single test step. + + Parameters + ---------- + batch : Sample + Input batch. + batch_idx : int + Batch index. + """ + source = batch["source"] + target = batch["target"] + center_index = target.shape[-3] // 2 + center_slice = slice(center_index, center_index + 1) + target = target[:, 0, center_slice] + if self.test_evaluate_cellpose: + pred = target + else: + pred = self.forward(source)[:, 0, center_slice] + # FIXME: Only works for batch size 1 and the first channel + self._log_regression_metrics(pred, target) + img_names, ts, zs = batch["index"] + position = float(img_names[0].split("/")[-2]) + self.log_dict( + { + "position": position, + "time": float(ts[0]), + "slice": float(zs[0]), + }, + on_step=True, + on_epoch=False, + ) + if "labels" in batch: + pred_labels = self._cellpose_predict(pred, f"p{int(position)}_t{ts[0]}_z{zs[0]}") + self._log_segmentation_metrics(pred_labels, batch["labels"][0]) + else: + self._log_segmentation_metrics(None, None) + + def _log_regression_metrics(self, pred: Tensor, target: Tensor): + """Log regression metrics for paired image translation.""" + # paired image translation metrics + self.log_dict( + { + # regression + "test_metrics/MAE": mean_absolute_error(pred, target), + "test_metrics/MSE": mean_squared_error(pred, target), + "test_metrics/cosine": cosine_similarity(pred, target, reduction="mean"), + "test_metrics/pearson": pearson_corrcoef(pred.flatten() * 1e4, target.flatten() * 1e4), + "test_metrics/r2": r2_score(pred.flatten(), target.flatten()), + # image perception + "test_metrics/SSIM": structural_similarity_index_measure( + pred, target, gaussian_kernel=False, kernel_size=21 + ), + }, + on_step=True, + on_epoch=True, + ) + + def _cellpose_predict(self, pred: Tensor, name: str) -> torch.ShortTensor: + """Run CellPose segmentation on predicted image.""" + pred_labels_np = self.cellpose_model.eval( + to_numpy(pred), channels=[0, 0], diameter=self.test_cellpose_diameter + )[0].astype(np.int16) + imwrite(os.path.join(self.logger.log_dir, f"{name}.png"), pred_labels_np) + return torch.from_numpy(pred_labels_np).to(self.device) + + def _log_segmentation_metrics(self, pred_labels: torch.ShortTensor, target_labels: torch.ShortTensor): + """Log segmentation metrics comparing predictions to ground truth.""" + compute = pred_labels is not None + if compute: + pred_binary = pred_labels > 0 + target_binary = target_labels > 0 + coco_metrics = mean_average_precision(pred_labels, target_labels) + _logger.debug(coco_metrics) + self.log_dict( + { + # semantic segmentation + "test_metrics/accuracy": (accuracy(pred_binary, target_binary, task="binary") if compute else -1), + "test_metrics/dice_score": ( + dice_score( + pred_binary.long(), + target_binary.long(), + num_classes=2, + input_format="index", + ) + if compute + else -1 + ), + "test_metrics/jaccard": (jaccard_index(pred_binary, target_binary, task="binary") if compute else -1), + "test_metrics/mAP": coco_metrics["map"] if compute else -1, + "test_metrics/mAP_50": coco_metrics["map_50"] if compute else -1, + "test_metrics/mAP_75": coco_metrics["map_75"] if compute else -1, + "test_metrics/mAR_100": coco_metrics["mar_100"] if compute else -1, + }, + on_step=True, + on_epoch=False, + ) + + def _pad_forward_crop(self, source: Tensor) -> Tensor: + """Pad input to divisible size, run forward pass, crop back.""" + original_shape = source.shape[2:] + source = self._predict_pad(source) + prediction = self.forward(source) + return _center_crop_to_shape(prediction, original_shape) + + def predict_step(self, batch: Sample, batch_idx: int, dataloader_idx: int = 0): + """Execute a single prediction step. + + Parameters + ---------- + batch : Sample + Input batch. + batch_idx : int + Batch index. + dataloader_idx : int + Dataloader index, defaults to 0. + + Returns + ------- + Tensor + Model prediction. + """ + source = batch["source"] + if self.test_time_augmentations: + prediction = self.perform_test_time_augmentations(source) + else: + prediction = self._pad_forward_crop(source) + + return prediction + + def perform_test_time_augmentations(self, source: Tensor) -> Tensor: + """Perform test time augmentations on the input source. + + Applies rotations and aggregates predictions using the specified method. + + Parameters + ---------- + source : Tensor + Input tensor. + + Returns + ------- + Tensor + Aggregated prediction. + """ + # Save the yx coords to crop post rotations + self._original_shape_yx = source.shape[-2:] + predictions = [] + for i in range(4): + augmented = self._rotate_volume(source, k=i, spatial_axes=(1, 2)) + de_augmented_prediction = self._pad_forward_crop(augmented) + de_augmented_prediction = self._rotate_volume(de_augmented_prediction, k=4 - i, spatial_axes=(1, 2)) + de_augmented_prediction = self._crop_to_original(de_augmented_prediction) + + # Undo rotation and padding + predictions.append(de_augmented_prediction) + + if self.tta_type == "mean": + prediction = torch.stack(predictions).mean(dim=0) + elif self.tta_type == "median": + prediction = torch.stack(predictions).median(dim=0).values + elif self.tta_type == "product": + # Perform multiplication of predictions in logarithmic space + # for numerical stability adding epsilon to avoid log(0) case + log_predictions = torch.stack([torch.log(p + 1e-9) for p in predictions]) + log_prediction_sum = log_predictions.sum(dim=0) + prediction = torch.exp(log_prediction_sum) + return prediction + + def on_train_epoch_end(self): + """Log training samples at end of epoch.""" + self._log_samples("train_samples", self.training_step_outputs) + self.training_step_outputs = [] + + def on_validation_epoch_end(self): + """Log validation samples and average losses at end of epoch.""" + super().on_validation_epoch_end() + self._log_samples("val_samples", self.validation_step_outputs) + # average within each dataloader + loss_means = [torch.tensor(losses).mean() for losses in self.validation_losses] + self.log( + "loss/validate", + torch.tensor(loss_means).mean().to(self.device), + sync_dist=True, + ) + self.validation_step_outputs.clear() + self.validation_losses.clear() + + def on_test_start(self): + """Load CellPose model for segmentation.""" + if self.test_cellpose_model_path is not None: + try: + from cellpose.models import CellposeModel + + self.cellpose_model = CellposeModel(model_type=self.test_cellpose_model_path, device=self.device) + except ImportError: + raise ImportError( + 'CellPose not installed. Please install the metrics dependency with `pip install viscy"[metrics]"`' + ) + + def on_predict_start(self): + """Pad the input shape to be divisible by the downsampling factor.""" + self._predict_pad = _make_divisible_pad(self.model) + + def configure_optimizers(self): + """Configure optimizer and learning rate scheduler.""" + if self.freeze_encoder: + if not hasattr(self.model, "encoder"): + raise ValueError( + f"freeze_encoder=True requires a model with an 'encoder' attribute " + f"(e.g. FullyConvolutionalMAE), got {type(self.model).__name__}" + ) + self.model.encoder.requires_grad_(False) + return configure_adamw_scheduler( + self, + self.model, + self.lr, + self.schedule, + warmup_steps=self.warmup_steps, + warmup_multiplier=self.warmup_multiplier, + ) + + def _log_samples(self, key: str, imgs: Sequence[Sequence[np.ndarray]]): + """Log image sample grid to the active logger (TensorBoard or W&B).""" + if not self.trainer.is_global_zero or self.logger is None: + return + log_image_grid(self.logger, key, imgs, self.current_epoch) + + def _rotate_volume(self, tensor: Tensor, k: int, spatial_axes: tuple) -> Tensor: + """Rotate a volume tensor by k*90 degrees.""" + # Padding to ensure square shape + max_dim = max(tensor.shape[-2], tensor.shape[-1]) + pad_transform = DivisiblePad((0, 0, max_dim, max_dim)) + padded_tensor = pad_transform(tensor) + + # Rotation + rotated_tensor = [] + rotate = Rotate90(k=k, spatial_axes=spatial_axes) + for b in range(padded_tensor.shape[0]): # iterate over batch + rotated_tensor.append(rotate(padded_tensor[b])) + + # Stack the list of tensors back into a single tensor + rotated_tensor = torch.stack(rotated_tensor) + del padded_tensor + # # Cropping to original shape + return rotated_tensor + + def _crop_to_original(self, tensor: Tensor) -> Tensor: + """Crop tensor back to original YX shape after rotation padding.""" + original_y, original_x = self._original_shape_yx + pad_y = (tensor.shape[-2] - original_y) // 2 + pad_x = (tensor.shape[-1] - original_x) // 2 + cropped_tensor = tensor[..., pad_y : pad_y + original_y, pad_x : pad_x + original_x] + return cropped_tensor + + +class AugmentedPredictionVSUNet(LightningModule): + """Apply test-time augmentations and sliding window prediction for image translation. + + Parameters + ---------- + model : nn.Module + The model to be used for prediction. + forward_transforms : list[Callable[[Tensor], Tensor]] or None, optional + Transforms to apply to the input before the model. Each is applied independently. + If None, defaults to a single identity transform. + inverse_transforms : list[Callable[[Tensor], Tensor]] or None, optional + Inverse transforms to apply to the model output before reduction. + If None, defaults to a single identity transform. + reduction : Literal["mean", "median"], optional + The reduction method to apply to the predictions, by default "mean" + + Notes + ----- + Given sample tensor ``x``, + model instance ``model()``, + a list of forward transforms ``[f1(), f2()]``, + a list of inverse transforms ``[i1(), i2()]``, + and reduction method ``reduce()``, + the prediction is computed as follows: + + prediction = reduce( + [ + i1(model(f1(x))), + i2(model(f2(x))), + ] + ) + """ + + def __init__( + self, + model: nn.Module, + forward_transforms: list[Callable[[Tensor], Tensor]] | None = None, + inverse_transforms: list[Callable[[Tensor], Tensor]] | None = None, + reduction: Literal["mean", "median"] = "mean", + ) -> None: + super().__init__() + self._predict_pad = _make_divisible_pad(model) + self.model = model + self._forward_transforms = forward_transforms or [_identity] + self._inverse_transforms = inverse_transforms or [_identity] + self._reduction = reduction + + def forward(self, x: Tensor) -> Tensor: + """Run forward pass through the model. + + Parameters + ---------- + x : Tensor + Input tensor. + + Returns + ------- + Tensor + Model output. + """ + return self.model(x) + + def setup(self, stage: str) -> None: + """Set up the module for the given stage. + + Parameters + ---------- + stage : str + Stage name (only "predict" is supported). + + Raises + ------ + NotImplementedError + If stage is not "predict". + """ + if stage != "predict": + raise NotImplementedError(f"Only the 'predict' stage is supported by {type(self)}") + + def _reduce_predictions(self, preds: list[Tensor]) -> Tensor: + """Reduce multiple predictions using the configured method.""" + prediction = torch.stack(preds, dim=0) + if self._reduction == "mean": + prediction = prediction.mean(dim=0) + elif self._reduction == "median": + prediction = prediction.median(dim=0).values + return prediction + + def _predict_with_tta(self, source: Tensor) -> Tensor: + """Apply test-time augmentations and reduce predictions. + + Parameters + ---------- + source : Tensor + Input tensor. + + Returns + ------- + Tensor + Prediction (reduced if multiple augmentations). + """ + preds = [] + for fwd_t, inv_t in zip(self._forward_transforms, self._inverse_transforms): + aug_source = fwd_t(source) + aug_source = self._predict_pad(aug_source) + pred = self.forward(aug_source) + pred = _center_crop_to_shape(pred, source.shape[2:]) + preds.append(inv_t(pred)) + if len(preds) == 1: + return preds[0] + return self._reduce_predictions(preds) + + def predict_step(self, batch: Sample, batch_idx: int, dataloader_idx: int = 0) -> Tensor: + """Execute a single prediction step with test-time augmentations. + + Parameters + ---------- + batch : Sample + Input batch containing "source" tensor. + batch_idx : int + Batch index. + dataloader_idx : int + Dataloader index, defaults to 0. + + Returns + ------- + Tensor + Model prediction. + """ + return self._predict_with_tta(batch["source"]) + + def predict_sliding_windows(self, x: Tensor, out_channel: int = 2, step: int = 1) -> Tensor: + """Run inference using sliding windows along Z with linear feathering blending. + + Produces the same results as ``viscy predict`` CLI (HCSPredictionWriter) + since both use the same ``_blend_in`` blending algorithm. + + Parameters + ---------- + x : Tensor + Input tensor of shape (B, C, Z, Y, X). + out_channel : int, optional + Number of output channels, by default 2. + step : int, optional + Step size for sliding window along Z, by default 1. + With step=1, every Z position is covered. With step>1, + trailing positions beyond the last full window are not predicted. + + Returns + ------- + Tensor + Output tensor of shape (B, out_channel, Z, Y, X). + + Raises + ------ + ValueError + If input is not 5D, model lacks ``out_stack_depth``, or + model's stack depth exceeds input depth. + """ + if x.ndim != 5: + raise ValueError(f"Expected input with 5 dimensions (B, C, Z, Y, X), got {x.shape}") + batch_size, _, depth, height, width = x.shape + in_stack_depth = getattr(self.model, "out_stack_depth", None) + if in_stack_depth is None: + raise ValueError( + f"Model {type(self.model).__name__} does not support sliding window " + "prediction (missing out_stack_depth attribute)." + ) + if in_stack_depth > depth: + raise ValueError(f"in_stack_depth {in_stack_depth} > input depth {depth}") + out_tensor = x.new_zeros((batch_size, out_channel, depth, height, width)) + for start in range(0, depth - in_stack_depth + 1, step): + end = start + in_stack_depth + pred = self._predict_with_tta(x[:, :, start:end]) + z_slice = slice(start, end) + out_tensor[:, :, z_slice] = _blend_in(out_tensor[:, :, z_slice], pred, z_slice) + return out_tensor + + +class FcmaeUNet(VSUNet): + """FCMAE-based U-Net for self-supervised pre-training and fine-tuning. + + Workflow + -------- + 1. **Pretrain** with ``fit_mask_ratio > 0`` and ``MaskedMSELoss``. + Set ``model_config["pretraining"] = True`` (the default). + 2. **Fine-tune** by loading the pretrained checkpoint with + ``encoder_only=True`` and ``ckpt_path=``. Set + ``model_config["pretraining"] = False`` and change + ``out_channels`` / loss as needed. Optionally set + ``freeze_encoder=True`` to freeze the encoder. + + Parameters + ---------- + fit_mask_ratio : float + Mask ratio for FCMAE pre-training, defaults to 0.0. + encoder_only : bool + When True and ``ckpt_path`` is set, load only encoder weights + from the checkpoint (ignoring decoder/head). Useful for + fine-tuning with a different number of output channels. + Defaults to False. + freeze_encoder : bool + Freeze encoder weights during fine-tuning (passed to VSUNet). + Defaults to False. + **kwargs + Additional keyword arguments passed to VSUNet. + """ + + def __init__( + self, + fit_mask_ratio: float = 0.0, + encoder_only: bool = False, + **kwargs, + ): + if encoder_only: + if "ckpt_path" not in kwargs or kwargs["ckpt_path"] is None: + raise ValueError("encoder_only=True requires ckpt_path") + ckpt_path = kwargs.pop("ckpt_path") + else: + ckpt_path = None + super().__init__(architecture="fcmae", **kwargs) + self.fit_mask_ratio = fit_mask_ratio + if ckpt_path is not None: + self._load_encoder_weights(ckpt_path) + self.save_hyperparameters(ignore=["loss_function", "ckpt_path", "encoder_only"]) + + def _load_encoder_weights(self, ckpt_path: str) -> None: + """Load only encoder weights from a pretrained checkpoint. + + Parameters + ---------- + ckpt_path : str + Path to the pretrained checkpoint file. + """ + state_dict = torch.load(ckpt_path, weights_only=True, map_location="cpu")["state_dict"] + prefix = "model.encoder." + encoder_weights = {k.removeprefix(prefix): v for k, v in state_dict.items() if k.startswith(prefix)} + self.model.encoder.load_state_dict(encoder_weights, strict=True) + _logger.info(f"Loaded {len(encoder_weights)} encoder parameters from {ckpt_path}") + + def on_fit_start(self): + """Validate datamodule configuration for FCMAE training.""" + dm = self.trainer.datamodule + if not isinstance(dm, CombinedDataModule): + raise ValueError(f"Container data module type {type(dm)} is not supported for FCMAE training") + for subdm in dm.data_modules: + if not isinstance(subdm, GPUTransformDataModule): + raise ValueError(f"Member data module type {type(subdm)} is not supported for FCMAE training") + if self.model.pretraining and not isinstance(self.loss_function, MaskedMSELoss): + raise ValueError(f"MaskedMSELoss is required for FCMAE pre-training, got {type(self.loss_function)}") + + def forward(self, x: Tensor, mask_ratio: float = 0.0): + """Run forward pass with optional masking. + + Parameters + ---------- + x : Tensor + Input tensor. + mask_ratio : float + Mask ratio for FCMAE, defaults to 0.0. + + Returns + ------- + Tensor + Model output. + """ + return self.model(x, mask_ratio) + + def forward_fit_fcmae(self, batch: Sample, return_target: bool = False) -> tuple[Tensor, Tensor | None, Tensor]: + """Forward pass for FCMAE pre-training. + + Parameters + ---------- + batch : Sample + Input batch. + return_target : bool + Whether to return the masked target. + + Returns + ------- + tuple[Tensor, Tensor | None, Tensor] + Prediction, optional target, and loss. + """ + x = batch["source"] + pred, mask = self.forward(x, mask_ratio=self.fit_mask_ratio) + loss = self.loss_function(pred, x, mask) + if return_target: + target = x * mask.unsqueeze(2) + else: + target = None + return pred, target, loss + + def forward_fit_supervised(self, batch: Sample) -> tuple[Tensor, Tensor, Tensor]: + """Forward pass for supervised fine-tuning. + + Parameters + ---------- + batch : Sample + Input batch. + + Returns + ------- + tuple[Tensor, Tensor, Tensor] + Prediction, target, and loss. + """ + x = batch["source"] + target = batch["target"] + pred = self.forward(x) + loss = self._compute_loss(pred, target, batch) + return pred, target, loss + + def forward_fit_task(self, batch: Sample, batch_idx: int) -> tuple[Tensor, Tensor | None, Tensor]: + """Dispatch to FCMAE or supervised forward pass based on model state. + + Parameters + ---------- + batch : Sample + Input batch. + batch_idx : int + Batch index. + + Returns + ------- + tuple[Tensor, Tensor | None, Tensor] + Prediction, optional target, and loss. + """ + return_target = False + if self.model.pretraining: + if batch_idx < self.log_batches_per_epoch: + return_target = True + pred, target, loss = self.forward_fit_fcmae(batch, return_target) + else: + pred, target, loss = self.forward_fit_supervised(batch) + return pred, target, loss + + @staticmethod + def _merge_batches(batch: list[Sample] | Sample) -> Sample: + """Merge per-dataset batches from CombinedLoader into one batch. + + Parameters + ---------- + batch : list[Sample] | Sample + List of per-dataset batches (training) or a single batch + (validation). + + Returns + ------- + Sample + Merged batch with concatenated tensors. + """ + if not isinstance(batch, list): + return batch + combined: dict[str, Tensor] = {} + for key in batch[0]: + vals = [b[key] for b in batch if key in b] + if isinstance(vals[0], Tensor): + combined[key] = torch.cat(vals, dim=0) + elif isinstance(vals[0], tuple): + # Merge inner elements of collated tuples + # (e.g. index = (list[str], Tensor, Tensor)). + merged = [] + for i in range(len(vals[0])): + elems = [v[i] for v in vals] + if isinstance(elems[0], Tensor): + merged.append(torch.cat(elems, dim=0)) + elif isinstance(elems[0], list): + merged.append([x for sublist in elems for x in sublist]) + else: + merged.append(elems[0]) + combined[key] = tuple(merged) + else: + combined[key] = vals[0] + return combined + + def training_step(self, batch: list[Sample] | Sample, batch_idx: int) -> Tensor: + """Execute a single FCMAE training step. + + Parameters + ---------- + batch : list[Sample] | Sample + Per-dataset batches from CombinedLoader (already transformed + by ``CombinedDataModule.on_after_batch_transfer``). + batch_idx : int + Batch index. + + Returns + ------- + Tensor + Training loss. + """ + batch = self._merge_batches(batch) + pred, target, loss = self.forward_fit_task(batch, batch_idx) + if batch_idx < self.log_batches_per_epoch: + self.training_step_outputs.extend( + detach_sample((batch["source"], target, pred), self.log_samples_per_batch) + ) + self.log( + "loss/train", + loss.to(self.device), + on_step=True, + on_epoch=True, + prog_bar=True, + logger=True, + sync_dist=True, + batch_size=pred.shape[0], + ) + return loss + + def validation_step(self, batch: Sample, batch_idx: int, dataloader_idx: int = 0) -> None: + """Execute a single FCMAE validation step. + + Parameters + ---------- + batch : Sample + Input batch (already transformed by + ``CombinedDataModule.on_after_batch_transfer``). + batch_idx : int + Batch index. + dataloader_idx : int + Dataloader index, defaults to 0. + """ + pred, target, loss = self.forward_fit_task(batch, batch_idx) + if dataloader_idx + 1 > len(self.validation_losses): + self.validation_losses.append([]) + self.validation_losses[dataloader_idx].append(loss.detach()) + self.log("loss/val", loss.to(self.device), sync_dist=True, batch_size=pred.shape[0]) + if batch_idx < self.log_batches_per_epoch: + self.validation_step_outputs.extend( + detach_sample((batch["source"], target, pred), self.log_samples_per_batch) + ) diff --git a/applications/cytoland/src/cytoland/evaluation.py b/applications/cytoland/src/cytoland/evaluation.py new file mode 100644 index 000000000..83426746d --- /dev/null +++ b/applications/cytoland/src/cytoland/evaluation.py @@ -0,0 +1,65 @@ +"""Test stage lightning module for comparing virtual staining and segmentations.""" + +import logging + +from lightning.pytorch import LightningModule +from torchmetrics.functional import accuracy, jaccard_index +from torchmetrics.functional.segmentation import dice_score + +from viscy_data import SegmentationSample +from viscy_utils.evaluation.metrics import mean_average_precision + +_logger = logging.getLogger("lightning.pytorch") + + +class SegmentationMetrics2D(LightningModule): + """Test runner for 2D segmentation. + + Parameters + ---------- + aggregate_epoch : bool + Whether to aggregate metrics over the epoch, defaults to False. + """ + + def __init__(self, aggregate_epoch: bool = False) -> None: + super().__init__() + self.aggregate_epoch = aggregate_epoch + + def test_step(self, batch: SegmentationSample, batch_idx: int) -> None: + """Execute a single test step for segmentation evaluation. + + Parameters + ---------- + batch : SegmentationSample + Input batch with pred and target segmentations. + batch_idx : int + Batch index. + """ + pred = batch["pred"] + target = batch["target"] + if not (pred.shape[0] == 1 and target.shape[0] == 1): + raise ValueError(f"Expected 2D segmentation, got {pred.shape[0]} and {target.shape[0]}") + pred = pred[0] + target = target[0] + pred_binary = pred > 0 + target_binary = target > 0 + coco_metrics = mean_average_precision(pred, target) + _logger.debug(coco_metrics) + self.log_dict( + { + "test_metrics/accuracy": accuracy(pred_binary, target_binary, task="binary"), + "test_metrics/dice": dice_score( + pred_binary.long()[None], + target_binary.long()[None], + num_classes=2, + input_format="index", + ), + "test_metrics/jaccard": jaccard_index(pred_binary, target_binary, task="binary"), + "test_metrics/mAP": coco_metrics["map"], + "test_metrics/mAP_50": coco_metrics["map_50"], + "test_metrics/mAP_75": coco_metrics["map_75"], + "test_metrics/mAR_100": coco_metrics["mar_100"], + }, + on_step=True, + on_epoch=False, + ) diff --git a/applications/cytoland/tests/conftest.py b/applications/cytoland/tests/conftest.py new file mode 100644 index 000000000..36530291d --- /dev/null +++ b/applications/cytoland/tests/conftest.py @@ -0,0 +1,268 @@ +"""Test fixtures for cytoland application tests.""" + +from pathlib import Path + +import numpy as np +import pytest +import torch +from iohub.ngff import open_ome_zarr +from lightning.pytorch import LightningDataModule +from monai.transforms.compose import Compose +from torch.utils.data import DataLoader, Dataset + +from viscy_data.combined import CombinedDataModule +from viscy_data.gpu_aug import GPUTransformDataModule +from viscy_transforms import BatchedStackChannelsd + +# Synthetic data dimensions +SYNTH_B = 2 # batch size +SYNTH_C = 1 # input channels (phase) +SYNTH_D = 5 # depth (z-stack) +SYNTH_H = 64 # height +SYNTH_W = 64 # width + +# FCMAE needs 128x128 (64x64 creates degenerate 2x2 bottleneck with 7x7 depthwise conv). +FCMAE_H = 128 +FCMAE_W = 128 + +# MixedLoss 5-scale MS-SSIM needs spatial/16 >= 11 (no padding in MONAI SSIM kernel). +MIXED_LOSS_H = 192 +MIXED_LOSS_W = 192 + +# HPC path constants for inference reproducibility tests. +CHECKPOINT_PATH = Path( + "/hpc/projects/comp.micro/virtual_staining/models/fcmae-cyto3d-sensor/" + "vscyto3d-logs/hek-a549-ipsc-finetune/checkpoints/" + "epoch=83-step=14532-loss=0.492.ckpt" +) + +DATA_ZARR_PATH = Path( + "/hpc/projects/virtual_staining/datasets/mehta-lab/VS_datasets/VSCyto3D/test/vscyto3d_test_fixture.zarr" +) + +REFERENCE_ZARR_PATH = Path( + "/hpc/projects/virtual_staining/datasets/mehta-lab/VS_datasets/VSCyto3D/test/vscyto3d_test_reference.zarr" +) + +HPC_PATHS_AVAILABLE = all(p.exists() for p in [CHECKPOINT_PATH, DATA_ZARR_PATH, REFERENCE_ZARR_PATH]) + +GPU_AVAILABLE = torch.cuda.is_available() + +requires_hpc_and_gpu = pytest.mark.skipif( + not (HPC_PATHS_AVAILABLE and GPU_AVAILABLE), + reason="Requires HPC data paths and CUDA GPU", +) + + +def pytest_configure(config): + """Register custom markers.""" + config.addinivalue_line("markers", "hpc_integration: requires HPC paths and GPU") + + +@pytest.fixture +def checkpoint_path(): + """Return path to vscyto3d checkpoint.""" + return CHECKPOINT_PATH + + +@pytest.fixture +def data_zarr_path(): + """Return path to input HCS OME-Zarr data.""" + return DATA_ZARR_PATH + + +@pytest.fixture +def reference_zarr_path(): + """Return path to reference prediction OME-Zarr.""" + return REFERENCE_ZARR_PATH + + +@pytest.fixture +def synth_dims(): + """Synthetic data dimensions shared across tests.""" + return { + "b": SYNTH_B, + "c": SYNTH_C, + "d": SYNTH_D, + "h": SYNTH_H, + "w": SYNTH_W, + "fcmae_h": FCMAE_H, + "fcmae_w": FCMAE_W, + "mixed_loss_h": MIXED_LOSS_H, + "mixed_loss_w": MIXED_LOSS_W, + } + + +@pytest.fixture +def synthetic_batch(): + """Create a synthetic batch dict matching the Sample type.""" + return { + "source": torch.randn(SYNTH_B, SYNTH_C, SYNTH_D, SYNTH_H, SYNTH_W), + "target": torch.randn(SYNTH_B, SYNTH_C, SYNTH_D, SYNTH_H, SYNTH_W), + "index": ( + ["row/col/pos/0" for _ in range(SYNTH_B)], + [torch.tensor(0) for _ in range(SYNTH_B)], + [torch.tensor(0) for _ in range(SYNTH_B)], + ), + } + + +# --------------------------------------------------------------------------- +# Synthetic datasets and data modules for training integration tests +# --------------------------------------------------------------------------- + + +class SyntheticHCSDataset(Dataset): + """Synthetic dataset returning Sample dicts with source, target, index.""" + + def __init__(self, size=8, n_channels=1, depth=SYNTH_D, height=SYNTH_H, width=SYNTH_W): + self.size = size + self.n_channels = n_channels + self.depth = depth + self.height = height + self.width = width + + def __len__(self): + return self.size + + def __getitem__(self, idx): + return { + "source": torch.randn(self.n_channels, self.depth, self.height, self.width), + "target": torch.randn(self.n_channels, self.depth, self.height, self.width), + "index": (f"row/col/pos/{idx}", torch.tensor(0), torch.tensor(0)), + } + + +class SyntheticHCSDataModule(LightningDataModule): + """DataModule wrapping SyntheticHCSDataset for VSUNet train/val.""" + + def __init__(self, batch_size=2, num_samples=8, **dataset_kwargs): + super().__init__() + self.batch_size = batch_size + self.num_samples = num_samples + self.dataset_kwargs = dataset_kwargs + + def train_dataloader(self): + return DataLoader( + SyntheticHCSDataset(self.num_samples, **self.dataset_kwargs), + batch_size=self.batch_size, + ) + + def val_dataloader(self): + return DataLoader( + SyntheticHCSDataset(self.num_samples, **self.dataset_kwargs), + batch_size=self.batch_size, + ) + + +class SyntheticGPUTransformDataset(Dataset): + """Synthetic dataset returning [dict] matching CachedOmeZarrDataset format. + + Each item is a list containing one dict with per-channel-name tensors, + compatible with ``list_data_collate``. + """ + + def __init__(self, size=8, depth=SYNTH_D, height=FCMAE_H, width=FCMAE_W): + self.size = size + self.depth = depth + self.height = height + self.width = width + + def __len__(self): + return self.size + + def __getitem__(self, idx): + return [ + { + "Phase3D": torch.randn(1, self.depth, self.height, self.width), + "Fluorescence": torch.randn(1, self.depth, self.height, self.width), + } + ] + + +class SyntheticGPUTransformDataModule(GPUTransformDataModule): + """Synthetic GPUTransformDataModule with BatchedStackChannelsd for FCMAE tests. + + GPU transforms use BatchedStackChannelsd to map channel-name keys + to source/target on batched ``(B, 1, Z, Y, X)`` tensors, matching the + production CachedOmeZarrDataModule pattern with batched GPU transforms. + """ + + def __init__(self, batch_size=2, num_samples=8, depth=SYNTH_D, height=FCMAE_H, width=FCMAE_W): + super().__init__() + self.batch_size = batch_size + self.num_workers = 0 + self.pin_memory = False + self.prefetch_factor = None + self._depth = depth + self._height = height + self._width = width + self._num_samples = num_samples + stack = BatchedStackChannelsd({"source": ["Phase3D"], "target": ["Fluorescence"]}) + self._train_gpu = Compose([stack]) + self._val_gpu = Compose([stack]) + + def setup(self, stage): + self.train_dataset = SyntheticGPUTransformDataset(self._num_samples, self._depth, self._height, self._width) + self.val_dataset = SyntheticGPUTransformDataset(self._num_samples, self._depth, self._height, self._width) + + @property + def train_cpu_transforms(self): + return Compose([]) + + @property + def train_gpu_transforms(self): + return self._train_gpu + + @property + def val_cpu_transforms(self): + return Compose([]) + + @property + def val_gpu_transforms(self): + return self._val_gpu + + +def make_synthetic_combined_datamodule(**kwargs): + """Create a CombinedDataModule wrapping one SyntheticGPUTransformDataModule.""" + return CombinedDataModule([SyntheticGPUTransformDataModule(**kwargs)]) + + +@pytest.fixture +def _SyntheticHCSDataModule(): + """Return the SyntheticHCSDataModule class.""" + return SyntheticHCSDataModule + + +@pytest.fixture +def _make_synthetic_combined_datamodule(): + """Return the make_synthetic_combined_datamodule factory function.""" + return make_synthetic_combined_datamodule + + +@pytest.fixture +def tiny_hcs_zarr(tmp_path): + """Create a minimal HCS OME-Zarr with 4 positions for integration tests. + + Uses FCMAE_H/W spatial dims so both VSUNet (with yx_patch_size crop) + and FCMAE tests can use the same fixture. + """ + zarr_path = tmp_path / "tiny.zarr" + channel_names = ["Phase3D", "Fluorescence"] + with open_ome_zarr(zarr_path, layout="hcs", mode="w", channel_names=channel_names) as dataset: + rng = np.random.default_rng(42) + for row in ("A",): + for col in ("1", "2"): + for fov in ("0", "1"): + pos = dataset.create_position(row, col, fov) + pos.create_image( + "0", + rng.random((1, len(channel_names), SYNTH_D, FCMAE_H, FCMAE_W)).astype(np.float32), + chunks=(1, 1, SYNTH_D, FCMAE_H, FCMAE_W), + ) + # Write per-FOV normalization metadata. + norm_meta = {ch: {"fov_statistics": {"mean": 0.5, "std": 0.29, "otsu_threshold": 0.5}} for ch in channel_names} + with open_ome_zarr(zarr_path, mode="r+") as ds: + for _, fov in ds.positions(): + fov.zattrs["normalization"] = norm_meta + return zarr_path diff --git a/applications/cytoland/tests/test_engine.py b/applications/cytoland/tests/test_engine.py new file mode 100644 index 000000000..de1ed5138 --- /dev/null +++ b/applications/cytoland/tests/test_engine.py @@ -0,0 +1,208 @@ +"""Smoke tests for cytoland engine modules.""" + +import subprocess +from pathlib import Path + +import pytest +import torch +from monai.data import get_track_meta, set_track_meta + +from cytoland.engine import AugmentedPredictionVSUNet, FcmaeUNet, VSUNet + + +def test_imports(): + """Verify all top-level imports work.""" + from cytoland import AugmentedPredictionVSUNet, FcmaeUNet, MaskedMSELoss, SegmentationMetrics2D, VSUNet + from viscy_utils.callbacks import HCSPredictionWriter + from viscy_utils.losses import MixedLoss + + assert VSUNet is not None + assert FcmaeUNet is not None + assert AugmentedPredictionVSUNet is not None + assert MaskedMSELoss is not None + assert SegmentationMetrics2D is not None + assert MixedLoss is not None + assert HCSPredictionWriter is not None + + +def test_vsunet_init(synth_dims): + """Verify VSUNet instantiates with UNeXt2 architecture.""" + model = VSUNet( + architecture="UNeXt2", + model_config={"in_channels": synth_dims["c"], "out_channels": 1, "in_stack_depth": synth_dims["d"]}, + ) + assert model.model is not None + assert model.lr == 1e-3 + + +def test_vsunet_forward(synthetic_batch, synth_dims): + """Verify VSUNet forward pass produces correct output shape.""" + model = VSUNet( + architecture="UNeXt2", + model_config={"in_channels": synth_dims["c"], "out_channels": 1, "in_stack_depth": synth_dims["d"]}, + ) + model.eval() + with torch.no_grad(): + output = model(synthetic_batch["source"]) + assert output.shape[0] == synth_dims["b"] + assert output.shape[1] == 1 # out_channels + + +def test_vsunet_state_dict_keys(synth_dims): + """State dict key regression test for checkpoint compatibility.""" + model = VSUNet( + architecture="UNeXt2", + model_config={"in_channels": synth_dims["c"], "out_channels": 1, "in_stack_depth": synth_dims["d"]}, + ) + state_dict = model.state_dict() + for key in state_dict: + assert key.startswith("model."), f"Unexpected key prefix: {key}" + key_names = set(state_dict.keys()) + assert any("model." in k for k in key_names), "No model keys found" + assert len(key_names) > 0, "Empty state dict" + + +def test_fnet3d_init(): + """Verify VSUNet instantiates with FNet3D architecture.""" + model = VSUNet( + architecture="FNet3D", + model_config={"in_channels": 1, "out_channels": 1, "depth": 1, "mult_chan": 8, "in_stack_depth": 4}, + ) + assert model.model is not None + + +def test_fnet3d_forward(): + """Verify FNet3D forward pass produces correct output shape.""" + model = VSUNet( + architecture="FNet3D", + model_config={"in_channels": 1, "out_channels": 1, "depth": 1, "mult_chan": 8, "in_stack_depth": 4}, + ) + model.eval() + x = torch.randn(2, 1, 4, 16, 16) + with torch.no_grad(): + y = model(x) + assert y.shape == (2, 1, 4, 16, 16) + + +def test_fnet3d_predict_start(): + """Verify on_predict_start works with FNet3D (requires num_blocks).""" + model = VSUNet( + architecture="FNet3D", + model_config={"in_channels": 1, "out_channels": 1, "depth": 1, "mult_chan": 8, "in_stack_depth": 4}, + ) + model.on_predict_start() + assert model._predict_pad is not None + + +def test_fnet3d_predict_sliding_windows(): + """Verify FNet wrapper exposes out_stack_depth for sliding window prediction.""" + model = VSUNet( + architecture="FNet3D", + model_config={"in_channels": 1, "out_channels": 1, "depth": 1, "mult_chan": 8, "in_stack_depth": 4}, + ) + vs = AugmentedPredictionVSUNet(model=model.model) + vs.eval() + x = torch.randn(1, 1, 8, 16, 16) + with torch.inference_mode(): + output = vs.predict_sliding_windows(x, out_channel=1, step=1) + assert output.shape == (1, 1, 8, 16, 16) + + +def test_mixed_loss_integration(synthetic_batch, synth_dims): + """Verify MixedLoss works as loss_function for VSUNet.""" + from viscy_utils.losses import MixedLoss + + loss_fn = MixedLoss(l1_alpha=0.5, l2_alpha=0.0, ms_dssim_alpha=0.5) + model = VSUNet( + architecture="UNeXt2", + model_config={"in_channels": synth_dims["c"], "out_channels": 1, "in_stack_depth": synth_dims["d"]}, + loss_function=loss_fn, + ) + assert model.loss_function is loss_fn + + +def test_fcmae_unet_init(synth_dims): + """Verify FcmaeUNet instantiates.""" + model = FcmaeUNet( + model_config={"in_channels": synth_dims["c"], "out_channels": 1, "in_stack_depth": synth_dims["d"]}, + ) + assert model.fit_mask_ratio == 0.0 + + +def test_no_old_imports(): + """Verify no old viscy.* import paths remain in source code.""" + src_dir = Path(__file__).resolve().parents[1] / "src" + result = subprocess.run( + ["grep", "-r", "from viscy\\.", str(src_dir)], + capture_output=True, + text=True, + ) + assert result.stdout == "", f"Old import paths found:\n{result.stdout}" + + +def test_augmented_prediction_optional_transforms(synth_dims): + """Verify AugmentedPredictionVSUNet works without specifying transforms.""" + previous_track_meta = get_track_meta() + set_track_meta(False) + try: + model = VSUNet( + architecture="UNeXt2", + model_config={"in_channels": synth_dims["c"], "out_channels": 1, "in_stack_depth": synth_dims["d"]}, + ) + vs = AugmentedPredictionVSUNet(model=model.model) + vs.eval() + x = torch.randn(synth_dims["b"], synth_dims["c"], synth_dims["d"], 64, 64) + with torch.inference_mode(): + output = vs._predict_with_tta(x) + finally: + set_track_meta(previous_track_meta) + assert output.shape[0] == synth_dims["b"] + assert output.shape[1] == 1 + + +def test_predict_sliding_windows_output_shape(synth_dims): + """Verify predict_sliding_windows produces correct output shape.""" + z_window = synth_dims["d"] + out_channels = 2 + depth = 12 + + model = VSUNet( + architecture="fcmae", + model_config={ + "in_channels": synth_dims["c"], + "out_channels": out_channels, + "encoder_blocks": [2, 2, 2, 2], + "dims": [4, 8, 16, 32], + "decoder_conv_blocks": 2, + "stem_kernel_size": [z_window, 4, 4], + "in_stack_depth": z_window, + "pretraining": False, + }, + ) + vs = AugmentedPredictionVSUNet(model=model.model) + vs.eval() + x = torch.randn(1, synth_dims["c"], depth, synth_dims["fcmae_h"], synth_dims["fcmae_w"]) + with torch.inference_mode(): + output = vs.predict_sliding_windows(x, out_channel=out_channels, step=1) + expected = (1, out_channels, depth, synth_dims["fcmae_h"], synth_dims["fcmae_w"]) + assert output.shape == expected, f"Expected {expected}, got {output.shape}" + + +def test_predict_sliding_windows_invalid_input(synth_dims): + """Verify predict_sliding_windows rejects non-5D input.""" + model = VSUNet( + architecture="UNeXt2", + model_config={"in_channels": 1, "out_channels": 1, "in_stack_depth": synth_dims["d"]}, + ) + vs = AugmentedPredictionVSUNet(model=model.model) + with pytest.raises(ValueError, match="5 dimensions"): + vs.predict_sliding_windows(torch.randn(1, synth_dims["d"], 64, 64)) + + +def test_predict_sliding_windows_missing_out_stack_depth(): + """Verify predict_sliding_windows rejects model without out_stack_depth.""" + model = torch.nn.Linear(10, 10) + model.num_blocks = 1 # satisfy DivisiblePad + vs = AugmentedPredictionVSUNet(model=model) + with pytest.raises(ValueError, match="out_stack_depth"): + vs.predict_sliding_windows(torch.randn(1, 1, 10, 4, 4)) diff --git a/applications/cytoland/tests/test_inference_reproducibility.py b/applications/cytoland/tests/test_inference_reproducibility.py new file mode 100644 index 000000000..2fb686d8f --- /dev/null +++ b/applications/cytoland/tests/test_inference_reproducibility.py @@ -0,0 +1,214 @@ +"""Integration tests for inference reproducibility of modular vscyto3d. + +Validates that the modular FcmaeUNet produces identical prediction results +to the reference predictions. Tests checkpoint loading and pixel-level +prediction exactness using the production pipeline (HCSDataModule + +HCSPredictionWriter + VisCyTrainer). + +The test fixture is a single 512x512 FOV cropped from the mehta-lab +VSCyto3D test dataset with pre-computed normalization metadata. +The reference predictions were generated using the same code and checkpoint. + +Tolerance rationale: GPU convolution non-determinism across CUDA/cuDNN +versions and hardware causes small numerical differences in deep ConvNeXt +models. We use the same tolerances as DynaCLR: + - atol=0.02 for element-wise checks + - Pearson correlation > 0.999 per channel +""" + +from pathlib import Path + +import numpy as np +import pytest +import torch +from iohub.ngff import open_ome_zarr +from lightning.pytorch import seed_everything +from scipy import stats + +from cytoland.engine import FcmaeUNet + +# HPC path constants +CHECKPOINT_PATH = Path( + "/hpc/projects/comp.micro/virtual_staining/models/fcmae-cyto3d-sensor/" + "vscyto3d-logs/hek-a549-ipsc-finetune/checkpoints/" + "epoch=83-step=14532-loss=0.492.ckpt" +) + +DATA_ZARR_PATH = Path( + "/hpc/projects/virtual_staining/datasets/mehta-lab/VS_datasets/VSCyto3D/test/vscyto3d_test_fixture.zarr" +) + +REFERENCE_ZARR_PATH = Path( + "/hpc/projects/virtual_staining/datasets/mehta-lab/VS_datasets/VSCyto3D/test/vscyto3d_test_reference.zarr" +) + +HPC_PATHS_AVAILABLE = all(p.exists() for p in [CHECKPOINT_PATH, DATA_ZARR_PATH, REFERENCE_ZARR_PATH]) +GPU_AVAILABLE = torch.cuda.is_available() + +requires_hpc_and_gpu = pytest.mark.skipif( + not (HPC_PATHS_AVAILABLE and GPU_AVAILABLE), + reason="Requires HPC data paths and CUDA GPU", +) + +# Model configuration — matches the fine-tuned vscyto3d checkpoint +# (from finetune_vscyto3d.py:163-174). +MODEL_CONFIG = { + "in_channels": 1, + "out_channels": 2, + "encoder_blocks": [3, 3, 9, 3], + "dims": [96, 192, 384, 768], + "decoder_conv_blocks": 2, + "stem_kernel_size": (5, 4, 4), + "in_stack_depth": 15, + "pretraining": False, +} + +# Source/target channel configuration. +SOURCE_CHANNEL = "Phase3D" +TARGET_CHANNELS = ["Membrane", "Nuclei"] + +# GPU non-determinism tolerance for FCMAE/ConvNeXt convolutions. +ATOL = 0.02 +RTOL = 1e-2 +MIN_PEARSON_R = 0.999 + + +def _build_module(checkpoint_path): + """Build FcmaeUNet and load pretrained checkpoint. + + Parameters + ---------- + checkpoint_path : Path + Path to Lightning checkpoint file. + + Returns + ------- + tuple[FcmaeUNet, object] + Module and load_state_dict result. + """ + module = FcmaeUNet(model_config=MODEL_CONFIG) + ckpt = torch.load(checkpoint_path, map_location="cpu", weights_only=True) + result = module.load_state_dict(ckpt["state_dict"]) + return module, result + + +@requires_hpc_and_gpu +@pytest.mark.hpc_integration +def test_checkpoint_loads_into_modular_fcmae_unet(checkpoint_path): + """Checkpoint loads without state dict key mismatches.""" + seed_everything(42) + module, result = _build_module(checkpoint_path) + + assert len(result.missing_keys) == 0, f"Missing keys: {result.missing_keys}" + assert len(result.unexpected_keys) == 0, f"Unexpected keys: {result.unexpected_keys}" + + # Smoke-test forward pass with correct input shape. + x = torch.randn(1, MODEL_CONFIG["in_channels"], MODEL_CONFIG["in_stack_depth"], 64, 64) + module.eval() + with torch.no_grad(): + output = module(x) + assert output.shape[0] == 1 + assert output.shape[1] == MODEL_CONFIG["out_channels"] + + +@requires_hpc_and_gpu +@pytest.mark.hpc_integration +def test_predict_and_match_reference( + tmp_path, + checkpoint_path, + data_zarr_path, + reference_zarr_path, +): + """Predict using production pipeline and compare against reference. + + Uses HCSDataModule + HCSPredictionWriter + VisCyTrainer, + following the demo_vscyto3d.py pattern. + """ + from viscy_data.hcs import HCSDataModule + from viscy_transforms import NormalizeSampled + from viscy_utils.callbacks import HCSPredictionWriter + from viscy_utils.trainer import VisCyTrainer + + seed_everything(42) + + module, _ = _build_module(checkpoint_path) + module.eval() + + # Single FOV path, following demo_vscyto3d.py pattern. + fov_path = data_zarr_path / "plate/0/0" + datamodule = HCSDataModule( + data_path=str(fov_path), + source_channel=SOURCE_CHANNEL, + target_channel=TARGET_CHANNELS, + z_window_size=MODEL_CONFIG["in_stack_depth"], + batch_size=2, + num_workers=0, + normalizations=[ + NormalizeSampled( + keys=[SOURCE_CHANNEL], + level="fov_statistics", + subtrahend="mean", + divisor="std", + ) + ], + ) + + output_path = tmp_path / "predictions.zarr" + writer = HCSPredictionWriter(str(output_path)) + + trainer = VisCyTrainer( + accelerator="gpu", + devices=1, + precision="32-true", + callbacks=[writer], + inference_mode=True, + enable_progress_bar=False, + logger=False, + ) + + trainer.predict(model=module, datamodule=datamodule, return_predictions=False) + assert output_path.exists(), f"Output zarr not written at {output_path}" + + # --- Compare predictions against reference --- + pred_plate = open_ome_zarr(str(output_path), mode="r") + ref_plate = open_ome_zarr(str(reference_zarr_path), mode="r") + + pred_positions = dict(pred_plate.positions()) + ref_positions = dict(ref_plate.positions()) + + assert set(pred_positions.keys()) == set(ref_positions.keys()), ( + f"Position mismatch: pred={set(pred_positions.keys())} vs ref={set(ref_positions.keys())}" + ) + + for pos_name in sorted(ref_positions.keys()): + pred_pos = pred_positions[pos_name] + ref_pos = ref_positions[pos_name] + + pred_img = np.asarray(pred_pos["0"][:], dtype=np.float32) + ref_img = np.asarray(ref_pos["0"][:], dtype=np.float32) + + assert pred_img.shape == ref_img.shape, ( + f"Shape mismatch at {pos_name}: pred={pred_img.shape} vs ref={ref_img.shape}" + ) + + n_channels = pred_img.shape[1] + for ch in range(n_channels): + pred_ch = pred_img[:, ch].flatten().astype(np.float64) + ref_ch = ref_img[:, ch].flatten().astype(np.float64) + + if np.all(ref_ch == 0) and np.all(pred_ch == 0): + continue + + r, _ = stats.pearsonr(pred_ch, ref_ch) + assert r > MIN_PEARSON_R, f"Pearson r={r:.6f} < {MIN_PEARSON_R} at position {pos_name}, channel {ch}" + + np.testing.assert_allclose( + pred_img[:, ch], + ref_img[:, ch], + rtol=RTOL, + atol=ATOL, + err_msg=f"Prediction exceeds tolerance at position {pos_name}, channel {ch}", + ) + + pred_plate.close() + ref_plate.close() diff --git a/applications/cytoland/tests/test_training_integration.py b/applications/cytoland/tests/test_training_integration.py new file mode 100644 index 000000000..d51ea18a2 --- /dev/null +++ b/applications/cytoland/tests/test_training_integration.py @@ -0,0 +1,552 @@ +"""Training integration tests for cytoland models. + +Validates that the forward+backward pass works for cytoland modules +using ``fast_dev_run=True`` (1 batch of train + val). Follows the DynaCLR +``test_training_integration.py`` pattern. + +Synthetic tests use lightweight random data and always run on CPU. +Real integration tests exercise the full data-to-model pipeline with a +tiny HCS OME-Zarr fixture. +""" + +import importlib +import sys +from pathlib import Path + +import pytest +import torch +import yaml +from lightning.pytorch import Trainer, seed_everything +from lightning.pytorch.loggers import TensorBoardLogger + +from cytoland.engine import FcmaeUNet, MaskedMSELoss, VSUNet +from viscy_data.combined import CombinedDataModule +from viscy_data.gpu_aug import CachedOmeZarrDataModule +from viscy_data.hcs import HCSDataModule +from viscy_transforms import BatchedStackChannelsd, RandSpatialCropd +from viscy_utils.cli import _maybe_compose_config +from viscy_utils.compose import load_composed_config +from viscy_utils.losses import MixedLoss, SpotlightLoss +from viscy_utils.meta_utils import generate_fg_masks + +# --------------------------------------------------------------------------- +# Synthetic tests (CPU, always run) +# --------------------------------------------------------------------------- + + +def test_vsunet_fast_dev_run(tmp_path, _SyntheticHCSDataModule, synth_dims): + """VSUNet + UNeXt2 + MSELoss trains for 1 batch.""" + seed_everything(42) + module = VSUNet( + architecture="UNeXt2", + model_config={"in_channels": 1, "out_channels": 1, "in_stack_depth": synth_dims["d"]}, + log_batches_per_epoch=1, + ) + trainer = Trainer( + fast_dev_run=True, + accelerator="cpu", + logger=TensorBoardLogger(save_dir=tmp_path), + enable_checkpointing=False, + enable_progress_bar=False, + ) + trainer.fit(module, datamodule=_SyntheticHCSDataModule()) + assert trainer.state.finished is True + assert trainer.state.status == "finished" + + +def test_vsunet_mixed_loss_fast_dev_run(tmp_path, _SyntheticHCSDataModule, synth_dims): + """VSUNet + UNeXt2 + MixedLoss (L1 + MS-DSSIM) trains for 1 batch.""" + seed_everything(42) + module = VSUNet( + architecture="UNeXt2", + model_config={"in_channels": 1, "out_channels": 1, "in_stack_depth": synth_dims["d"]}, + loss_function=MixedLoss(l1_alpha=0.5, ms_dssim_alpha=0.5), + log_batches_per_epoch=1, + ) + trainer = Trainer( + fast_dev_run=True, + accelerator="cpu", + logger=TensorBoardLogger(save_dir=tmp_path), + enable_checkpointing=False, + enable_progress_bar=False, + ) + # 192x192 spatial needed: MS-SSIM kernel 11x11, 5 scales → spatial/16 >= 11. + trainer.fit( + module, + datamodule=_SyntheticHCSDataModule(height=synth_dims["mixed_loss_h"], width=synth_dims["mixed_loss_w"]), + ) + assert trainer.state.finished is True + assert trainer.state.status == "finished" + + +def test_fnet3d_fast_dev_run(tmp_path, _SyntheticHCSDataModule): + """VSUNet + FNet3D + MSELoss trains for 1 batch.""" + seed_everything(42) + module = VSUNet( + architecture="FNet3D", + model_config={ + "in_channels": 1, + "out_channels": 1, + "depth": 1, + "mult_chan": 8, + "in_stack_depth": 4, + }, + log_batches_per_epoch=1, + ) + trainer = Trainer( + fast_dev_run=True, + accelerator="cpu", + logger=TensorBoardLogger(save_dir=tmp_path), + enable_checkpointing=False, + enable_progress_bar=False, + ) + trainer.fit(module, datamodule=_SyntheticHCSDataModule(depth=4)) + assert trainer.state.finished is True + assert trainer.state.status == "finished" + + +def test_spotlight_with_fg_mask_fast_dev_run(tmp_path, tiny_hcs_zarr): + """VSUNet + FNet3D + SpotlightLoss with precomputed fg_mask trains for 1 batch.""" + + # Fixture already has otsu_threshold in norm_meta; just generate masks + generate_fg_masks(tiny_hcs_zarr, channel_names=["Fluorescence"]) + + seed_everything(42) + module = VSUNet( + architecture="FNet3D", + model_config={ + "in_channels": 1, + "out_channels": 1, + "depth": 1, + "mult_chan": 8, + "in_stack_depth": 4, + }, + loss_function=SpotlightLoss(lambda_mse=0.5, sigmoid_k=-0.95), + log_batches_per_epoch=1, + ) + datamodule = HCSDataModule( + data_path=tiny_hcs_zarr, + source_channel="Phase3D", + target_channel="Fluorescence", + z_window_size=4, + batch_size=2, + num_workers=0, + yx_patch_size=(32, 32), + fg_mask_key="fg_mask", + split_ratio=0.5, + augmentations=[ + RandSpatialCropd(keys=["Phase3D", "Fluorescence"], roi_size=[4, 32, 32]), + ], + ) + trainer = Trainer( + fast_dev_run=True, + accelerator="cpu", + logger=TensorBoardLogger(save_dir=tmp_path), + enable_checkpointing=False, + enable_progress_bar=False, + ) + trainer.fit(module, datamodule=datamodule) + assert trainer.state.finished is True + assert trainer.state.status == "finished" + + +def test_spotlight_fast_dev_run(tmp_path, _SyntheticHCSDataModule): + """VSUNet + FNet3D + SpotlightLoss trains for 1 batch.""" + + seed_everything(42) + module = VSUNet( + architecture="FNet3D", + model_config={ + "in_channels": 1, + "out_channels": 1, + "depth": 1, + "mult_chan": 8, + "in_stack_depth": 4, + }, + loss_function=SpotlightLoss(lambda_mse=0.5, sigmoid_k=-0.95), + log_batches_per_epoch=1, + ) + trainer = Trainer( + fast_dev_run=True, + accelerator="cpu", + logger=TensorBoardLogger(save_dir=tmp_path), + enable_checkpointing=False, + enable_progress_bar=False, + ) + trainer.fit(module, datamodule=_SyntheticHCSDataModule(depth=4)) + assert trainer.state.finished is True + assert trainer.state.status == "finished" + + +def test_fcmae_pretrain_fast_dev_run(tmp_path, _make_synthetic_combined_datamodule, synth_dims): + """FcmaeUNet FCMAE pretraining (MaskedMSELoss) trains for 1 batch.""" + seed_everything(42) + module = FcmaeUNet( + model_config={"in_channels": 1, "out_channels": 1, "in_stack_depth": synth_dims["d"]}, + loss_function=MaskedMSELoss(), + fit_mask_ratio=0.5, + log_batches_per_epoch=1, + ) + trainer = Trainer( + fast_dev_run=True, + accelerator="cpu", + logger=TensorBoardLogger(save_dir=tmp_path), + enable_checkpointing=False, + enable_progress_bar=False, + ) + trainer.fit(module, datamodule=_make_synthetic_combined_datamodule()) + assert trainer.state.finished is True + assert trainer.state.status == "finished" + + +def test_fcmae_finetune_fast_dev_run(tmp_path, _make_synthetic_combined_datamodule, synth_dims): + """FcmaeUNet supervised fine-tuning (MSELoss) trains for 1 batch.""" + seed_everything(42) + module = FcmaeUNet( + model_config={ + "in_channels": 1, + "out_channels": 1, + "in_stack_depth": synth_dims["d"], + "pretraining": False, + }, + log_batches_per_epoch=1, + ) + trainer = Trainer( + fast_dev_run=True, + accelerator="cpu", + logger=TensorBoardLogger(save_dir=tmp_path), + enable_checkpointing=False, + enable_progress_bar=False, + ) + trainer.fit(module, datamodule=_make_synthetic_combined_datamodule()) + assert trainer.state.finished is True + assert trainer.state.status == "finished" + + +def test_fcmae_encoder_only_load(tmp_path, synth_dims): + """FcmaeUNet encoder_only=True loads only encoder weights from a checkpoint.""" + seed_everything(42) + pretrain_model = FcmaeUNet( + model_config={"in_channels": 1, "out_channels": 1, "in_stack_depth": synth_dims["d"]}, + loss_function=MaskedMSELoss(), + fit_mask_ratio=0.5, + ) + ckpt_path = str(tmp_path / "pretrained.ckpt") + torch.save({"state_dict": pretrain_model.state_dict()}, ckpt_path) + + # Load encoder-only into a model with different out_channels + finetune_model = FcmaeUNet( + model_config={ + "in_channels": 1, + "out_channels": 2, + "in_stack_depth": synth_dims["d"], + "pretraining": False, + }, + encoder_only=True, + ckpt_path=ckpt_path, + ) + + # Verify encoder weights match + for key in pretrain_model.model.encoder.state_dict(): + assert torch.equal( + pretrain_model.model.encoder.state_dict()[key], + finetune_model.model.encoder.state_dict()[key], + ), f"Encoder weight mismatch for key: {key}" + + # Verify forward pass with new out_channels + x = torch.randn(2, 1, synth_dims["d"], synth_dims["fcmae_h"], synth_dims["fcmae_w"]) + finetune_model.eval() + with torch.no_grad(): + out = finetune_model(x) + assert out.shape[1] == 2, f"Expected out_channels=2, got {out.shape[1]}" + + +def test_fcmae_encoder_only_requires_ckpt(): + """FcmaeUNet encoder_only=True without ckpt_path raises ValueError.""" + with pytest.raises(ValueError, match="encoder_only=True requires ckpt_path"): + FcmaeUNet(encoder_only=True) + + +def test_fcmae_finetune_encoder_only_fast_dev_run(tmp_path, _make_synthetic_combined_datamodule, synth_dims): + """FcmaeUNet fine-tuning with encoder_only=True trains for 1 batch.""" + seed_everything(42) + pretrain_model = FcmaeUNet( + model_config={"in_channels": 1, "out_channels": 1, "in_stack_depth": synth_dims["d"]}, + loss_function=MaskedMSELoss(), + fit_mask_ratio=0.5, + ) + ckpt_path = str(tmp_path / "pretrained.ckpt") + torch.save({"state_dict": pretrain_model.state_dict()}, ckpt_path) + + finetune_model = FcmaeUNet( + model_config={ + "in_channels": 1, + "out_channels": 1, + "in_stack_depth": synth_dims["d"], + "pretraining": False, + }, + encoder_only=True, + ckpt_path=ckpt_path, + log_batches_per_epoch=1, + ) + trainer = Trainer( + fast_dev_run=True, + accelerator="cpu", + logger=TensorBoardLogger(save_dir=tmp_path), + enable_checkpointing=False, + enable_progress_bar=False, + ) + trainer.fit(finetune_model, datamodule=_make_synthetic_combined_datamodule()) + assert trainer.state.finished is True + assert trainer.state.status == "finished" + + +# --------------------------------------------------------------------------- +# Real integration tests (CPU, tiny HCS OME-Zarr) +# --------------------------------------------------------------------------- + + +def test_vsunet_real_datamodule_fast_dev_run(tmp_path, tiny_hcs_zarr, synth_dims): + """VSUNet + real HCSDataModule end-to-end training for 1 batch.""" + + seed_everything(42) + module = VSUNet( + architecture="UNeXt2", + model_config={"in_channels": 1, "out_channels": 1, "in_stack_depth": synth_dims["d"]}, + log_batches_per_epoch=1, + ) + datamodule = HCSDataModule( + data_path=str(tiny_hcs_zarr), + source_channel="Phase3D", + target_channel="Fluorescence", + z_window_size=synth_dims["d"], + batch_size=2, + num_workers=0, + yx_patch_size=(synth_dims["h"], synth_dims["w"]), + augmentations=[ + RandSpatialCropd( + keys=["Phase3D", "Fluorescence"], + roi_size=[synth_dims["d"], synth_dims["h"], synth_dims["w"]], + ), + ], + ) + trainer = Trainer( + fast_dev_run=True, + accelerator="cpu", + logger=TensorBoardLogger(save_dir=tmp_path), + enable_checkpointing=False, + enable_progress_bar=False, + ) + trainer.fit(module, datamodule=datamodule) + assert trainer.state.finished is True + assert trainer.state.status == "finished" + + +def test_fnet3d_real_datamodule_fast_dev_run(tmp_path, tiny_hcs_zarr): + """VSUNet + FNet3D + real HCSDataModule end-to-end training for 1 batch.""" + + seed_everything(42) + module = VSUNet( + architecture="FNet3D", + model_config={ + "in_channels": 1, + "out_channels": 1, + "depth": 1, + "mult_chan": 8, + "in_stack_depth": 4, + }, + log_batches_per_epoch=1, + ) + datamodule = HCSDataModule( + data_path=str(tiny_hcs_zarr), + source_channel="Phase3D", + target_channel="Fluorescence", + z_window_size=4, + batch_size=2, + num_workers=0, + yx_patch_size=(32, 32), + augmentations=[ + RandSpatialCropd(keys=["Phase3D", "Fluorescence"], roi_size=[4, 32, 32]), + ], + ) + trainer = Trainer( + fast_dev_run=True, + accelerator="cpu", + logger=TensorBoardLogger(save_dir=tmp_path), + enable_checkpointing=False, + enable_progress_bar=False, + ) + trainer.fit(module, datamodule=datamodule) + assert trainer.state.finished is True + assert trainer.state.status == "finished" + + +def test_fcmae_real_datamodule_fast_dev_run(tmp_path, tiny_hcs_zarr, synth_dims): + """FcmaeUNet + real CachedOmeZarrDataModule + CombinedDataModule for 1 batch.""" + + seed_everything(42) + stack = BatchedStackChannelsd({"source": ["Phase3D"], "target": ["Fluorescence"]}) + dm = CachedOmeZarrDataModule( + data_path=tiny_hcs_zarr, + channels=["Phase3D", "Fluorescence"], + batch_size=2, + num_workers=0, + split_ratio=0.5, + train_cpu_transforms=[], + val_cpu_transforms=[], + train_gpu_transforms=[stack], + val_gpu_transforms=[stack], + pin_memory=False, + ) + combined = CombinedDataModule([dm]) + module = FcmaeUNet( + model_config={ + "in_channels": 1, + "out_channels": 1, + "in_stack_depth": synth_dims["d"], + "pretraining": False, + }, + log_batches_per_epoch=1, + ) + trainer = Trainer( + fast_dev_run=True, + accelerator="cpu", + logger=TensorBoardLogger(save_dir=tmp_path), + enable_checkpointing=False, + enable_progress_bar=False, + ) + trainer.fit(module, datamodule=combined) + assert trainer.state.finished is True + assert trainer.state.status == "finished" + + +# --------------------------------------------------------------------------- +# Config validation tests +# --------------------------------------------------------------------------- + + +def _extract_class_paths(obj): + """Recursively extract all class_path values from a parsed YAML dict.""" + paths = [] + if isinstance(obj, dict): + for key, value in obj.items(): + if key == "class_path" and isinstance(value, str): + paths.append(value) + else: + paths.extend(_extract_class_paths(value)) + elif isinstance(obj, list): + for item in obj: + paths.extend(_extract_class_paths(item)) + return paths + + +def _resolve_class_path(class_path: str): + """Resolve a dotted class_path to the actual class object.""" + module_path, class_name = class_path.rsplit(".", 1) + mod = importlib.import_module(module_path) + return getattr(mod, class_name) + + +def _discover_leaf_configs(): + """Discover all leaf configs (skip recipes/ directory).""" + configs_dir = Path(__file__).parents[1] / "examples" / "configs" + leaf_configs = [] + for yml in sorted(configs_dir.rglob("*.yml")): + if "recipes" not in yml.parts: + leaf_configs.append(yml) + return leaf_configs + + +@pytest.mark.parametrize("config_path", _discover_leaf_configs(), ids=lambda p: str(p.relative_to(p.parents[1]))) +def test_config_class_paths_resolve(config_path): + """All class_path entries in composed example configs resolve to importable classes.""" + + assert config_path.exists(), f"Config file not found: {config_path}" + composed = load_composed_config(config_path) + class_paths = _extract_class_paths(composed) + assert len(class_paths) > 0, f"No class_path entries found in {config_path.name}" + + for cp in class_paths: + cls = _resolve_class_path(cp) + assert cls is not None, f"Failed to resolve class_path: {cp}" + + +def test_compose_passthrough_without_base(tmp_path): + """Config without base: key is returned unchanged.""" + config = {"model": {"class_path": "torch.nn.Identity"}, "data": {"batch_size": 4}} + config_path = tmp_path / "plain.yml" + config_path.write_text(yaml.dump(config)) + result = load_composed_config(config_path) + assert result == config + + +def test_compose_spotlight_overrides_normalizations(): + """Spotlight mode replaces data recipe's default normalizations.""" + configs_dir = Path(__file__).parents[1] / "examples" / "configs" + cfg = load_composed_config(configs_dir / "vscyto3d" / "train_spotlight.yml") + # Spotlight must set Otsu normalization + norm = cfg["data"]["init_args"]["normalizations"][0] + assert norm["init_args"]["subtrahend"] == "otsu_threshold" + # Spotlight must set fg_mask_key + assert cfg["data"]["init_args"]["fg_mask_key"] == "fg_mask" + # Spotlight must set loss + assert cfg["model"]["init_args"]["loss_function"]["class_path"] == "viscy_utils.losses.SpotlightLoss" + + +def test_cli_compose_with_long_flag(tmp_path): + """CLI composes leaf config when --config is used.""" + # Create a minimal base recipe + base_dir = tmp_path / "recipes" + base_dir.mkdir() + base_path = base_dir / "base.yml" + base_path.write_text(yaml.dump({"trainer": {"accelerator": "cpu"}})) + # Create a leaf config referencing the base + leaf_path = tmp_path / "leaf.yml" + leaf_path.write_text(yaml.dump({"base": ["recipes/base.yml"], "seed_everything": 42})) + # Simulate CLI args + original_argv = sys.argv[:] + sys.argv = ["fit", "--config", str(leaf_path)] + try: + _maybe_compose_config() + # sys.argv should now point to a composed temp file + composed_path = sys.argv[2] + assert composed_path != str(leaf_path) + with open(composed_path) as f: + composed = yaml.safe_load(f) + assert composed["trainer"]["accelerator"] == "cpu" + assert composed["seed_everything"] == 42 + assert "base" not in composed + finally: + sys.argv = original_argv + + +def test_cli_compose_with_short_flag(tmp_path): + """CLI composes leaf config when -c is used.""" + base_path = tmp_path / "base.yml" + base_path.write_text(yaml.dump({"model": {"lr": 0.001}})) + leaf_path = tmp_path / "leaf.yml" + leaf_path.write_text(yaml.dump({"base": ["base.yml"], "model": {"name": "test"}})) + original_argv = sys.argv[:] + sys.argv = ["fit", "-c", str(leaf_path)] + try: + _maybe_compose_config() + with open(sys.argv[2]) as f: + composed = yaml.safe_load(f) + assert composed["model"]["lr"] == 0.001 + assert composed["model"]["name"] == "test" + finally: + sys.argv = original_argv + + +def test_cli_passthrough_without_base(tmp_path): + """CLI passes config unchanged when no base: key.""" + config_path = tmp_path / "plain.yml" + config_path.write_text(yaml.dump({"trainer": {"devices": 1}})) + original_argv = sys.argv[:] + sys.argv = ["fit", "--config", str(config_path)] + try: + _maybe_compose_config() + # sys.argv should be unchanged — no temp file created + assert sys.argv[2] == str(config_path) + finally: + sys.argv = original_argv diff --git a/applications/dynacell/.gitignore b/applications/dynacell/.gitignore new file mode 100644 index 000000000..0cc49df5c --- /dev/null +++ b/applications/dynacell/.gitignore @@ -0,0 +1,3 @@ +lightning_logs/ +outputs/ +__pycache__/ diff --git a/applications/dynacell/CLAUDE.md b/applications/dynacell/CLAUDE.md new file mode 100644 index 000000000..39d328998 --- /dev/null +++ b/applications/dynacell/CLAUDE.md @@ -0,0 +1,186 @@ +# dynacell — Claude Code reference + +## Model name conventions + +Code names (used in YAML config keys, prediction zarr filenames, eval pipeline keys, W&B run names) differ from the paper names. When writing/reading anything that crosses the code/paper boundary (figures, tables, Confluence pages, manuscripts), translate: + +| Code name (config / zarr / W&B) | Paper / display name | +| --- | --- | +| `fcmae_vscyto3d_scratch` | **UNeXt2** | +| `fcmae_vscyto3d_pretrained` | **VSCyto3D** (FCMAE-pretrained is the canonical VSCyto3D variant) | +| `unext2` | UNeXt2 (legacy zarr prefix; superseded by `fcmae_vscyto3d_scratch`) | +| `vscyto3d` | VSCyto3D (display key in Dihan's eval pipeline; sources `*_fcmae_vscyto3d_pretrained` predictions) | +| `unetvit3d` | UNetViT3D | +| `fnet3d_paper` | FNet3D | +| `celldiff` | CELL-Diff (variants: `iterative`, `sliding_window`, `denoise`/Mean Predictor) | +| `fcmae_vscyto3d_pretrained_randinit` | **VSCyto3D-RandInit** (untrained ablation; one frozen ckpt per organelle persisted by `save_random_init_vscyto3d_ckpts.py`) | +| `fcmae_vscyto3d_pretrained_cytoland` | **VSCyto3D-Cytoland** (cytoland public ckpt evaluated without dynacell FT) | +| `fcmae_vscyto3d_pretrained_infectionft` | **VSCyto3D-InfectionFT** (cytoland → A549-infection-FT ckpt evaluated without further FT) | +| `vscyto3d_cytolandft` | **VSCyto3D-CytolandFT** (cytoland ckpt + dynacell FT; dual nucleus+membrane, 2-channel) | +| `vscyto3d_infectionft_dynacellft` | **VSCyto3D-InfectionFT-DynacellFT** (cytoland → A549-infection-FT → dynacell FT; dual nucleus+membrane) | + +**Training-set infixes for the no-FT ablations** (Track A/B in `vscyto3d-ablations`): + +| Infix in zarr filename | Meaning | +| --- | --- | +| `_randinit` | random init, no training (Track A) | +| `_cytoland` | cytoland public ckpt, no FT (Track B1) | +| `_infectionft` | VSCyto3D-A549-infection-finetune ckpt, no FT (Track B2) | +| `_cytolandft` | cytoland init + dynacell FT (Track C1) — combines with `_a549trained` for A549-trained variants | +| `_infectionft_dynacellft` | infection-FT init + dynacell FT (Track C2) — combines with `_a549trained` similarly | + +Eval-pipeline directory naming (`/hpc/projects/virtual_staining/training/dynacell/{ipsc,a549}/evaluations/eval__[_]`) uses the **paper key** (`unext2`, `vscyto3d`, `fnet3d`, `unetvit3d`, `celldiff_*`), not the config key. So `eval_unext2_membrane` maps to the `fcmae_vscyto3d_scratch` predictions, `eval_vscyto3d_membrane` maps to `fcmae_vscyto3d_pretrained`. + +## Prediction zarr naming convention + +Set by `trainer.callbacks[…HCSPredictionWriter].init_args.output_store` in each leaf of `applications/dynacell/configs/benchmarks/virtual_staining////predict__*.yml`. The infix between model name and the optional plate condition flags the **training set** of the source model: + +| Trained on | Test set | Filename | +| --- | --- | --- | +| iPSC | iPSC | `_.zarr` | +| iPSC | A549 plate | `__.zarr` | +| A549 | iPSC | `__a549trained.zarr` | +| A549 | A549 plate | `__a549trained_.zarr` | +| Joint (iPSC + A549) | iPSC | `__jointtrained.zarr` | +| Joint (iPSC + A549) | A549 plate | `__jointtrained_.zarr` | + +Where `` is `nucl` / `memb` / `sec61b` / `tomm20`, `` is the **code name** from the table above (e.g. `fcmae_vscyto3d_scratch`, `fnet3d_paper`), and `` is `mock` / `denv` / `zikv`. The (no-infix) iPSC-trained naming is historical baggage from before joint/A549 training existed; don't add a `_ipsctrained` infix retroactively. Output dirs: iPSC test predictions land under `ipsc/predictions/`, A549 plate predictions under `a549/predictions/`, regardless of training set. + +Caveat: Dihan's earlier ER + Mito iPSC-trained zarrs use a legacy `____.zarr` shape (e.g. `sec61b_fcmae_vscyto3d_scratch__sec61b_mock.zarr`, double-underscore + redundant gene prefix). New leaves should follow the table above; do not propagate the legacy form. + +### CellDiff-R2 joint predictions: separate dir + no `_jointtrained` infix + +CellDiff-R2 joint predicts (model `celldiff_r2` trained on iPSC + A549 mantis) deviate from the convention above in **two** ways. Predict configs live at `applications/dynacell/configs/benchmarks/virtual_staining//celldiff/joint_ipsc_confocal_a549_mantis/predict__*.yml` — the directory name says `celldiff/` but the YAMLs hard-code `ckpt_path: .../celldiff_r2/checkpoints/last.ckpt`, so the model variant is selected by the checkpoint, not by the directory. + +The submitter that wires these up is `/hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/VisCy/plot_related/run_celldiff_r2_pred_joint.slurm` (16-task array; submitted 2026-05-18, completed 2026-05-20 as job `33021852`). + +| Trained on | Test set | Output path | +| --- | --- | --- | +| Joint (iPSC + A549) | iPSC | `ipsc/joint_predictions/_celldiff_r2.zarr` | +| Joint (iPSC + A549) | A549 plate | `a549/joint_predictions/_celldiff_r2_.zarr` | + +Note the differences vs. the joint rows in the main table: +- Output dir is `joint_predictions/`, **not** `predictions/`. Sweeps that only walk `predictions/` will miss every joint zarr. +- Filename has **no** `_jointtrained` infix. The model name `celldiff_r2` alone implies joint here. The same naming inconsistency does not apply to FCMAE / fnet3d joint zarrs, which correctly land at `predictions/__jointtrained[_].zarr`. + +Counts: 4 iPSC zarrs (one per organelle, 100 positions each) + 12 A549 zarrs (4 organelles × 3 plates, 14 positions each). Coexisting joint predicts for FCMAE/fnet3d under the same `joint_predictions/` dir follow the `_jointtrained_` convention — only CellDiff-R2's are bare. When inspecting "joint training results for CellDiff-R2," check `joint_predictions/` first. + +## Eval runtime / parallelism + +`dynacell.evaluation.runtime` (added 2026-05) provides three layered thread-cap entry points + optional FOV-level parallelism via spawn-context `ProcessPoolExecutor`. Defaults preserve sequential behavior; opt in via the `runtime:` block in `eval.yaml`. + +### Config block (in `_configs/eval.yaml`) + +```yaml +runtime: + fov_workers: 1 # int | "auto" + threads_per_worker: "auto" # int | "auto" -> cpu_count // fov_workers + executor: "serial" # "serial" | "process" + cuda_empty_cache_every_n_timepoints: 0 # 0 = off + gc_collect_every_n_fovs: 0 # 0 = off +``` + +- `executor=serial` (default): inline FOV loop, identical to pre-runtime-module behavior. +- `executor=process`: spawn-context `ProcessPoolExecutor` over FOVs. Each worker independently lazy-loads `seg_model` + extractors under an fcntl GPU lock at `/tmp/dynacell_gpu_.lock`, so models stay GPU-resident per worker (N × model weights on the GPU) but only one worker runs GPU work at a time. Suitable when GPU memory has headroom for N model copies. +- `fov_workers: "auto"` resolves to 1 under `executor=serial`; under `executor=process` it clamps to `min(cpu_count // threads_per_worker, n_positions)`. +- Literal `fov_workers > 1` with `executor=serial` raises. Literal `fov_workers=1` with `executor=process` auto-demotes to `serial` to avoid the ~5 s spawn cold-start cost for a single-worker pool. +- Two-phase resolve: parent applies BLAS cap at function entry (provisional), then re-resolves after the position list is built with `freeze_threads_per_worker=` so worker initializers see the same value the parent capped to. + +### Env-var entry points + +- `DYNACELL_THREADS_PER_WORKER=N` — set in SLURM scripts BEFORE invoking `dynacell evaluate`. Exports `OMP_NUM_THREADS` / `MKL_NUM_THREADS` / `OPENBLAS_NUM_THREADS` at C-extension load time (in-process `apply_thread_budget` is a runtime safety net but can come after BLAS load). The `__main__:main_cli` first statement reads this var. +- `DYNACELL_FORCE_PER_T_HYGIENE=1` — operator escape hatch: flips both `cuda_empty_cache_every_n_timepoints` and `gc_collect_every_n_fovs` to ≥1 at runtime regardless of YAML defaults. Useful for post-ship mitigation of per-T memory degradation without a code change. + +### Timing instrumentation + +Region timers are always on (overhead is ~120 ms on a 9-h eval, ~4e-6 of wall time). Output goes to `/eval_timing.csv` at end of run with columns `pos_name, t, region, seconds`. Regions tag the FOV-level work (`mask_gt`, `mask_pred`, `cp_gt`, `deep_gt_{dinov3,dynaclr,celldino}`, `features_pred_per_t`, `microssim`, `seg_write`) and the per-T work (`pixel_metrics`, `mask_metrics`, `feature_pairwise`). + +Under `executor=process`, workers return their slice of the timing log inside `FovResult.timings`; the parent aggregator concatenates. + +### Reality check for process mode + +- The parent still loads the seg model once via `load_eval_models(config)` at the start of `evaluate_predictions` in `pipeline.py` (used for checkpoint pre-warm side-effect under `process` mode + the seg model itself under `serial` mode). +- `precompute_deep_features` (the upfront batched feature pre-fill) stays in the parent — single-pass over positions with the `DeepFeatureBatcher` cross-FOV amortization. Parallelizing precompute is a separate plan. +- Workers cannot share open iohub plate handles or torch modules across the pickle boundary; each worker re-opens plates on first FOV. +- `ProcessPoolExecutor.shutdown(wait=False, cancel_futures=True)` cancels queued futures only — in-flight workers continue to completion (~minutes if mid-cellpose). Ctrl-C may need `scancel` to fully release GPU memory. +- Tests for the runtime module + FovResult pickle contract live in `applications/dynacell/tests/test_runtime.py` and `tests/test_evaluation_pipeline_parallel.py`. An end-to-end serial-vs-process parity test on a real iohub fixture is a follow-up (see plan `.claude/plans/eval-parallelism.md` §C5). + +### Grouped multi-condition eval + +When evaluating the same `(model, organelle)` across multiple I/O variants — typically the three A549 treatment plates (`mock`, `denv`, `zikv`), but also any case where the same trained model is scored on multiple datasets — use `dynacell evaluate-grouped`. The driver loads `SuperModel` + `DinoV3` + `DynaCLR` + `CELL-DINO` once, then loops over the conditions calling `evaluate_predictions(merged_cfg, models=...)` so the per-condition cold-start (~30–90 s of weight load + checkpoint warmup) is paid once total instead of N times. + +```yaml +# applications/dynacell/configs/.../eval_grouped_a549_mantis_er.yml +defaults: + - eval_grouped + - _self_ + +target_name: er # MUST be set at the base; conditions cannot override it +compute_feature_metrics: true +feature_extractor: + dinov3: { ... } # shared across all conditions + dynaclr: { checkpoint: /path/to/dynaclr.ckpt, ... } + celldino: { weights_path: /path/to/celldino.ckpt, ... } + +conditions: + - name: a549_mock + io: { pred_path: ..., gt_path: ..., gt_cache_dir: ..., pred_cache_dir: ..., cell_segmentation_path: ... } + save: { save_dir: /path/to/out/a549_mock } + - name: a549_denv + io: { ... } + save: { save_dir: /path/to/out/a549_denv } + - name: a549_zikv + io: { ... } + save: { save_dir: /path/to/out/a549_zikv } +``` + +Invoke (leaves live under +`applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/` +and are discovered via `_EXTERNAL_SEARCHPATHS` in `__main__.py`): +```sh +uv run dynacell evaluate-grouped leaf=grouped//eval_grouped +``` + +`-c` is Hydra's `--cfg` flag (accepts `job`, `hydra`, or `all` for config +display only); it cannot select the leaf. Use the `leaf=` group override +instead — that's how single-condition leaves discover their YAML too. + +Constraints (enforced at runtime by `_check_grouped_field_invariants`): +- Per-condition overlays may freely override `io.*`, `save.*`, `runtime.*`, `limit_positions`, `force_recompute.*`, and carry a `name` label. +- Overlays MUST NOT change `target_name`, `feature_extractor.*`, `compute_feature_metrics`, or `use_gpu` — those gate model loading and must be identical across conditions. Run such variants separately. +- Each condition independently honors `_final_metrics_cache_valid` — if a condition's CSV/NPY already exist AND both `force_recompute.all=false` and `force_recompute.final_metrics=false`, the driver skips it and loads the cached outputs. Either flag set to `true` bypasses the cache. + +Process-mode caveat: under `runtime.executor=process`, the parent's shared `EvalModels` is passed to `evaluate_predictions(..., models=...)` so the parent-side pre-warm is amortized, but each condition still spawns a fresh `ProcessPoolExecutor` whose workers re-load their own model copies. Use `executor=serial` to maximize the amortization benefit. The driver tiers its message based on the cache mode: + +- `executor=process` + `require_complete_cache=true` + `n_conditions > 1` → mild **note**: workers re-init per condition but skip `prepare_segmentation_model` (returns None) and don't instantiate extractors, so the cost is just N pool spawns. +- `executor=process` + `require_complete_cache=false` + `n_conditions > 1` → loud **WARNING**: each condition's worker pool independently loads SuperModel + DINOv3 + DynaCLR + CELL-DINO. Total waste is ~30-90 s × `runtime.fov_workers` × `n_conditions`. Fix: switch to `executor=serial` so the parent's pre-loaded models are reused across conditions; reserve `process` for per-FOV parallelism within a *single* condition. + +For an A40 / single-GPU interactive node where you'd run serial anyway, this is the default win. + +Tests: `applications/dynacell/tests/test_evaluation_grouped.py` validates byte-equal parity against sequential per-condition runs on the same cache-only fixture used by `test_evaluation_pipeline_parallel_cpu.py`, plus rejection cases for empty `conditions` and forbidden model-loading-field overrides. + +## Predict submission modes + +`tools/submit_benchmark_batch.py` (and the `tools/predict_batch.sh` wrapper) covers three submission shapes. They are mutually exclusive — pick by parallelism shape, not by familiarity: + +| Mode | Flag | Squeue rows | Per-GPU concurrency | Cross-sbatch concurrency | +|---|---|---|---|---| +| Serial (default) | (none) | 1 | 1 | — | +| Array | `--array [--max-array-concurrency K]` | 1 array (N tasks) | 1 per task | K | +| Chunked | `--parallel P` (P > 1) | ceil(N/P) | P (bare-background `&`) | full queue | + +Selection guide: + +- **One small set, contiguous time, want minimal queue footprint** → serial. One srun per leaf in series; least queue overhead. +- **Many leaves on different GPUs, want SLURM to throttle concurrent allocations** → `--array --max-array-concurrency K`. Each task gets its own allocation. +- **Few leaves but predict is GPU-light** → `--parallel P`. One GPU runs P leaves in parallel (memory-confirmed 2-up on A40, 2–4 on H200/H100). Faster wall time per chunk than serial without using more total GPU-hours. +- **Leaves span mixed hardware profiles (e.g., some H200, some A40)** → `--array --allow-mixed-directives`. Buckets leaves and submits one array per directive bucket. Only mode that handles this. **NOT compatible with `--parallel`.** + +Hardening to know about when you read the rendered sbatch: + +- `--parallel > 1` scales `cpus_per_task` by the chunk size and pins `OMP_NUM_THREADS`/`MKL_NUM_THREADS`/`OPENBLAS_NUM_THREADS` per backgrounded process so concurrent children don't oversubscribe by all reading `SLURM_CPUS_PER_TASK`. Per-leaf logs land at `{run_root}/slurm/${SLURM_JOB_ID}_.log`; the sbatch's own `%j.out` only sees the driver banner and any chunk-level failure summary. +- PIDs are captured and `wait $pid` is called per child. Bare `wait` (no args) returns only the LAST child's status and would silently mask earlier crashes as `COMPLETED` — the rendered bash propagates non-zero exit codes explicitly. +- Submission loop catches `sbatch` failures per script and reports queued-vs-skipped (matters for `--parallel > 1` and `--array --allow-mixed-directives` since both produce multiple sbatches per invocation). Single-failure no longer hides an opaque traceback. +- Soft warning at `cpus_per_task > 128`. Most cluster nodes top out around there; scaling `--parallel` past that often makes chunks pend forever. + +For local foreground execution (no sbatch), `tools/predict_local.sh --parallel N` has its own backgrounding implementation on the current host's GPU. Confirmed safe 2-up on A40 (`gpu-e-2` interactive). Don't confuse it with `--parallel` on the sbatch helper — different invocation paths. diff --git a/applications/dynacell/README.md b/applications/dynacell/README.md new file mode 100644 index 000000000..764871776 --- /dev/null +++ b/applications/dynacell/README.md @@ -0,0 +1,114 @@ +# Dynacell + +Benchmark virtual staining application for deterministic and generative architectures. + +## Usage + +Set `data_path` in the config file or pass it on the command line: + +```bash +cd applications/dynacell/configs/examples + +# Deterministic models +uv run dynacell fit -c fnet3d/fit.yml --data.init_args.data_path=/path/to/data.zarr +uv run dynacell fit -c unext2/fit.yml --data.init_args.data_path=/path/to/data.zarr +uv run dynacell fit -c unetvit3d/fit.yml --data.init_args.data_path=/path/to/data.zarr + +# Flow-matching CellDiff +uv run dynacell fit -c celldiff/fit.yml --data.init_args.data_path=/path/to/data.zarr +``` + +## Architectures + +### Deterministic (DynacellUNet) + +- **UNetViT3D**: 3D U-Net with Vision Transformer bottleneck +- **UNeXt2**: timm encoder with custom stem, decoder, and head (VSCyto3D backbone) +- **FNet3D**: Recursive encoder-decoder baseline (Ounkomol et al. 2018) + +### Generative (DynacellFlowMatching) + +- **CellDiff**: Flow-matching virtual staining with CELLDiffNet backbone. + Uses ODE sampling for inference. No external loss function needed — + the flow-matching loss is computed internally. + +## Config Structure + +- `configs/recipes/` — reusable fragments (model, trainer, data, modes) +- `configs/examples/` — generic fit/predict pair per model family (stubs with + `#TODO` placeholders) +- `configs/benchmarks/virtual_staining/` — runnable benchmark leaves composed + from shared axes. One file per (organelle, train_set, model) for fit and + one per (organelle, train_set, model, predict_set) for predict. See + `configs/benchmarks/virtual_staining/README.md` for the layout and + composition order. +- `tools/submit_benchmark_job.py` — drives one benchmark leaf end-to-end + (compose → strip launcher metadata → render sbatch → submit). Use + `--print-script` for a safe preview on any leaf, or `--dry-run` to + stage artifacts to `launcher.run_root` without submitting (requires + write permission on that path). + +### Benchmark submit + +```bash +LEAF=applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/train.yml + +# Preview the rendered sbatch to stdout — safe on any leaf, no disk writes: +uv run python applications/dynacell/tools/submit_benchmark_job.py $LEAF --print-script + +# Preview the resolved LightningCLI config (launcher+benchmark stripped): +uv run python applications/dynacell/tools/submit_benchmark_job.py $LEAF --print-resolved-config + +# Stage artifacts to launcher.run_root without submitting (requires write perms): +uv run python applications/dynacell/tools/submit_benchmark_job.py $LEAF --dry-run + +# Submit: +uv run python applications/dynacell/tools/submit_benchmark_job.py $LEAF + +# Dotlist overrides deep-merge after compose (repeatable, no ${...} interpolation): +uv run python applications/dynacell/tools/submit_benchmark_job.py $LEAF \ + --override trainer.max_epochs=50 \ + --override data.init_args.batch_size=2 +``` + +Flag semantics: + +- `--print-script` / `--print-resolved-config` — pure preview: stdout + only, no disk writes, no submission. Safe against run_roots the caller + can't write to. +- `--dry-run` alone — write resolved YAML + rendered sbatch under + `launcher.run_root`, but skip `sbatch`. Requires write permission on + that path. +- `--dry-run` combined with any `--print-*` — preview wins (no writes). +- Bare invocation — write artifacts **and** submit. + +Benchmark leaves carry two reserved top-level YAML keys (`launcher:` and +`benchmark:`) that are stripped automatically before the config reaches +LightningCLI, so `uv run dynacell fit -c ` also works +without the submit tool. + +See `configs/benchmarks/virtual_staining/README.md` for the shared-axis +layout, composition order, and reserved-key contract. + +## Manifest registry (drift policy) + +Benchmark leaves resolve `benchmark.dataset_ref` lookups against a bundled +manifest registry shipped with the dynacell wheel at +`applications/dynacell/src/dynacell/_manifests/`. The resolver auto-discovers +this via the `dynacell.manifest_roots` entry point, so `uv run dynacell +predict -c ` works out of the box without `DYNACELL_MANIFEST_ROOTS`. +Override the env var to point at an alternate registry for testing. + +VisCy is the source of truth for manifest **content**; `dynacell-paper` +remains the source of truth for manifest **authoring**. When a new plate +is preprocessed in `dynacell-paper`, mirror the new manifest (and its +`splits/` siblings) into `applications/dynacell/src/dynacell/_manifests/`. +The `tests/test_manifest_sync.py` suite catches drift when run with +`DYNACELL_PAPER_PATH=/path/to/dynacell-paper` set. + +## Supported subcommands + +- `fit` and `validate`: fully supported for all architectures +- `predict`: supported; uses `HCSPredictionWriter` to write predictions to OME-Zarr. + For UNetViT3D and CellDiff, `yx_patch_size` and `z_window_size` in the data config must match the model's `input_spatial_size`. +- `test`: raises `MisconfigurationException` (no `test_step` override) diff --git a/applications/dynacell/configs/benchmarks/A549_EXPANSION_ROADMAP.md b/applications/dynacell/configs/benchmarks/A549_EXPANSION_ROADMAP.md new file mode 100644 index 000000000..64b886767 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/A549_EXPANSION_ROADMAP.md @@ -0,0 +1,160 @@ +# A549 Expansion Roadmap + +Multi-stage rollout adding A549/mantis-lightsheet alongside the +existing iPSC/confocal benchmark cells, with a manifest-driven +dataset resolver as the foundation. + +## Goal + +- **Two training sets per (organelle, model) cell**: `ipsc_confocal` + and `joint_ipsc_confocal_a549_mantis`. +- **Two held-out evaluation splits per trained model**: + `ipsc_confocal` and `a549_mantis`. Every trained model evaluates on + both, regardless of training source, so cross-dataset transfer is + measurable. + +The post-reorg layout (`14f59f1`) supports this — each +`///` dir is a training experiment with room +for multiple `predict__.yml` and +`eval__.yaml` leaves. The resolver removed the data-path +duplication that would otherwise blow up across ~60 new leaves. + +## Status snapshot (2026-04-26) + +| Stage | Description | Status | +|---|---|---| +| 1 | Resolver core + 1 migration (`er_sec61b`, `ipsc_confocal`) | **Done** — `38d47b3`, `4bb9f09` | +| 2 | Migrate `mito_tomm20`, `nucleus`, `membrane` to `dataset_ref` | **Done** — `11836c8`, `326b2d0`, `6273439` | +| 3 | Hydra-side hook + 4 eval target YAMLs migrated | **Done** — `8924ab2`, `f5a6e56`, `a984384` | +| 4 | (folded into Stage 3) | n/a | +| 5 | Register a549-mantis manifests | **Partial** — done in dynacell-paper (`aeef64c`, 7 per-plate manifests 2024_10_29 → 2025_08_26); VisCy fixture mirror missing. A549 zarr normalization-stats backfill closed 2026-04-24 (dynacell-paper `f4120e0` + 17-zarr backfill). | +| 6 | Single-dataset a549 predict + eval leaves | **Not started** | +| 7 | Joint training leaves (ipsc + a549) | **In flight — blocked**. First leaf + smoke variants shipped (`er/celldiff`: `9654e2b`, `4d399d5`, `234819a`). 4-GPU DDP smoke still hangs after PR #413 (`0b04b24`) — a second deadlock surface remains; see `.claude/handoffs/handoff-batched-concat-ddp-hang-followup-2026-04-26.md`. | + +## Remaining work + +### Stage 5 — VisCy bundled manifest registry + +The canonical a549-mantis manifests live in `dynacell-paper`. VisCy +ships its own copy of the canonical YAMLs as a bundled registry under +`applications/dynacell/src/dynacell/_manifests/`, registered as a +`dynacell.manifest_roots` entry-point provider in +`applications/dynacell/pyproject.toml`. The resolver auto-discovers +this without any `DYNACELL_MANIFEST_ROOTS` env var configuration — +works on a fresh clone for any Stage 6 a549 leaf. Drift between the +mirror and dynacell-paper canonical is guarded by +`tests/test_manifest_sync.py`, which is skipped unless +`DYNACELL_PAPER_PATH` is set (typical CI / local dev environment). + +The a549 zarr normalization-stats gap (every `mantis_v1///.zarr` +missing `normalization` zattrs at plate and position level) closed on +2026-04-24: dynacell-paper `f4120e0` adds `generate_normalization_metadata` +as a post-write step in the assembly pipeline, and the 17 pre-hook +zarrs were backfilled in 5.7 min. Joint leaves consuming these stores +no longer fail or asymmetrically normalize at training time. Treat as +done; no VisCy-side action. + +### Stage 6 — single-dataset a549 predict + eval leaves + +Add `predict__a549_mantis.yml` + `eval__a549_mantis.yaml` to existing +`//ipsc_confocal/` cells so iPSC-trained models can +be evaluated on the a549 test split. + +Sub-scope (from the original roadmap, still unresolved): + +- **(iv) full-but-predictable-only** — the 8 cells that already have + `_predict.yml` overlays (celldiff + unetvit3d × 4 organelles). + Recommended starting point. +- **(iii) full-all-models** — additionally create skeleton + `fcmae_vscyto3d_predict.yml`, `fnet3d_paper_predict.yml`, + `unext2_predict.yml` overlays. Defer unless needed. + +Each leaf is 5–10 lines: + +```yaml +# eval__a549_mantis.yaml +defaults: + - override /target: er_sec61b +benchmark: + dataset_ref: {dataset: a549-mantis-2024_11_07, target: sec61b} +io: + pred_path: /hpc/.../sec61b_celldiff_on_a549.zarr +save: + save_dir: /hpc/.../eval_sec61b_celldiff_on_a549 +``` + +### Stage 7 — joint training leaf expansion + +The joint-loader infrastructure landed in `4bc2e53` (sharded sampler +in `BatchedConcatDataModule`) and `5950576` (split fit overlays). PR +#413 (`0b04b24`) addressed one DDP deadlock surface (the +`use_thread_workers=True` thread-shim under real `init_process_group`) +but the 4-GPU smoke still hangs at the same milestone — a second +deadlock surface remains; see +`.claude/handoffs/handoff-batched-concat-ddp-hang-followup-2026-04-26.md`. +Joint leaf expansion is blocked until this resolves. + +The first joint leaf shipped at +`er/celldiff/joint_ipsc_confocal_a549_mantis/train.yml` (`9654e2b`); +smoke variants followed (single-GPU `4d399d5`, 4-GPU DDP `234819a`). +The single-GPU smoke runs end-to-end against `_test48` debug zarrs; +the 4-GPU DDP smoke is the failing reproducer for the open deadlock. + +Smoke leaves rely on the `_test48` debug-zarr convention documented +in this app's `CLAUDE.md` and mirrored in `dynacell-paper`'s `CLAUDE.md`: +short-wall validation jobs override `data_path` to the colocated +`_test48.zarr` so `mmap_preload` finishes staging in under a +minute instead of 45+ min on the full 500-FOV stores. + +Joint leaves bypass the single-dataset `dataset_ref` resolver and +author the data block inline because hparams live on each child. +Shared HCS init_args factor via a YAML merge anchor. + +Remaining matrix: + +- Other organelles for `celldiff`: `mito`, `nucleus`, `membrane`. +- Other models for `er`: `unetvit3d`, `fcmae_vscyto3d_{scratch,pretrained}`, + `fnet3d_paper`, `unext2`. +- Cross-product: 4 organelles × 6 models = 24 cells (minus the one + already shipped). +- Companion leaves per joint cell: `predict__ipsc_confocal.yml`, + `predict__a549_mantis.yml`, `eval__ipsc_confocal.yaml`, + `eval__a549_mantis.yaml`. + +Decision pending: order of expansion. Reasonable defaults are +"finish the celldiff row first" (organelle sweep on a known-good +model) or "finish the er column first" (model sweep on a known-good +organelle). Pick when the next paper experiment lands. + +## Dependency graph + +``` +Stage 1 ✅ ─> Stage 2 ✅ ─> Stage 3 ✅ + └─> Stage 6 (predict/eval on a549) + ^ + │ +Stage 5 (a549 manifest) — partial ────┘ + canonical: done + VisCy fixture mirror: pending + +Stage 7 (joint training leaves) — independent of resolver path + first leaf + smoke variants: done + 4-GPU DDP smoke: blocked on remaining deadlock (see followup handoff) + expansion (24 cells + companion leaves): pending P0 deadlock fix +``` + +Stages 1–3 and 5 (canonical) blocked Stage 6. The remaining gap on +the VisCy side is the fixture mirror. Stage 7 has its own +infrastructure (`BatchedConcatDataModule` + `ShardedDistributedSampler`) +and is orthogonal to the resolver path. + +## Non-goals + +- FOV-level split resolution (Phase 5D of the dynacell-paper refactor — + about *FOV membership*, not *dataset facts*). +- New CLI flags on `dynacell fit` / `predict` — the resolver is implicit + via the composition hook. +- Reporting-side path resolution — reporting consumes eval outputs, not + source data. +- Changes to `_internal/shared/model/model_overlays/` or + `launcher_profiles/` — those are model/hardware concerns, orthogonal. diff --git a/applications/dynacell/configs/benchmarks/UNEXT2_VS_FCMAE_CLASSES.md b/applications/dynacell/configs/benchmarks/UNEXT2_VS_FCMAE_CLASSES.md new file mode 100644 index 000000000..eecfad8d9 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/UNEXT2_VS_FCMAE_CLASSES.md @@ -0,0 +1,297 @@ +# `UNeXt2` vs `FullyConvolutionalMAE`: one paper architecture, two PyTorch models + +Reconciling the Cytoland paper +([Liu et al., *Nat. Mach. Intell.* 2025, doi:10.1038/s42256-025-01046-2](https://doi.org/10.1038/s42256-025-01046-2)) +with the two independent Python classes that claim to implement its +"UNeXt2" architecture. Needed while planning FCMAE-pretrained finetune +runs on `dynacell-models`, where the naming otherwise misleads. + +## TL;DR + +- The paper (Fig 1b ↔ 1c) describes **one** architecture — "UNeXt2" — + trained twice: first self-supervised via FCMAE masking, then supervised + with the pretrained encoder transferred in. +- The code has **two independent Python classes** claiming to implement + that architecture: `viscy_models.unet.unext2.UNeXt2` (timm-backed) and + `viscy_models.unet.fcmae.FullyConvolutionalMAE` (custom masked + re-implementation). They have **incompatible state_dicts** AND + **structurally different models** — verified below by parameter count. +- The split predates the packaging refactor and predates the `UNeXt2` + rename. The supervised path started as `viscy/unet/networks/Unet21D.py` + in August 2023, and the masked FCMAE path was added as + `viscy/unet/networks/fcmae.py` in April 2024. The key reason for the + second implementation was masked pre-training: `timm.models.convnext` + did not expose the per-block masking hooks needed by FCMAE, so Ziwen + Liu (paper lead author) wrote a standalone masked ConvNeXtV2 encoder. + Some of the larger architectural divergence we see today is current + implementation reality, not necessarily the original motivation. +- In the paper's published workflow, + **`FcmaeUNet(architecture="fcmae")` is used for BOTH the self-supervised + pretrain AND the supervised finetune** (the `pretraining` boolean + toggles masking in `forward`). The timm-backed `UNeXt2` class is + **never** used with FCMAE-pretrained weights. +- The checkpoint matters. The published and current fine-tuning script + `/hpc/mydata/alex.kalinin/vs_test/finetune_3d.py` loads + `/hpc/projects/virtual_staining/models/mehta-lab/VSCyto3D/fcmae.ckpt`, + and that checkpoint **does** load into the current + `FullyConvolutionalMAE`/`FcmaeUNet` path. The other checkpoint explored + during planning, + `/hpc/projects/comp.micro/virtual_staining/models/fcmae-3d/fit_v1/.../last.ckpt`, + does **not** load into the current packaged FCMAE class because its + stem tensor shapes differ. +- **Setting `pretraining=False` on the FCMAE model does not produce the + same PyTorch model as `UNeXt2`.** They differ in stem (LayerNorm or + not), head (trainable Conv3d or pure PixelShuffle), num_blocks (6 vs 8), + total parameter count (32.4M vs 32.1M), and block forward numerics. + They are the same *conceptual* architecture from the paper's pen-and- + paper diagram, not the same PyTorch hypothesis class. +- So the currently-running dynacell `unext2.yml` job (timm-backed + `UNeXt2`) is a valid "from-scratch ConvNeXtV2-tiny baseline" but is + **not** the apples-to-apples random-init control for a FCMAE-pretrained + finetune. For a clean comparison, both runs must be + `FullyConvolutionalMAE(pretraining=False)`. + +## What the paper says (Fig 1b ↔ 1c) + +One architecture, called **UNeXt2** = +*3D projection stem + 2D encoder + 2D decoder + 3D head*. +Trained twice: + +- **1b (FCMAE pretrain):** masked input, reconstruction loss on masked + regions. +- **1c (virtual-staining supervised):** same net, pretrained encoder + weights copied in, decoder trained from scratch, phase→fluor regression. + +Unambiguous — it's the *same* network, two training regimes. + +## What the code actually has + +Two independent classes under `packages/viscy-models/src/viscy_models/unet/`: + +| | `unext2.py::UNeXt2` | `fcmae.py::FullyConvolutionalMAE` | +|---|---|---| +| Encoder impl | `timm.create_model("convnextv2_tiny", features_only=True)` with `stem_0 → nn.Identity()`, separate `UNeXt2Stem` prepended | Custom `MaskedMultiscaleEncoder` built from `MaskedConvNeXtV2Block` + `MaskedAdaptiveProjection` — from-scratch re-implementation of ConvNeXtV2 with masking hooks in every block | +| Stem params | `stem.weight`, `stem_1.weight` | `encoder.stem.conv3d.*`, `encoder.stem.conv2d.*`, `encoder.stem.norm.*` | +| Block params | `encoder_stages.stages_0.blocks.0.conv_dw.weight`, `.norm.weight` | `encoder.stages.0.blocks.0.dwconv.weight`, `.layernorm.weight` | +| Masking hook | none — inference only | `unmasked: BoolTensor \| None` kwarg threaded through every block's `forward` | +| State_dict interchange | — | **Not compatible.** No adapter exists in the codebase. | + +## Why `pretraining=False` does **not** collapse the gap + +The natural intuition is that `FullyConvolutionalMAE(pretraining=False)` +with `mask_ratio=0.0, unmasked=None` degenerates to a plain ConvNeXtV2 +forward pass and should therefore be structurally equivalent to `UNeXt2` +(both wrap ConvNeXtV2-tiny). Probing both classes at matching config +(`backbone=convnextv2_tiny, in_stack_depth=15, stem_kernel_size=[5,4,4], +decoder_conv_blocks=2, in_channels=1, out_channels=1, drop_path_rate=0.1`) +shows that is not the case: + +``` +UNeXt2 total params: 32,426,277 num_blocks: 6 +FullyConvolutionalMAE(p=F) total params: 32,148,528 num_blocks: 8 + delta: -277,749 (-0.86%) + +UNeXt2 children FCMAE(p=F) children + encoder_stages: 27,860,256 encoder: 27,857,856 (stem folded in) + stem: 2,592 decoder: 4,290,672 + decoder: 4,561,616 head: 0 + head: 1,813 (no separate stem module) + +UNeXt2 stem has LayerNorm? False +FCMAE encoder.stem has norm? True +``` + +Concrete structural differences that survive `unmasked=None`: + +1. **Stem normalization.** `MaskedAdaptiveProjection` applies + `nn.LayerNorm(out_channels)` after the 3D→channels projection. + `UNeXt2Stem` is just `Conv3d + reshape` with no normalization. The + first activations handed to stage 0 have different statistics in the + two classes. + +2. **Head is structurally different.** `UNeXt2.head` is + `PixelToVoxelHead` = `UpSample(pixelshuffle) + Conv3d + icnr_init + + PixelShuffle` (1,813 trainable params). + `FullyConvolutionalMAE.head` defaults to `PixelToVoxelShuffleHead` = + a pure `UpSample(pixelshuffle)` (**0 trainable params**) and pushes + all channel math into the decoder's last stage. Not the same output + pathway. `FullyConvolutionalMAE(head_conv=True, ...)` would select + `PixelToVoxelHead` but with different channel wiring than `UNeXt2`. + +3. **`num_blocks` differs (6 vs 8).** Consumed by + `DynacellUNet._make_divisible_pad` / `VSUNet._make_divisible_pad` to + require input spatial dims divisible by `2**num_blocks`. UNeXt2 needs + multiples of 64; FCMAE needs multiples of 256. A YX patch size that + validates for one will not necessarily validate for the other. + +4. **Block forward numerics diverge.** `MaskedConvNeXtV2Block.forward` is + `shortcut → dwconv → masked_patchify(x, unmasked=None) (flatten to + BLC) → LayerNorm on channels-last → GlobalResponseNormMlp(unsqueeze→ + squeeze) → masked_unpatchify (reshape back to BCHW) → drop_path + + shortcut`. Timm's `ConvNeXtV2Block.forward` is `shortcut → conv_dw → + norm (as LayerNorm2d in channels-first, or permute-for-channels-last + if `use_conv_mlp`) → mlp → gamma-scale (LayerScale when + `ls_init_value` is set) → drop_path + shortcut`. The masked block + always pays the patchify↔unpatchify reshape even in the no-mask case; + timm stays channels-first throughout; the LayerScale `gamma` + parameter is present in timm and absent in the masked block. Given + identical parameter tensors the two forward passes would not produce + bit-identical outputs. + +5. **Parameter count delta of 277,749 is structural, not initialization + noise.** Sources: the stem LayerNorm (+2 params), the head/decoder + partition difference (UNeXt2 head 1,813 + decoder 4,561,616 = 4,563,429 + vs FCMAE head 0 + decoder 4,290,672 = 4,290,672, delta 272,757 in the + decoder-plus-head block), and the block-level presence/absence of the + LayerScale `gamma` parameter. + +Conclusion: these are the same *conceptual* architecture from Fig 1 but +not the same PyTorch hypothesis class. Training one from scratch does +not yield an equivalent starting point to training the other from +scratch — different parameter sets, different normalization pathways, +different forward numerics. + +## Archaeology: why two on pre-refactor `main` + +History on `origin/main` (all commits by Ziwen Liu, paper's lead author): + +| SHA | Date | PR | Change | +|---|---|---|---| +| `b4ec13c` | 2023-08-30 | #37 | `viscy/unet/networks/Unet21D.py` introduced — supervised ConvNeXt-backed virtual-staining model with custom 3D stem and 3D head. This is the ancestor of today's `UNeXt2` class. | +| **`0536d29`** | **2024-04-08** | **#67** | **`viscy/unet/networks/fcmae.py` added as a new file**, commit titled "Masked autoencoder pre-training for virtual staining models". Squashed commit text explicitly shows the new masked encoder work: `draft fcmae encoder` → `add stem to the encoder` → `wip: masked stem layernorm` → `wip: patchify masked features for linear` → `use mlp from timm`. This was a new implementation, not a refactor of `Unet21D.py`. | +| `9a0fe64` | 2024-06-11 | #84 | `viscy/unet/networks/Unet21D.py` → `viscy/unet/networks/unext2.py`; class lineage rebranded to `UNeXt2`. `fcmae.py` remained a separate file. | + +**Why a standalone class instead of reusing Unet21D / UNeXt2?** +`timm.models.convnext.ConvNeXtBlock` has no per-block mask argument — +its `forward` computes `dwconv → norm → mlp → residual` with no hooks +for zeroing out masked activations or for sparse-gradient propagation. +FCMAE requires all three: masked dwconv input, +`masked_patchify`/`masked_unpatchify` around the pointwise MLP (so the +MLP only runs on visible patches and GRN statistics aren't polluted by +masked zeros), and drop-path/shortcut that skip the masked regions. The +clean path was to write `MaskedConvNeXtV2Block` from scratch with those +hooks baked in; monkey-patching timm's ConvNeXtBlock would have been +fragile across timm upgrades. + +**Why didn't the two codepaths converge later?** +There is no evidence that state_dict compatibility between the two +classes was ever a goal. The paper and the published scripts use the +FCMAE-side class for FCMAE pre-train and FCMAE-initialized finetune, and +use the supervised/timm side for scratch supervised baselines. So the +code never needed a translation layer to support the published workflow. +That explains the persistent key mismatch: `UNeXt2` inherits timm-style +naming (`stages_N`, `conv_dw`, `norm`), whereas the masked path uses its +own naming (`stages.N`, `dwconv`, `layernorm`). No adapter or +equivalence tests were added because the two state_dicts were not +expected to cross in production. + +## How the paper's own workflow handles the split + +The published fine-tuning path as currently exercised by +`/hpc/mydata/alex.kalinin/vs_test/finetune_3d.py` uses +**`FcmaeUNet` for both regimes**: + +```python +unet = FcmaeUNet(model_config=dict( + in_channels=1, out_channels=2, + encoder_blocks=[3, 3, 9, 3], encoder_drop_path_rate=0.1, + dims=[96, 192, 384, 768], decoder_conv_blocks=2, + stem_kernel_size=(5, 4, 4), in_stack_depth=15, + pretraining=False, # supervised mode, no masking in forward +)) + +if encoder_only: + encoder_weights = { + k.split("model.encoder.")[1]: v + for k, v in pretrained["state_dict"].items() + if "encoder" in k + } + unet.model.encoder.load_state_dict(encoder_weights) # same class, trivial load +``` + +`FcmaeUNet` wraps `FullyConvolutionalMAE`. The `pretraining` flag inside +`model_config` toggles masking in `forward`: +- `pretraining=True` → masked input + reconstruction loss (Fig 1b regime) +- `pretraining=False` → no masking + supervised regression loss (Fig 1c regime) + +Weight transfer between the two regimes is **trivial** because both +sides are `FullyConvolutionalMAE` — identical parameter names throughout. +No key translation, no adapter needed. + +On pre-refactor `main`, the encoder-only transfer lived in *user code*, +inside the fine-tune script, not in the library. The +`encoder_only` / `_load_encoder_weights` helper on +`cytoland.engine.FcmaeUNet` was added later on the modular branch to +formalize that same pattern. + +## Implications for our benchmarks + +The two Python classes serve distinct roles: + +- `FullyConvolutionalMAE` (via `FcmaeUNet`) — the FCMAE pretrain ⇄ + finetune codepath. This is what the paper's Fig 1b/1c workflow uses, + on both sides. +- `UNeXt2` — from-scratch supervised training *without* FCMAE + pretraining. Used for baselines / ablations that skip FCMAE entirely. + +**"UNeXt2" in the paper refers to the conceptual architecture, not the +Python class of the same name.** The Python class `UNeXt2` has never +been used with FCMAE-pretrained weights in any checked-in script or +benchmark — not on main, not on this branch, not in the published +artifacts. + +Dynacell's currently-running from-scratch job +(`benchmarks/virtual_staining/er/unext2/ipsc_confocal/train.yml`, SLURM +31122607) uses `DynacellUNet(architecture="UNeXt2")` — the timm-backed +class. That's a valid "from-scratch baseline with a timm ConvNeXtV2-tiny +encoder," but it trains a structurally different model (stem without +LayerNorm, Conv3d-backed head, 277k extra params, num_blocks=6) from +the FCMAE codepath. It is **not** the apples-to-apples random-init +control for an FCMAE-pretrained-init finetune: it's a different +hypothesis class that happens to share the paper's conceptual name. A +paper-faithful comparison requires both runs to use +`FullyConvolutionalMAE(pretraining=False)`. + +### Recommended benchmark layout for dynacell + +Do **not** treat the current `unext2.yml` leaf as the random-init control +for an FCMAE-pretrained run. Keep it, but label it honestly as the +timm-backed supervised UNeXt2 baseline. + +For the FCMAE question, add a separate pair of leaves that use the same +class on both sides: + +- `fcmae_vscyto3d_scratch` +- `fcmae_vscyto3d_pretrained` + +Those two leaves should be identical except for encoder initialization: + +- same `FullyConvolutionalMAE(pretraining=False)` / `FcmaeUNet`-style model +- same decoder config +- same LR / batch / crops / epochs +- only `encoder_only + ckpt_path` differs + +Use the compatible checkpoint from the latest fine-tuning script: + +- `/hpc/projects/virtual_staining/models/mehta-lab/VSCyto3D/fcmae.ckpt` + +Do **not** use the incompatible checkpoint: + +- `/hpc/projects/comp.micro/virtual_staining/models/fcmae-3d/fit_v1/lightning_logs/pretrain-neuro-aic-hek-200ep_maxsize_fry1_resume4/checkpoints/last.ckpt` + +### Alternative paths + +1. **Use `FullyConvolutionalMAE(pretraining=False)` for both the + random-init and FCMAE-pretrained-init leaves** (retire the + timm-backed `unext2.yml` leaf, or re-frame it as a separate + baseline). Paper-faithful. The only axis of comparison between the + two new leaves is the encoder init. +2. **Keep the existing timm-backed `unext2.yml` as an informal baseline**, + add a `FullyConvolutionalMAE(pretraining=False)` FCMAE-finetune leaf + on the side. Comparison has an architecture asterisk — same paper + concept, structurally different PyTorch models (param count, stem, + head, num_blocks). +3. **Unify the two classes in `viscy-models`** (replace `UNeXt2`'s timm + encoder with a shared backbone that supports optional masking, or + make the timm encoder's state_dict transformable to FCMAE naming via + a one-shot adapter). Clean but a separate `viscy-models` PR. diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/README.md b/applications/dynacell/configs/benchmarks/virtual_staining/README.md new file mode 100644 index 000000000..67b03d569 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/README.md @@ -0,0 +1,298 @@ +# Virtual Staining Benchmark Configs + +Composable leaf-per-experiment configs for dynacell virtual-staining +benchmarks. Train, predict, and eval leaves for one training run live +side-by-side under `///` — one subdir per training +experiment so a trained model, its predictions, and its evaluations +form one coherent unit. + +## Reserved top-level keys + +Two top-level YAML keys are **reserved for dynacell** and are stripped +from the composed config before it reaches LightningCLI: + +- `launcher:` — sbatch directives, runtime env, job metadata. Consumed by + `applications/dynacell/tools/submit_benchmark_job.py`. +- `benchmark:` — informational experiment metadata (target, train_set, + experiment_id). Readable by downstream reporting; not consumed by + Lightning. + +The strip happens inside `viscy_utils.cli._maybe_compose_config`. This +means `uv run dynacell fit -c ` works for any benchmark leaf +without the dedicated submit tool. + +The reserved top-level YAML key `benchmark:` (above) is unrelated to the +Hydra `leaf=` selector used for eval. The Hydra selector was +previously named `benchmark=`; both names referring to "benchmark" were +a source of confusion and the eval selector has been renamed. + +## Layout + +``` +virtual_staining/ + README.md + /// + train.yml # LightningCLI fit leaf + predict__.yml # LightningCLI predict leaf + eval__.yaml # Hydra eval leaf (canonical location) + _internal/ # hidden support tree — not for browsing + shared/ + model/ + train_sets/.yml # train-set metadata + benchmark.dataset_ref.dataset + HCS defaults + predict_sets/.yml # predict-set metadata + benchmark.dataset_ref.dataset + targets/.yml # benchmark.dataset_ref.target + target-specific norms / CPU augs + data_overlays/ + _fit.yml # per-model HCS data hparams (batch_size, z_window, gpu_augs) + model_overlays/ + _fit.yml # model + fit trainer (no data: block — joint leaves compose + # only this half and author their own data: block) + _predict.yml # model + predict trainer + predict data hparams + launcher_profiles/ + mode_.yml # launcher.mode + hardware_.yml # sbatch directives + trainer.devices + runtime_shared.yml # launcher.runtime + launcher.env + eval/ + target/.yaml # target_name + benchmark.dataset_ref.target + feature_extractor/dynaclr/ # DynaCLR checkpoint + encoder kwargs + leaf/ # symlink tree aliasing canonical eval leaves + ///eval__.yaml -> ../../../../..////eval__.yaml +``` + +Leaves are grouped by **train set** inside each `//` cell so +that a training experiment (train + the predict/eval variants fed by its +checkpoint) lives in one directory. Adding a new training run — e.g. the +planned `joint_ipsc_confocal_a549_mantis` mix — means creating one new +subdir; deleting one is `rm -r`. Each train-set dir holds one `train.yml` +plus one `predict__.yml` and `eval__.yaml` per +held-out split the model is evaluated on. + +The top level of `virtual_staining/` shows only biology (`er/`, `membrane/`, +`mito/`, `nucleus/`) plus `_internal/` — a hidden support tree whose +leading underscore signals "implementation detail; don't browse here for +science." All Hydra group files, all shared composition building blocks, +and the `leaf/` symlink adapter live under `_internal/`. + +Train/predict leaves use LightningCLI (`.yml`). Eval leaves use Hydra and +keep `.yaml` because Hydra's group resolution only discovers `.yaml` files. +The `_internal/leaf/` symlink tree aliases each canonical eval leaf so +Hydra's `leaf=` selector can discover them at +`/leaf/.yaml`. + +Eval runtime uses two search paths injected by `dynacell.__main__`: +`virtual_staining/_internal/` (for the `leaf/` tree) and +`virtual_staining/_internal/shared/eval/` (for the `target/` and +`feature_extractor/dynaclr/` groups). Schema-only eval configs ship +inside the dynacell package; wheel installs without the repo don't see +the HPC-bound groups and external users provide their own via +`--config-dir`. See `applications/dynacell/src/dynacell/evaluation/README.md`. + +## Composition order + +Last wins via deep-merge. Lists replace wholesale — layers that own list +fields (`callbacks`, `augmentations`, etc.) own the **full** list. + +**Single-store train leaf** (at `///train.yml`): + +```yaml +base: + - ../../../_internal/shared/model/train_sets/.yml + - ../../../_internal/shared/model/targets/.yml + - ../../../_internal/shared/model/data_overlays/_fit.yml + - ../../../_internal/shared/model/model_overlays/_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml +``` + +**Joint train leaf** (e.g. `er/celldiff/joint_ipsc_confocal_a549_mantis/train.yml`): + +Joint leaves (multi-dataset fit) bypass the single-dataset `dataset_ref` +resolver and use `viscy_data.BatchedConcatDataModule` with explicit +child `viscy_data.HCSDataModule` blocks per zarr / experiment. They +compose only `model_overlays/_fit.yml` + launcher profiles — +the `data:` block is authored inline because joint hparams live on the +children. See `MULTI_DATASET_TRAINING_RECOMMENDATION.md` for rationale. + +**Joint smoke sibling** (e.g. `er/celldiff/joint_ipsc_confocal_a549_mantis/train_smoke.yml`): + +A `train_smoke.yml` lives next to the production `train.yml` for any +joint leaf that needs a smoke runner. The smoke sibling pre-swaps each +child's `data_path` to its colocated `_test48.zarr` debug variant +(or keeps the path when the train split is already small) and uses a +single-GPU launcher profile (`hardware_h200_single`) instead of +multi-GPU DDP. The reason it's a sibling leaf rather than `--override` +flags at submit time: `submit_benchmark_job.py`'s dotlist override +parser does not index into list elements +(`data.init_args.data_modules.0.init_args.data_path=...` is parsed as +a dict-with-string-key, not a list index), so swapping a single +child's zarr at submit time is not supported. Pair the smoke leaf +with `--override trainer.fast_dev_run=true` (or `trainer.max_steps=N`) +to bound the run. + +**Predict leaf** (at `///predict__.yml`): + +```yaml +base: + - ../../../_internal/shared/model/predict_sets/.yml + - ../../../_internal/shared/model/targets/.yml + - ../../../_internal/shared/model/model_overlays/_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml +``` + +Predict leaves use `hardware_predict_any_gpu.yml` (single GPU, no +vendor constraint) — measured 6.6 GB / 100% SM on celldiff at FP32 on +H200, so a40 / a6000 / l40s / l4 all run the workload and drain the +queue faster than pinning Hopper. Train leaves stay on +`hardware_h200_single.yml` (or `hardware_4gpu.yml` for DDP) because +their memory + bandwidth profiles differ. + +**Eval leaf** (at `///eval__.yaml`): + +```yaml +# @package _global_ +defaults: + - override /target: + - override /predict_set: + - override /feature_extractor/dinov3: lvd1689m + - override /feature_extractor/dynaclr: default + +io: + pred_path: /hpc/.../predictions.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/.../eval_results +``` + +## Running + +The default `trainer.logger` in `configs/recipes/trainer/fit.yml` is +`lightning.pytorch.loggers.WandbLogger`. Install dynacell with the +`wandb` extra to satisfy this default (`uv add 'dynacell[wandb]'` / +`pip install 'dynacell[wandb]'`). Without `wandb` installed, +LightningCLI / jsonargparse rejects the leaf at schema-validation +time. To opt out of W&B without installing it, override the logger +in the leaf or via `--override trainer.logger.class_path=...` to a +different Lightning logger (e.g. `lightning.pytorch.loggers.CSVLogger`). + +Direct LightningCLI (no sbatch): + +- `uv run dynacell fit -c configs/benchmarks/virtual_staining////train.yml` +- `uv run dynacell predict -c configs/benchmarks/virtual_staining////predict__.yml` + +Hydra eval: + +- `uv run dynacell evaluate leaf=///eval__` + +Via sbatch with `submit_benchmark_job.py`: + +```bash +LEAF=configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/train.yml + +# Pure preview (no disk writes, safe on any run_root): +uv run python applications/dynacell/tools/submit_benchmark_job.py $LEAF --print-script +uv run python applications/dynacell/tools/submit_benchmark_job.py $LEAF --print-resolved-config + +# Stage artifacts to launcher.run_root but skip submission (requires write perms): +uv run python applications/dynacell/tools/submit_benchmark_job.py $LEAF --dry-run + +# Submit: +uv run python applications/dynacell/tools/submit_benchmark_job.py $LEAF + +# Dotlist overrides deep-merge after compose (repeatable; ${...} interpolation is rejected): +uv run python applications/dynacell/tools/submit_benchmark_job.py $LEAF \ + --override trainer.max_epochs=50 --override data.init_args.batch_size=2 +``` + +`--dry-run` combined with `--print-*` drops the disk writes (preview +wins). `trainer.devices` and `launcher.sbatch.gpus` must match or +submission fails fast. + +### Multi-leaf submission with `submit_benchmark_batch.py` + +When you want to run several predict leaves under one launcher call +(e.g., 3 A549 plates per organelle, or all 24 predicts in a Track-A +ablation sweep), pick a mode by parallelism shape: + +| Mode | Flag | Squeue rows | Per-GPU concurrency | When to use | +|---|---|---|---|---| +| Serial (default) | (none) | 1 sbatch | 1 | N leaves back-to-back in one allocation; cheapest queue footprint | +| Array | `--array` (+ `--max-array-concurrency K`) | 1 array (N tasks) | 1 per task | Each leaf gets its own GPU; cap concurrent tasks with K | +| Chunked | `--parallel P` | ceil(N/P) sbatches | P (backgrounded) | One GPU runs P leaves concurrently (predict is GPU-light; 2–4 fit on A40 / H200) | + +`--parallel` is mutually exclusive with `--array` (rejected at parse +time). With `--parallel > 1`, `cpus_per_task` scales by the chunk +size, `OMP_NUM_THREADS`/`MKL_NUM_THREADS`/`OPENBLAS_NUM_THREADS` are +pinned per backgrounded process, and per-leaf logs land at +`{run_root}/slurm/${SLURM_JOB_ID}_.log`. Soft warning at +`cpus_per_task > 128` if the scaled request would exceed typical node +geometry. + +```bash +LEAVES=configs/benchmarks/virtual_staining/er/fnet3d_paper/ipsc_confocal +SET=( + $LEAVES/predict__a549_mantis_mock.yml + $LEAVES/predict__a549_mantis_denv.yml + $LEAVES/predict__a549_mantis_zikv.yml +) + +# Serial (single sbatch, 3 srun in series — original behavior, default): +uv run python applications/dynacell/tools/submit_benchmark_batch.py "${SET[@]}" --dry-run + +# Array (one sbatch array, K concurrent tasks each on its own GPU): +uv run python applications/dynacell/tools/submit_benchmark_batch.py "${SET[@]}" \ + --array --max-array-concurrency 2 --dry-run + +# Chunked (2 sbatches, each runs 2 predicts concurrent on one GPU): +uv run python applications/dynacell/tools/submit_benchmark_batch.py "${SET[@]}" \ + --parallel 2 --dry-run + +# Mixed hardware profiles across leaves (array mode only): +uv run python applications/dynacell/tools/submit_benchmark_batch.py "${SET[@]}" \ + --array --allow-mixed-directives --dry-run +``` + +For the common `predict__.yml` per-plate sweep over one +`///` cell, the thin wrapper +`tools/predict_batch.sh` discovers the leaves automatically and +forwards every flag to `submit_benchmark_batch.py`: + +```bash +# 3 A549 plates for er + fnet3d_paper + iPSC-trained, chunked 2-up: +bash applications/dynacell/tools/predict_batch.sh er fnet3d_paper ipsc a549 --parallel 2 +``` + +For local execution (no sbatch — runs on the current host's GPU), +`tools/predict_local.sh` has its own `--parallel N` that backgrounds +N concurrent predicts on the foreground node (memory-confirmed 2-up +on an A40). + +Failure handling for batched submissions: each rendered sbatch is +submitted independently; if one fails, the helper reports which were +queued vs skipped and exits 1. `scancel` the queued chunks listed in +the stderr summary if you need to abort the whole batch. + +## Dataset reference contract + +Single-dataset train/predict leaves split `benchmark.dataset_ref` across +shared fragments: + +- `train_sets/.yml` and `predict_sets/.yml` contribute + `benchmark.dataset_ref.dataset` plus HCS defaults for that split. +- `targets/.yml` contributes `benchmark.dataset_ref.target` + plus target-specific normalizations and augmentations. +- The compose-time resolver fills `data.init_args.data_path`, + `source_channel`, and `target_channel` from the manifest, so those + fields are no longer duplicated across train/predict leaves. + +Eval leaves follow the same split on the Hydra side: + +- `target/.yaml` contributes `benchmark.dataset_ref.target`. +- `predict_set/.yaml` contributes `benchmark.dataset_ref.dataset`. +- `dynacell.evaluation._ref_hook.apply_dataset_ref()` fills + `io.gt_path`, `io.cell_segmentation_path`, `io.gt_channel_name`, + `io.pred_channel_name`, `io.gt_cache_dir`, and + `pixel_metrics.spacing` from the manifest. diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/fcmae_vscyto3d_pretrained/_no_train_cytoland/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/fcmae_vscyto3d_pretrained/_no_train_cytoland/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..38b7b0a7e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/fcmae_vscyto3d_pretrained/_no_train_cytoland/predict__a549_mantis_denv.yml @@ -0,0 +1,42 @@ +# VSCyto3D-Cytoland predict: dual nucleus+membrane (no FT), A549 denv plate. +# 2-channel output zarr; per-channel eval reads `Nuclei_prediction` / `Membrane_prediction`. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_dual_denv.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + trained_on: cytoland + predict_set: a549_mantis_dual_denv + model_name: fcmae_vscyto3d_pretrained_cytoland + experiment_id: dual_nucl_memb__cytoland__fcmae_vscyto3d_pretrained__a549_mantis_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/datasets/public/VS_models/VSCyto3D/epoch=83-step=14532-loss=0.492.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_fcmae_vscyto3d_pretrained_cytoland_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Cytoland_PRED_DUAL_ON_A549_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/fcmae_vscyto3d_pretrained/_no_train_cytoland/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/fcmae_vscyto3d_pretrained/_no_train_cytoland/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..6716b9f2e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/fcmae_vscyto3d_pretrained/_no_train_cytoland/predict__a549_mantis_mock.yml @@ -0,0 +1,42 @@ +# VSCyto3D-Cytoland predict: dual nucleus+membrane (no FT), A549 mock plate. +# 2-channel output zarr; per-channel eval reads `Nuclei_prediction` / `Membrane_prediction`. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_dual_mock.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + trained_on: cytoland + predict_set: a549_mantis_dual_mock + model_name: fcmae_vscyto3d_pretrained_cytoland + experiment_id: dual_nucl_memb__cytoland__fcmae_vscyto3d_pretrained__a549_mantis_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/datasets/public/VS_models/VSCyto3D/epoch=83-step=14532-loss=0.492.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_fcmae_vscyto3d_pretrained_cytoland_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Cytoland_PRED_DUAL_ON_A549_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/fcmae_vscyto3d_pretrained/_no_train_cytoland/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/fcmae_vscyto3d_pretrained/_no_train_cytoland/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..f02835959 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/fcmae_vscyto3d_pretrained/_no_train_cytoland/predict__a549_mantis_zikv.yml @@ -0,0 +1,42 @@ +# VSCyto3D-Cytoland predict: dual nucleus+membrane (no FT), A549 zikv plate. +# 2-channel output zarr; per-channel eval reads `Nuclei_prediction` / `Membrane_prediction`. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_dual_zikv.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + trained_on: cytoland + predict_set: a549_mantis_dual_zikv + model_name: fcmae_vscyto3d_pretrained_cytoland + experiment_id: dual_nucl_memb__cytoland__fcmae_vscyto3d_pretrained__a549_mantis_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/datasets/public/VS_models/VSCyto3D/epoch=83-step=14532-loss=0.492.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_fcmae_vscyto3d_pretrained_cytoland_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Cytoland_PRED_DUAL_ON_A549_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/fcmae_vscyto3d_pretrained/_no_train_cytoland/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/fcmae_vscyto3d_pretrained/_no_train_cytoland/predict__ipsc_confocal.yml new file mode 100644 index 000000000..c121b8a0e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/fcmae_vscyto3d_pretrained/_no_train_cytoland/predict__ipsc_confocal.yml @@ -0,0 +1,43 @@ +# VSCyto3D-Cytoland predict: dual nucleus+membrane (no FT), iPSC test set. +# 2-channel output zarr with `Nuclei_prediction` and `Membrane_prediction` channels. +# Per-channel eval reads `Nuclei_prediction` / `Membrane_prediction` from this same zarr. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal_dual.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + trained_on: cytoland + predict_set: ipsc_confocal_dual + model_name: fcmae_vscyto3d_pretrained_cytoland + experiment_id: dual_nucl_memb__cytoland__fcmae_vscyto3d_pretrained__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/datasets/public/VS_models/VSCyto3D/epoch=83-step=14532-loss=0.492.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/dual_nucl_memb_fcmae_vscyto3d_pretrained_cytoland.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Cytoland_PRED_DUAL + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/fcmae_vscyto3d_pretrained/_no_train_cytoland/predict__ipsc_confocal_smoke.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/fcmae_vscyto3d_pretrained/_no_train_cytoland/predict__ipsc_confocal_smoke.yml new file mode 100644 index 000000000..00f88ca36 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/fcmae_vscyto3d_pretrained/_no_train_cytoland/predict__ipsc_confocal_smoke.yml @@ -0,0 +1,44 @@ +# Gate 10 smoke leaf: same as predict__ipsc_confocal.yml but writes to a +# smoke-only output_store and runs ONE batch via trainer.fast_dev_run. +# NOT FOR PRODUCTION — delete after Phase 1 gate verification. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal_dual.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + trained_on: cytoland + predict_set: ipsc_confocal_dual + model_name: fcmae_vscyto3d_pretrained_cytoland + experiment_id: dual_nucl_memb__cytoland__fcmae_vscyto3d_pretrained__ipsc_confocal_smoke + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/datasets/public/VS_models/VSCyto3D/epoch=83-step=14532-loss=0.492.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + fast_dev_run: true + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/_smoke_gate10_dual_nucl_memb_cytoland.zarr + +launcher: + job_name: SMOKE_GATE10_Cytoland_PRED_DUAL + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/fcmae_vscyto3d_pretrained/_no_train_infectionft/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/fcmae_vscyto3d_pretrained/_no_train_infectionft/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..364d4c56f --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/fcmae_vscyto3d_pretrained/_no_train_infectionft/predict__a549_mantis_denv.yml @@ -0,0 +1,42 @@ +# VSCyto3D-InfectionFT predict: dual nucleus+membrane (no FT), A549 denv plate. +# 2-channel output zarr; per-channel eval reads `Nuclei_prediction` / `Membrane_prediction`. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_dual_denv.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + trained_on: infectionft + predict_set: a549_mantis_dual_denv + model_name: fcmae_vscyto3d_pretrained_infectionft + experiment_id: dual_nucl_memb__infectionft__fcmae_vscyto3d_pretrained__a549_mantis_denv + +model: + init_args: + ckpt_path: /hpc/projects/organelle_phenotyping/models/VSCyto3D-A549-infection-finetune/4gpu_bf16_bs16_to_ep7/checkpoints/epoch=7-step=832.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_fcmae_vscyto3d_pretrained_infectionft_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_InfectionFT_PRED_DUAL_ON_A549_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/fcmae_vscyto3d_pretrained/_no_train_infectionft/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/fcmae_vscyto3d_pretrained/_no_train_infectionft/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..fa8203136 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/fcmae_vscyto3d_pretrained/_no_train_infectionft/predict__a549_mantis_mock.yml @@ -0,0 +1,42 @@ +# VSCyto3D-InfectionFT predict: dual nucleus+membrane (no FT), A549 mock plate. +# 2-channel output zarr; per-channel eval reads `Nuclei_prediction` / `Membrane_prediction`. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_dual_mock.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + trained_on: infectionft + predict_set: a549_mantis_dual_mock + model_name: fcmae_vscyto3d_pretrained_infectionft + experiment_id: dual_nucl_memb__infectionft__fcmae_vscyto3d_pretrained__a549_mantis_mock + +model: + init_args: + ckpt_path: /hpc/projects/organelle_phenotyping/models/VSCyto3D-A549-infection-finetune/4gpu_bf16_bs16_to_ep7/checkpoints/epoch=7-step=832.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_fcmae_vscyto3d_pretrained_infectionft_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_InfectionFT_PRED_DUAL_ON_A549_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/fcmae_vscyto3d_pretrained/_no_train_infectionft/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/fcmae_vscyto3d_pretrained/_no_train_infectionft/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..1f6a3eec7 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/fcmae_vscyto3d_pretrained/_no_train_infectionft/predict__a549_mantis_zikv.yml @@ -0,0 +1,42 @@ +# VSCyto3D-InfectionFT predict: dual nucleus+membrane (no FT), A549 zikv plate. +# 2-channel output zarr; per-channel eval reads `Nuclei_prediction` / `Membrane_prediction`. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_dual_zikv.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + trained_on: infectionft + predict_set: a549_mantis_dual_zikv + model_name: fcmae_vscyto3d_pretrained_infectionft + experiment_id: dual_nucl_memb__infectionft__fcmae_vscyto3d_pretrained__a549_mantis_zikv + +model: + init_args: + ckpt_path: /hpc/projects/organelle_phenotyping/models/VSCyto3D-A549-infection-finetune/4gpu_bf16_bs16_to_ep7/checkpoints/epoch=7-step=832.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_fcmae_vscyto3d_pretrained_infectionft_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_InfectionFT_PRED_DUAL_ON_A549_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/fcmae_vscyto3d_pretrained/_no_train_infectionft/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/fcmae_vscyto3d_pretrained/_no_train_infectionft/predict__ipsc_confocal.yml new file mode 100644 index 000000000..d2fd37cbc --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/fcmae_vscyto3d_pretrained/_no_train_infectionft/predict__ipsc_confocal.yml @@ -0,0 +1,43 @@ +# VSCyto3D-InfectionFT predict: dual nucleus+membrane (no FT), iPSC test set. +# 2-channel output zarr with `Nuclei_prediction` and `Membrane_prediction` channels. +# Per-channel eval reads `Nuclei_prediction` / `Membrane_prediction` from this same zarr. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal_dual.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + trained_on: infectionft + predict_set: ipsc_confocal_dual + model_name: fcmae_vscyto3d_pretrained_infectionft + experiment_id: dual_nucl_memb__infectionft__fcmae_vscyto3d_pretrained__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/organelle_phenotyping/models/VSCyto3D-A549-infection-finetune/4gpu_bf16_bs16_to_ep7/checkpoints/epoch=7-step=832.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/dual_nucl_memb_fcmae_vscyto3d_pretrained_infectionft.zarr + +launcher: + job_name: FCMAE_VSCyto3D_InfectionFT_PRED_DUAL + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..66ab4d93f --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,43 @@ +# vscyto3d_cytolandft (Cytoland-init + dynacell FT on A549) predict: +# dual nucleus+membrane on A549 denv plate. ckpt_path is filled in +# post-training from the best-val checkpoint of the matching train.yml run. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_dual_denv.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + trained_on: a549_mantis + predict_set: a549_mantis_dual_denv + model_name: vscyto3d_cytolandft + experiment_id: dual_nucl_memb__a549_mantis__vscyto3d_cytolandft__a549_mantis_denv + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_cytolandft_a549trained_denv.zarr + +launcher: + job_name: VSCyto3D_Cytoland_A549_PRED_DUAL_ON_A549_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..cb75ceb69 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,43 @@ +# vscyto3d_cytolandft (Cytoland-init + dynacell FT on A549) predict: +# dual nucleus+membrane on A549 mock plate. ckpt_path is filled in +# post-training from the best-val checkpoint of the matching train.yml run. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_dual_mock.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + trained_on: a549_mantis + predict_set: a549_mantis_dual_mock + model_name: vscyto3d_cytolandft + experiment_id: dual_nucl_memb__a549_mantis__vscyto3d_cytolandft__a549_mantis_mock + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_cytolandft_a549trained_mock.zarr + +launcher: + job_name: VSCyto3D_Cytoland_A549_PRED_DUAL_ON_A549_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..8cd4a03df --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,43 @@ +# vscyto3d_cytolandft (Cytoland-init + dynacell FT on A549) predict: +# dual nucleus+membrane on A549 zikv plate. ckpt_path is filled in +# post-training from the best-val checkpoint of the matching train.yml run. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_dual_zikv.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + trained_on: a549_mantis + predict_set: a549_mantis_dual_zikv + model_name: vscyto3d_cytolandft + experiment_id: dual_nucl_memb__a549_mantis__vscyto3d_cytolandft__a549_mantis_zikv + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_cytolandft_a549trained_zikv.zarr + +launcher: + job_name: VSCyto3D_Cytoland_A549_PRED_DUAL_ON_A549_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..e9bdb9ddb --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,43 @@ +# vscyto3d_cytolandft (Cytoland-init + dynacell FT on A549) predict: +# dual nucleus+membrane on iPSC test set. ckpt_path is filled in post-training +# from the best-val checkpoint of the matching train.yml run. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal_dual.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + trained_on: a549_mantis + predict_set: ipsc_confocal_dual + model_name: vscyto3d_cytolandft + experiment_id: dual_nucl_memb__a549_mantis__vscyto3d_cytolandft__ipsc_confocal + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/dual_nucl_memb_vscyto3d_cytolandft_a549trained.zarr + +launcher: + job_name: VSCyto3D_Cytoland_A549_PRED_DUAL + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/a549_mantis/train.yml new file mode 100644 index 000000000..e1b136fcb --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/a549_mantis/train.yml @@ -0,0 +1,58 @@ +# VSCyto3D dual nucleus+membrane FT: cytoland-init -> dynacell FT on A549. +# Full-weight load (no encoder_only) — source ckpt is 2-channel and matches +# the fcmae_vscyto3d_2chan_fit arch exactly. Sampling bias is Nuclei-foreground +# weighted (w_key=Nuclei in targets/dual_nucl_memb.yml + bumped num_samples +# here); revisit if validation loss for Membrane channel diverges from the +# fcmae_vscyto3d_pretrained membrane baseline. +base: + - ../../../_internal/shared/model/train_sets/a549_mantis_dual.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/data_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + train_set: a549_mantis + model_name: vscyto3d_cytolandft + experiment_id: dual_nucl_memb__a549_mantis__vscyto3d_cytolandft + +# Bump num_samples from the dual_nucl_memb default (2) to 4 to match the +# single-organelle FCMAE recipe at the same crop size. +data: + init_args: + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Nuclei, Membrane] + w_key: Nuclei + spatial_size: [15, 600, 600] + num_samples: 4 + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/datasets/public/VS_models/VSCyto3D/epoch=83-step=14532-loss=0.492.ckpt + +trainer: + logger: + init_args: + name: VSCyto3D_Cytoland_A549_Dual + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549/dual_nucl_memb/vscyto3d_cytolandft + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549/dual_nucl_memb/vscyto3d_cytolandft/checkpoints + +launcher: + job_name: VSCyto3D_Cytoland_A549_Dual + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549/dual_nucl_memb/vscyto3d_cytolandft diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/ipsc_confocal/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/ipsc_confocal/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..db204e526 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/ipsc_confocal/predict__a549_mantis_denv.yml @@ -0,0 +1,43 @@ +# vscyto3d_cytolandft (Cytoland-init + dynacell FT on iPSC) predict: +# dual nucleus+membrane on A549 denv plate. ckpt_path is filled in +# post-training from the best-val checkpoint of the matching train.yml run. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_dual_denv.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + trained_on: ipsc_confocal + predict_set: a549_mantis_dual_denv + model_name: vscyto3d_cytolandft + experiment_id: dual_nucl_memb__ipsc_confocal__vscyto3d_cytolandft__a549_mantis_denv + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_cytolandft_denv.zarr + +launcher: + job_name: VSCyto3D_Cytoland_iPSC_PRED_DUAL_ON_A549_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/ipsc_confocal/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/ipsc_confocal/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..18237c97f --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/ipsc_confocal/predict__a549_mantis_mock.yml @@ -0,0 +1,43 @@ +# vscyto3d_cytolandft (Cytoland-init + dynacell FT on iPSC) predict: +# dual nucleus+membrane on A549 mock plate. ckpt_path is filled in +# post-training from the best-val checkpoint of the matching train.yml run. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_dual_mock.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + trained_on: ipsc_confocal + predict_set: a549_mantis_dual_mock + model_name: vscyto3d_cytolandft + experiment_id: dual_nucl_memb__ipsc_confocal__vscyto3d_cytolandft__a549_mantis_mock + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_cytolandft_mock.zarr + +launcher: + job_name: VSCyto3D_Cytoland_iPSC_PRED_DUAL_ON_A549_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/ipsc_confocal/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/ipsc_confocal/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..b94461b79 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/ipsc_confocal/predict__a549_mantis_zikv.yml @@ -0,0 +1,43 @@ +# vscyto3d_cytolandft (Cytoland-init + dynacell FT on iPSC) predict: +# dual nucleus+membrane on A549 zikv plate. ckpt_path is filled in +# post-training from the best-val checkpoint of the matching train.yml run. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_dual_zikv.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + trained_on: ipsc_confocal + predict_set: a549_mantis_dual_zikv + model_name: vscyto3d_cytolandft + experiment_id: dual_nucl_memb__ipsc_confocal__vscyto3d_cytolandft__a549_mantis_zikv + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_cytolandft_zikv.zarr + +launcher: + job_name: VSCyto3D_Cytoland_iPSC_PRED_DUAL_ON_A549_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/ipsc_confocal/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/ipsc_confocal/predict__ipsc_confocal.yml new file mode 100644 index 000000000..f545aa20f --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/ipsc_confocal/predict__ipsc_confocal.yml @@ -0,0 +1,43 @@ +# vscyto3d_cytolandft (Cytoland-init + dynacell FT on iPSC) predict: +# dual nucleus+membrane on iPSC test set. ckpt_path is filled in post-training +# from the best-val checkpoint of the matching train.yml run. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal_dual.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + trained_on: ipsc_confocal + predict_set: ipsc_confocal_dual + model_name: vscyto3d_cytolandft + experiment_id: dual_nucl_memb__ipsc_confocal__vscyto3d_cytolandft__ipsc_confocal + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/dual_nucl_memb_vscyto3d_cytolandft.zarr + +launcher: + job_name: VSCyto3D_Cytoland_iPSC_PRED_DUAL + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/ipsc_confocal/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/ipsc_confocal/train.yml new file mode 100644 index 000000000..9ecd0b8ca --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/ipsc_confocal/train.yml @@ -0,0 +1,58 @@ +# VSCyto3D dual nucleus+membrane FT: cytoland-init -> dynacell FT on iPSC. +# Full-weight load (no encoder_only) — source ckpt is 2-channel and matches +# the fcmae_vscyto3d_2chan_fit arch exactly. Sampling bias is Nuclei-foreground +# weighted (w_key=Nuclei in targets/dual_nucl_memb.yml + bumped num_samples +# here); revisit if validation loss for Membrane channel diverges from the +# fcmae_vscyto3d_pretrained membrane baseline. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal_dual.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/data_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + train_set: ipsc_confocal + model_name: vscyto3d_cytolandft + experiment_id: dual_nucl_memb__ipsc_confocal__vscyto3d_cytolandft + +# Bump num_samples from the dual_nucl_memb default (2) to 4 to match the +# single-organelle FCMAE recipe at the same crop size. +data: + init_args: + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Nuclei, Membrane] + w_key: Nuclei + spatial_size: [15, 600, 600] + num_samples: 4 + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/datasets/public/VS_models/VSCyto3D/epoch=83-step=14532-loss=0.492.ckpt + +trainer: + logger: + init_args: + name: VSCyto3D_Cytoland_iPSC_Dual + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/dual_nucl_memb/vscyto3d_cytolandft + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/dual_nucl_memb/vscyto3d_cytolandft/checkpoints + +launcher: + job_name: VSCyto3D_Cytoland_iPSC_Dual + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/dual_nucl_memb/vscyto3d_cytolandft diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/ipsc_confocal/train_smoke.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/ipsc_confocal/train_smoke.yml new file mode 100644 index 000000000..fd8b36f7d --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_cytolandft/ipsc_confocal/train_smoke.yml @@ -0,0 +1,59 @@ +# Gate 12 smoke leaf: same as train.yml but trainer.fast_dev_run=true on +# single GPU. NOT FOR PRODUCTION — delete after Phase 1 gate verification. +# Purpose: verify full-weight cytoland ckpt load into fcmae_vscyto3d_2chan_fit +# arch succeeds, one fwd/bwd pass completes, no shape errors. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal_dual.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/data_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + train_set: ipsc_confocal + model_name: vscyto3d_cytolandft + experiment_id: dual_nucl_memb__ipsc_confocal__vscyto3d_cytolandft__smoke + +data: + init_args: + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Nuclei, Membrane] + w_key: Nuclei + spatial_size: [15, 600, 600] + num_samples: 4 + # Cut workers + skip mmap_preload to keep startup overhead small for a + # 1-batch run — preloading 500 iPSC FOVs into /dev/shm peaks at ~185G, + # which OOMs at the smoke mem budget below. + num_workers: 0 + persistent_workers: false + mmap_preload: false + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/datasets/public/VS_models/VSCyto3D/epoch=83-step=14532-loss=0.492.ckpt + +trainer: + # Override topology to single-GPU + 1-batch fast_dev_run. + fast_dev_run: true + strategy: auto + devices: 1 + num_nodes: 1 + logger: false + callbacks: [] + +launcher: + job_name: SMOKE_GATE12_Cytoland_FT_iPSC_Dual + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/dual_nucl_memb/vscyto3d_cytolandft/_smoke + sbatch: + ntasks_per_node: 1 + cpus_per_task: 8 + gpus: 1 + mem: "128G" + constraint: "h100|h200" + time: "00:30:00" diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..b7aa49b76 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,43 @@ +# vscyto3d_infectionft_dynacellft (InfectionFTDynacell-init + dynacell FT on A549) predict: +# dual nucleus+membrane on A549 denv plate. ckpt_path is filled in +# post-training from the best-val checkpoint of the matching train.yml run. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_dual_denv.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + trained_on: a549_mantis + predict_set: a549_mantis_dual_denv + model_name: vscyto3d_infectionft_dynacellft + experiment_id: dual_nucl_memb__a549_mantis__vscyto3d_infectionft_dynacellft__a549_mantis_denv + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_infectionft_dynacellft_a549trained_denv.zarr + +launcher: + job_name: VSCyto3D_InfectionFTDynacell_A549_PRED_DUAL_ON_A549_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..ecaaacf62 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,43 @@ +# vscyto3d_infectionft_dynacellft (InfectionFTDynacell-init + dynacell FT on A549) predict: +# dual nucleus+membrane on A549 mock plate. ckpt_path is filled in +# post-training from the best-val checkpoint of the matching train.yml run. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_dual_mock.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + trained_on: a549_mantis + predict_set: a549_mantis_dual_mock + model_name: vscyto3d_infectionft_dynacellft + experiment_id: dual_nucl_memb__a549_mantis__vscyto3d_infectionft_dynacellft__a549_mantis_mock + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_infectionft_dynacellft_a549trained_mock.zarr + +launcher: + job_name: VSCyto3D_InfectionFTDynacell_A549_PRED_DUAL_ON_A549_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..2a6cc320a --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,43 @@ +# vscyto3d_infectionft_dynacellft (InfectionFTDynacell-init + dynacell FT on A549) predict: +# dual nucleus+membrane on A549 zikv plate. ckpt_path is filled in +# post-training from the best-val checkpoint of the matching train.yml run. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_dual_zikv.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + trained_on: a549_mantis + predict_set: a549_mantis_dual_zikv + model_name: vscyto3d_infectionft_dynacellft + experiment_id: dual_nucl_memb__a549_mantis__vscyto3d_infectionft_dynacellft__a549_mantis_zikv + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_infectionft_dynacellft_a549trained_zikv.zarr + +launcher: + job_name: VSCyto3D_InfectionFTDynacell_A549_PRED_DUAL_ON_A549_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..df65e3ccc --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,43 @@ +# vscyto3d_infectionft_dynacellft (InfectionFTDynacell-init + dynacell FT on A549) predict: +# dual nucleus+membrane on iPSC test set. ckpt_path is filled in post-training +# from the best-val checkpoint of the matching train.yml run. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal_dual.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + trained_on: a549_mantis + predict_set: ipsc_confocal_dual + model_name: vscyto3d_infectionft_dynacellft + experiment_id: dual_nucl_memb__a549_mantis__vscyto3d_infectionft_dynacellft__ipsc_confocal + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/dual_nucl_memb_vscyto3d_infectionft_dynacellft_a549trained.zarr + +launcher: + job_name: VSCyto3D_InfectionFTDynacell_A549_PRED_DUAL + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/a549_mantis/train.yml new file mode 100644 index 000000000..4d3664d46 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/a549_mantis/train.yml @@ -0,0 +1,58 @@ +# VSCyto3D dual nucleus+membrane FT: cytoland -> A549-infection-FT -> dynacell FT (3-stage) on A549. +# Full-weight load (no encoder_only) — source ckpt is 2-channel and matches +# the fcmae_vscyto3d_2chan_fit arch exactly. Sampling bias is Nuclei-foreground +# weighted (w_key=Nuclei in targets/dual_nucl_memb.yml + bumped num_samples +# here); revisit if validation loss for Membrane channel diverges from the +# fcmae_vscyto3d_pretrained membrane baseline. +base: + - ../../../_internal/shared/model/train_sets/a549_mantis_dual.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/data_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + train_set: a549_mantis + model_name: vscyto3d_infectionft_dynacellft + experiment_id: dual_nucl_memb__a549_mantis__vscyto3d_infectionft_dynacellft + +# Bump num_samples from the dual_nucl_memb default (2) to 4 to match the +# single-organelle FCMAE recipe at the same crop size. +data: + init_args: + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Nuclei, Membrane] + w_key: Nuclei + spatial_size: [15, 600, 600] + num_samples: 4 + +model: + init_args: + ckpt_path: /hpc/projects/organelle_phenotyping/models/VSCyto3D-A549-infection-finetune/4gpu_bf16_bs16_to_ep7/checkpoints/epoch=7-step=832.ckpt + +trainer: + logger: + init_args: + name: VSCyto3D_InfectionFTDynacell_A549_Dual + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549/dual_nucl_memb/vscyto3d_infectionft_dynacellft + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549/dual_nucl_memb/vscyto3d_infectionft_dynacellft/checkpoints + +launcher: + job_name: VSCyto3D_InfectionFTDynacell_A549_Dual + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549/dual_nucl_memb/vscyto3d_infectionft_dynacellft diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/ipsc_confocal/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/ipsc_confocal/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..4563d616d --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/ipsc_confocal/predict__a549_mantis_denv.yml @@ -0,0 +1,43 @@ +# vscyto3d_infectionft_dynacellft (InfectionFTDynacell-init + dynacell FT on iPSC) predict: +# dual nucleus+membrane on A549 denv plate. ckpt_path is filled in +# post-training from the best-val checkpoint of the matching train.yml run. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_dual_denv.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + trained_on: ipsc_confocal + predict_set: a549_mantis_dual_denv + model_name: vscyto3d_infectionft_dynacellft + experiment_id: dual_nucl_memb__ipsc_confocal__vscyto3d_infectionft_dynacellft__a549_mantis_denv + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_infectionft_dynacellft_denv.zarr + +launcher: + job_name: VSCyto3D_InfectionFTDynacell_iPSC_PRED_DUAL_ON_A549_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/ipsc_confocal/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/ipsc_confocal/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..81b3b48d6 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/ipsc_confocal/predict__a549_mantis_mock.yml @@ -0,0 +1,43 @@ +# vscyto3d_infectionft_dynacellft (InfectionFTDynacell-init + dynacell FT on iPSC) predict: +# dual nucleus+membrane on A549 mock plate. ckpt_path is filled in +# post-training from the best-val checkpoint of the matching train.yml run. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_dual_mock.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + trained_on: ipsc_confocal + predict_set: a549_mantis_dual_mock + model_name: vscyto3d_infectionft_dynacellft + experiment_id: dual_nucl_memb__ipsc_confocal__vscyto3d_infectionft_dynacellft__a549_mantis_mock + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_infectionft_dynacellft_mock.zarr + +launcher: + job_name: VSCyto3D_InfectionFTDynacell_iPSC_PRED_DUAL_ON_A549_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/ipsc_confocal/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/ipsc_confocal/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..c39a4a573 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/ipsc_confocal/predict__a549_mantis_zikv.yml @@ -0,0 +1,43 @@ +# vscyto3d_infectionft_dynacellft (InfectionFTDynacell-init + dynacell FT on iPSC) predict: +# dual nucleus+membrane on A549 zikv plate. ckpt_path is filled in +# post-training from the best-val checkpoint of the matching train.yml run. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_dual_zikv.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + trained_on: ipsc_confocal + predict_set: a549_mantis_dual_zikv + model_name: vscyto3d_infectionft_dynacellft + experiment_id: dual_nucl_memb__ipsc_confocal__vscyto3d_infectionft_dynacellft__a549_mantis_zikv + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_infectionft_dynacellft_zikv.zarr + +launcher: + job_name: VSCyto3D_InfectionFTDynacell_iPSC_PRED_DUAL_ON_A549_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/ipsc_confocal/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/ipsc_confocal/predict__ipsc_confocal.yml new file mode 100644 index 000000000..de97a81f9 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/ipsc_confocal/predict__ipsc_confocal.yml @@ -0,0 +1,43 @@ +# vscyto3d_infectionft_dynacellft (InfectionFTDynacell-init + dynacell FT on iPSC) predict: +# dual nucleus+membrane on iPSC test set. ckpt_path is filled in post-training +# from the best-val checkpoint of the matching train.yml run. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal_dual.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + trained_on: ipsc_confocal + predict_set: ipsc_confocal_dual + model_name: vscyto3d_infectionft_dynacellft + experiment_id: dual_nucl_memb__ipsc_confocal__vscyto3d_infectionft_dynacellft__ipsc_confocal + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/dual_nucl_memb_vscyto3d_infectionft_dynacellft.zarr + +launcher: + job_name: VSCyto3D_InfectionFTDynacell_iPSC_PRED_DUAL + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/ipsc_confocal/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/ipsc_confocal/train.yml new file mode 100644 index 000000000..211506a16 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_dual_nucl_memb/vscyto3d_infectionft_dynacellft/ipsc_confocal/train.yml @@ -0,0 +1,58 @@ +# VSCyto3D dual nucleus+membrane FT: cytoland -> A549-infection-FT -> dynacell FT (3-stage) on iPSC. +# Full-weight load (no encoder_only) — source ckpt is 2-channel and matches +# the fcmae_vscyto3d_2chan_fit arch exactly. Sampling bias is Nuclei-foreground +# weighted (w_key=Nuclei in targets/dual_nucl_memb.yml + bumped num_samples +# here); revisit if validation loss for Membrane channel diverges from the +# fcmae_vscyto3d_pretrained membrane baseline. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal_dual.yml + - ../../../_internal/shared/model/targets/dual_nucl_memb.yml + - ../../../_internal/shared/model/data_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: dual_nucl_memb + train_set: ipsc_confocal + model_name: vscyto3d_infectionft_dynacellft + experiment_id: dual_nucl_memb__ipsc_confocal__vscyto3d_infectionft_dynacellft + +# Bump num_samples from the dual_nucl_memb default (2) to 4 to match the +# single-organelle FCMAE recipe at the same crop size. +data: + init_args: + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Nuclei, Membrane] + w_key: Nuclei + spatial_size: [15, 600, 600] + num_samples: 4 + +model: + init_args: + ckpt_path: /hpc/projects/organelle_phenotyping/models/VSCyto3D-A549-infection-finetune/4gpu_bf16_bs16_to_ep7/checkpoints/epoch=7-step=832.ckpt + +trainer: + logger: + init_args: + name: VSCyto3D_InfectionFTDynacell_iPSC_Dual + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/dual_nucl_memb/vscyto3d_infectionft_dynacellft + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/dual_nucl_memb/vscyto3d_infectionft_dynacellft/checkpoints + +launcher: + job_name: VSCyto3D_InfectionFTDynacell_iPSC_Dual + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/dual_nucl_memb/vscyto3d_infectionft_dynacellft diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/celldiff/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/celldiff/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 120000 index 000000000..bf10d7589 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/celldiff/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1 @@ +../../../../../er/celldiff/ipsc_confocal/eval__a549_mantis_denv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/celldiff/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/celldiff/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 120000 index 000000000..a4c5ea9be --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/celldiff/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1 @@ +../../../../../er/celldiff/ipsc_confocal/eval__a549_mantis_mock.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/celldiff/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/celldiff/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 120000 index 000000000..a15141b73 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/celldiff/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1 @@ +../../../../../er/celldiff/ipsc_confocal/eval__a549_mantis_zikv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/celldiff/ipsc_confocal/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/celldiff/ipsc_confocal/eval__ipsc_confocal.yaml new file mode 120000 index 000000000..237286899 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/celldiff/ipsc_confocal/eval__ipsc_confocal.yaml @@ -0,0 +1 @@ +../../../../../er/celldiff/ipsc_confocal/eval__ipsc_confocal.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 120000 index 000000000..2e2ea2bdf --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1 @@ +../../../../../er/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_denv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 120000 index 000000000..f6177069d --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1 @@ +../../../../../er/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_mock.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 120000 index 000000000..42ee842e6 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1 @@ +../../../../../er/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_zikv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fcmae_vscyto3d_pretrained_randinit/randinit/eval__a549_mantis.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fcmae_vscyto3d_pretrained_randinit/randinit/eval__a549_mantis.yaml new file mode 100644 index 000000000..bfecc5a41 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fcmae_vscyto3d_pretrained_randinit/randinit/eval__a549_mantis.yaml @@ -0,0 +1,41 @@ +# @package _global_ +# Track A (random init, no training) grouped eval for er on A549. +# One process covers mock + denv + zikv via the conditions list, amortizing the +# DINOv3 + DynaCLR + CELL-DINO load. Reads the matching frozen-randinit zarrs. +defaults: + - override /target: er_sec61b + - override /predict_set: a549_mantis_sec61b_mock + +# Base anchors to the {gene}-{first_cond} manifest; each condition overlay below +# swaps dataset, pred_path, and save_dir for mock / denv / zikv in one process. +benchmark: + dataset_ref: + target: sec61b + +compute_feature_metrics: true + +conditions: + - name: a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-mock + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_pretrained_randinit_mock.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_randinit/eval_vscyto3d_randinit_er_mock + - name: a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-denv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_pretrained_randinit_denv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_randinit/eval_vscyto3d_randinit_er_denv + - name: a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-zikv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_pretrained_randinit_zikv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_randinit/eval_vscyto3d_randinit_er_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fcmae_vscyto3d_pretrained_randinit/randinit/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fcmae_vscyto3d_pretrained_randinit/randinit/eval__ipsc_confocal.yaml new file mode 100644 index 000000000..9b9f52b8c --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fcmae_vscyto3d_pretrained_randinit/randinit/eval__ipsc_confocal.yaml @@ -0,0 +1,15 @@ +# @package _global_ +# Track A (random init, no training) eval for er on iPSC. +# Reads the frozen-randinit predict zarr produced by +# /fcmae_vscyto3d_pretrained/_no_train_randinit/predict__ipsc_confocal.yml. +defaults: + - override /target: er_sec61b + - override /predict_set: ipsc_confocal + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_fcmae_vscyto3d_pretrained_randinit.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_randinit/eval_vscyto3d_randinit_er diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 120000 index 000000000..252099ab4 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1 @@ +../../../../../er/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_denv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 120000 index 000000000..296ab3ad9 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1 @@ +../../../../../er/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_mock.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 120000 index 000000000..ee8c4f389 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1 @@ +../../../../../er/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_zikv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fnet3d_paper/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fnet3d_paper/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 120000 index 000000000..e52418d5e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fnet3d_paper/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1 @@ +../../../../../er/fnet3d_paper/ipsc_confocal/eval__a549_mantis_denv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fnet3d_paper/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fnet3d_paper/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 120000 index 000000000..3e637e458 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fnet3d_paper/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1 @@ +../../../../../er/fnet3d_paper/ipsc_confocal/eval__a549_mantis_mock.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fnet3d_paper/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fnet3d_paper/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 120000 index 000000000..795e29808 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/fnet3d_paper/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1 @@ +../../../../../er/fnet3d_paper/ipsc_confocal/eval__a549_mantis_zikv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/unetvit3d/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/unetvit3d/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 120000 index 000000000..71612648e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/unetvit3d/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1 @@ +../../../../../er/unetvit3d/ipsc_confocal/eval__a549_mantis_denv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/unetvit3d/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/unetvit3d/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 120000 index 000000000..83c1332e3 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/unetvit3d/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1 @@ +../../../../../er/unetvit3d/ipsc_confocal/eval__a549_mantis_mock.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/unetvit3d/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/unetvit3d/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 120000 index 000000000..ff3a386de --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/unetvit3d/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1 @@ +../../../../../er/unetvit3d/ipsc_confocal/eval__a549_mantis_zikv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/unetvit3d/ipsc_confocal/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/unetvit3d/ipsc_confocal/eval__ipsc_confocal.yaml new file mode 120000 index 000000000..9c0e95db5 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/er/unetvit3d/ipsc_confocal/eval__ipsc_confocal.yaml @@ -0,0 +1 @@ +../../../../../er/unetvit3d/ipsc_confocal/eval__ipsc_confocal.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/README.md b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/README.md new file mode 100644 index 000000000..a56c8e1ec --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/README.md @@ -0,0 +1,21 @@ +# Grouped re-eval leaves +Auto-generated by `applications/dynacell/tools/generate_grouped_eval_configs.py`. +## Bucket summary +| Organelle | Train set | Conditions | +|---|---|---| +| er | ipsc_trained | 22 | +| er | joint | 16 | +| er | a549_trained | 16 | +| mitochondria | ipsc_trained | 22 | +| mitochondria | joint | 16 | +| mitochondria | a549_trained | 16 | +| nucleus | ipsc_trained | 22 | +| nucleus | joint | 12 | +| nucleus | a549_trained | 16 | +| membrane | ipsc_trained | 22 | +| membrane | joint | 16 | +| membrane | a549_trained | 16 | + +## Probe leaf + +6 conditions under `_probe/`. diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/_probe/eval_grouped.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/_probe/eval_grouped.yaml new file mode 100644 index 000000000..3a9e0e3a6 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/_probe/eval_grouped.yaml @@ -0,0 +1,74 @@ +# @package _global_ +# Grouped probe leaf: er bucket, 6 conditions covering every code path. All save_dirs/pred_cache_dirs under /tmp/reeval_probe/ so partial runs cannot corrupt production data. Used by Step 5 of the campaign. +target_name: er +compute_feature_metrics: true +use_gpu: true +io: + require_complete_cache: false +runtime: + executor: serial + fov_workers: 1 + threads_per_worker: auto +force_recompute: + final_metrics: true +conditions: +- name: celldiff_r2__a549_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_celldiff_r2_a549trained.zarr + pred_cache_dir: /tmp/reeval_probe/cache/celldiff_r2__a549_trained__ipsc + save: + save_dir: /tmp/reeval_probe/out/celldiff_r2__a549_trained__ipsc +- name: celldiff_r2_denoise__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_celldiff_r2_denoise.zarr + pred_cache_dir: /tmp/reeval_probe/cache/celldiff_r2_denoise__ipsc_trained__ipsc + save: + save_dir: /tmp/reeval_probe/out/celldiff_r2_denoise__ipsc_trained__ipsc +- name: vscyto3d__joint__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_fcmae_vscyto3d_pretrained_jointtrained.zarr + pred_cache_dir: /tmp/reeval_probe/cache/vscyto3d__joint__ipsc + save: + save_dir: /tmp/reeval_probe/out/vscyto3d__joint__ipsc +- name: celldiff_r2__a549_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-denv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_celldiff_r2_a549trained_denv.zarr + pred_cache_dir: /tmp/reeval_probe/cache/celldiff_r2__a549_trained__a549_denv + save: + save_dir: /tmp/reeval_probe/out/celldiff_r2__a549_trained__a549_denv +- name: celldiff_r2_iterative__ipsc_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-denv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_celldiff_r2_iterative__sec61b_denv.zarr + pred_cache_dir: /tmp/reeval_probe/cache/celldiff_r2_iterative__ipsc_trained__a549_denv + save: + save_dir: /tmp/reeval_probe/out/celldiff_r2_iterative__ipsc_trained__a549_denv +- name: vscyto3d__joint__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-denv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_pretrained_jointtrained_denv.zarr + pred_cache_dir: /tmp/reeval_probe/cache/vscyto3d__joint__a549_denv + save: + save_dir: /tmp/reeval_probe/out/vscyto3d__joint__a549_denv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/er_a549_trained/eval_grouped.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/er_a549_trained/eval_grouped.yaml new file mode 100644 index 000000000..051afaccf --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/er_a549_trained/eval_grouped.yaml @@ -0,0 +1,174 @@ +# @package _global_ +# Grouped leaf: er bucket, a549_trained models (16 conditions). Auto-generated by tools/generate_grouped_eval_configs.py. +target_name: er +compute_feature_metrics: true +use_gpu: true +io: + require_complete_cache: false +runtime: + executor: serial + fov_workers: 1 + threads_per_worker: auto +force_recompute: + final_metrics: true +conditions: +- name: celldiff_r2__a549_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-denv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_celldiff_r2_a549trained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/celldiff_r2/sec61b_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_celldiff_r2_a549trained_er_denv +- name: vscyto3d__a549_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-denv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_pretrained_a549trained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fcmae_vscyto3d_pretrained/sec61b_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_vscyto3d_a549trained_er_denv +- name: unext2__a549_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-denv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_scratch_a549trained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fcmae_vscyto3d_scratch/sec61b_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_unext2_a549trained_er_denv +- name: fnet3d__a549_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-denv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fnet3d_paper_a549trained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fnet3d_paper/sec61b_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_fnet3d_a549trained_er_denv +- name: celldiff_r2__a549_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-mock + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_celldiff_r2_a549trained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/celldiff_r2/sec61b_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_celldiff_r2_a549trained_er_mock +- name: vscyto3d__a549_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-mock + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_pretrained_a549trained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fcmae_vscyto3d_pretrained/sec61b_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_vscyto3d_a549trained_er_mock +- name: unext2__a549_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-mock + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_scratch_a549trained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fcmae_vscyto3d_scratch/sec61b_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_unext2_a549trained_er_mock +- name: fnet3d__a549_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-mock + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fnet3d_paper_a549trained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fnet3d_paper/sec61b_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_fnet3d_a549trained_er_mock +- name: celldiff_r2__a549_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-zikv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_celldiff_r2_a549trained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/celldiff_r2/sec61b_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_celldiff_r2_a549trained_er_zikv +- name: vscyto3d__a549_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-zikv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_pretrained_a549trained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fcmae_vscyto3d_pretrained/sec61b_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_vscyto3d_a549trained_er_zikv +- name: unext2__a549_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-zikv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_scratch_a549trained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fcmae_vscyto3d_scratch/sec61b_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_unext2_a549trained_er_zikv +- name: fnet3d__a549_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-zikv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fnet3d_paper_a549trained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fnet3d_paper/sec61b_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_fnet3d_a549trained_er_zikv +- name: celldiff_r2__a549_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_celldiff_r2_a549trained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/a549_trained/celldiff_r2/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_a549trained_with_embeddings/eval_celldiff_r2_a549trained_er +- name: vscyto3d__a549_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_fcmae_vscyto3d_pretrained_a549trained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/a549_trained/fcmae_vscyto3d_pretrained/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_a549trained_with_embeddings/eval_vscyto3d_a549trained_er +- name: unext2__a549_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_fcmae_vscyto3d_scratch_a549trained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/a549_trained/fcmae_vscyto3d_scratch/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_a549trained_with_embeddings/eval_unext2_a549trained_er +- name: fnet3d__a549_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_fnet3d_paper_a549trained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/a549_trained/fnet3d_paper/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_a549trained_with_embeddings/eval_fnet3d_a549trained_er diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/er_celldiff_r2_a549trained/eval_grouped.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/er_celldiff_r2_a549trained/eval_grouped.yaml new file mode 100644 index 000000000..718eda481 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/er_celldiff_r2_a549trained/eval_grouped.yaml @@ -0,0 +1,56 @@ +# @package _global_ +# Grouped leaf: er bucket, celldiff_r2 a549-trained model (4 conditions). +# Covers iPSC test + A549 mantis (denv/mock/zikv). +# Predictions: sec61b_celldiff_r2_a549trained.zarr / sec61b_celldiff_r2_a549trained_{cond}.zarr +target_name: er +compute_feature_metrics: true +use_gpu: true +io: + require_complete_cache: false +runtime: + executor: serial + fov_workers: 1 + threads_per_worker: auto +force_recompute: + final_metrics: true +conditions: +- name: celldiff_r2_a549trained__a549_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_celldiff_r2_a549trained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/a549_trained/celldiff_r2_a549trained/sec61b_ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_a549trained_with_embeddings/eval_celldiff_r2_a549trained_er +- name: celldiff_r2_a549trained__a549_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-denv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_celldiff_r2_a549trained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/celldiff_r2_a549trained/sec61b_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_celldiff_r2_a549trained_er_denv +- name: celldiff_r2_a549trained__a549_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-mock + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_celldiff_r2_a549trained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/celldiff_r2_a549trained/sec61b_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_celldiff_r2_a549trained_er_mock +- name: celldiff_r2_a549trained__a549_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-zikv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_celldiff_r2_a549trained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/celldiff_r2_a549trained/sec61b_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_celldiff_r2_a549trained_er_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/er_ipsc_trained/eval_grouped.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/er_ipsc_trained/eval_grouped.yaml new file mode 100644 index 000000000..a216bf475 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/er_ipsc_trained/eval_grouped.yaml @@ -0,0 +1,234 @@ +# @package _global_ +# Grouped leaf: er bucket, ipsc_trained models (22 conditions). Auto-generated by tools/generate_grouped_eval_configs.py. +target_name: er +compute_feature_metrics: true +use_gpu: true +io: + require_complete_cache: false +runtime: + executor: serial + fov_workers: 1 + threads_per_worker: auto +force_recompute: + final_metrics: true +conditions: +- name: celldiff_r2_iterative__ipsc_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-denv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_celldiff_r2_iterative__sec61b_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/celldiff_r2_iterative/sec61b_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_celldiff_r2_iterative_er_denv +- name: vscyto3d__ipsc_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-denv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_pretrained__sec61b_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_pretrained/sec61b_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_vscyto3d_er_denv +- name: unext2__ipsc_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-denv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_scratch__sec61b_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_scratch/sec61b_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_unext2_er_denv +- name: fnet3d__ipsc_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-denv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fnet3d_paper__sec61b_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fnet3d_paper/sec61b_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_fnet3d_er_denv +- name: unetvit3d__ipsc_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-denv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_unetvit3d__sec61b_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/unetvit3d/sec61b_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_unetvit3d_er_denv +- name: celldiff_r2_iterative__ipsc_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-mock + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_celldiff_r2_iterative__sec61b_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/celldiff_r2_iterative/sec61b_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_celldiff_r2_iterative_er_mock +- name: vscyto3d__ipsc_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-mock + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_pretrained__sec61b_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_pretrained/sec61b_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_vscyto3d_er_mock +- name: unext2__ipsc_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-mock + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_scratch__sec61b_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_scratch/sec61b_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_unext2_er_mock +- name: fnet3d__ipsc_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-mock + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fnet3d_paper__sec61b_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fnet3d_paper/sec61b_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_fnet3d_er_mock +- name: unetvit3d__ipsc_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-mock + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_unetvit3d__sec61b_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/unetvit3d/sec61b_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_unetvit3d_er_mock +- name: celldiff_r2_iterative__ipsc_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-zikv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_celldiff_r2_iterative__sec61b_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/celldiff_r2_iterative/sec61b_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_celldiff_r2_iterative_er_zikv +- name: vscyto3d__ipsc_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-zikv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_pretrained__sec61b_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_pretrained/sec61b_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_vscyto3d_er_zikv +- name: unext2__ipsc_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-zikv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_scratch__sec61b_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_scratch/sec61b_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_unext2_er_zikv +- name: fnet3d__ipsc_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-zikv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fnet3d_paper__sec61b_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fnet3d_paper/sec61b_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_fnet3d_er_zikv +- name: unetvit3d__ipsc_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-zikv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_unetvit3d__sec61b_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/unetvit3d/sec61b_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_unetvit3d_er_zikv +- name: celldiff_r2_denoise__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_celldiff_r2_denoise.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/ipsc_trained/celldiff_r2_denoise/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_with_embeddings/eval_celldiff_r2_denoise_er +- name: celldiff_r2_iterative__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_celldiff_r2_iterative.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/ipsc_trained/celldiff_r2_iterative/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_with_embeddings/eval_celldiff_r2_iterative_er +- name: celldiff_r2_sliding_window__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_celldiff_r2_sliding_window.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/ipsc_trained/celldiff_r2_sliding_window/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_with_embeddings/eval_celldiff_r2_sliding_window_er +- name: vscyto3d__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_fcmae_vscyto3d_pretrained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_pretrained/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_with_embeddings/eval_vscyto3d_er +- name: unext2__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_fcmae_vscyto3d_scratch.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_scratch/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_with_embeddings/eval_unext2_er +- name: fnet3d__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_fnet3d_paper.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/ipsc_trained/fnet3d_paper/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_with_embeddings/eval_fnet3d_er +- name: unetvit3d__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_unetvit3d.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/ipsc_trained/unetvit3d/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_with_embeddings/eval_unetvit3d_er diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/er_joint/eval_grouped.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/er_joint/eval_grouped.yaml new file mode 100644 index 000000000..a564f5136 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/er_joint/eval_grouped.yaml @@ -0,0 +1,174 @@ +# @package _global_ +# Grouped leaf: er bucket, joint models (16 conditions). Auto-generated by tools/generate_grouped_eval_configs.py. +target_name: er +compute_feature_metrics: true +use_gpu: true +io: + require_complete_cache: false +runtime: + executor: serial + fov_workers: 1 + threads_per_worker: auto +force_recompute: + final_metrics: true +conditions: +- name: celldiff_r2__joint__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-denv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/sec61b_celldiff_r2_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/celldiff_r2/sec61b_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_celldiff_r2_jointtrained_er_denv +- name: vscyto3d__joint__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-denv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_pretrained_jointtrained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fcmae_vscyto3d_pretrained/sec61b_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_vscyto3d_jointtrained_er_denv +- name: unext2__joint__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-denv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_scratch_jointtrained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fcmae_vscyto3d_scratch/sec61b_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_unext2_jointtrained_er_denv +- name: fnet3d__joint__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-denv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fnet3d_paper_jointtrained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fnet3d_paper/sec61b_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_fnet3d_jointtrained_er_denv +- name: celldiff_r2__joint__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-mock + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/sec61b_celldiff_r2_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/celldiff_r2/sec61b_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_celldiff_r2_jointtrained_er_mock +- name: vscyto3d__joint__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-mock + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_pretrained_jointtrained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fcmae_vscyto3d_pretrained/sec61b_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_vscyto3d_jointtrained_er_mock +- name: unext2__joint__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-mock + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_scratch_jointtrained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fcmae_vscyto3d_scratch/sec61b_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_unext2_jointtrained_er_mock +- name: fnet3d__joint__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-mock + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fnet3d_paper_jointtrained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fnet3d_paper/sec61b_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_fnet3d_jointtrained_er_mock +- name: celldiff_r2__joint__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-zikv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/sec61b_celldiff_r2_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/celldiff_r2/sec61b_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_celldiff_r2_jointtrained_er_zikv +- name: vscyto3d__joint__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-zikv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_pretrained_jointtrained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fcmae_vscyto3d_pretrained/sec61b_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_vscyto3d_jointtrained_er_zikv +- name: unext2__joint__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-zikv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_scratch_jointtrained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fcmae_vscyto3d_scratch/sec61b_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_unext2_jointtrained_er_zikv +- name: fnet3d__joint__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-sec61b-zikv + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fnet3d_paper_jointtrained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fnet3d_paper/sec61b_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_fnet3d_jointtrained_er_zikv +- name: celldiff_r2__joint__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions/sec61b_celldiff_r2.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/joint/celldiff_r2/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_jointtrained_with_embeddings/eval_celldiff_r2_jointtrained_er +- name: vscyto3d__joint__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_fcmae_vscyto3d_pretrained_jointtrained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/joint/fcmae_vscyto3d_pretrained/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_jointtrained_with_embeddings/eval_vscyto3d_jointtrained_er +- name: unext2__joint__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_fcmae_vscyto3d_scratch_jointtrained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/joint/fcmae_vscyto3d_scratch/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_jointtrained_with_embeddings/eval_unext2_jointtrained_er +- name: fnet3d__joint__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: sec61b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_fnet3d_paper_jointtrained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/joint/fnet3d_paper/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_jointtrained_with_embeddings/eval_fnet3d_jointtrained_er diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/membrane_a549_trained/eval_grouped.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/membrane_a549_trained/eval_grouped.yaml new file mode 100644 index 000000000..070ca2751 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/membrane_a549_trained/eval_grouped.yaml @@ -0,0 +1,174 @@ +# @package _global_ +# Grouped leaf: membrane bucket, a549_trained models (16 conditions). Auto-generated by tools/generate_grouped_eval_configs.py. +target_name: membrane +compute_feature_metrics: true +use_gpu: true +io: + require_complete_cache: false +runtime: + executor: serial + fov_workers: 1 + threads_per_worker: auto +force_recompute: + final_metrics: true +conditions: +- name: celldiff_r2__a549_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-denv + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_celldiff_r2_a549trained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/celldiff_r2/caax_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_celldiff_r2_a549trained_membrane_denv +- name: vscyto3d__a549_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-denv + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_pretrained_a549trained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fcmae_vscyto3d_pretrained/caax_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_vscyto3d_a549trained_membrane_denv +- name: unext2__a549_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-denv + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_scratch_a549trained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fcmae_vscyto3d_scratch/caax_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_unext2_a549trained_membrane_denv +- name: fnet3d__a549_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-denv + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fnet3d_paper_a549trained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fnet3d_paper/caax_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_fnet3d_a549trained_membrane_denv +- name: celldiff_r2__a549_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-caax-mock + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_celldiff_r2_a549trained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/celldiff_r2/caax_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_celldiff_r2_a549trained_membrane_mock +- name: vscyto3d__a549_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-caax-mock + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_pretrained_a549trained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fcmae_vscyto3d_pretrained/caax_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_vscyto3d_a549trained_membrane_mock +- name: unext2__a549_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-caax-mock + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_scratch_a549trained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fcmae_vscyto3d_scratch/caax_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_unext2_a549trained_membrane_mock +- name: fnet3d__a549_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-caax-mock + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fnet3d_paper_a549trained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fnet3d_paper/caax_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_fnet3d_a549trained_membrane_mock +- name: celldiff_r2__a549_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-zikv + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_celldiff_r2_a549trained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/celldiff_r2/caax_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_celldiff_r2_a549trained_membrane_zikv +- name: vscyto3d__a549_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-zikv + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_pretrained_a549trained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fcmae_vscyto3d_pretrained/caax_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_vscyto3d_a549trained_membrane_zikv +- name: unext2__a549_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-zikv + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_scratch_a549trained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fcmae_vscyto3d_scratch/caax_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_unext2_a549trained_membrane_zikv +- name: fnet3d__a549_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-zikv + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fnet3d_paper_a549trained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fnet3d_paper/caax_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_fnet3d_a549trained_membrane_zikv +- name: celldiff_r2__a549_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: membrane + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_celldiff_r2_a549trained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/a549_trained/celldiff_r2/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_a549trained_with_embeddings/eval_celldiff_r2_a549trained_membrane +- name: vscyto3d__a549_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: membrane + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_fcmae_vscyto3d_pretrained_a549trained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/a549_trained/fcmae_vscyto3d_pretrained/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_a549trained_with_embeddings/eval_vscyto3d_a549trained_membrane +- name: unext2__a549_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: membrane + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_fcmae_vscyto3d_scratch_a549trained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/a549_trained/fcmae_vscyto3d_scratch/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_a549trained_with_embeddings/eval_unext2_a549trained_membrane +- name: fnet3d__a549_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: membrane + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_fnet3d_paper_a549trained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/a549_trained/fnet3d_paper/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_a549trained_with_embeddings/eval_fnet3d_a549trained_membrane diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/membrane_celldiff_r2_a549trained/eval_grouped.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/membrane_celldiff_r2_a549trained/eval_grouped.yaml new file mode 100644 index 000000000..b6750ad24 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/membrane_celldiff_r2_a549trained/eval_grouped.yaml @@ -0,0 +1,35 @@ +# @package _global_ +# Grouped leaf: membrane bucket, celldiff_r2 a549-trained model (2 conditions available now). +# iPSC + A549 denv. A549 mock/zikv predictions still in-flight — handled by membrane_celldiff_r2_a549trained_later. +target_name: membrane +compute_feature_metrics: true +use_gpu: true +io: + require_complete_cache: false +runtime: + executor: serial + fov_workers: 1 + threads_per_worker: auto +force_recompute: + final_metrics: true +conditions: +- name: celldiff_r2_a549trained__a549_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: membrane + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_celldiff_r2_a549trained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/a549_trained/celldiff_r2_a549trained/memb_ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_a549trained_with_embeddings/eval_celldiff_r2_a549trained_membrane +- name: celldiff_r2_a549trained__a549_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-denv + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_celldiff_r2_a549trained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/celldiff_r2_a549trained/caax_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_celldiff_r2_a549trained_membrane_denv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/membrane_ipsc_trained/eval_grouped.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/membrane_ipsc_trained/eval_grouped.yaml new file mode 100644 index 000000000..b76dc245c --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/membrane_ipsc_trained/eval_grouped.yaml @@ -0,0 +1,234 @@ +# @package _global_ +# Grouped leaf: membrane bucket, ipsc_trained models (22 conditions). Auto-generated by tools/generate_grouped_eval_configs.py. +target_name: membrane +compute_feature_metrics: true +use_gpu: true +io: + require_complete_cache: false +runtime: + executor: serial + fov_workers: 1 + threads_per_worker: auto +force_recompute: + final_metrics: true +conditions: +- name: celldiff_r2_iterative__ipsc_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-denv + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_celldiff_r2_iterative_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/celldiff_r2_iterative/caax_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_celldiff_r2_iterative_membrane_denv +- name: vscyto3d__ipsc_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-denv + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_pretrained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_pretrained/caax_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_vscyto3d_membrane_denv +- name: unext2__ipsc_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-denv + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_scratch_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_scratch/caax_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_unext2_membrane_denv +- name: fnet3d__ipsc_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-denv + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fnet3d_paper_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fnet3d_paper/caax_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_fnet3d_membrane_denv +- name: unetvit3d__ipsc_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-denv + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_unetvit3d_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/unetvit3d/caax_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_unetvit3d_membrane_denv +- name: celldiff_r2_iterative__ipsc_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-caax-mock + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_celldiff_r2_iterative_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/celldiff_r2_iterative/caax_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_celldiff_r2_iterative_membrane_mock +- name: vscyto3d__ipsc_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-caax-mock + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_pretrained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_pretrained/caax_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_vscyto3d_membrane_mock +- name: unext2__ipsc_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-caax-mock + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_scratch_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_scratch/caax_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_unext2_membrane_mock +- name: fnet3d__ipsc_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-caax-mock + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fnet3d_paper_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fnet3d_paper/caax_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_fnet3d_membrane_mock +- name: unetvit3d__ipsc_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-caax-mock + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_unetvit3d_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/unetvit3d/caax_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_unetvit3d_membrane_mock +- name: celldiff_r2_iterative__ipsc_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-zikv + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_celldiff_r2_iterative_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/celldiff_r2_iterative/caax_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_celldiff_r2_iterative_membrane_zikv +- name: vscyto3d__ipsc_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-zikv + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_pretrained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_pretrained/caax_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_vscyto3d_membrane_zikv +- name: unext2__ipsc_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-zikv + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_scratch_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_scratch/caax_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_unext2_membrane_zikv +- name: fnet3d__ipsc_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-zikv + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fnet3d_paper_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fnet3d_paper/caax_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_fnet3d_membrane_zikv +- name: unetvit3d__ipsc_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-zikv + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_unetvit3d_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/unetvit3d/caax_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_unetvit3d_membrane_zikv +- name: celldiff_r2_denoise__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: membrane + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_celldiff_r2_denoise.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/ipsc_trained/celldiff_r2_denoise/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_with_embeddings/eval_celldiff_r2_denoise_membrane +- name: celldiff_r2_iterative__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: membrane + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_celldiff_r2_iterative.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/ipsc_trained/celldiff_r2_iterative/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_with_embeddings/eval_celldiff_r2_iterative_membrane +- name: celldiff_r2_sliding_window__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: membrane + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_celldiff_r2_sliding_window.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/ipsc_trained/celldiff_r2_sliding_window/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_with_embeddings/eval_celldiff_r2_sliding_window_membrane +- name: vscyto3d__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: membrane + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_fcmae_vscyto3d_pretrained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_pretrained/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_with_embeddings/eval_vscyto3d_membrane +- name: unext2__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: membrane + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_fcmae_vscyto3d_scratch.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_scratch/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_with_embeddings/eval_unext2_membrane +- name: fnet3d__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: membrane + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_fnet3d_paper.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/ipsc_trained/fnet3d_paper/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_with_embeddings/eval_fnet3d_membrane +- name: unetvit3d__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: membrane + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_unetvit3d.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/ipsc_trained/unetvit3d/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_with_embeddings/eval_unetvit3d_membrane diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/membrane_joint/eval_grouped.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/membrane_joint/eval_grouped.yaml new file mode 100644 index 000000000..e4f87cb43 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/membrane_joint/eval_grouped.yaml @@ -0,0 +1,174 @@ +# @package _global_ +# Grouped leaf: membrane bucket, joint models (16 conditions). Auto-generated by tools/generate_grouped_eval_configs.py. +target_name: membrane +compute_feature_metrics: true +use_gpu: true +io: + require_complete_cache: false +runtime: + executor: serial + fov_workers: 1 + threads_per_worker: auto +force_recompute: + final_metrics: true +conditions: +- name: celldiff_r2__joint__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-denv + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/memb_celldiff_r2_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/celldiff_r2/caax_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_celldiff_r2_jointtrained_membrane_denv +- name: vscyto3d__joint__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-denv + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/memb_fcmae_vscyto3d_pretrained_jointtrained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fcmae_vscyto3d_pretrained/caax_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_vscyto3d_jointtrained_membrane_denv +- name: unext2__joint__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-denv + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/memb_fcmae_vscyto3d_scratch_jointtrained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fcmae_vscyto3d_scratch/caax_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_unext2_jointtrained_membrane_denv +- name: fnet3d__joint__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-denv + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/memb_fnet3d_paper_jointtrained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fnet3d_paper/caax_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_fnet3d_jointtrained_membrane_denv +- name: celldiff_r2__joint__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-caax-mock + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/memb_celldiff_r2_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/celldiff_r2/caax_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_celldiff_r2_jointtrained_membrane_mock +- name: vscyto3d__joint__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-caax-mock + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/memb_fcmae_vscyto3d_pretrained_jointtrained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fcmae_vscyto3d_pretrained/caax_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_vscyto3d_jointtrained_membrane_mock +- name: unext2__joint__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-caax-mock + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/memb_fcmae_vscyto3d_scratch_jointtrained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fcmae_vscyto3d_scratch/caax_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_unext2_jointtrained_membrane_mock +- name: fnet3d__joint__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-caax-mock + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/memb_fnet3d_paper_jointtrained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fnet3d_paper/caax_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_fnet3d_jointtrained_membrane_mock +- name: celldiff_r2__joint__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-zikv + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/memb_celldiff_r2_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/celldiff_r2/caax_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_celldiff_r2_jointtrained_membrane_zikv +- name: vscyto3d__joint__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-zikv + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/memb_fcmae_vscyto3d_pretrained_jointtrained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fcmae_vscyto3d_pretrained/caax_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_vscyto3d_jointtrained_membrane_zikv +- name: unext2__joint__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-zikv + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/memb_fcmae_vscyto3d_scratch_jointtrained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fcmae_vscyto3d_scratch/caax_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_unext2_jointtrained_membrane_zikv +- name: fnet3d__joint__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-zikv + target: caax + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/memb_fnet3d_paper_jointtrained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fnet3d_paper/caax_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_fnet3d_jointtrained_membrane_zikv +- name: celldiff_r2__joint__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: membrane + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions/memb_celldiff_r2.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/joint/celldiff_r2/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_jointtrained_with_embeddings/eval_celldiff_r2_jointtrained_membrane +- name: vscyto3d__joint__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: membrane + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions/memb_fcmae_vscyto3d_pretrained_jointtrained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/joint/fcmae_vscyto3d_pretrained/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_jointtrained_with_embeddings/eval_vscyto3d_jointtrained_membrane +- name: unext2__joint__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: membrane + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions/memb_fcmae_vscyto3d_scratch_jointtrained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/joint/fcmae_vscyto3d_scratch/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_jointtrained_with_embeddings/eval_unext2_jointtrained_membrane +- name: fnet3d__joint__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: membrane + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions/memb_fnet3d_paper_jointtrained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/joint/fnet3d_paper/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_jointtrained_with_embeddings/eval_fnet3d_jointtrained_membrane diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/mitochondria_a549_trained/eval_grouped.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/mitochondria_a549_trained/eval_grouped.yaml new file mode 100644 index 000000000..a7e37502c --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/mitochondria_a549_trained/eval_grouped.yaml @@ -0,0 +1,174 @@ +# @package _global_ +# Grouped leaf: mitochondria bucket, a549_trained models (16 conditions). Auto-generated by tools/generate_grouped_eval_configs.py. +target_name: mitochondria +compute_feature_metrics: true +use_gpu: true +io: + require_complete_cache: false +runtime: + executor: serial + fov_workers: 1 + threads_per_worker: auto +force_recompute: + final_metrics: true +conditions: +- name: celldiff_r2__a549_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-denv + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_celldiff_r2_a549trained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/celldiff_r2/tomm20_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_celldiff_r2_a549trained_mitochondria_denv +- name: vscyto3d__a549_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-denv + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_pretrained_a549trained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fcmae_vscyto3d_pretrained/tomm20_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_vscyto3d_a549trained_mitochondria_denv +- name: unext2__a549_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-denv + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_scratch_a549trained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fcmae_vscyto3d_scratch/tomm20_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_unext2_a549trained_mitochondria_denv +- name: fnet3d__a549_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-denv + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fnet3d_paper_a549trained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fnet3d_paper/tomm20_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_fnet3d_a549trained_mitochondria_denv +- name: celldiff_r2__a549_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-mock + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_celldiff_r2_a549trained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/celldiff_r2/tomm20_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_celldiff_r2_a549trained_mitochondria_mock +- name: vscyto3d__a549_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-mock + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_pretrained_a549trained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fcmae_vscyto3d_pretrained/tomm20_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_vscyto3d_a549trained_mitochondria_mock +- name: unext2__a549_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-mock + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_scratch_a549trained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fcmae_vscyto3d_scratch/tomm20_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_unext2_a549trained_mitochondria_mock +- name: fnet3d__a549_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-mock + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fnet3d_paper_a549trained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fnet3d_paper/tomm20_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_fnet3d_a549trained_mitochondria_mock +- name: celldiff_r2__a549_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-zikv + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_celldiff_r2_a549trained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/celldiff_r2/tomm20_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_celldiff_r2_a549trained_mitochondria_zikv +- name: vscyto3d__a549_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-zikv + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_pretrained_a549trained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fcmae_vscyto3d_pretrained/tomm20_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_vscyto3d_a549trained_mitochondria_zikv +- name: unext2__a549_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-zikv + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_scratch_a549trained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fcmae_vscyto3d_scratch/tomm20_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_unext2_a549trained_mitochondria_zikv +- name: fnet3d__a549_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-zikv + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fnet3d_paper_a549trained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fnet3d_paper/tomm20_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_fnet3d_a549trained_mitochondria_zikv +- name: celldiff_r2__a549_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_celldiff_r2_a549trained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/a549_trained/celldiff_r2/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_a549trained_with_embeddings/eval_celldiff_r2_a549trained_mitochondria +- name: vscyto3d__a549_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_fcmae_vscyto3d_pretrained_a549trained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/a549_trained/fcmae_vscyto3d_pretrained/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_a549trained_with_embeddings/eval_vscyto3d_a549trained_mitochondria +- name: unext2__a549_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_fcmae_vscyto3d_scratch_a549trained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/a549_trained/fcmae_vscyto3d_scratch/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_a549trained_with_embeddings/eval_unext2_a549trained_mitochondria +- name: fnet3d__a549_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_fnet3d_paper_a549trained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/a549_trained/fnet3d_paper/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_a549trained_with_embeddings/eval_fnet3d_a549trained_mitochondria diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/mitochondria_celldiff_r2_a549trained/eval_grouped.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/mitochondria_celldiff_r2_a549trained/eval_grouped.yaml new file mode 100644 index 000000000..a6d8b3e84 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/mitochondria_celldiff_r2_a549trained/eval_grouped.yaml @@ -0,0 +1,25 @@ +# @package _global_ +# Grouped leaf: mitochondria bucket, celldiff_r2 a549-trained model (1 condition available now). +# Only iPSC test. A549 tomm20 celldiff_r2_a549trained predictions still in-flight — handled by mitochondria_celldiff_r2_a549trained_later. +target_name: mitochondria +compute_feature_metrics: true +use_gpu: true +io: + require_complete_cache: false +runtime: + executor: serial + fov_workers: 1 + threads_per_worker: auto +force_recompute: + final_metrics: true +conditions: +- name: celldiff_r2_a549trained__a549_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_celldiff_r2_a549trained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/a549_trained/celldiff_r2_a549trained/mito_ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_a549trained_with_embeddings/eval_celldiff_r2_a549trained_mitochondria diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/mitochondria_ipsc_trained/eval_grouped.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/mitochondria_ipsc_trained/eval_grouped.yaml new file mode 100644 index 000000000..58bbdb9fc --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/mitochondria_ipsc_trained/eval_grouped.yaml @@ -0,0 +1,234 @@ +# @package _global_ +# Grouped leaf: mitochondria bucket, ipsc_trained models (22 conditions). Auto-generated by tools/generate_grouped_eval_configs.py. +target_name: mitochondria +compute_feature_metrics: true +use_gpu: true +io: + require_complete_cache: false +runtime: + executor: serial + fov_workers: 1 + threads_per_worker: auto +force_recompute: + final_metrics: true +conditions: +- name: celldiff_r2_iterative__ipsc_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-denv + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_celldiff_r2_iterative__tomm20_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/celldiff_r2_iterative/tomm20_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_celldiff_r2_iterative_mitochondria_denv +- name: vscyto3d__ipsc_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-denv + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_pretrained__tomm20_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_pretrained/tomm20_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_vscyto3d_mitochondria_denv +- name: unext2__ipsc_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-denv + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_scratch__tomm20_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_scratch/tomm20_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_unext2_mitochondria_denv +- name: fnet3d__ipsc_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-denv + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fnet3d_paper__tomm20_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fnet3d_paper/tomm20_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_fnet3d_mitochondria_denv +- name: unetvit3d__ipsc_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-denv + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_unetvit3d__tomm20_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/unetvit3d/tomm20_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_unetvit3d_mitochondria_denv +- name: celldiff_r2_iterative__ipsc_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-mock + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_celldiff_r2_iterative__tomm20_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/celldiff_r2_iterative/tomm20_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_celldiff_r2_iterative_mitochondria_mock +- name: vscyto3d__ipsc_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-mock + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_pretrained__tomm20_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_pretrained/tomm20_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_vscyto3d_mitochondria_mock +- name: unext2__ipsc_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-mock + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_scratch__tomm20_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_scratch/tomm20_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_unext2_mitochondria_mock +- name: fnet3d__ipsc_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-mock + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fnet3d_paper__tomm20_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fnet3d_paper/tomm20_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_fnet3d_mitochondria_mock +- name: unetvit3d__ipsc_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-mock + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_unetvit3d__tomm20_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/unetvit3d/tomm20_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_unetvit3d_mitochondria_mock +- name: celldiff_r2_iterative__ipsc_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-zikv + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_celldiff_r2_iterative__tomm20_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/celldiff_r2_iterative/tomm20_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_celldiff_r2_iterative_mitochondria_zikv +- name: vscyto3d__ipsc_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-zikv + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_pretrained__tomm20_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_pretrained/tomm20_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_vscyto3d_mitochondria_zikv +- name: unext2__ipsc_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-zikv + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_scratch__tomm20_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_scratch/tomm20_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_unext2_mitochondria_zikv +- name: fnet3d__ipsc_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-zikv + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fnet3d_paper__tomm20_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fnet3d_paper/tomm20_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_fnet3d_mitochondria_zikv +- name: unetvit3d__ipsc_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-zikv + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_unetvit3d__tomm20_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/unetvit3d/tomm20_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_unetvit3d_mitochondria_zikv +- name: celldiff_r2_denoise__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_celldiff_r2_denoise.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/ipsc_trained/celldiff_r2_denoise/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_with_embeddings/eval_celldiff_r2_denoise_mitochondria +- name: celldiff_r2_iterative__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_celldiff_r2_iterative.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/ipsc_trained/celldiff_r2_iterative/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_with_embeddings/eval_celldiff_r2_iterative_mitochondria +- name: celldiff_r2_sliding_window__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_celldiff_r2_sliding_window.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/ipsc_trained/celldiff_r2_sliding_window/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_with_embeddings/eval_celldiff_r2_sliding_window_mitochondria +- name: vscyto3d__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_fcmae_vscyto3d_pretrained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_pretrained/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_with_embeddings/eval_vscyto3d_mitochondria +- name: unext2__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_fcmae_vscyto3d_scratch.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_scratch/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_with_embeddings/eval_unext2_mitochondria +- name: fnet3d__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_fnet3d_paper.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/ipsc_trained/fnet3d_paper/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_with_embeddings/eval_fnet3d_mitochondria +- name: unetvit3d__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_unetvit3d.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/ipsc_trained/unetvit3d/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_with_embeddings/eval_unetvit3d_mitochondria diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/mitochondria_joint/eval_grouped.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/mitochondria_joint/eval_grouped.yaml new file mode 100644 index 000000000..8c019fbc2 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/mitochondria_joint/eval_grouped.yaml @@ -0,0 +1,174 @@ +# @package _global_ +# Grouped leaf: mitochondria bucket, joint models (16 conditions). Auto-generated by tools/generate_grouped_eval_configs.py. +target_name: mitochondria +compute_feature_metrics: true +use_gpu: true +io: + require_complete_cache: false +runtime: + executor: serial + fov_workers: 1 + threads_per_worker: auto +force_recompute: + final_metrics: true +conditions: +- name: celldiff_r2__joint__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-denv + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/tomm20_celldiff_r2_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/celldiff_r2/tomm20_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_celldiff_r2_jointtrained_mitochondria_denv +- name: vscyto3d__joint__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-denv + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_pretrained_jointtrained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fcmae_vscyto3d_pretrained/tomm20_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_vscyto3d_jointtrained_mitochondria_denv +- name: unext2__joint__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-denv + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_scratch_jointtrained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fcmae_vscyto3d_scratch/tomm20_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_unext2_jointtrained_mitochondria_denv +- name: fnet3d__joint__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-denv + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fnet3d_paper_jointtrained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fnet3d_paper/tomm20_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_fnet3d_jointtrained_mitochondria_denv +- name: celldiff_r2__joint__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-mock + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/tomm20_celldiff_r2_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/celldiff_r2/tomm20_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_celldiff_r2_jointtrained_mitochondria_mock +- name: vscyto3d__joint__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-mock + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_pretrained_jointtrained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fcmae_vscyto3d_pretrained/tomm20_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_vscyto3d_jointtrained_mitochondria_mock +- name: unext2__joint__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-mock + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_scratch_jointtrained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fcmae_vscyto3d_scratch/tomm20_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_unext2_jointtrained_mitochondria_mock +- name: fnet3d__joint__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-mock + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fnet3d_paper_jointtrained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fnet3d_paper/tomm20_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_fnet3d_jointtrained_mitochondria_mock +- name: celldiff_r2__joint__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-zikv + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/tomm20_celldiff_r2_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/celldiff_r2/tomm20_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_celldiff_r2_jointtrained_mitochondria_zikv +- name: vscyto3d__joint__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-zikv + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_pretrained_jointtrained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fcmae_vscyto3d_pretrained/tomm20_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_vscyto3d_jointtrained_mitochondria_zikv +- name: unext2__joint__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-zikv + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_scratch_jointtrained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fcmae_vscyto3d_scratch/tomm20_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_unext2_jointtrained_mitochondria_zikv +- name: fnet3d__joint__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-zikv + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fnet3d_paper_jointtrained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fnet3d_paper/tomm20_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_fnet3d_jointtrained_mitochondria_zikv +- name: celldiff_r2__joint__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions/tomm20_celldiff_r2.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/joint/celldiff_r2/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_jointtrained_with_embeddings/eval_celldiff_r2_jointtrained_mitochondria +- name: vscyto3d__joint__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_fcmae_vscyto3d_pretrained_jointtrained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/joint/fcmae_vscyto3d_pretrained/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_jointtrained_with_embeddings/eval_vscyto3d_jointtrained_mitochondria +- name: unext2__joint__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_fcmae_vscyto3d_scratch_jointtrained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/joint/fcmae_vscyto3d_scratch/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_jointtrained_with_embeddings/eval_unext2_jointtrained_mitochondria +- name: fnet3d__joint__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: tomm20 + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_fnet3d_paper_jointtrained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/joint/fnet3d_paper/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_jointtrained_with_embeddings/eval_fnet3d_jointtrained_mitochondria diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/nucleus_a549_trained/eval_grouped.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/nucleus_a549_trained/eval_grouped.yaml new file mode 100644 index 000000000..9635499d2 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/nucleus_a549_trained/eval_grouped.yaml @@ -0,0 +1,174 @@ +# @package _global_ +# Grouped leaf: nucleus bucket, a549_trained models (16 conditions). Auto-generated by tools/generate_grouped_eval_configs.py. +target_name: nucleus +compute_feature_metrics: true +use_gpu: true +io: + require_complete_cache: false +runtime: + executor: serial + fov_workers: 1 + threads_per_worker: auto +force_recompute: + final_metrics: true +conditions: +- name: celldiff_r2__a549_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-denv + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_celldiff_r2_a549trained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/celldiff_r2/h2b_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_celldiff_r2_a549trained_nucleus_denv +- name: vscyto3d__a549_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-denv + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_pretrained_a549trained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fcmae_vscyto3d_pretrained/h2b_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_vscyto3d_a549trained_nucleus_denv +- name: unext2__a549_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-denv + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_scratch_a549trained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fcmae_vscyto3d_scratch/h2b_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_unext2_a549trained_nucleus_denv +- name: fnet3d__a549_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-denv + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fnet3d_paper_a549trained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fnet3d_paper/h2b_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_fnet3d_a549trained_nucleus_denv +- name: celldiff_r2__a549_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-mock + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_celldiff_r2_a549trained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/celldiff_r2/h2b_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_celldiff_r2_a549trained_nucleus_mock +- name: vscyto3d__a549_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-mock + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_pretrained_a549trained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fcmae_vscyto3d_pretrained/h2b_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_vscyto3d_a549trained_nucleus_mock +- name: unext2__a549_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-mock + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_scratch_a549trained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fcmae_vscyto3d_scratch/h2b_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_unext2_a549trained_nucleus_mock +- name: fnet3d__a549_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-mock + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fnet3d_paper_a549trained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fnet3d_paper/h2b_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_fnet3d_a549trained_nucleus_mock +- name: celldiff_r2__a549_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-zikv + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_celldiff_r2_a549trained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/celldiff_r2/h2b_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_celldiff_r2_a549trained_nucleus_zikv +- name: vscyto3d__a549_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-zikv + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_pretrained_a549trained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fcmae_vscyto3d_pretrained/h2b_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_vscyto3d_a549trained_nucleus_zikv +- name: unext2__a549_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-zikv + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_scratch_a549trained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fcmae_vscyto3d_scratch/h2b_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_unext2_a549trained_nucleus_zikv +- name: fnet3d__a549_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-zikv + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fnet3d_paper_a549trained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/a549_trained/fnet3d_paper/h2b_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained_with_embeddings/eval_fnet3d_a549trained_nucleus_zikv +- name: celldiff_r2__a549_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: nucleus + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_celldiff_r2_a549trained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/a549_trained/celldiff_r2/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_a549trained_with_embeddings/eval_celldiff_r2_a549trained_nucleus +- name: vscyto3d__a549_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: nucleus + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_fcmae_vscyto3d_pretrained_a549trained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/a549_trained/fcmae_vscyto3d_pretrained/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_a549trained_with_embeddings/eval_vscyto3d_a549trained_nucleus +- name: unext2__a549_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: nucleus + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_fcmae_vscyto3d_scratch_a549trained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/a549_trained/fcmae_vscyto3d_scratch/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_a549trained_with_embeddings/eval_unext2_a549trained_nucleus +- name: fnet3d__a549_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: nucleus + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_fnet3d_paper_a549trained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/a549_trained/fnet3d_paper/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_a549trained_with_embeddings/eval_fnet3d_a549trained_nucleus diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/nucleus_celldiff_r2_a549trained/eval_grouped.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/nucleus_celldiff_r2_a549trained/eval_grouped.yaml new file mode 100644 index 000000000..6e2daf2a2 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/nucleus_celldiff_r2_a549trained/eval_grouped.yaml @@ -0,0 +1,25 @@ +# @package _global_ +# Grouped leaf: nucleus bucket, celldiff_r2 a549-trained model (1 condition available now). +# Only iPSC test. A549 nucleus celldiff_r2_a549trained predictions still in-flight — handled by nucleus_celldiff_r2_a549trained_later. +target_name: nucleus +compute_feature_metrics: true +use_gpu: true +io: + require_complete_cache: false +runtime: + executor: serial + fov_workers: 1 + threads_per_worker: auto +force_recompute: + final_metrics: true +conditions: +- name: celldiff_r2_a549trained__a549_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: nucleus + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_celldiff_r2_a549trained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/a549_trained/celldiff_r2_a549trained/nucl_ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_a549trained_with_embeddings/eval_celldiff_r2_a549trained_nucleus diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/nucleus_ipsc_trained/eval_grouped.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/nucleus_ipsc_trained/eval_grouped.yaml new file mode 100644 index 000000000..1c9717212 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/nucleus_ipsc_trained/eval_grouped.yaml @@ -0,0 +1,234 @@ +# @package _global_ +# Grouped leaf: nucleus bucket, ipsc_trained models (22 conditions). Auto-generated by tools/generate_grouped_eval_configs.py. +target_name: nucleus +compute_feature_metrics: true +use_gpu: true +io: + require_complete_cache: false +runtime: + executor: serial + fov_workers: 1 + threads_per_worker: auto +force_recompute: + final_metrics: true +conditions: +- name: celldiff_r2_iterative__ipsc_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-denv + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_celldiff_r2_iterative_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/celldiff_r2_iterative/h2b_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_celldiff_r2_iterative_nucleus_denv +- name: vscyto3d__ipsc_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-denv + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_pretrained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_pretrained/h2b_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_vscyto3d_nucleus_denv +- name: unext2__ipsc_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-denv + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_scratch_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_scratch/h2b_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_unext2_nucleus_denv +- name: fnet3d__ipsc_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-denv + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fnet3d_paper_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fnet3d_paper/h2b_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_fnet3d_nucleus_denv +- name: unetvit3d__ipsc_trained__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-denv + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucleus_unetvit3d_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/unetvit3d/h2b_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_unetvit3d_nucleus_denv +- name: celldiff_r2_iterative__ipsc_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-mock + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_celldiff_r2_iterative_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/celldiff_r2_iterative/h2b_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_celldiff_r2_iterative_nucleus_mock +- name: vscyto3d__ipsc_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-mock + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_pretrained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_pretrained/h2b_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_vscyto3d_nucleus_mock +- name: unext2__ipsc_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-mock + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_scratch_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_scratch/h2b_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_unext2_nucleus_mock +- name: fnet3d__ipsc_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-mock + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fnet3d_paper_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fnet3d_paper/h2b_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_fnet3d_nucleus_mock +- name: unetvit3d__ipsc_trained__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-mock + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucleus_unetvit3d_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/unetvit3d/h2b_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_unetvit3d_nucleus_mock +- name: celldiff_r2_iterative__ipsc_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-zikv + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_celldiff_r2_iterative_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/celldiff_r2_iterative/h2b_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_celldiff_r2_iterative_nucleus_zikv +- name: vscyto3d__ipsc_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-zikv + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_pretrained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_pretrained/h2b_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_vscyto3d_nucleus_zikv +- name: unext2__ipsc_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-zikv + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_scratch_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_scratch/h2b_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_unext2_nucleus_zikv +- name: fnet3d__ipsc_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-zikv + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fnet3d_paper_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/fnet3d_paper/h2b_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_fnet3d_nucleus_zikv +- name: unetvit3d__ipsc_trained__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-zikv + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucleus_unetvit3d_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/ipsc_trained/unetvit3d/h2b_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings/eval_unetvit3d_nucleus_zikv +- name: celldiff_r2_denoise__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: nucleus + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_celldiff_r2_denoise.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/ipsc_trained/celldiff_r2_denoise/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_with_embeddings/eval_celldiff_r2_denoise_nucleus +- name: celldiff_r2_iterative__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: nucleus + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_celldiff_r2_iterative.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/ipsc_trained/celldiff_r2_iterative/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_with_embeddings/eval_celldiff_r2_iterative_nucleus +- name: celldiff_r2_sliding_window__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: nucleus + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_celldiff_r2_sliding_window.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/ipsc_trained/celldiff_r2_sliding_window/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_with_embeddings/eval_celldiff_r2_sliding_window_nucleus +- name: vscyto3d__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: nucleus + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_fcmae_vscyto3d_pretrained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_pretrained/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_with_embeddings/eval_vscyto3d_nucleus +- name: unext2__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: nucleus + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_fcmae_vscyto3d_scratch.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/ipsc_trained/fcmae_vscyto3d_scratch/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_with_embeddings/eval_unext2_nucleus +- name: fnet3d__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: nucleus + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_fnet3d_paper.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/ipsc_trained/fnet3d_paper/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_with_embeddings/eval_fnet3d_nucleus +- name: unetvit3d__ipsc_trained__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: nucleus + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_unetvit3d.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/ipsc_trained/unetvit3d/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_with_embeddings/eval_unetvit3d_nucleus diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/nucleus_joint/eval_grouped.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/nucleus_joint/eval_grouped.yaml new file mode 100644 index 000000000..292d0383e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/grouped/nucleus_joint/eval_grouped.yaml @@ -0,0 +1,134 @@ +# @package _global_ +# Grouped leaf: nucleus bucket, joint models (12 conditions). Auto-generated by tools/generate_grouped_eval_configs.py. +target_name: nucleus +compute_feature_metrics: true +use_gpu: true +io: + require_complete_cache: false +runtime: + executor: serial + fov_workers: 1 + threads_per_worker: auto +force_recompute: + final_metrics: true +conditions: +- name: celldiff_r2__joint__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-denv + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/nucl_celldiff_r2_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/celldiff_r2/h2b_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_celldiff_r2_jointtrained_nucleus_denv +- name: unext2__joint__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-denv + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/nucl_fcmae_vscyto3d_scratch_jointtrained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fcmae_vscyto3d_scratch/h2b_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_unext2_jointtrained_nucleus_denv +- name: fnet3d__joint__a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-denv + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/nucl_fnet3d_paper_jointtrained_denv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fnet3d_paper/h2b_denv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_fnet3d_jointtrained_nucleus_denv +- name: celldiff_r2__joint__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-mock + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/nucl_celldiff_r2_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/celldiff_r2/h2b_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_celldiff_r2_jointtrained_nucleus_mock +- name: unext2__joint__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-mock + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/nucl_fcmae_vscyto3d_scratch_jointtrained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fcmae_vscyto3d_scratch/h2b_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_unext2_jointtrained_nucleus_mock +- name: fnet3d__joint__a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-mock + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/nucl_fnet3d_paper_jointtrained_mock.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fnet3d_paper/h2b_mock + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_fnet3d_jointtrained_nucleus_mock +- name: celldiff_r2__joint__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-zikv + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/nucl_celldiff_r2_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/celldiff_r2/h2b_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_celldiff_r2_jointtrained_nucleus_zikv +- name: unext2__joint__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-zikv + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/nucl_fcmae_vscyto3d_scratch_jointtrained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fcmae_vscyto3d_scratch/h2b_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_unext2_jointtrained_nucleus_zikv +- name: fnet3d__joint__a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-zikv + target: h2b + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/nucl_fnet3d_paper_jointtrained_zikv.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache_pred/joint/fnet3d_paper/h2b_zikv + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_jointtrained_with_embeddings/eval_fnet3d_jointtrained_nucleus_zikv +- name: celldiff_r2__joint__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: nucleus + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions/nucl_celldiff_r2.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/joint/celldiff_r2/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_jointtrained_with_embeddings/eval_celldiff_r2_jointtrained_nucleus +- name: unext2__joint__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: nucleus + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions/nucl_fcmae_vscyto3d_scratch_jointtrained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/joint/fcmae_vscyto3d_scratch/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_jointtrained_with_embeddings/eval_unext2_jointtrained_nucleus +- name: fnet3d__joint__ipsc + benchmark: + dataset_ref: + dataset: aics-hipsc + target: nucleus + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions/nucl_fnet3d_paper_jointtrained.zarr + pred_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache_pred/joint/fnet3d_paper/ipsc + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_jointtrained_with_embeddings/eval_fnet3d_jointtrained_nucleus diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/celldiff/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/celldiff/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 120000 index 000000000..f5fd532e1 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/celldiff/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1 @@ +../../../../../membrane/celldiff/ipsc_confocal/eval__a549_mantis_denv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/celldiff/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/celldiff/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 120000 index 000000000..2353e2df1 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/celldiff/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1 @@ +../../../../../membrane/celldiff/ipsc_confocal/eval__a549_mantis_mock.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/celldiff/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/celldiff/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 120000 index 000000000..bee63297a --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/celldiff/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1 @@ +../../../../../membrane/celldiff/ipsc_confocal/eval__a549_mantis_zikv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/celldiff/ipsc_confocal/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/celldiff/ipsc_confocal/eval__ipsc_confocal.yaml new file mode 120000 index 000000000..8ac201261 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/celldiff/ipsc_confocal/eval__ipsc_confocal.yaml @@ -0,0 +1 @@ +../../../../../membrane/celldiff/ipsc_confocal/eval__ipsc_confocal.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 120000 index 000000000..ec7df7fac --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1 @@ +../../../../../membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_denv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 120000 index 000000000..4a18b3957 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1 @@ +../../../../../membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_mock.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 120000 index 000000000..55ad06bc1 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1 @@ +../../../../../membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_zikv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_pretrained_cytoland/cytoland/eval__a549_mantis.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_pretrained_cytoland/cytoland/eval__a549_mantis.yaml new file mode 100644 index 000000000..a85fcb805 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_pretrained_cytoland/cytoland/eval__a549_mantis.yaml @@ -0,0 +1,40 @@ +# @package _global_ +# Track B (Cytoland, no FT) grouped eval for membrane channel of the +# dual predict zarr on A549 (mock + denv + zikv in one process). +defaults: + - override /target: membrane + - override /predict_set: a549_mantis_caax_mock + +# Base anchors to the {gene}-{first_cond} manifest; each condition overlay below +# swaps dataset, pred_path, and save_dir for mock / denv / zikv in one process. +benchmark: + dataset_ref: + target: caax + +compute_feature_metrics: true + +conditions: + - name: a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-caax-mock + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_fcmae_vscyto3d_pretrained_cytoland_mock.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_cytoland/eval_vscyto3d_cytoland_membrane_mock + - name: a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-denv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_fcmae_vscyto3d_pretrained_cytoland_denv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_cytoland/eval_vscyto3d_cytoland_membrane_denv + - name: a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-zikv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_fcmae_vscyto3d_pretrained_cytoland_zikv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_cytoland/eval_vscyto3d_cytoland_membrane_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_pretrained_cytoland/cytoland/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_pretrained_cytoland/cytoland/eval__ipsc_confocal.yaml new file mode 100644 index 000000000..d0c5e6663 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_pretrained_cytoland/cytoland/eval__ipsc_confocal.yaml @@ -0,0 +1,15 @@ +# @package _global_ +# Track B (Cytoland, no FT) eval for membrane channel of the dual +# predict zarr on iPSC. _ref_hook derives pred_channel_name from dataset_ref.target +# so this leaf reads the Membrane_prediction channel from the dual zarr. +defaults: + - override /target: membrane + - override /predict_set: ipsc_confocal + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/dual_nucl_memb_fcmae_vscyto3d_pretrained_cytoland.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_cytoland/eval_vscyto3d_cytoland_membrane diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_pretrained_infectionft/infectionft/eval__a549_mantis.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_pretrained_infectionft/infectionft/eval__a549_mantis.yaml new file mode 100644 index 000000000..979a30305 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_pretrained_infectionft/infectionft/eval__a549_mantis.yaml @@ -0,0 +1,40 @@ +# @package _global_ +# Track B (InfectionFT, no FT) grouped eval for membrane channel of the +# dual predict zarr on A549 (mock + denv + zikv in one process). +defaults: + - override /target: membrane + - override /predict_set: a549_mantis_caax_mock + +# Base anchors to the {gene}-{first_cond} manifest; each condition overlay below +# swaps dataset, pred_path, and save_dir for mock / denv / zikv in one process. +benchmark: + dataset_ref: + target: caax + +compute_feature_metrics: true + +conditions: + - name: a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-caax-mock + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_fcmae_vscyto3d_pretrained_infectionft_mock.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_infectionft/eval_vscyto3d_infectionft_membrane_mock + - name: a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-denv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_fcmae_vscyto3d_pretrained_infectionft_denv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_infectionft/eval_vscyto3d_infectionft_membrane_denv + - name: a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-zikv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_fcmae_vscyto3d_pretrained_infectionft_zikv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_infectionft/eval_vscyto3d_infectionft_membrane_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_pretrained_infectionft/infectionft/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_pretrained_infectionft/infectionft/eval__ipsc_confocal.yaml new file mode 100644 index 000000000..bb940e541 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_pretrained_infectionft/infectionft/eval__ipsc_confocal.yaml @@ -0,0 +1,15 @@ +# @package _global_ +# Track B (InfectionFT, no FT) eval for membrane channel of the dual +# predict zarr on iPSC. _ref_hook derives pred_channel_name from dataset_ref.target +# so this leaf reads the Membrane_prediction channel from the dual zarr. +defaults: + - override /target: membrane + - override /predict_set: ipsc_confocal + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/dual_nucl_memb_fcmae_vscyto3d_pretrained_infectionft.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_infectionft/eval_vscyto3d_infectionft_membrane diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_pretrained_randinit/randinit/eval__a549_mantis.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_pretrained_randinit/randinit/eval__a549_mantis.yaml new file mode 100644 index 000000000..2b914cad8 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_pretrained_randinit/randinit/eval__a549_mantis.yaml @@ -0,0 +1,41 @@ +# @package _global_ +# Track A (random init, no training) grouped eval for membrane on A549. +# One process covers mock + denv + zikv via the conditions list, amortizing the +# DINOv3 + DynaCLR + CELL-DINO load. Reads the matching frozen-randinit zarrs. +defaults: + - override /target: membrane + - override /predict_set: a549_mantis_caax_mock + +# Base anchors to the {gene}-{first_cond} manifest; each condition overlay below +# swaps dataset, pred_path, and save_dir for mock / denv / zikv in one process. +benchmark: + dataset_ref: + target: caax + +compute_feature_metrics: true + +conditions: + - name: a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-caax-mock + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_pretrained_randinit_mock.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_randinit/eval_vscyto3d_randinit_membrane_mock + - name: a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-denv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_pretrained_randinit_denv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_randinit/eval_vscyto3d_randinit_membrane_denv + - name: a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-zikv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_pretrained_randinit_zikv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_randinit/eval_vscyto3d_randinit_membrane_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_pretrained_randinit/randinit/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_pretrained_randinit/randinit/eval__ipsc_confocal.yaml new file mode 100644 index 000000000..9da30a8b9 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_pretrained_randinit/randinit/eval__ipsc_confocal.yaml @@ -0,0 +1,15 @@ +# @package _global_ +# Track A (random init, no training) eval for membrane on iPSC. +# Reads the frozen-randinit predict zarr produced by +# /fcmae_vscyto3d_pretrained/_no_train_randinit/predict__ipsc_confocal.yml. +defaults: + - override /target: membrane + - override /predict_set: ipsc_confocal + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_fcmae_vscyto3d_pretrained_randinit.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_randinit/eval_vscyto3d_randinit_membrane diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 120000 index 000000000..d825510f9 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1 @@ +../../../../../membrane/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_denv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 120000 index 000000000..d66c60b89 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1 @@ +../../../../../membrane/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_mock.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 120000 index 000000000..b23a90c58 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1 @@ +../../../../../membrane/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_zikv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fnet3d_paper/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fnet3d_paper/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 120000 index 000000000..12895ce10 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fnet3d_paper/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1 @@ +../../../../../membrane/fnet3d_paper/ipsc_confocal/eval__a549_mantis_denv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fnet3d_paper/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fnet3d_paper/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 120000 index 000000000..495ec10aa --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fnet3d_paper/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1 @@ +../../../../../membrane/fnet3d_paper/ipsc_confocal/eval__a549_mantis_mock.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fnet3d_paper/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fnet3d_paper/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 120000 index 000000000..a05677367 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/fnet3d_paper/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1 @@ +../../../../../membrane/fnet3d_paper/ipsc_confocal/eval__a549_mantis_zikv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/unetvit3d/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/unetvit3d/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 120000 index 000000000..69af3ff0d --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/unetvit3d/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1 @@ +../../../../../membrane/unetvit3d/ipsc_confocal/eval__a549_mantis_denv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/unetvit3d/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/unetvit3d/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 120000 index 000000000..1c891950f --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/unetvit3d/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1 @@ +../../../../../membrane/unetvit3d/ipsc_confocal/eval__a549_mantis_mock.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/unetvit3d/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/unetvit3d/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 120000 index 000000000..b5384f051 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/unetvit3d/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1 @@ +../../../../../membrane/unetvit3d/ipsc_confocal/eval__a549_mantis_zikv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/unetvit3d/ipsc_confocal/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/unetvit3d/ipsc_confocal/eval__ipsc_confocal.yaml new file mode 120000 index 000000000..b6113f5d2 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/unetvit3d/ipsc_confocal/eval__ipsc_confocal.yaml @@ -0,0 +1 @@ +../../../../../membrane/unetvit3d/ipsc_confocal/eval__ipsc_confocal.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/vscyto3d_cytolandft/a549_mantis/eval__a549_mantis.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/vscyto3d_cytolandft/a549_mantis/eval__a549_mantis.yaml new file mode 100644 index 000000000..68e56a721 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/vscyto3d_cytolandft/a549_mantis/eval__a549_mantis.yaml @@ -0,0 +1,40 @@ +# @package _global_ +# Track C (vscyto3d_cytolandft trained on a549_mantis) grouped eval for membrane +# channel of the dual predict zarr on A549 (mock + denv + zikv in one process). +defaults: + - override /target: membrane + - override /predict_set: a549_mantis_caax_mock + +# Base anchors to the {gene}-{first_cond} manifest; each condition overlay below +# swaps dataset, pred_path, and save_dir for mock / denv / zikv in one process. +benchmark: + dataset_ref: + target: caax + +compute_feature_metrics: true + +conditions: + - name: a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-caax-mock + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_cytolandft_a549trained_mock.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_cytolandft_a549trained/eval_vscyto3d_cytolandft_a549trained_membrane_mock + - name: a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-denv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_cytolandft_a549trained_denv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_cytolandft_a549trained/eval_vscyto3d_cytolandft_a549trained_membrane_denv + - name: a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-zikv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_cytolandft_a549trained_zikv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_cytolandft_a549trained/eval_vscyto3d_cytolandft_a549trained_membrane_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/vscyto3d_cytolandft/a549_mantis/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/vscyto3d_cytolandft/a549_mantis/eval__ipsc_confocal.yaml new file mode 100644 index 000000000..34a348525 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/vscyto3d_cytolandft/a549_mantis/eval__ipsc_confocal.yaml @@ -0,0 +1,14 @@ +# @package _global_ +# Track C (vscyto3d_cytolandft trained on a549_mantis) eval for membrane channel +# of the dual predict zarr on iPSC. +defaults: + - override /target: membrane + - override /predict_set: ipsc_confocal + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/dual_nucl_memb_vscyto3d_cytolandft_a549trained.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_cytolandft_a549trained/eval_vscyto3d_cytolandft_a549trained_membrane diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/vscyto3d_cytolandft/ipsc_confocal/eval__a549_mantis.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/vscyto3d_cytolandft/ipsc_confocal/eval__a549_mantis.yaml new file mode 100644 index 000000000..733ac832e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/vscyto3d_cytolandft/ipsc_confocal/eval__a549_mantis.yaml @@ -0,0 +1,40 @@ +# @package _global_ +# Track C (vscyto3d_cytolandft trained on ipsc_confocal) grouped eval for membrane +# channel of the dual predict zarr on A549 (mock + denv + zikv in one process). +defaults: + - override /target: membrane + - override /predict_set: a549_mantis_caax_mock + +# Base anchors to the {gene}-{first_cond} manifest; each condition overlay below +# swaps dataset, pred_path, and save_dir for mock / denv / zikv in one process. +benchmark: + dataset_ref: + target: caax + +compute_feature_metrics: true + +conditions: + - name: a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-caax-mock + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_cytolandft_mock.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_cytolandft/eval_vscyto3d_cytolandft_membrane_mock + - name: a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-denv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_cytolandft_denv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_cytolandft/eval_vscyto3d_cytolandft_membrane_denv + - name: a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-zikv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_cytolandft_zikv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_cytolandft/eval_vscyto3d_cytolandft_membrane_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/vscyto3d_cytolandft/ipsc_confocal/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/vscyto3d_cytolandft/ipsc_confocal/eval__ipsc_confocal.yaml new file mode 100644 index 000000000..9facbec1a --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/vscyto3d_cytolandft/ipsc_confocal/eval__ipsc_confocal.yaml @@ -0,0 +1,14 @@ +# @package _global_ +# Track C (vscyto3d_cytolandft trained on ipsc_confocal) eval for membrane channel +# of the dual predict zarr on iPSC. +defaults: + - override /target: membrane + - override /predict_set: ipsc_confocal + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/dual_nucl_memb_vscyto3d_cytolandft.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_cytolandft/eval_vscyto3d_cytolandft_membrane diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/vscyto3d_infectionft_dynacellft/a549_mantis/eval__a549_mantis.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/vscyto3d_infectionft_dynacellft/a549_mantis/eval__a549_mantis.yaml new file mode 100644 index 000000000..f1fa3b703 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/vscyto3d_infectionft_dynacellft/a549_mantis/eval__a549_mantis.yaml @@ -0,0 +1,40 @@ +# @package _global_ +# Track C (vscyto3d_infectionft_dynacellft trained on a549_mantis) grouped eval for membrane +# channel of the dual predict zarr on A549 (mock + denv + zikv in one process). +defaults: + - override /target: membrane + - override /predict_set: a549_mantis_caax_mock + +# Base anchors to the {gene}-{first_cond} manifest; each condition overlay below +# swaps dataset, pred_path, and save_dir for mock / denv / zikv in one process. +benchmark: + dataset_ref: + target: caax + +compute_feature_metrics: true + +conditions: + - name: a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-caax-mock + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_infectionft_dynacellft_a549trained_mock.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_infectionft_dynacellft_a549trained/eval_vscyto3d_infectionft_dynacellft_a549trained_membrane_mock + - name: a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-denv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_infectionft_dynacellft_a549trained_denv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_infectionft_dynacellft_a549trained/eval_vscyto3d_infectionft_dynacellft_a549trained_membrane_denv + - name: a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-zikv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_infectionft_dynacellft_a549trained_zikv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_infectionft_dynacellft_a549trained/eval_vscyto3d_infectionft_dynacellft_a549trained_membrane_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/vscyto3d_infectionft_dynacellft/a549_mantis/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/vscyto3d_infectionft_dynacellft/a549_mantis/eval__ipsc_confocal.yaml new file mode 100644 index 000000000..c1e1f12fd --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/vscyto3d_infectionft_dynacellft/a549_mantis/eval__ipsc_confocal.yaml @@ -0,0 +1,14 @@ +# @package _global_ +# Track C (vscyto3d_infectionft_dynacellft trained on a549_mantis) eval for membrane channel +# of the dual predict zarr on iPSC. +defaults: + - override /target: membrane + - override /predict_set: ipsc_confocal + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/dual_nucl_memb_vscyto3d_infectionft_dynacellft_a549trained.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_infectionft_dynacellft_a549trained/eval_vscyto3d_infectionft_dynacellft_a549trained_membrane diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/vscyto3d_infectionft_dynacellft/ipsc_confocal/eval__a549_mantis.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/vscyto3d_infectionft_dynacellft/ipsc_confocal/eval__a549_mantis.yaml new file mode 100644 index 000000000..1c8f2f77e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/vscyto3d_infectionft_dynacellft/ipsc_confocal/eval__a549_mantis.yaml @@ -0,0 +1,40 @@ +# @package _global_ +# Track C (vscyto3d_infectionft_dynacellft trained on ipsc_confocal) grouped eval for membrane +# channel of the dual predict zarr on A549 (mock + denv + zikv in one process). +defaults: + - override /target: membrane + - override /predict_set: a549_mantis_caax_mock + +# Base anchors to the {gene}-{first_cond} manifest; each condition overlay below +# swaps dataset, pred_path, and save_dir for mock / denv / zikv in one process. +benchmark: + dataset_ref: + target: caax + +compute_feature_metrics: true + +conditions: + - name: a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-caax-mock + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_infectionft_dynacellft_mock.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_infectionft_dynacellft/eval_vscyto3d_infectionft_dynacellft_membrane_mock + - name: a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-denv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_infectionft_dynacellft_denv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_infectionft_dynacellft/eval_vscyto3d_infectionft_dynacellft_membrane_denv + - name: a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-caax-zikv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_infectionft_dynacellft_zikv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_infectionft_dynacellft/eval_vscyto3d_infectionft_dynacellft_membrane_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/vscyto3d_infectionft_dynacellft/ipsc_confocal/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/vscyto3d_infectionft_dynacellft/ipsc_confocal/eval__ipsc_confocal.yaml new file mode 100644 index 000000000..5148c21fb --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/membrane/vscyto3d_infectionft_dynacellft/ipsc_confocal/eval__ipsc_confocal.yaml @@ -0,0 +1,14 @@ +# @package _global_ +# Track C (vscyto3d_infectionft_dynacellft trained on ipsc_confocal) eval for membrane channel +# of the dual predict zarr on iPSC. +defaults: + - override /target: membrane + - override /predict_set: ipsc_confocal + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/dual_nucl_memb_vscyto3d_infectionft_dynacellft.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_infectionft_dynacellft/eval_vscyto3d_infectionft_dynacellft_membrane diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/celldiff/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/celldiff/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 120000 index 000000000..f11672d97 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/celldiff/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1 @@ +../../../../../mito/celldiff/ipsc_confocal/eval__a549_mantis_denv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/celldiff/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/celldiff/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 120000 index 000000000..36476fb4e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/celldiff/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1 @@ +../../../../../mito/celldiff/ipsc_confocal/eval__a549_mantis_mock.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/celldiff/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/celldiff/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 120000 index 000000000..ff457651d --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/celldiff/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1 @@ +../../../../../mito/celldiff/ipsc_confocal/eval__a549_mantis_zikv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/celldiff/ipsc_confocal/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/celldiff/ipsc_confocal/eval__ipsc_confocal.yaml new file mode 120000 index 000000000..de8318344 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/celldiff/ipsc_confocal/eval__ipsc_confocal.yaml @@ -0,0 +1 @@ +../../../../../mito/celldiff/ipsc_confocal/eval__ipsc_confocal.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 120000 index 000000000..58d6f5ef9 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1 @@ +../../../../../mito/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_denv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 120000 index 000000000..d9abc8a70 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1 @@ +../../../../../mito/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_mock.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 120000 index 000000000..672c4b9b7 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1 @@ +../../../../../mito/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_zikv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fcmae_vscyto3d_pretrained_randinit/randinit/eval__a549_mantis.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fcmae_vscyto3d_pretrained_randinit/randinit/eval__a549_mantis.yaml new file mode 100644 index 000000000..0fa8b8b6c --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fcmae_vscyto3d_pretrained_randinit/randinit/eval__a549_mantis.yaml @@ -0,0 +1,41 @@ +# @package _global_ +# Track A (random init, no training) grouped eval for mitochondria on A549. +# One process covers mock + denv + zikv via the conditions list, amortizing the +# DINOv3 + DynaCLR + CELL-DINO load. Reads the matching frozen-randinit zarrs. +defaults: + - override /target: mito_tomm20 + - override /predict_set: a549_mantis_tomm20_mock + +# Base anchors to the {gene}-{first_cond} manifest; each condition overlay below +# swaps dataset, pred_path, and save_dir for mock / denv / zikv in one process. +benchmark: + dataset_ref: + target: tomm20 + +compute_feature_metrics: true + +conditions: + - name: a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-mock + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_pretrained_randinit_mock.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_randinit/eval_vscyto3d_randinit_mitochondria_mock + - name: a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-denv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_pretrained_randinit_denv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_randinit/eval_vscyto3d_randinit_mitochondria_denv + - name: a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-tomm20-zikv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_pretrained_randinit_zikv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_randinit/eval_vscyto3d_randinit_mitochondria_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fcmae_vscyto3d_pretrained_randinit/randinit/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fcmae_vscyto3d_pretrained_randinit/randinit/eval__ipsc_confocal.yaml new file mode 100644 index 000000000..9735d1ce6 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fcmae_vscyto3d_pretrained_randinit/randinit/eval__ipsc_confocal.yaml @@ -0,0 +1,15 @@ +# @package _global_ +# Track A (random init, no training) eval for mitochondria on iPSC. +# Reads the frozen-randinit predict zarr produced by +# /fcmae_vscyto3d_pretrained/_no_train_randinit/predict__ipsc_confocal.yml. +defaults: + - override /target: mito_tomm20 + - override /predict_set: ipsc_confocal + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_fcmae_vscyto3d_pretrained_randinit.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_randinit/eval_vscyto3d_randinit_mitochondria diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 120000 index 000000000..1011b8fbe --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1 @@ +../../../../../mito/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_denv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 120000 index 000000000..37f841983 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1 @@ +../../../../../mito/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_mock.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 120000 index 000000000..fdddfb41f --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1 @@ +../../../../../mito/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_zikv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fnet3d_paper/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fnet3d_paper/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 120000 index 000000000..7023b987f --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fnet3d_paper/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1 @@ +../../../../../mito/fnet3d_paper/ipsc_confocal/eval__a549_mantis_denv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fnet3d_paper/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fnet3d_paper/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 120000 index 000000000..665a14a37 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fnet3d_paper/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1 @@ +../../../../../mito/fnet3d_paper/ipsc_confocal/eval__a549_mantis_mock.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fnet3d_paper/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fnet3d_paper/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 120000 index 000000000..f6803af88 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/fnet3d_paper/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1 @@ +../../../../../mito/fnet3d_paper/ipsc_confocal/eval__a549_mantis_zikv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/unetvit3d/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/unetvit3d/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 120000 index 000000000..9b19caf19 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/unetvit3d/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1 @@ +../../../../../mito/unetvit3d/ipsc_confocal/eval__a549_mantis_denv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/unetvit3d/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/unetvit3d/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 120000 index 000000000..8450554cc --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/unetvit3d/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1 @@ +../../../../../mito/unetvit3d/ipsc_confocal/eval__a549_mantis_mock.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/unetvit3d/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/unetvit3d/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 120000 index 000000000..ec036b12a --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/unetvit3d/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1 @@ +../../../../../mito/unetvit3d/ipsc_confocal/eval__a549_mantis_zikv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/unetvit3d/ipsc_confocal/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/unetvit3d/ipsc_confocal/eval__ipsc_confocal.yaml new file mode 120000 index 000000000..8438345ae --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/mito/unetvit3d/ipsc_confocal/eval__ipsc_confocal.yaml @@ -0,0 +1 @@ +../../../../../mito/unetvit3d/ipsc_confocal/eval__ipsc_confocal.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/celldiff/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/celldiff/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 120000 index 000000000..8edc2a391 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/celldiff/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1 @@ +../../../../../nucleus/celldiff/ipsc_confocal/eval__a549_mantis_denv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/celldiff/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/celldiff/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 120000 index 000000000..160a32964 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/celldiff/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1 @@ +../../../../../nucleus/celldiff/ipsc_confocal/eval__a549_mantis_mock.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/celldiff/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/celldiff/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 120000 index 000000000..fb805e3ca --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/celldiff/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1 @@ +../../../../../nucleus/celldiff/ipsc_confocal/eval__a549_mantis_zikv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/celldiff/ipsc_confocal/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/celldiff/ipsc_confocal/eval__ipsc_confocal.yaml new file mode 120000 index 000000000..c530b0717 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/celldiff/ipsc_confocal/eval__ipsc_confocal.yaml @@ -0,0 +1 @@ +../../../../../nucleus/celldiff/ipsc_confocal/eval__ipsc_confocal.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 120000 index 000000000..09d15f98d --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1 @@ +../../../../../nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_denv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 120000 index 000000000..8d160143e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1 @@ +../../../../../nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_mock.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 120000 index 000000000..131a499e5 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1 @@ +../../../../../nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_zikv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_pretrained_cytoland/cytoland/eval__a549_mantis.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_pretrained_cytoland/cytoland/eval__a549_mantis.yaml new file mode 100644 index 000000000..500c92272 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_pretrained_cytoland/cytoland/eval__a549_mantis.yaml @@ -0,0 +1,40 @@ +# @package _global_ +# Track B (Cytoland, no FT) grouped eval for nucleus channel of the +# dual predict zarr on A549 (mock + denv + zikv in one process). +defaults: + - override /target: nucleus + - override /predict_set: a549_mantis_h2b_mock + +# Base anchors to the {gene}-{first_cond} manifest; each condition overlay below +# swaps dataset, pred_path, and save_dir for mock / denv / zikv in one process. +benchmark: + dataset_ref: + target: h2b + +compute_feature_metrics: true + +conditions: + - name: a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-mock + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_fcmae_vscyto3d_pretrained_cytoland_mock.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_cytoland/eval_vscyto3d_cytoland_nucleus_mock + - name: a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-denv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_fcmae_vscyto3d_pretrained_cytoland_denv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_cytoland/eval_vscyto3d_cytoland_nucleus_denv + - name: a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-zikv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_fcmae_vscyto3d_pretrained_cytoland_zikv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_cytoland/eval_vscyto3d_cytoland_nucleus_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_pretrained_cytoland/cytoland/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_pretrained_cytoland/cytoland/eval__ipsc_confocal.yaml new file mode 100644 index 000000000..d6a25cf2f --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_pretrained_cytoland/cytoland/eval__ipsc_confocal.yaml @@ -0,0 +1,15 @@ +# @package _global_ +# Track B (Cytoland, no FT) eval for nucleus channel of the dual +# predict zarr on iPSC. _ref_hook derives pred_channel_name from dataset_ref.target +# so this leaf reads the Nucleus_prediction channel from the dual zarr. +defaults: + - override /target: nucleus + - override /predict_set: ipsc_confocal + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/dual_nucl_memb_fcmae_vscyto3d_pretrained_cytoland.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_cytoland/eval_vscyto3d_cytoland_nucleus diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_pretrained_infectionft/infectionft/eval__a549_mantis.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_pretrained_infectionft/infectionft/eval__a549_mantis.yaml new file mode 100644 index 000000000..0153e2c2e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_pretrained_infectionft/infectionft/eval__a549_mantis.yaml @@ -0,0 +1,40 @@ +# @package _global_ +# Track B (InfectionFT, no FT) grouped eval for nucleus channel of the +# dual predict zarr on A549 (mock + denv + zikv in one process). +defaults: + - override /target: nucleus + - override /predict_set: a549_mantis_h2b_mock + +# Base anchors to the {gene}-{first_cond} manifest; each condition overlay below +# swaps dataset, pred_path, and save_dir for mock / denv / zikv in one process. +benchmark: + dataset_ref: + target: h2b + +compute_feature_metrics: true + +conditions: + - name: a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-mock + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_fcmae_vscyto3d_pretrained_infectionft_mock.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_infectionft/eval_vscyto3d_infectionft_nucleus_mock + - name: a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-denv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_fcmae_vscyto3d_pretrained_infectionft_denv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_infectionft/eval_vscyto3d_infectionft_nucleus_denv + - name: a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-zikv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_fcmae_vscyto3d_pretrained_infectionft_zikv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_infectionft/eval_vscyto3d_infectionft_nucleus_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_pretrained_infectionft/infectionft/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_pretrained_infectionft/infectionft/eval__ipsc_confocal.yaml new file mode 100644 index 000000000..b6f152229 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_pretrained_infectionft/infectionft/eval__ipsc_confocal.yaml @@ -0,0 +1,15 @@ +# @package _global_ +# Track B (InfectionFT, no FT) eval for nucleus channel of the dual +# predict zarr on iPSC. _ref_hook derives pred_channel_name from dataset_ref.target +# so this leaf reads the Nucleus_prediction channel from the dual zarr. +defaults: + - override /target: nucleus + - override /predict_set: ipsc_confocal + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/dual_nucl_memb_fcmae_vscyto3d_pretrained_infectionft.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_infectionft/eval_vscyto3d_infectionft_nucleus diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_pretrained_randinit/randinit/eval__a549_mantis.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_pretrained_randinit/randinit/eval__a549_mantis.yaml new file mode 100644 index 000000000..c8b90b9c6 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_pretrained_randinit/randinit/eval__a549_mantis.yaml @@ -0,0 +1,41 @@ +# @package _global_ +# Track A (random init, no training) grouped eval for nucleus on A549. +# One process covers mock + denv + zikv via the conditions list, amortizing the +# DINOv3 + DynaCLR + CELL-DINO load. Reads the matching frozen-randinit zarrs. +defaults: + - override /target: nucleus + - override /predict_set: a549_mantis_h2b_mock + +# Base anchors to the {gene}-{first_cond} manifest; each condition overlay below +# swaps dataset, pred_path, and save_dir for mock / denv / zikv in one process. +benchmark: + dataset_ref: + target: h2b + +compute_feature_metrics: true + +conditions: + - name: a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-mock + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_pretrained_randinit_mock.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_randinit/eval_vscyto3d_randinit_nucleus_mock + - name: a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-denv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_pretrained_randinit_denv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_randinit/eval_vscyto3d_randinit_nucleus_denv + - name: a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-zikv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_pretrained_randinit_zikv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_randinit/eval_vscyto3d_randinit_nucleus_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_pretrained_randinit/randinit/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_pretrained_randinit/randinit/eval__ipsc_confocal.yaml new file mode 100644 index 000000000..a96a03a3b --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_pretrained_randinit/randinit/eval__ipsc_confocal.yaml @@ -0,0 +1,15 @@ +# @package _global_ +# Track A (random init, no training) eval for nucleus on iPSC. +# Reads the frozen-randinit predict zarr produced by +# /fcmae_vscyto3d_pretrained/_no_train_randinit/predict__ipsc_confocal.yml. +defaults: + - override /target: nucleus + - override /predict_set: ipsc_confocal + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_fcmae_vscyto3d_pretrained_randinit.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_randinit/eval_vscyto3d_randinit_nucleus diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 120000 index 000000000..d97f17296 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1 @@ +../../../../../nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_denv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 120000 index 000000000..e14458f48 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1 @@ +../../../../../nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_mock.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 120000 index 000000000..847aa1ad0 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1 @@ +../../../../../nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_zikv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fnet3d_paper/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fnet3d_paper/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 120000 index 000000000..eada00bf5 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fnet3d_paper/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1 @@ +../../../../../nucleus/fnet3d_paper/ipsc_confocal/eval__a549_mantis_denv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fnet3d_paper/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fnet3d_paper/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 120000 index 000000000..1071d3912 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fnet3d_paper/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1 @@ +../../../../../nucleus/fnet3d_paper/ipsc_confocal/eval__a549_mantis_mock.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fnet3d_paper/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fnet3d_paper/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 120000 index 000000000..524868764 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/fnet3d_paper/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1 @@ +../../../../../nucleus/fnet3d_paper/ipsc_confocal/eval__a549_mantis_zikv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/unetvit3d/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/unetvit3d/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 120000 index 000000000..1715307a0 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/unetvit3d/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1 @@ +../../../../../nucleus/unetvit3d/ipsc_confocal/eval__a549_mantis_denv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/unetvit3d/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/unetvit3d/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 120000 index 000000000..019928b53 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/unetvit3d/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1 @@ +../../../../../nucleus/unetvit3d/ipsc_confocal/eval__a549_mantis_mock.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/unetvit3d/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/unetvit3d/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 120000 index 000000000..6025b9bf4 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/unetvit3d/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1 @@ +../../../../../nucleus/unetvit3d/ipsc_confocal/eval__a549_mantis_zikv.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/unetvit3d/ipsc_confocal/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/unetvit3d/ipsc_confocal/eval__ipsc_confocal.yaml new file mode 120000 index 000000000..6fa52b633 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/unetvit3d/ipsc_confocal/eval__ipsc_confocal.yaml @@ -0,0 +1 @@ +../../../../../nucleus/unetvit3d/ipsc_confocal/eval__ipsc_confocal.yaml \ No newline at end of file diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/vscyto3d_cytolandft/a549_mantis/eval__a549_mantis.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/vscyto3d_cytolandft/a549_mantis/eval__a549_mantis.yaml new file mode 100644 index 000000000..4ed250245 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/vscyto3d_cytolandft/a549_mantis/eval__a549_mantis.yaml @@ -0,0 +1,40 @@ +# @package _global_ +# Track C (vscyto3d_cytolandft trained on a549_mantis) grouped eval for nucleus +# channel of the dual predict zarr on A549 (mock + denv + zikv in one process). +defaults: + - override /target: nucleus + - override /predict_set: a549_mantis_h2b_mock + +# Base anchors to the {gene}-{first_cond} manifest; each condition overlay below +# swaps dataset, pred_path, and save_dir for mock / denv / zikv in one process. +benchmark: + dataset_ref: + target: h2b + +compute_feature_metrics: true + +conditions: + - name: a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-mock + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_cytolandft_a549trained_mock.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_cytolandft_a549trained/eval_vscyto3d_cytolandft_a549trained_nucleus_mock + - name: a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-denv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_cytolandft_a549trained_denv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_cytolandft_a549trained/eval_vscyto3d_cytolandft_a549trained_nucleus_denv + - name: a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-zikv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_cytolandft_a549trained_zikv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_cytolandft_a549trained/eval_vscyto3d_cytolandft_a549trained_nucleus_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/vscyto3d_cytolandft/a549_mantis/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/vscyto3d_cytolandft/a549_mantis/eval__ipsc_confocal.yaml new file mode 100644 index 000000000..77ca9fe6b --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/vscyto3d_cytolandft/a549_mantis/eval__ipsc_confocal.yaml @@ -0,0 +1,14 @@ +# @package _global_ +# Track C (vscyto3d_cytolandft trained on a549_mantis) eval for nucleus channel +# of the dual predict zarr on iPSC. +defaults: + - override /target: nucleus + - override /predict_set: ipsc_confocal + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/dual_nucl_memb_vscyto3d_cytolandft_a549trained.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_cytolandft_a549trained/eval_vscyto3d_cytolandft_a549trained_nucleus diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/vscyto3d_cytolandft/ipsc_confocal/eval__a549_mantis.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/vscyto3d_cytolandft/ipsc_confocal/eval__a549_mantis.yaml new file mode 100644 index 000000000..91b04c150 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/vscyto3d_cytolandft/ipsc_confocal/eval__a549_mantis.yaml @@ -0,0 +1,40 @@ +# @package _global_ +# Track C (vscyto3d_cytolandft trained on ipsc_confocal) grouped eval for nucleus +# channel of the dual predict zarr on A549 (mock + denv + zikv in one process). +defaults: + - override /target: nucleus + - override /predict_set: a549_mantis_h2b_mock + +# Base anchors to the {gene}-{first_cond} manifest; each condition overlay below +# swaps dataset, pred_path, and save_dir for mock / denv / zikv in one process. +benchmark: + dataset_ref: + target: h2b + +compute_feature_metrics: true + +conditions: + - name: a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-mock + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_cytolandft_mock.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_cytolandft/eval_vscyto3d_cytolandft_nucleus_mock + - name: a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-denv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_cytolandft_denv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_cytolandft/eval_vscyto3d_cytolandft_nucleus_denv + - name: a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-zikv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_cytolandft_zikv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_cytolandft/eval_vscyto3d_cytolandft_nucleus_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/vscyto3d_cytolandft/ipsc_confocal/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/vscyto3d_cytolandft/ipsc_confocal/eval__ipsc_confocal.yaml new file mode 100644 index 000000000..bd9563ba3 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/vscyto3d_cytolandft/ipsc_confocal/eval__ipsc_confocal.yaml @@ -0,0 +1,14 @@ +# @package _global_ +# Track C (vscyto3d_cytolandft trained on ipsc_confocal) eval for nucleus channel +# of the dual predict zarr on iPSC. +defaults: + - override /target: nucleus + - override /predict_set: ipsc_confocal + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/dual_nucl_memb_vscyto3d_cytolandft.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_cytolandft/eval_vscyto3d_cytolandft_nucleus diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/vscyto3d_infectionft_dynacellft/a549_mantis/eval__a549_mantis.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/vscyto3d_infectionft_dynacellft/a549_mantis/eval__a549_mantis.yaml new file mode 100644 index 000000000..fd4a06621 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/vscyto3d_infectionft_dynacellft/a549_mantis/eval__a549_mantis.yaml @@ -0,0 +1,40 @@ +# @package _global_ +# Track C (vscyto3d_infectionft_dynacellft trained on a549_mantis) grouped eval for nucleus +# channel of the dual predict zarr on A549 (mock + denv + zikv in one process). +defaults: + - override /target: nucleus + - override /predict_set: a549_mantis_h2b_mock + +# Base anchors to the {gene}-{first_cond} manifest; each condition overlay below +# swaps dataset, pred_path, and save_dir for mock / denv / zikv in one process. +benchmark: + dataset_ref: + target: h2b + +compute_feature_metrics: true + +conditions: + - name: a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-mock + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_infectionft_dynacellft_a549trained_mock.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_infectionft_dynacellft_a549trained/eval_vscyto3d_infectionft_dynacellft_a549trained_nucleus_mock + - name: a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-denv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_infectionft_dynacellft_a549trained_denv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_infectionft_dynacellft_a549trained/eval_vscyto3d_infectionft_dynacellft_a549trained_nucleus_denv + - name: a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-zikv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_infectionft_dynacellft_a549trained_zikv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_infectionft_dynacellft_a549trained/eval_vscyto3d_infectionft_dynacellft_a549trained_nucleus_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/vscyto3d_infectionft_dynacellft/a549_mantis/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/vscyto3d_infectionft_dynacellft/a549_mantis/eval__ipsc_confocal.yaml new file mode 100644 index 000000000..7061cc016 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/vscyto3d_infectionft_dynacellft/a549_mantis/eval__ipsc_confocal.yaml @@ -0,0 +1,14 @@ +# @package _global_ +# Track C (vscyto3d_infectionft_dynacellft trained on a549_mantis) eval for nucleus channel +# of the dual predict zarr on iPSC. +defaults: + - override /target: nucleus + - override /predict_set: ipsc_confocal + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/dual_nucl_memb_vscyto3d_infectionft_dynacellft_a549trained.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_infectionft_dynacellft_a549trained/eval_vscyto3d_infectionft_dynacellft_a549trained_nucleus diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/vscyto3d_infectionft_dynacellft/ipsc_confocal/eval__a549_mantis.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/vscyto3d_infectionft_dynacellft/ipsc_confocal/eval__a549_mantis.yaml new file mode 100644 index 000000000..1b20611d8 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/vscyto3d_infectionft_dynacellft/ipsc_confocal/eval__a549_mantis.yaml @@ -0,0 +1,40 @@ +# @package _global_ +# Track C (vscyto3d_infectionft_dynacellft trained on ipsc_confocal) grouped eval for nucleus +# channel of the dual predict zarr on A549 (mock + denv + zikv in one process). +defaults: + - override /target: nucleus + - override /predict_set: a549_mantis_h2b_mock + +# Base anchors to the {gene}-{first_cond} manifest; each condition overlay below +# swaps dataset, pred_path, and save_dir for mock / denv / zikv in one process. +benchmark: + dataset_ref: + target: h2b + +compute_feature_metrics: true + +conditions: + - name: a549_mock + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-mock + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_infectionft_dynacellft_mock.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_infectionft_dynacellft/eval_vscyto3d_infectionft_dynacellft_nucleus_mock + - name: a549_denv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-denv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_infectionft_dynacellft_denv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_infectionft_dynacellft/eval_vscyto3d_infectionft_dynacellft_nucleus_denv + - name: a549_zikv + benchmark: + dataset_ref: + dataset: a549-mantis-h2b-zikv + io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/dual_nucl_memb_vscyto3d_infectionft_dynacellft_zikv.zarr + save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/evaluations_infectionft_dynacellft/eval_vscyto3d_infectionft_dynacellft_nucleus_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/vscyto3d_infectionft_dynacellft/ipsc_confocal/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/vscyto3d_infectionft_dynacellft/ipsc_confocal/eval__ipsc_confocal.yaml new file mode 100644 index 000000000..411ab4f07 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/leaf/nucleus/vscyto3d_infectionft_dynacellft/ipsc_confocal/eval__ipsc_confocal.yaml @@ -0,0 +1,14 @@ +# @package _global_ +# Track C (vscyto3d_infectionft_dynacellft trained on ipsc_confocal) eval for nucleus channel +# of the dual predict zarr on iPSC. +defaults: + - override /target: nucleus + - override /predict_set: ipsc_confocal + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/dual_nucl_memb_vscyto3d_infectionft_dynacellft.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_infectionft_dynacellft/eval_vscyto3d_infectionft_dynacellft_nucleus diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/eval/feature_extractor/celldino/default.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/eval/feature_extractor/celldino/default.yaml new file mode 100644 index 000000000..2dd0dd3bc --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/eval/feature_extractor/celldino/default.yaml @@ -0,0 +1,8 @@ +# Canonical CELL-DINO checkpoint for organelle virtual-staining eval. +# Channel-adaptive ViT-L/16 pretrained on cell images (HPA) — matches the +# CellDinoModel wrapper's single-channel ViT-L/16 contract (img_size=224, +# patch_size=16, init_values=1.0, block_chunks=4). Ed Hirata's weights tree +# at /hpc/projects/organelle_phenotyping/models/CELL-DINO/model_weights/. +weights_path: /hpc/projects/organelle_phenotyping/models/CELL-DINO/model_weights/weights/channel_adaptive_dino_vitl16_pretrain_cells-ef7c17ff.pth +img_size: 224 +patch_size: 16 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/eval/feature_extractor/dynaclr/default.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/eval/feature_extractor/dynaclr/default.yaml new file mode 100644 index 000000000..d42771b12 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/eval/feature_extractor/dynaclr/default.yaml @@ -0,0 +1,12 @@ +# Canonical DynaCLR encoder for organelle-sensor virtual-staining eval. +# Encoder kwargs from resolved config.yaml of the jbrwhzr3 run. +checkpoint: /hpc/projects/organelle_phenotyping/models/DynaCLR-2D-MIP-BagOfChannels/2d-mip-ntxent-t0p2-lr2e5-bs256-192to160-zext11-single-marker-fix-shuffler/DynaCLR-2D-MIP-BagOfChannels/jbrwhzr3/checkpoints/epoch=105-step=84800.ckpt +encoder: + backbone: convnext_tiny + in_channels: 1 + in_stack_depth: 1 + stem_kernel_size: [1, 4, 4] + stem_stride: [1, 4, 4] + embedding_dim: 768 + projection_dim: 32 + drop_path_rate: 0.1 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/eval/target/er_sec61b.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/eval/target/er_sec61b.yaml new file mode 100644 index 000000000..55eb4c439 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/eval/target/er_sec61b.yaml @@ -0,0 +1,6 @@ +# @package _global_ +# Target group: ER marked by SEC61B, iPSC dataset v4 test split. +target_name: er +benchmark: + dataset_ref: + target: sec61b diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/eval/target/membrane.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/eval/target/membrane.yaml new file mode 100644 index 000000000..3f62a7271 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/eval/target/membrane.yaml @@ -0,0 +1,6 @@ +# @package _global_ +# Target group: membrane channel of the multi-marker cell.zarr, iPSC dataset v4 test split. +target_name: membrane +benchmark: + dataset_ref: + target: membrane diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/eval/target/mito_tomm20.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/eval/target/mito_tomm20.yaml new file mode 100644 index 000000000..07b266a23 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/eval/target/mito_tomm20.yaml @@ -0,0 +1,6 @@ +# @package _global_ +# Target group: mitochondria marked by TOMM20, iPSC dataset v4 test split. +target_name: mitochondria +benchmark: + dataset_ref: + target: tomm20 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/eval/target/nucleus.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/eval/target/nucleus.yaml new file mode 100644 index 000000000..c22230c6f --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/eval/target/nucleus.yaml @@ -0,0 +1,6 @@ +# @package _global_ +# Target group: nuclei channel of the multi-marker cell.zarr, iPSC dataset v4 test split. +target_name: nucleus +benchmark: + dataset_ref: + target: nucleus diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/data_overlays/celldiff_fit.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/data_overlays/celldiff_fit.yml new file mode 100644 index 000000000..f262ec6ef --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/data_overlays/celldiff_fit.yml @@ -0,0 +1,57 @@ +# CellDiff fit-time HCS data hparams. +# Lifted from model_overlays/celldiff_fit.yml so the model+trainer half +# there stays composable by joint-dataset (BatchedConcatDataModule) +# leaves that author their own data: block. +data: + init_args: + z_window_size: 13 + batch_size: 4 + num_workers: 4 + yx_patch_size: [512, 512] + gpu_augmentations: + # GPU: affine on oversized patch → center crop to final 8×512×512. + # safe_crop_size clamps scale so the rotated 624px source always + # covers the 512px crop, eliminating zero-corner artifacts. + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + safe_crop_size: [8, 512, 512] + safe_crop_coverage: 0.9 + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + # CellDiff requires exact input_spatial_size (fixed ViT positional embeddings). + # DivisibleCropd is insufficient — must center-crop to exact model input size. + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/data_overlays/fcmae_vscyto3d_fit.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/data_overlays/fcmae_vscyto3d_fit.yml new file mode 100644 index 000000000..8e21ccf59 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/data_overlays/fcmae_vscyto3d_fit.yml @@ -0,0 +1,57 @@ +# FCMAE-class (VSCyto3D FullyConvolutionalMAE) fit-time HCS data hparams. +# Lifted from model_overlays/fcmae_vscyto3d_fit.yml so the model+trainer +# half there stays composable by joint-dataset +# (BatchedConcatDataModule) leaves that author their own data: block. +data: + init_args: + z_window_size: 20 + batch_size: 32 + num_workers: 4 + yx_patch_size: [384, 384] + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Structure] + w_key: Structure + spatial_size: [20, 600, 600] + num_samples: 4 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/data_overlays/fnet3d_paper_fit.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/data_overlays/fnet3d_paper_fit.yml new file mode 100644 index 000000000..4d751cd7a --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/data_overlays/fnet3d_paper_fit.yml @@ -0,0 +1,54 @@ +# FNet3D paper-baseline fit-time HCS data hparams. +# Lifted from model_overlays/fnet3d_paper_fit.yml so the model+trainer +# half there stays composable by joint-dataset +# (BatchedConcatDataModule) leaves that author their own data: block. +# +# Diverges from shared/model/targets/er_sec61b.yml on two fields because +# the paper's stats + sampling differ from the CellDiff/UNetViT +# conventions: Structure is normalized with mean/std (not median/iqr), +# and 8 small weighted crops per FOV replace the 2 oversized transformer +# crops. +data: + init_args: + z_window_size: 32 + batch_size: 48 + num_workers: 8 + yx_patch_size: [64, 64] + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Structure] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: + # CPU: 8 patches per FOV (amortizes zarr decompression). + # batch_size=48 → DataLoader loads 6 FOVs, each yields 8 patches = 48. + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Structure] + w_key: Structure + spatial_size: [32, 64, 64] + num_samples: 8 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandFlipd + init_args: + keys: [source, target] + spatial_axes: [1] + prob: 0.5 + - class_path: viscy_transforms.BatchedRandFlipd + init_args: + keys: [source, target] + spatial_axes: [2] + prob: 0.5 + val_augmentations: + - class_path: viscy_transforms.CenterSpatialCropd + init_args: + keys: [Phase3D, Structure] + roi_size: [32, 64, 64] diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/data_overlays/unetvit3d_fit.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/data_overlays/unetvit3d_fit.yml new file mode 100644 index 000000000..70fb2fa99 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/data_overlays/unetvit3d_fit.yml @@ -0,0 +1,60 @@ +# UNetViT3D fit-time HCS data hparams. +# Lifted from model_overlays/unetvit3d_fit.yml so the model+trainer half +# there stays composable by joint-dataset (BatchedConcatDataModule) +# leaves that author their own data: block. +# +# Identical to data_overlays/celldiff_fit.yml — divergence expected once +# UNetViT3D training data shape is retuned independently. +data: + init_args: + z_window_size: 13 + batch_size: 4 + num_workers: 4 + yx_patch_size: [512, 512] + gpu_augmentations: + # GPU: affine on oversized patch → center crop to final 8×512×512. + # safe_crop_size clamps scale so the rotated 624px source always + # covers the 512px crop, eliminating zero-corner artifacts. + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + safe_crop_size: [8, 512, 512] + safe_crop_coverage: 0.9 + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + # UNetViT3D requires exact input_spatial_size (fixed ViT positional embeddings). + # DivisibleCropd is insufficient — must center-crop to exact model input size. + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/data_overlays/unext2_fit.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/data_overlays/unext2_fit.yml new file mode 100644 index 000000000..ba78e6fee --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/data_overlays/unext2_fit.yml @@ -0,0 +1,63 @@ +# UNeXt2 (VSCyto3D) fit-time HCS data hparams — Run 4 baseline +# (lr=0.0004, bs=32, z=20). Lifted from model_overlays/unext2_fit.yml so +# the model+trainer half there stays composable by joint-dataset +# (BatchedConcatDataModule) leaves that author their own data: block. +data: + init_args: + z_window_size: 20 + batch_size: 32 + num_workers: 8 + yx_patch_size: [384, 384] + augmentations: + # List-replaces target's default CPU augmentations with UNeXt2's + # z=20 / 600 YX oversized crop at 4 patches per FOV. + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Structure] + w_key: Structure + spatial_size: [20, 600, 600] + num_samples: 4 + gpu_augmentations: + # Run 4 affine has no safe_crop_size — that's a later addition. The + # val_gpu_augmentations center-crop handles the post-affine cleanup. + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + # Center-crop to model input size: Z from 20→15, YX to 384×384. + # 384 is divisible by 64 (UNeXt2 downsampling factor). + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/launcher_profiles/hardware_4gpu.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/launcher_profiles/hardware_4gpu.yml new file mode 100644 index 000000000..8a44737d9 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/launcher_profiles/hardware_4gpu.yml @@ -0,0 +1,33 @@ +# Hardware profile: 4 GPU DDP on H100/H200, A100 excluded. +# +# 4 GPUs, DDP strategy, 512G host mem, 4-day wall-time per restart. +# +# host mem rationale (post-mmap_preload-fix, commit 6ec0d6f7): +# The earlier 1024G ceiling was sized for the oindex/CoordinateIndexer +# broadcast bloat in HCSDataModule.prepare_data, which inflated heap +# ~7x for sharded zarr reads. With per-channel BasicIndexer reads, +# joint cell.zarr + A549-pooled preload now peaks at ~185 GB +# (cell.zarr 500 FOVs ≈110 GB + a549 30 FOVs ≈75 GB tmpfs files in +# /dev/shm + small process baseline). 512G gives ~325 GB of headroom +# for worker buffers, persistent_workers transients, and validation- +# time spikes. Single-set workloads peak at ~110 GB of the 512G cap. +# +# GPU constraint rationale: +# Restricted to H100/H200 (80–96 GB VRAM) because FCMAE/UNeXt2 train +# at large spatial patches (e.g. 20×600×600) where a single DDP rank +# already needs 30–50 GB; A40/A6000/L40S (48 GB) leave no headroom +# for activation transients. A100 nodes are excluded separately due +# to repeat NCCL BROADCAST/ALLREDUCE hangs at first-batch coordination +# on this cluster's A100 partition. Leaves that intentionally want +# the smaller cards must opt out via +# `--override launcher.sbatch.constraint=h100|h200|a40|a6000|l40s`. +launcher: + sbatch: + partition: gpu + nodes: 1 + ntasks_per_node: 4 + cpus_per_task: 8 + gpus: 4 + mem: "512G" + constraint: "h100|h200" + time: "4-00:00:00" diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/launcher_profiles/hardware_gpu_any_long.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/launcher_profiles/hardware_gpu_any_long.yml new file mode 100644 index 000000000..41b2b85d1 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/launcher_profiles/hardware_gpu_any_long.yml @@ -0,0 +1,21 @@ +# Hardware profile: 1 GPU, any model (no constraint), long wall-time. +# +# Matches the FNet3D paper-baseline run's actual slurm directives: +# the paper runs were submitted without --constraint (they landed on +# RTX A6000s) and with a 20-day wall-time budget so the job wouldn't +# timeout across multi-day training. 32 CPUs and 256G mem are the same +# as hardware_h200_single; only constraint and time differ. +# +# Leaves whose training zarr is large enough to push mmap_preload over +# the 256G cap (e.g. cell.zarr-backed nucleus/membrane) override +# launcher.sbatch.mem in the leaf body. +launcher: + sbatch: + partition: gpu + nodes: 1 + ntasks_per_node: 1 + cpus_per_task: 32 + gpus: 1 + mem: "256G" + constraint: null + time: "20-00:00:00" diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/launcher_profiles/hardware_h200_single.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/launcher_profiles/hardware_h200_single.yml new file mode 100644 index 000000000..baf4c4194 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/launcher_profiles/hardware_h200_single.yml @@ -0,0 +1,13 @@ +# Hardware profile: single H200 GPU. Pair with recipes/topology/single_gpu.yml. +# launcher.sbatch.gpus must match the topology recipe's trainer.devices +# (enforced by submit_benchmark_job). +launcher: + sbatch: + partition: gpu + nodes: 1 + ntasks_per_node: 1 + cpus_per_task: 32 + gpus: 1 + mem: "256G" + constraint: "h200" + time: "4-00:00:00" diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml new file mode 100644 index 000000000..62406fee4 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml @@ -0,0 +1,23 @@ +# Hardware profile: single GPU, no vendor constraint, for predict mode. +# Pair with recipes/topology/single_gpu.yml. +# +# Predict at FP32 fits in ~7 GB GPU memory and is compute-bound at 100% SM +# utilization (verified 2026-05 on celldiff_r2 a549trained membrane mock, +# H200: 6.6 GB / 143 GB HBM, 15-20% memory bandwidth). Bandwidth headroom +# means Hopper-only acceleration (HBM3e, FP8/Transformer Engine) is not +# used — A40 / A6000 / L40S / L4 all work and drain the queue faster than +# pinning Hopper. +# +# Hardware-shape constants (gpus, cpus_per_task, mem) match hardware_h200_single +# so resource accounting is identical; only the GPU vendor constraint is +# relaxed. +launcher: + sbatch: + partition: gpu + nodes: 1 + ntasks_per_node: 1 + cpus_per_task: 32 + gpus: 1 + mem: "256G" + constraint: null + time: "4-00:00:00" diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/launcher_profiles/mode_fit.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/launcher_profiles/mode_fit.yml new file mode 100644 index 000000000..77054287d --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/launcher_profiles/mode_fit.yml @@ -0,0 +1,3 @@ +# Launcher profile: fit mode. +launcher: + mode: fit diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/launcher_profiles/mode_predict.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/launcher_profiles/mode_predict.yml new file mode 100644 index 000000000..0fedc1b62 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/launcher_profiles/mode_predict.yml @@ -0,0 +1,3 @@ +# Launcher profile: predict mode. +launcher: + mode: predict diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/launcher_profiles/runtime_shared.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/launcher_profiles/runtime_shared.yml new file mode 100644 index 000000000..3a6e99c20 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/launcher_profiles/runtime_shared.yml @@ -0,0 +1,24 @@ +# Runtime profile: shared srun + env defaults (not topology-specific). +launcher: + runtime: + use_srun: true + cleanup_tmp: true + env: + PYTHONUNBUFFERED: "1" + NCCL_DEBUG: INFO + PYTHONFAULTHANDLER: "1" + # Use expandable VA segments in PyTorch's CUDA caching allocator. + # Avoids "tried to allocate N GiB; X GiB free, Y GiB reserved" OOMs + # caused by allocator fragmentation across the variable-shape U-Net + # forward+backward (skip-concat doubles channel counts mid-decoder). + # Hit on J31821456 (A40 48GB, fnet3d joint nucl): 45 GB allocated + + # 2.4 GB free could not fit a 3 GB cat. PyTorch's own OOM message + # explicitly recommends this setting. No known regressions. + PYTORCH_ALLOC_CONF: "expandable_segments:True" + # Shared Hugging Face hub cache on project storage: the first user + # with gated-repo access downloads each model (e.g. DINOv3) once + # into this dir, and every subsequent job on any dynacell team + # account reuses those weights instead of re-downloading to per-user + # ~/.cache/huggingface/hub. HF_HUB_CACHE (not HF_HOME) so each user's + # auth token at ~/.cache/huggingface/token still controls gate ACLs. + HF_HUB_CACHE: /hpc/projects/comp.micro/virtual_staining/models/dynacell/evaluation/hf_cache diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/launcher_profiles/wall_smoke.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/launcher_profiles/wall_smoke.yml new file mode 100644 index 000000000..14c281f34 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/launcher_profiles/wall_smoke.yml @@ -0,0 +1,7 @@ +# Smoke wall override. Stack AFTER any hardware profile in `base:` to cap +# launcher.sbatch.time at 30 min so a smoke job cannot sit on a multi-day +# allocation. Pair with `--override trainer.fast_dev_run=true` or +# `--override trainer.max_steps=N` so the run actually exits inside the wall. +launcher: + sbatch: + time: "00:30:00" diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/celldiff_fit.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/celldiff_fit.yml new file mode 100644 index 000000000..fd3d51dd2 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/celldiff_fit.yml @@ -0,0 +1,20 @@ +# CellDiff fit overlay — model + trainer only. +# HCS data hparams live in data_overlays/celldiff_fit.yml; single-store +# train leaves compose both, joint (BatchedConcatDataModule) leaves +# compose only this one and author data: themselves. +base: + - ../../../../../../recipes/models/celldiff_fm.yml + - ../../../../../../recipes/trainer/fit.yml + - ../../../../../../recipes/topology/single_gpu.yml +model: + init_args: + net_config: + input_spatial_size: [8, 512, 512] + lr: 0.0003 + schedule: WarmupCosine + warmup_steps: 8500 + warmup_multiplier: 1e-3 + num_log_steps: 10 +trainer: + precision: bf16-mixed + max_epochs: 20 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/celldiff_predict.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/celldiff_predict.yml new file mode 100644 index 000000000..fbca171a2 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/celldiff_predict.yml @@ -0,0 +1,22 @@ +# CellDiff predict overlay. +# Binds the flow-matching model recipe + predict trainer recipe, then layers +# predict-time model hparams and data-loader settings. +# Predict-time normalizations and data_path are leaf-owned (leaf overrides +# target-inherited values to match each organelle's test_cropped store). +base: + - ../../../../../../recipes/models/celldiff_fm.yml + - ../../../../../../recipes/trainer/predict.yml + - ../../../../../../recipes/topology/single_gpu.yml +model: + init_args: + net_config: + input_spatial_size: [8, 512, 512] + num_generate_steps: 100 + predict_method: iterative + predict_overlap: [4, 256, 256] +data: + init_args: + z_window_size: 40 + batch_size: 1 + num_workers: 0 + yx_patch_size: [512, 512] diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_fit.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_fit.yml new file mode 100644 index 000000000..26a48d76f --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_fit.yml @@ -0,0 +1,39 @@ +# 2-channel FCMAE fit overlay for Track C (dual nucleus+membrane) fine-tuning. +# Mirrors fcmae_vscyto3d_fit.yml exactly but with out_channels=2 so the +# decoder matches the cytoland public / infection-FT source ckpts at full- +# weight load (no encoder_only). Used by both Track C1 (vscyto3d_cytolandft) +# and Track C2 (vscyto3d_infectionft_dynacellft) train leaves; ckpt_path is +# set per-leaf. +base: + - ../../../../../../recipes/trainer/fit.yml + - ../../../../../../recipes/topology/ddp_4gpu.yml +model: + class_path: dynacell.engine.DynacellUNet + init_args: + architecture: fcmae + model_config: + in_channels: 1 + out_channels: 2 + encoder_blocks: [3, 3, 9, 3] + encoder_drop_path_rate: 0.1 + dims: [96, 192, 384, 768] + decoder_conv_blocks: 2 + stem_kernel_size: [5, 4, 4] + in_stack_depth: 15 + pretraining: false + loss_function: + class_path: viscy_utils.losses.MixedLoss + init_args: + l1_alpha: 0.5 + l2_alpha: 0.0 + ms_dssim_alpha: 0.5 + lr: 0.0004 + schedule: WarmupCosine + warmup_steps: 8500 + warmup_multiplier: 1e-3 +trainer: + # FullyConvolutionalMAE(pretraining=False) has decoder/head params that only + # receive gradients on some forward paths; default ddp errors at step 1. + strategy: ddp_find_unused_parameters_true + precision: 16-mixed + max_epochs: 200 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_predict.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_predict.yml new file mode 100644 index 000000000..474949885 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/fcmae_vscyto3d_2chan_predict.yml @@ -0,0 +1,37 @@ +# 2-channel FCMAE predict overlay for VSCyto3D Nuclei + Membrane outputs. +# Mirrors fcmae_vscyto3d_predict.yml but with out_channels=2 so DynacellUNet +# instantiates the dual-head decoder that matches cytoland public + VSCyto3D +# A549-infection-finetune state-dict shapes. All other arch hparams stay +# identical to the single-channel overlay (verified against cytoland +# checkpoint hparams: in_stack_depth=15, stem_kernel_size=(5,4,4), drop_path=0.1). +base: + - ../../../../../../recipes/trainer/predict.yml + - ../../../../../../recipes/topology/single_gpu.yml +model: + class_path: dynacell.engine.DynacellUNet + init_args: + architecture: fcmae + model_config: + in_channels: 1 + out_channels: 2 + encoder_blocks: [3, 3, 9, 3] + encoder_drop_path_rate: 0.1 + dims: [96, 192, 384, 768] + decoder_conv_blocks: 2 + stem_kernel_size: [5, 4, 4] + in_stack_depth: 15 + pretraining: false + loss_function: + class_path: viscy_utils.losses.MixedLoss + init_args: + l1_alpha: 0.5 + l2_alpha: 0.0 + ms_dssim_alpha: 0.5 + predict_method: full_image + predict_overlap: [4, 256, 256] +data: + init_args: + z_window_size: 15 + batch_size: 1 + num_workers: 0 + yx_patch_size: [512, 512] diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml new file mode 100644 index 000000000..6148672f8 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml @@ -0,0 +1,48 @@ +# Shared FCMAE-class (FullyConvolutionalMAE with pretraining=False) fit +# overlay. Model/loss/schedule come from the canonical +# vs_test/finetune_3d.py:load_model recipe; data pipeline (bs=32, z=20, +# yx=384) and lr=0.0004 match the retuned unext2_fit.yml Run 4 baseline +# so the FCMAE runs are directly comparable to the timm-backed unext2 +# job on the same data throughput. Used by both +# fcmae_vscyto3d_scratch.yml and fcmae_vscyto3d_pretrained.yml — +# encoder_only + ckpt_path are set only in the pretrained leaf so init +# is the only difference between the two. +base: + - ../../../../../../recipes/trainer/fit.yml + - ../../../../../../recipes/topology/ddp_4gpu.yml +model: + class_path: dynacell.engine.DynacellUNet + init_args: + architecture: fcmae + model_config: + in_channels: 1 + out_channels: 1 + encoder_blocks: [3, 3, 9, 3] + encoder_drop_path_rate: 0.1 + dims: [96, 192, 384, 768] + decoder_conv_blocks: 2 + stem_kernel_size: [5, 4, 4] + in_stack_depth: 15 + pretraining: false + loss_function: + class_path: viscy_utils.losses.MixedLoss + init_args: + l1_alpha: 0.5 + l2_alpha: 0.0 + ms_dssim_alpha: 0.5 + lr: 0.0004 + schedule: WarmupCosine + warmup_steps: 8500 # ~1 epoch for FCMAE at bs=32, 4 GPUs + warmup_multiplier: 1e-3 +trainer: + # FullyConvolutionalMAE(pretraining=False) has decoder/head params that + # only receive gradients on some forward paths; default ddp with + # find_unused_parameters=False errors at step 1. Matches the canonical + # vs_test/finetune_3d.py:215 recipe. + strategy: ddp_find_unused_parameters_true + precision: 16-mixed + max_epochs: 200 +# HCS data hparams (bs=32, z=20, yx=384, augs) live in +# data_overlays/fcmae_vscyto3d_fit.yml; single-store train leaves compose +# both, joint (BatchedConcatDataModule) leaves compose only this one and +# author data: themselves. diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml new file mode 100644 index 000000000..f59fd24c7 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml @@ -0,0 +1,43 @@ +# FCMAE-class (FullyConvolutionalMAE, pretraining=False) predict overlay. +# Mirrors the model block of fcmae_vscyto3d_fit.yml so Lightning's +# load_from_checkpoint instantiates the matching architecture, then layers +# predict-time hparams. Unlike celldiff_predict / unetvit3d_predict / +# fnet3d_paper_predict, FCMAE has no `recipes/models/.yml` — the +# model block is defined inline in the fit overlay. Duplicating here +# keeps both overlays standalone; consolidate via a shared recipe if +# fcmae cells need joint leaves too. +# Used by both fcmae_vscyto3d_pretrained and fcmae_vscyto3d_scratch +# predict leaves — predict loads the full trained checkpoint, so the +# pretrained-encoder warm-start path (encoder_only + ckpt_path) from the +# fit leaf does NOT belong here. +base: + - ../../../../../../recipes/trainer/predict.yml + - ../../../../../../recipes/topology/single_gpu.yml +model: + class_path: dynacell.engine.DynacellUNet + init_args: + architecture: fcmae + model_config: + in_channels: 1 + out_channels: 1 + encoder_blocks: [3, 3, 9, 3] + encoder_drop_path_rate: 0.1 + dims: [96, 192, 384, 768] + decoder_conv_blocks: 2 + stem_kernel_size: [5, 4, 4] + in_stack_depth: 15 + pretraining: false + loss_function: + class_path: viscy_utils.losses.MixedLoss + init_args: + l1_alpha: 0.5 + l2_alpha: 0.0 + ms_dssim_alpha: 0.5 + predict_method: full_image + predict_overlap: [4, 256, 256] +data: + init_args: + z_window_size: 15 + batch_size: 1 + num_workers: 0 + yx_patch_size: [512, 512] diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/fnet3d_paper_fit.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/fnet3d_paper_fit.yml new file mode 100644 index 000000000..b475307a2 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/fnet3d_paper_fit.yml @@ -0,0 +1,24 @@ +# FNet3D paper-baseline fit overlay — model + trainer only. +# HCS data hparams (including the mean/std Structure normalization and +# the 8-crops-per-FOV sampling that diverge from the CellDiff/UNetViT +# conventions) live in data_overlays/fnet3d_paper_fit.yml; single-store +# train leaves compose both, joint (BatchedConcatDataModule) leaves +# compose only this one and author data: themselves. +# +# Reproduces pytorch_fnet paper defaults on DynaCell data. Reference run +# (launched before this schema existed): +# /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/fnet3d_paper/ +base: + - ../../../../../../recipes/models/fnet3d.yml + - ../../../../../../recipes/trainer/fit.yml + - ../../../../../../recipes/topology/single_gpu.yml +seed_everything: 0 +model: + init_args: + loss_function: + class_path: torch.nn.MSELoss + lr: 0.001 + schedule: Constant +trainer: + precision: 32-true + max_steps: 200000 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/fnet3d_paper_predict.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/fnet3d_paper_predict.yml new file mode 100644 index 000000000..90bee1b0e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/fnet3d_paper_predict.yml @@ -0,0 +1,20 @@ +# FNet3D paper-baseline predict overlay. +# Binds the FNet3D model recipe + predict trainer recipe, then layers +# predict-time model hparams and data-loader settings. +# Predict-time normalizations and data_path are leaf-owned (leaf +# overrides target-inherited values to match each organelle's +# test_cropped store). +base: + - ../../../../../../recipes/models/fnet3d.yml + - ../../../../../../recipes/trainer/predict.yml + - ../../../../../../recipes/topology/single_gpu.yml +model: + init_args: + predict_method: full_image + predict_overlap: [4, 256, 256] +data: + init_args: + z_window_size: 32 + batch_size: 1 + num_workers: 0 + yx_patch_size: [512, 512] diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/pix2pix3d_unetvit_fit.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/pix2pix3d_unetvit_fit.yml new file mode 100644 index 000000000..27482e104 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/pix2pix3d_unetvit_fit.yml @@ -0,0 +1,25 @@ +# pix2pix3d_unetvit fit overlay — model + trainer only. +# HCS data hparams reuse data_overlays/unetvit3d_fit.yml; single-store +# train leaves compose both, joint (BatchedConcatDataModule) leaves +# compose only this one and author data: themselves. +# +# Differs from unetvit3d_fit.yml by splitting the LR into lr_g/lr_d +# (DynacellGAN has no `lr` param — it would raise UnexpectedKeyword). +# +# TTUR: lr_d = lr_g / 10. The 1:1 schedule collapsed to D-dominance +# (loss/d_train -> 1e-4 by epoch ~5; loss/g_adv pinned at 1.0, no +# adversarial gradient). Slowing D 10x is the canonical pix2pix fix. +base: + - ../../../../../../recipes/models/pix2pix3d_unetvit.yml + - ../../../../../../recipes/trainer/fit.yml + - ../../../../../../recipes/topology/single_gpu.yml +model: + init_args: + lr_g: 0.0003 + lr_d: 3.0e-5 + schedule: WarmupCosine + warmup_steps: 8500 + warmup_multiplier: 1e-3 +trainer: + precision: bf16-mixed + max_epochs: 20 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/pix2pix3d_unetvit_fit_ddp.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/pix2pix3d_unetvit_fit_ddp.yml new file mode 100644 index 000000000..2ccf9925b --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/pix2pix3d_unetvit_fit_ddp.yml @@ -0,0 +1,26 @@ +# pix2pix3d_unetvit fit overlay — 4-GPU DDP variant. +# +# Sibling of pix2pix3d_unetvit_fit.yml that swaps in the GAN-aware DDP +# topology (find_unused_parameters=True). All model and trainer hparams +# (lr_g/lr_d/schedule/warmup, precision, max_epochs) match the single-GPU +# overlay — we evaluate DDP at the same per-rank optimization settings. +# +# TTUR: lr_d = lr_g / 10 (see pix2pix3d_unetvit_fit.yml for rationale — +# 1:1 lr collapsed to D-dominance within ~5 epochs on the prior DDP run). +# +# See recipes/topology/ddp_4gpu_gan.yml for why plain ddp_4gpu.yml is not +# compatible with the alternating-optimizer training_step. +base: + - ../../../../../../recipes/models/pix2pix3d_unetvit.yml + - ../../../../../../recipes/trainer/fit.yml + - ../../../../../../recipes/topology/ddp_4gpu_gan.yml +model: + init_args: + lr_g: 0.0003 + lr_d: 3.0e-5 + schedule: WarmupCosine + warmup_steps: 8500 + warmup_multiplier: 1e-3 +trainer: + precision: bf16-mixed + max_epochs: 20 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/pix2pix3d_unetvit_fit_ddp_modernized.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/pix2pix3d_unetvit_fit_ddp_modernized.yml new file mode 100644 index 000000000..48e714c13 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/pix2pix3d_unetvit_fit_ddp_modernized.yml @@ -0,0 +1,31 @@ +# pix2pix3d_unetvit fit overlay — modernized recipe, 4-GPU DDP variant. +# +# Sibling of pix2pix3d_unetvit_fit_modernized.yml that swaps in the +# GAN-aware DDP topology (find_unused_parameters=True). All model knobs +# match the single-GPU modernized overlay — DDP runs at the same per-rank +# settings. +# +# See pix2pix3d_unetvit_fit_modernized.yml for rationale and plan reference. +base: + - ../../../../../../recipes/models/pix2pix3d_unetvit.yml + - ../../../../../../recipes/trainer/fit.yml + - ../../../../../../recipes/topology/ddp_4gpu_gan.yml +model: + init_args: + loss_type: nonsat + lambda_adv: 1.0 + r1_gamma: 10.0 + r2_gamma: 0.0 + r1_every: 16 + ema_kimg: 10.0 + lecam_gamma: 0.0 + lecam_decay: 0.9 + use_ema_at_predict: true + lr_g: 2.0e-4 + lr_d: 2.0e-4 + schedule: WarmupCosine + warmup_steps: 8500 + warmup_multiplier: 1e-3 +trainer: + precision: bf16-mixed + max_epochs: 20 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/pix2pix3d_unetvit_fit_modernized.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/pix2pix3d_unetvit_fit_modernized.yml new file mode 100644 index 000000000..8b9915ca2 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/pix2pix3d_unetvit_fit_modernized.yml @@ -0,0 +1,51 @@ +# pix2pix3d_unetvit fit overlay — STYLEGAN2/R3GAN-STYLE MODERNIZED RECIPE. +# +# Opt-in modernization of the LSGAN baseline. See PR +# https://github.com/mehta-lab/VisCy/pull/428 for the recipe and rationale. +# +# Differences from pix2pix3d_unetvit_fit.yml: +# - loss_type: lsgan -> nonsat (StyleGAN2 default; softplus-based) +# - lr_g/lr_d: 3e-4 / 3e-5 -> 2e-4 / 2e-4 (equal LR; matches StyleGAN2-ADA / +# StyleGAN3 / StyleGAN-XL / R3GAN; the 10x +# D-slower TTUR was a pre-R1-era hack) +# - r1_gamma: 0 -> 10.0 (Mescheder zero-centered grad penalty, +# StyleGAN2 256² default; ablate {1,5,10,20}) +# - r1_every: n/a -> 16 (StyleGAN2 D_reg_interval; lazy reg +# with `* r1_every` unbiased rescaling) +# - ema_kimg: null -> 10.0 (G EMA half-life 10k images; +# StyleGAN2 256² default. Decay derived +# per-step from global batch size.) +# - lambda_adv: implicit 1.0 -> 1.0 (now an explicit knob) +# +# Unchanged from the LSGAN baseline: +# - generator backbone (UNetViT3D) +# - discriminator backbone (MultiScalePatchGAN3D, num_scales=2, SN on) +# - lambda_l1 (=100.0 from recipes/models/pix2pix3d_unetvit.yml) +# - warmup_steps, warmup_multiplier, schedule, precision, max_epochs +# +# Architectural fairness with CellDiff / FNet3D / etc. is preserved: only +# the training RECIPE differs from LSGAN; the model under test is still +# pix2pix3d_unetvit. +base: + - ../../../../../../recipes/models/pix2pix3d_unetvit.yml + - ../../../../../../recipes/trainer/fit.yml + - ../../../../../../recipes/topology/single_gpu.yml +model: + init_args: + loss_type: nonsat + lambda_adv: 1.0 + r1_gamma: 10.0 + r2_gamma: 0.0 + r1_every: 16 + ema_kimg: 10.0 + lecam_gamma: 0.0 + lecam_decay: 0.9 + use_ema_at_predict: true + lr_g: 2.0e-4 + lr_d: 2.0e-4 + schedule: WarmupCosine + warmup_steps: 8500 + warmup_multiplier: 1e-3 +trainer: + precision: bf16-mixed + max_epochs: 20 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml new file mode 100644 index 000000000..0c365440e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml @@ -0,0 +1,21 @@ +# pix2pix3d_unetvit predict overlay. +# Binds the pix2pix3d_unetvit model recipe + predict trainer recipe, then +# layers predict-time model hparams and data-loader settings. +# Predict-time normalizations and data_path are leaf-owned (leaf overrides +# target-inherited values to match each organelle's test_cropped store). +# Predict path uses self.generator only via DynacellGAN.predict_step; the +# discriminator is not exposed at inference. +base: + - ../../../../../../recipes/models/pix2pix3d_unetvit.yml + - ../../../../../../recipes/trainer/predict.yml + - ../../../../../../recipes/topology/single_gpu.yml +model: + init_args: + predict_method: full_image + predict_overlap: [4, 256, 256] +data: + init_args: + z_window_size: 8 + batch_size: 1 + num_workers: 0 + yx_patch_size: [512, 512] diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/unetvit3d_fit.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/unetvit3d_fit.yml new file mode 100644 index 000000000..639bf794b --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/unetvit3d_fit.yml @@ -0,0 +1,20 @@ +# UNetViT3D fit overlay — model + trainer only. +# HCS data hparams live in data_overlays/unetvit3d_fit.yml; single-store +# train leaves compose both, joint (BatchedConcatDataModule) leaves +# compose only this one and author data: themselves. +# +# Hparams (lr, schedule, epochs) match celldiff_fit.yml — the only +# functional difference here is the model class. +base: + - ../../../../../../recipes/models/unetvit3d.yml + - ../../../../../../recipes/trainer/fit.yml + - ../../../../../../recipes/topology/single_gpu.yml +model: + init_args: + lr: 0.0003 + schedule: WarmupCosine + warmup_steps: 8500 + warmup_multiplier: 1e-3 +trainer: + precision: bf16-mixed + max_epochs: 20 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/unetvit3d_predict.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/unetvit3d_predict.yml new file mode 100644 index 000000000..8d784083b --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/unetvit3d_predict.yml @@ -0,0 +1,19 @@ +# UNetViT3D predict overlay. +# Binds the UNetViT3D model recipe + predict trainer recipe, then layers +# predict-time model hparams and data-loader settings. +# Predict-time normalizations and data_path are leaf-owned (leaf overrides +# target-inherited values to match each organelle's test_cropped store). +base: + - ../../../../../../recipes/models/unetvit3d.yml + - ../../../../../../recipes/trainer/predict.yml + - ../../../../../../recipes/topology/single_gpu.yml +model: + init_args: + predict_method: full_image + predict_overlap: [4, 256, 256] +data: + init_args: + z_window_size: 8 + batch_size: 1 + num_workers: 0 + yx_patch_size: [512, 512] diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/unext2_fit.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/unext2_fit.yml new file mode 100644 index 000000000..6b3fe6cef --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/model_overlays/unext2_fit.yml @@ -0,0 +1,30 @@ +# UNeXt2 (VSCyto3D) fit overlay — reproduces the Run 4 SEC61B config +# from legacy commit 46e4c79 (`examples/configs/sec61b/fit_unext2.yml`). +# Architecture: convnextv2_tiny z=15, MixedLoss(L1+DSSIM), 4-GPU DDP. +# +# Earlier runs in the wandb series (20260403-210816, 20260406-094805, +# 20260406-225302) used lr=0.0002, bs=8, z=15; this overlay reproduces the +# retuned Run 4 (20260409-020023) with lr=0.0004, bs=32, z=20. +base: + - ../../../../../../recipes/models/unext2_3d.yml + - ../../../../../../recipes/trainer/fit.yml + - ../../../../../../recipes/topology/ddp_4gpu.yml +model: + init_args: + loss_function: + class_path: viscy_utils.losses.MixedLoss + init_args: + l1_alpha: 0.5 + l2_alpha: 0.0 + ms_dssim_alpha: 0.5 + lr: 0.0004 + schedule: WarmupCosine + warmup_steps: 8500 + warmup_multiplier: 1e-3 +trainer: + precision: 16-mixed + max_epochs: 200 +# HCS data hparams (bs=32, z=20, yx=384, augs) live in +# data_overlays/unext2_fit.yml; single-store train leaves compose both, +# joint (BatchedConcatDataModule) leaves compose only this one and +# author data: themselves. diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_caax_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_caax_denv.yml new file mode 100644 index 000000000..2bb5765d9 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_caax_denv.yml @@ -0,0 +1,12 @@ +# Predict set: A549 mantis CAAX on DENV (condition-pooled test store). +# data_path resolves to the test store in predict mode via dataset_ref. +# Pool naming inside the store is sequential 0/0/fov; plate +# provenance lives in per-position zattrs and the colocated +# .provenance.json sidecar (see dynacell-paper assemble-pool docs). +benchmark: + predict_set: a549_mantis_caax_denv + dataset_ref: + dataset: a549-mantis-caax-denv +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: {} diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_caax_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_caax_mock.yml new file mode 100644 index 000000000..f027f45ec --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_caax_mock.yml @@ -0,0 +1,12 @@ +# Predict set: A549 mantis CAAX on mock (condition-pooled test store). +# data_path resolves to the test store in predict mode via dataset_ref. +# Pool naming inside the store is sequential 0/0/fov; plate +# provenance lives in per-position zattrs and the colocated +# .provenance.json sidecar (see dynacell-paper assemble-pool docs). +benchmark: + predict_set: a549_mantis_caax_mock + dataset_ref: + dataset: a549-mantis-caax-mock +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: {} diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_caax_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_caax_zikv.yml new file mode 100644 index 000000000..e9b81a8ae --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_caax_zikv.yml @@ -0,0 +1,12 @@ +# Predict set: A549 mantis CAAX on ZIKV (condition-pooled test store). +# data_path resolves to the test store in predict mode via dataset_ref. +# Pool naming inside the store is sequential 0/0/fov; plate +# provenance lives in per-position zattrs and the colocated +# .provenance.json sidecar (see dynacell-paper assemble-pool docs). +benchmark: + predict_set: a549_mantis_caax_zikv + dataset_ref: + dataset: a549-mantis-caax-zikv +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: {} diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_dual_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_dual_denv.yml new file mode 100644 index 000000000..07e196ca7 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_dual_denv.yml @@ -0,0 +1,12 @@ +# Predict set: A549 mantis dual Nuclei+Membrane prediction on DENV plate. +# No dataset_ref: see predict_sets/ipsc_confocal_dual.yml for rationale. +# Phase3D is byte-equal between CAAX_DENV.ozx and H2B_DENV.ozx (verified at +# Phase 0); CAAX is the canonical pick for input source. +benchmark: + predict_set: a549_mantis_dual_denv +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_DENV.ozx + source_channel: [Phase3D] + target_channel: [Nuclei, Membrane] diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_dual_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_dual_mock.yml new file mode 100644 index 000000000..8ad79a38d --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_dual_mock.yml @@ -0,0 +1,12 @@ +# Predict set: A549 mantis dual Nuclei+Membrane prediction on mock plate. +# No dataset_ref: see predict_sets/ipsc_confocal_dual.yml for rationale. +# Phase3D is byte-equal between CAAX_mock.ozx and H2B_mock.ozx (verified at +# Phase 0); CAAX is the canonical pick for input source. +benchmark: + predict_set: a549_mantis_dual_mock +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_mock.ozx + source_channel: [Phase3D] + target_channel: [Nuclei, Membrane] diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_dual_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_dual_zikv.yml new file mode 100644 index 000000000..0bcd4ff8f --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_dual_zikv.yml @@ -0,0 +1,12 @@ +# Predict set: A549 mantis dual Nuclei+Membrane prediction on ZIKV plate. +# No dataset_ref: see predict_sets/ipsc_confocal_dual.yml for rationale. +# Phase3D is byte-equal between CAAX_ZIKV.ozx and H2B_ZIKV.ozx (verified at +# Phase 0); CAAX is the canonical pick for input source. +benchmark: + predict_set: a549_mantis_dual_zikv +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_ZIKV.ozx + source_channel: [Phase3D] + target_channel: [Nuclei, Membrane] diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_h2b_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_h2b_denv.yml new file mode 100644 index 000000000..896520228 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_h2b_denv.yml @@ -0,0 +1,12 @@ +# Predict set: A549 mantis H2B on DENV (condition-pooled test store). +# data_path resolves to the test store in predict mode via dataset_ref. +# Pool naming inside the store is sequential 0/0/fov; plate +# provenance lives in per-position zattrs and the colocated +# .provenance.json sidecar (see dynacell-paper assemble-pool docs). +benchmark: + predict_set: a549_mantis_h2b_denv + dataset_ref: + dataset: a549-mantis-h2b-denv +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: {} diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_h2b_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_h2b_mock.yml new file mode 100644 index 000000000..f54707ef4 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_h2b_mock.yml @@ -0,0 +1,12 @@ +# Predict set: A549 mantis H2B on mock (condition-pooled test store). +# data_path resolves to the test store in predict mode via dataset_ref. +# Pool naming inside the store is sequential 0/0/fov; plate +# provenance lives in per-position zattrs and the colocated +# .provenance.json sidecar (see dynacell-paper assemble-pool docs). +benchmark: + predict_set: a549_mantis_h2b_mock + dataset_ref: + dataset: a549-mantis-h2b-mock +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: {} diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_h2b_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_h2b_zikv.yml new file mode 100644 index 000000000..b067a18da --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_h2b_zikv.yml @@ -0,0 +1,12 @@ +# Predict set: A549 mantis H2B on ZIKV (condition-pooled test store). +# data_path resolves to the test store in predict mode via dataset_ref. +# Pool naming inside the store is sequential 0/0/fov; plate +# provenance lives in per-position zattrs and the colocated +# .provenance.json sidecar (see dynacell-paper assemble-pool docs). +benchmark: + predict_set: a549_mantis_h2b_zikv + dataset_ref: + dataset: a549-mantis-h2b-zikv +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: {} diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_sec61b_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_sec61b_denv.yml new file mode 100644 index 000000000..0b4910aba --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_sec61b_denv.yml @@ -0,0 +1,12 @@ +# Predict set: A549 mantis SEC61B on DENV (condition-pooled test store). +# data_path resolves to the test store in predict mode via dataset_ref. +# Pool naming inside the store is sequential 0/0/fov; plate +# provenance lives in per-position zattrs and the colocated +# .provenance.json sidecar (see dynacell-paper assemble-pool docs). +benchmark: + predict_set: a549_mantis_sec61b_denv + dataset_ref: + dataset: a549-mantis-sec61b-denv +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: {} diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_sec61b_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_sec61b_mock.yml new file mode 100644 index 000000000..d4ae2a646 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_sec61b_mock.yml @@ -0,0 +1,12 @@ +# Predict set: A549 mantis SEC61B on mock (condition-pooled test store). +# data_path resolves to the test store in predict mode via dataset_ref. +# Pool naming inside the store is sequential 0/0/fov; plate +# provenance lives in per-position zattrs and the colocated +# .provenance.json sidecar (see dynacell-paper assemble-pool docs). +benchmark: + predict_set: a549_mantis_sec61b_mock + dataset_ref: + dataset: a549-mantis-sec61b-mock +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: {} diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_sec61b_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_sec61b_zikv.yml new file mode 100644 index 000000000..c5e2ae9d4 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_sec61b_zikv.yml @@ -0,0 +1,12 @@ +# Predict set: A549 mantis SEC61B on ZIKV (condition-pooled test store). +# data_path resolves to the test store in predict mode via dataset_ref. +# Pool naming inside the store is sequential 0/0/fov; plate +# provenance lives in per-position zattrs and the colocated +# .provenance.json sidecar (see dynacell-paper assemble-pool docs). +benchmark: + predict_set: a549_mantis_sec61b_zikv + dataset_ref: + dataset: a549-mantis-sec61b-zikv +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: {} diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_tomm20_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_tomm20_denv.yml new file mode 100644 index 000000000..c86d0293c --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_tomm20_denv.yml @@ -0,0 +1,12 @@ +# Predict set: A549 mantis TOMM20 on DENV (condition-pooled test store). +# data_path resolves to the test store in predict mode via dataset_ref. +# Pool naming inside the store is sequential 0/0/fov; plate +# provenance lives in per-position zattrs and the colocated +# .provenance.json sidecar (see dynacell-paper assemble-pool docs). +benchmark: + predict_set: a549_mantis_tomm20_denv + dataset_ref: + dataset: a549-mantis-tomm20-denv +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: {} diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_tomm20_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_tomm20_mock.yml new file mode 100644 index 000000000..ec58c2793 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_tomm20_mock.yml @@ -0,0 +1,12 @@ +# Predict set: A549 mantis TOMM20 on mock (condition-pooled test store). +# data_path resolves to the test store in predict mode via dataset_ref. +# Pool naming inside the store is sequential 0/0/fov; plate +# provenance lives in per-position zattrs and the colocated +# .provenance.json sidecar (see dynacell-paper assemble-pool docs). +benchmark: + predict_set: a549_mantis_tomm20_mock + dataset_ref: + dataset: a549-mantis-tomm20-mock +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: {} diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_tomm20_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_tomm20_zikv.yml new file mode 100644 index 000000000..b5508b18d --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/a549_mantis_tomm20_zikv.yml @@ -0,0 +1,12 @@ +# Predict set: A549 mantis TOMM20 on ZIKV (condition-pooled test store). +# data_path resolves to the test store in predict mode via dataset_ref. +# Pool naming inside the store is sequential 0/0/fov; plate +# provenance lives in per-position zattrs and the colocated +# .provenance.json sidecar (see dynacell-paper assemble-pool docs). +benchmark: + predict_set: a549_mantis_tomm20_zikv + dataset_ref: + dataset: a549-mantis-tomm20-zikv +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: {} diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/ipsc_confocal.yml new file mode 100644 index 000000000..2fa30db3b --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/ipsc_confocal.yml @@ -0,0 +1,9 @@ +# Predict set: AICS iPSC confocal, self-predict against test_cropped/. +# data_path resolves to the test store in predict mode via dataset_ref. +benchmark: + predict_set: ipsc_confocal + dataset_ref: + dataset: aics-hipsc +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: {} diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/ipsc_confocal_dual.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/ipsc_confocal_dual.yml new file mode 100644 index 000000000..ddda6486e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/predict_sets/ipsc_confocal_dual.yml @@ -0,0 +1,13 @@ +# Predict set: AICS iPSC confocal, dual Nuclei+Membrane prediction. +# No dataset_ref: dual target_channel cannot be resolved via the +# single-target manifest schema (see targets/dual_nucl_memb.yml). +# data_path / source_channel / target_channel are spliced inline so +# leaves don't need to override the resolver. +benchmark: + predict_set: ipsc_confocal_dual +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell.zarr + source_channel: [Phase3D] + target_channel: [Nuclei, Membrane] diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/targets/dual_nucl_memb.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/targets/dual_nucl_memb.yml new file mode 100644 index 000000000..89c512ec2 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/targets/dual_nucl_memb.yml @@ -0,0 +1,47 @@ +# Target: dual nucleus + membrane (paired Nuclei and Membrane channels in +# one zarr, both predicted from Phase3D). Used by Track B (cytoland / +# infection-FT no-FT predicts) and Track C (cytoland / infection-FT + +# dynacell FT) — both expect 2-channel `DynacellUNet` outputs in one zarr. +# +# Deliberately NO `dataset_ref.target` — the manifest schema has one +# `target_channel` per entry, and there is no single-target manifest row +# that declares both Nuclei + Membrane. Leaves composing this fragment +# inline `data.init_args.data_path` + `source_channel` + `target_channel` +# in the predict_set / train_set fragment (see predict_sets/ipsc_confocal_dual.yml +# and train_sets/{ipsc_confocal_dual,a549_mantis_dual}.yml). +# +# Normalization parameters copied verbatim from targets/nucleus.yml and +# targets/membrane.yml (Phase3D fov mean/std; Nuclei + Membrane fov median/iqr). +# `RandWeightedCropd.w_key: Nuclei` is a sampling-bias choice for Track C +# dual fine-tuning (revisit if validation loss for Membrane channel diverges). +benchmark: + target: dual_nucl_memb + target_id: dual_nucl_memb +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Nuclei] + level: fov_statistics + subtrahend: median + divisor: iqr + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Membrane] + level: fov_statistics + subtrahend: median + divisor: iqr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Nuclei, Membrane] + w_key: Nuclei + spatial_size: [15, 600, 600] + num_samples: 2 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/targets/er_sec61b.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/targets/er_sec61b.yml new file mode 100644 index 000000000..93d5def22 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/targets/er_sec61b.yml @@ -0,0 +1,30 @@ +# Target: ER (SEC61B marker). data_path / source_channel / target_channel +# resolved from the manifest via dataset_ref. +benchmark: + target: er + gene: SEC61B + target_id: er_sec61b + dataset_ref: + target: sec61b +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Structure] + level: fov_statistics + subtrahend: median + divisor: iqr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Structure] + w_key: Structure + spatial_size: [13, 624, 624] + num_samples: 2 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/targets/er_sec61b_celldiff.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/targets/er_sec61b_celldiff.yml new file mode 100644 index 000000000..fffed67ef --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/targets/er_sec61b_celldiff.yml @@ -0,0 +1,27 @@ +# Target: ER (SEC61B marker). data_path / source_channel / target_channel +# resolved from the manifest via dataset_ref. +# CellDiff variant: uses MinMaxSampled (p1/p99 → [-1,1]) instead of NormalizeSampled. +benchmark: + target: er + gene: SEC61B + target_id: er_sec61b + dataset_ref: + target: sec61b +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Structure] + level: timepoint_statistics + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Structure] + w_key: Structure + spatial_size: [13, 624, 624] + num_samples: 2 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/targets/membrane.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/targets/membrane.yml new file mode 100644 index 000000000..e4d9fc45a --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/targets/membrane.yml @@ -0,0 +1,30 @@ +# Target: membrane (Membrane channel of the multi-marker cell.zarr). data_path / +# source_channel / target_channel resolved from the manifest via dataset_ref. +benchmark: + target: membrane + gene: Membrane + target_id: membrane + dataset_ref: + target: membrane +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Membrane] + level: fov_statistics + subtrahend: median + divisor: iqr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Membrane] + w_key: Membrane + spatial_size: [13, 624, 624] + num_samples: 2 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/targets/membrane_celldiff.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/targets/membrane_celldiff.yml new file mode 100644 index 000000000..a242885a1 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/targets/membrane_celldiff.yml @@ -0,0 +1,27 @@ +# Target: membrane (Membrane channel of the multi-marker cell.zarr). data_path / +# source_channel / target_channel resolved from the manifest via dataset_ref. +# CellDiff variant: uses MinMaxSampled (p1/p99 → [-1,1]) instead of NormalizeSampled. +benchmark: + target: membrane + gene: Membrane + target_id: membrane + dataset_ref: + target: membrane +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Membrane] + level: timepoint_statistics + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Membrane] + w_key: Membrane + spatial_size: [13, 624, 624] + num_samples: 2 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/targets/mito_tomm20.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/targets/mito_tomm20.yml new file mode 100644 index 000000000..0a96af1bf --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/targets/mito_tomm20.yml @@ -0,0 +1,30 @@ +# Target: mitochondria (TOMM20 marker). data_path / source_channel / +# target_channel resolved from the manifest via dataset_ref. +benchmark: + target: mito + gene: TOMM20 + target_id: mito_tomm20 + dataset_ref: + target: tomm20 +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Structure] + level: fov_statistics + subtrahend: median + divisor: iqr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Structure] + w_key: Structure + spatial_size: [13, 624, 624] + num_samples: 2 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/targets/mito_tomm20_celldiff.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/targets/mito_tomm20_celldiff.yml new file mode 100644 index 000000000..ce63f1792 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/targets/mito_tomm20_celldiff.yml @@ -0,0 +1,27 @@ +# Target: mitochondria (TOMM20 marker). data_path / source_channel / +# target_channel resolved from the manifest via dataset_ref. +# CellDiff variant: uses MinMaxSampled (p1/p99 → [-1,1]) instead of NormalizeSampled. +benchmark: + target: mito + gene: TOMM20 + target_id: mito_tomm20 + dataset_ref: + target: tomm20 +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Structure] + level: timepoint_statistics + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Structure] + w_key: Structure + spatial_size: [13, 624, 624] + num_samples: 2 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/targets/nucleus.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/targets/nucleus.yml new file mode 100644 index 000000000..156ee8e39 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/targets/nucleus.yml @@ -0,0 +1,30 @@ +# Target: nucleus (Nuclei channel of the multi-marker cell.zarr). data_path / +# source_channel / target_channel resolved from the manifest via dataset_ref. +benchmark: + target: nucleus + gene: Nuclei + target_id: nucleus + dataset_ref: + target: nucleus +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Nuclei] + level: fov_statistics + subtrahend: median + divisor: iqr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Nuclei] + w_key: Nuclei + spatial_size: [13, 624, 624] + num_samples: 2 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/targets/nucleus_celldiff.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/targets/nucleus_celldiff.yml new file mode 100644 index 000000000..cced0add4 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/targets/nucleus_celldiff.yml @@ -0,0 +1,27 @@ +# Target: nucleus (Nuclei channel of the multi-marker cell.zarr). data_path / +# source_channel / target_channel resolved from the manifest via dataset_ref. +# CellDiff variant: uses MinMaxSampled (p1/p99 → [-1,1]) instead of NormalizeSampled. +benchmark: + target: nucleus + gene: Nuclei + target_id: nucleus + dataset_ref: + target: nucleus +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Nuclei] + level: timepoint_statistics + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Nuclei] + w_key: Nuclei + spatial_size: [13, 624, 624] + num_samples: 2 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/train_sets/a549_mantis.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/train_sets/a549_mantis.yml new file mode 100644 index 000000000..d87e637e5 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/train_sets/a549_mantis.yml @@ -0,0 +1,30 @@ +# Train set: A549 mantis-lightsheet, condition-pooled (mock + DENV + ZIKV +# all in one store per target). Pooled stores live at +# `/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/_all.zarr` +# and are NOT registered in the canonical manifest registry — the +# per-treatment manifests under `a549-mantis/-/` +# point at per-treatment ozx files (used for predict/eval), not the +# pooled train zarrs. +# +# Because there is no canonical manifest for the pooled train stores, +# leaves consuming this fragment author `data.init_args.{data_path, +# target_channel}` inline (no resolver) — same shape as the joint +# leaves, but with a single HCSDataModule child instead of two. +# +# `dataset_ref` is intentionally not set: the resolver hook +# (`_compose_hook._dynacell_ref_resolver`) is a strict partial-ref +# no-op when `dataset_ref.dataset` is missing, so composing +# `targets/.yml` (which sets `dataset_ref.target`) alongside this +# fragment is safe and keeps the per-target normalizations / +# augmentations from the target fragment. +benchmark: + train_set: a549_mantis + dataset_group: a549-mantis +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: + source_channel: Phase3D + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/train_sets/a549_mantis_dual.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/train_sets/a549_mantis_dual.yml new file mode 100644 index 000000000..ee1b595f5 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/train_sets/a549_mantis_dual.yml @@ -0,0 +1,22 @@ +# Train set: A549 mantis, dual Nuclei+Membrane targets in one zarr. +# Points at the fused 2-channel store produced by Phase 0 +# (applications/dynacell/tools/fuse_a549_dual_channel_zarr.py); the two +# pooled-per-marker train stores (CAAX_all.zarr, H2B_all.zarr) are channel- +# extracted views of the same source plate (verified via source_position +# join; Phase 0 also gates on Phase3D byte-equality between the two views). +# +# Used by Track C1 (vscyto3d_cytolandft/a549_mantis) and Track C2 +# (vscyto3d_infectionft_dynacellft/a549_mantis) train leaves. +benchmark: + train_set: a549_mantis + dataset_group: a549-mantis +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: + data_path: /hpc/projects/comp.micro/virtual_staining/datasets/dynacell/a549_mantis_dual_nucl_memb_all.zarr + source_channel: [Phase3D] + target_channel: [Nuclei, Membrane] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/train_sets/ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/train_sets/ipsc_confocal.yml new file mode 100644 index 000000000..e5e3f78a7 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/train_sets/ipsc_confocal.yml @@ -0,0 +1,15 @@ +# Train set: AICS iPSC confocal. dataset_ref.dataset lives here (dataset +# identity is train_set-scoped); target fragments carry dataset_ref.target +# and declare source_channel themselves. +benchmark: + train_set: ipsc_confocal + dataset_group: aics-hipsc + dataset_ref: + dataset: aics-hipsc +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/train_sets/ipsc_confocal_dual.yml b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/train_sets/ipsc_confocal_dual.yml new file mode 100644 index 000000000..832a1f0a4 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/_internal/shared/model/train_sets/ipsc_confocal_dual.yml @@ -0,0 +1,20 @@ +# Train set: AICS iPSC confocal, dual Nuclei+Membrane targets. Mirrors +# train_sets/ipsc_confocal.yml but bypasses the manifest resolver because +# dataset_ref.target can only carry one organelle key — the dual targets +# (Nuclei + Membrane) are inlined here. +# +# Used by Track C1 (vscyto3d_cytolandft/ipsc_confocal) and Track C2 +# (vscyto3d_infectionft_dynacellft/ipsc_confocal) train leaves. +benchmark: + train_set: ipsc_confocal + dataset_group: aics-hipsc +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/cell.zarr + source_channel: [Phase3D] + target_channel: [Nuclei, Membrane] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..9933cf9ea --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: ER (SEC61B) trained on A549 mantis, predicting against a549_mantis_sec61b_denv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_denv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: a549_mantis + predict_set: a549_mantis_sec61b_denv + model_name: celldiff + experiment_id: er__a549_mantis__celldiff__a549_mantis_sec61b_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/sec61b/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_celldiff_r2_a549trained_denv.zarr + +launcher: + job_name: CELLDiff_A549_PRED_SEC61B_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..7e6d82de4 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: ER (SEC61B) trained on A549 mantis, predicting against a549_mantis_sec61b_mock test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_mock.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: a549_mantis + predict_set: a549_mantis_sec61b_mock + model_name: celldiff + experiment_id: er__a549_mantis__celldiff__a549_mantis_sec61b_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/sec61b/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_celldiff_r2_a549trained_mock.zarr + +launcher: + job_name: CELLDiff_A549_PRED_SEC61B_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..462a31c6f --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: ER (SEC61B) trained on A549 mantis, predicting against a549_mantis_sec61b_zikv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_zikv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: a549_mantis + predict_set: a549_mantis_sec61b_zikv + model_name: celldiff + experiment_id: er__a549_mantis__celldiff__a549_mantis_sec61b_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/sec61b/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_celldiff_r2_a549trained_zikv.zarr + +launcher: + job_name: CELLDiff_A549_PRED_SEC61B_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..e9993ec75 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: ER (SEC61B) trained on A549 mantis, predicting against ipsc_confocal test (OOD). +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: a549_mantis + predict_set: ipsc_confocal + model_name: celldiff + experiment_id: er__a549_mantis__celldiff__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/sec61b/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 40 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_celldiff_r2_a549trained.zarr + +launcher: + job_name: CELLDiff_A549_PRED_SEC61B_ON_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/a549_mantis/train.yml new file mode 100644 index 000000000..946053d7a --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/a549_mantis/train.yml @@ -0,0 +1,42 @@ +# CellDiff fit on ER (SEC61B marker) — A549 mantis-lightsheet pooled (mock + DENV + ZIKV). +base: + - ../../../_internal/shared/model/train_sets/a549_mantis.yml + - ../../../_internal/shared/model/targets/er_sec61b_celldiff.yml + - ../../../_internal/shared/model/data_overlays/celldiff_fit.yml + - ../../../_internal/shared/model/model_overlays/celldiff_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + train_set: a549_mantis + model_name: celldiff + experiment_id: er__a549_mantis__celldiff + +trainer: + logger: + init_args: + name: CELLDiff_A549_SEC61B + save_dir: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/sec61b/celldiff_r2 + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + every_n_epochs: 1 + save_top_k: -1 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/sec61b/celldiff_r2/checkpoints + +data: + init_args: + # A549 pooled store + target_channel — no resolver in this train_set. + target_channel: Structure + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/SEC61B_all.zarr + +launcher: + job_name: CELLDiff_A549_SEC61B + run_root: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/sec61b/celldiff_r2 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 100644 index 000000000..deb70ecb5 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: ER (SEC61B) predicted by CellDiff on a549-mantis-sec61b-denv. +defaults: + - override /target: er_sec61b + - override /predict_set: a549_mantis_sec61b_denv + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_celldiff_iterative__sec61b_denv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_sec61b_celldiff_iterative__sec61b_denv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 100644 index 000000000..6a78964f9 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: ER (SEC61B) predicted by CellDiff on a549-mantis-sec61b-mock. +defaults: + - override /target: er_sec61b + - override /predict_set: a549_mantis_sec61b_mock + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_celldiff_iterative__sec61b_mock.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_sec61b_celldiff_iterative__sec61b_mock diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 100644 index 000000000..acd167801 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: ER (SEC61B) predicted by CellDiff on a549-mantis-sec61b-zikv. +defaults: + - override /target: er_sec61b + - override /predict_set: a549_mantis_sec61b_zikv + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_celldiff_iterative__sec61b_zikv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_sec61b_celldiff_iterative__sec61b_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/eval__ipsc_confocal.yaml new file mode 100644 index 000000000..e11c6a274 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/eval__ipsc_confocal.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: ER (SEC61B) predicted by CellDiff on iPSC confocal. +defaults: + - override /target: er_sec61b + - override /predict_set: ipsc_confocal + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_celldiff_iterative.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/eval_sec61b_celldiff_iterative diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..21d73c326 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/predict__a549_mantis_denv.yml @@ -0,0 +1,42 @@ +# CellDiff predict: ER (SEC61B) trained on iPSC, predicting against a549_mantis_sec61b_denv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_denv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: ipsc_confocal + predict_set: a549_mantis_sec61b_denv + model_name: celldiff + experiment_id: er__ipsc_confocal__celldiff__a549_mantis_sec61b_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/sec61b/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative # denoise, generate, sliding_window, or iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 # 8 for denoise and generate, 48 for iterative and sliding_window. + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_celldiff_r2_iterative__sec61b_denv.zarr + +launcher: + job_name: CELLDiff_PRED_SEC61B_ON_A549_sec61b_denv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..f804ff5cf --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/predict__a549_mantis_mock.yml @@ -0,0 +1,42 @@ +# CellDiff predict: ER (SEC61B) trained on iPSC, predicting against a549_mantis_sec61b_mock test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_mock.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: ipsc_confocal + predict_set: a549_mantis_sec61b_mock + model_name: celldiff + experiment_id: er__ipsc_confocal__celldiff__a549_mantis_sec61b_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/sec61b/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative # denoise, generate, sliding_window, or iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 # 8 for denoise and generate, 40 for iterative and sliding_window. + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_celldiff_r2_iterative__sec61b_mock.zarr + +launcher: + job_name: CELLDiff_PRED_SEC61B_ON_A549_sec61b_mock + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..cc8fd0a1e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/predict__a549_mantis_zikv.yml @@ -0,0 +1,42 @@ +# CellDiff predict: ER (SEC61B) trained on iPSC, predicting against a549_mantis_sec61b_zikv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_zikv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: ipsc_confocal + predict_set: a549_mantis_sec61b_zikv + model_name: celldiff + experiment_id: er__ipsc_confocal__celldiff__a549_mantis_sec61b_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/sec61b/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative # denoise, generate, sliding_window, or iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 # 8 for denoise and generate, 40 for iterative and sliding_window. + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_celldiff_r2_iterative__sec61b_zikv.zarr + +launcher: + job_name: CELLDiff_PRED_SEC61B_ON_A549_sec61b_zikv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/predict__ipsc_confocal__denoise.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/predict__ipsc_confocal__denoise.yml new file mode 100644 index 000000000..9df6078d8 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/predict__ipsc_confocal__denoise.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: ER (SEC61B) on ipsc_confocal — denoise method. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: celldiff + experiment_id: er__ipsc_confocal__celldiff__ipsc_confocal__denoise + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/sec61b/celldiff_r2/checkpoints/last.ckpt + predict_method: denoise + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 8 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_celldiff_r2_denoise.zarr + +launcher: + job_name: CELLDiff_PRED_SEC61B_DN + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/predict__ipsc_confocal__iterative.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/predict__ipsc_confocal__iterative.yml new file mode 100644 index 000000000..dc89acf8e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/predict__ipsc_confocal__iterative.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: ER (SEC61B) on ipsc_confocal — iterative method. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: celldiff + experiment_id: er__ipsc_confocal__celldiff__ipsc_confocal__iterative + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/sec61b/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 40 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_celldiff_r2_iterative.zarr + +launcher: + job_name: CELLDiff_PRED_SEC61B_ITER + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/predict__ipsc_confocal__sliding_window.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/predict__ipsc_confocal__sliding_window.yml new file mode 100644 index 000000000..820af3536 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/predict__ipsc_confocal__sliding_window.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: ER (SEC61B) on ipsc_confocal — sliding_window method. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: celldiff + experiment_id: er__ipsc_confocal__celldiff__ipsc_confocal__sliding_window + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/sec61b/celldiff_r2/checkpoints/last.ckpt + predict_method: sliding_window + predict_overlap: [0, 0, 0] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 40 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_celldiff_r2_sliding_window.zarr + +launcher: + job_name: CELLDiff_PRED_SEC61B_SW + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/train.yml new file mode 100644 index 000000000..9d96e154a --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/ipsc_confocal/train.yml @@ -0,0 +1,36 @@ +# CellDiff fit on ER (SEC61B marker) — AICS iPSC confocal. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/er_sec61b_celldiff.yml + - ../../../_internal/shared/model/data_overlays/celldiff_fit.yml + - ../../../_internal/shared/model/model_overlays/celldiff_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + train_set: ipsc_confocal + model_name: celldiff + experiment_id: er__ipsc_confocal__celldiff + +trainer: + logger: + init_args: + name: CELLDiff_iPSC_SEC61B + save_dir: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/sec61b/celldiff_r2 + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + every_n_epochs: 1 + save_top_k: -1 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/sec61b/celldiff_r2/checkpoints + +launcher: + job_name: CELLDiff_SEC61B + run_root: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/sec61b/celldiff_r2 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..f39c43165 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: ER (SEC61B) trained on joint iPSC+A549, predicting against a549_mantis_sec61b_denv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_denv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_sec61b_denv + model_name: celldiff + experiment_id: er__joint_ipsc_confocal_a549_mantis__celldiff__a549_mantis_sec61b_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/sec61b/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/sec61b_celldiff_r2_denv.zarr + +launcher: + job_name: CELLDiff_JOINT_PRED_SEC61B_ON_A549_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..b829c3aa4 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: ER (SEC61B) trained on joint iPSC+A549, predicting against a549_mantis_sec61b_mock test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_mock.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_sec61b_mock + model_name: celldiff + experiment_id: er__joint_ipsc_confocal_a549_mantis__celldiff__a549_mantis_sec61b_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/sec61b/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/sec61b_celldiff_r2_mock.zarr + +launcher: + job_name: CELLDiff_JOINT_PRED_SEC61B_ON_A549_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..06fa202d2 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: ER (SEC61B) trained on joint iPSC+A549, predicting against a549_mantis_sec61b_zikv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_zikv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_sec61b_zikv + model_name: celldiff + experiment_id: er__joint_ipsc_confocal_a549_mantis__celldiff__a549_mantis_sec61b_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/sec61b/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/sec61b_celldiff_r2_zikv.zarr + +launcher: + job_name: CELLDiff_JOINT_PRED_SEC61B_ON_A549_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..d02c88a9f --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: ER (SEC61B) trained on joint iPSC+A549, predicting against ipsc_confocal test. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: ipsc_confocal + model_name: celldiff + experiment_id: er__joint_ipsc_confocal_a549_mantis__celldiff__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/sec61b/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 40 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions/sec61b_celldiff_r2.zarr + +launcher: + job_name: CELLDiff_JOINT_PRED_SEC61B_ON_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/joint_ipsc_confocal_a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/joint_ipsc_confocal_a549_mantis/train.yml new file mode 100644 index 000000000..9b8d5d6fe --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/joint_ipsc_confocal_a549_mantis/train.yml @@ -0,0 +1,148 @@ +# CellDiff fit on ER (SEC61B) — joint ipsc_confocal + a549_mantis_2024_11_07. +# +# First joint train leaf per Stage 7 of A549_EXPANSION_ROADMAP.md. +# Uses BatchedConcatDataModule with two explicit HCSDataModule children +# (no benchmark.dataset_ref — joint leaves bypass the single-dataset +# resolver). Only model_overlays/celldiff_fit.yml is composed; the data +# block is authored inline because joint hparams live on the children. +# +# Topology: single H200, single GPU — same as celldiff/ipsc_confocal/train.yml. +# The paper baseline pattern is single-GPU and we keep that here so +# iPSC-only and joint runs are apples-to-apples. +base: + - ../../../_internal/shared/model/model_overlays/celldiff_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + gene: SEC61B + target: er + target_id: er_sec61b + train_set: joint_ipsc_confocal_a549_mantis + model_name: celldiff + experiment_id: er__joint_ipsc_confocal_a549_mantis__celldiff + +trainer: + logger: + init_args: + name: CELLDiff_JOINT_SEC61B + save_dir: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/sec61b/celldiff_r2 + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + every_n_epochs: 1 + save_top_k: -1 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/sec61b/celldiff_r2/checkpoints + +# Child HCSDataModule init_args shared across both datasets (only data_path +# differs). Factored as a YAML anchor so the joint leaf stays auditable; +# this is the first joint leaf — if the pattern sticks we can promote to a +# reusable fragment. +# +# Naming convention: top-level keys starting with `_` are private to the +# YAML compose layer and are stripped by `load_composed_config` before +# the dict reaches LightningCLI / jsonargparse (which would reject them +# as unknown options). The merge expansion under `data:` survives. +_hcs_init_args: &hcs_init_args + source_channel: Phase3D + target_channel: Structure + z_window_size: 13 + # batch_size=2 (not the celldiff_fit.yml default of 4): BatchedConcatDataModule + # does NOT divide by num_samples (see CLAUDE.md), so 4 × num_samples=2 = 8 GPU + # samples/step OOMs H200 140 GiB at unet/blocks.py:187 (h + res_conv(x)). + batch_size: 2 + num_workers: 4 + yx_patch_size: [512, 512] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Structure] + level: timepoint_statistics + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Structure] + w_key: Structure + spatial_size: [13, 624, 624] + num_samples: 2 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + safe_crop_size: [8, 512, 512] + safe_crop_coverage: 0.9 + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + # ipsc_confocal — aics-hipsc SEC61B train store + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/SEC61B.zarr + # a549_mantis — pooled SEC61B all-conditions train store (mantis_v1/train/SEC61B_all.zarr) + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/SEC61B_all.zarr + +launcher: + job_name: CELLDiff_JOINT_SEC61B + run_root: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/sec61b/celldiff_r2 + # Joint preloads two stores (iPSC + A549 pool) into /dev/shm; the default + # 256G cap is too tight (256G iPSC mem + ~50G A549 + worker peak OOMs). + # 512G is the smallest tier that fits joint preload + worker overhead. + sbatch: + mem: "512G" diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/joint_ipsc_confocal_a549_mantis/train_smoke.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/joint_ipsc_confocal_a549_mantis/train_smoke.yml new file mode 100644 index 000000000..60bac2fe9 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/joint_ipsc_confocal_a549_mantis/train_smoke.yml @@ -0,0 +1,141 @@ +# Joint smoke variant of train.yml — small iPSC zarr, single H200, 30-min wall. +# +# Purpose: validate joint compose / instantiate / training-loop end-to-end +# without the 250 GB+ mmap_preload staging that blows a smoke wall. Pair +# this leaf with `--override trainer.fast_dev_run=true` (or +# `--override trainer.max_steps=5`) at submit time to bound the run. +# +# Why a sibling leaf rather than --override at submit time: dotlist / +# bracket syntax (`data.init_args.data_modules.0.init_args.data_path=...`) +# does not index into list elements via submit_benchmark_job.py's override +# parser. Pre-swapping data_paths in a sibling leaf is the supported fix. +base: + - ../../../_internal/shared/model/model_overlays/celldiff_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/wall_smoke.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + gene: SEC61B + target: er + target_id: er_sec61b + train_set: joint_ipsc_confocal_a549_mantis + model_name: celldiff + experiment_id: er__joint_ipsc_confocal_a549_mantis__celldiff__smoke + +trainer: + # Smoke runs don't need a logger. `false` disables the recipe's WandbLogger + # so consumers don't have to remember --override trainer.logger=false. + # LearningRateMonitor (recipe default) raises without a logger, so the + # callbacks list is replaced with only ModelCheckpoint (lists replace + # wholesale under deep_merge). + logger: false + callbacks: + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + every_n_epochs: 1 + save_top_k: -1 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/sec61b/celldiff_r2/smoke/checkpoints + +# `_`-prefixed top-level keys are stripped by load_composed_config; see +# train.yml in this directory for the full anchor-convention rationale. +_hcs_init_args: &hcs_init_args + source_channel: [Phase3D] + target_channel: [Structure] + z_window_size: 13 + # batch_size=1 (vs train.yml's 4) so the smoke fits a single H200. The + # 4-GPU train.yml hparams OOM on one GPU because per-step memory is + # batch_size * num_samples patches at [8, 512, 512]; scaling batch_size + # alone keeps patch shape identical to train.yml so the validation is + # apples-to-apples. + batch_size: 1 + num_workers: 4 + yx_patch_size: [512, 512] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Structure] + level: timepoint_statistics + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Structure] + w_key: Structure + spatial_size: [13, 624, 624] + # num_samples=1 (vs train.yml's 2) — HCSDataModule requires + # batch_size % num_samples == 0 and the smoke uses batch_size=1. + num_samples: 1 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + safe_crop_size: [8, 512, 512] + safe_crop_coverage: 0.9 + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + # ipsc_confocal — aics-hipsc SEC61B test48 zarr (48 FOVs, smoke-sized). + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/SEC61B_test48.zarr + # a549_mantis — 2024_11_07 SEC61B train store. Already 4 FOVs, no + # smoke variant needed. + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/SEC61B_all.zarr + +launcher: + job_name: CELLDiff_JOINT_SEC61B_SMOKE + run_root: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/sec61b/celldiff_r2/smoke diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/joint_ipsc_confocal_a549_mantis/train_smoke_4gpu.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/joint_ipsc_confocal_a549_mantis/train_smoke_4gpu.yml new file mode 100644 index 000000000..06ed4f0cb --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/celldiff/joint_ipsc_confocal_a549_mantis/train_smoke_4gpu.yml @@ -0,0 +1,158 @@ +# 4-GPU DDP smoke variant of train.yml — small zarrs, 4 H200s, 30-min wall. +# +# Purpose: validate `BatchedConcatDataModule` + `ShardedDistributedSampler` +# integration on the real DDP topology used in production. The single-GPU +# `train_smoke.yml` already proved the joint loader and training/val loops +# work end-to-end; this leaf isolates the *sharding* behavior — that each +# rank pulls a disjoint slice of the joint dataset and the sampler attaches +# automatically once `torch.distributed` is initialized. +# +# Why a sibling leaf rather than --override on train.yml: train.yml points +# at the full 423-FOV iPSC SEC61B store, which `mmap_preload` stages to +# /dev/shm in 45+ min — blows the 30-min smoke wall before the first step. +# We swap the iPSC data_path to its `_test48` companion (48 FOVs, ~24 GB) +# so staging finishes in under a minute. submit_benchmark_job.py's --override +# parser cannot index into list elements (`data_modules.0` / `data_modules[0]` +# both fail), so pre-swapping in a sibling leaf is the supported fix — same +# rationale as train_smoke.yml. +# +# Why batch_size=1 / num_samples=1: matches train_smoke.yml. The point of +# this smoke is "does the sampler shard the joint dataset across ranks", +# not "does train.yml's heavier per-rank hparams (batch=4, num_samples=2) +# fit on H200". Validating sharding at small batch isolates the question; +# memory tuning is a follow-up smoke if needed. +# +# Why max_steps is baked in (not --override): the wall_smoke.yml docstring +# explicitly says to bound the run, and we just spent a 30-min wall on +# train_smoke.yml because we forgot the override at submit time. Bake it +# in so the leaf is self-bounded. +base: + - ../../../_internal/shared/model/model_overlays/celldiff_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/wall_smoke.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + gene: SEC61B + target: er + target_id: er_sec61b + train_set: joint_ipsc_confocal_a549_mantis + model_name: celldiff + experiment_id: er__joint_ipsc_confocal_a549_mantis__celldiff__smoke_4gpu + +trainer: + # Override the single_gpu topology pulled in by model_overlays/celldiff_fit.yml. + strategy: ddp + devices: 4 + # Bound the run so the leaf is self-contained (see header). + max_steps: 5 + # Smoke runs don't need a logger. `false` disables the recipe's WandbLogger + # so consumers don't have to remember --override trainer.logger=false. + # LearningRateMonitor (recipe default) raises without a logger, so the + # callbacks list is replaced with only ModelCheckpoint (lists replace + # wholesale under deep_merge). + logger: false + callbacks: + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + every_n_epochs: 1 + save_top_k: -1 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/sec61b/celldiff_r2/smoke_4gpu/checkpoints + +# `_`-prefixed top-level keys are stripped by load_composed_config; see +# train.yml in this directory for the full anchor-convention rationale. +_hcs_init_args: &hcs_init_args + source_channel: [Phase3D] + target_channel: [Structure] + z_window_size: 13 + # See header — kept at 1 to isolate sharding from per-rank memory tuning. + batch_size: 1 + num_workers: 4 + yx_patch_size: [512, 512] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Structure] + level: timepoint_statistics + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Structure] + w_key: Structure + spatial_size: [13, 624, 624] + # Must satisfy batch_size % num_samples == 0; batch_size=1 forces 1. + num_samples: 1 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + safe_crop_size: [8, 512, 512] + safe_crop_coverage: 0.9 + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + # ipsc_confocal — aics-hipsc SEC61B test48 zarr (48 FOVs, smoke-sized). + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/SEC61B_test48.zarr + # a549_mantis — 2024_11_07 SEC61B train store. Already 4 FOVs, no + # smoke variant needed. + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/SEC61B_all.zarr + +launcher: + job_name: CELLDiff_JOINT_SEC61B_SMOKE_4GPU + run_root: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/sec61b/celldiff_r2/smoke_4gpu diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..8a55a1256 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_denv.yml @@ -0,0 +1,46 @@ +# VSCyto3D random-init predict: er (frozen randinit ckpt), A549 denv plate. +# Control ablation. A549 manifest keys er by gene (`sec61b`); override the +# iPSC-side `er_sec61b` target_id from targets/er_sec61b.yml so the resolver finds the +# sec61b target on a549-mantis-sec61b-denv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_denv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: randinit + predict_set: a549_mantis_sec61b_denv + model_name: fcmae_vscyto3d_pretrained_randinit + experiment_id: er__randinit__fcmae_vscyto3d_pretrained__a549_mantis_sec61b_denv + dataset_ref: + target: sec61b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/randinit/sec61b/fcmae_vscyto3d_pretrained/checkpoints/randinit.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_pretrained_randinit_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_RandInit_PRED_SEC61B_ON_A549_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..af265a180 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_mock.yml @@ -0,0 +1,46 @@ +# VSCyto3D random-init predict: er (frozen randinit ckpt), A549 mock plate. +# Control ablation. A549 manifest keys er by gene (`sec61b`); override the +# iPSC-side `er_sec61b` target_id from targets/er_sec61b.yml so the resolver finds the +# sec61b target on a549-mantis-sec61b-mock. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_mock.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: randinit + predict_set: a549_mantis_sec61b_mock + model_name: fcmae_vscyto3d_pretrained_randinit + experiment_id: er__randinit__fcmae_vscyto3d_pretrained__a549_mantis_sec61b_mock + dataset_ref: + target: sec61b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/randinit/sec61b/fcmae_vscyto3d_pretrained/checkpoints/randinit.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_pretrained_randinit_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_RandInit_PRED_SEC61B_ON_A549_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..f65dfacf3 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_zikv.yml @@ -0,0 +1,46 @@ +# VSCyto3D random-init predict: er (frozen randinit ckpt), A549 zikv plate. +# Control ablation. A549 manifest keys er by gene (`sec61b`); override the +# iPSC-side `er_sec61b` target_id from targets/er_sec61b.yml so the resolver finds the +# sec61b target on a549-mantis-sec61b-zikv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_zikv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: randinit + predict_set: a549_mantis_sec61b_zikv + model_name: fcmae_vscyto3d_pretrained_randinit + experiment_id: er__randinit__fcmae_vscyto3d_pretrained__a549_mantis_sec61b_zikv + dataset_ref: + target: sec61b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/randinit/sec61b/fcmae_vscyto3d_pretrained/checkpoints/randinit.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_pretrained_randinit_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_RandInit_PRED_SEC61B_ON_A549_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__ipsc_confocal.yml new file mode 100644 index 000000000..8974b693c --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__ipsc_confocal.yml @@ -0,0 +1,44 @@ +# VSCyto3D random-init predict: er (frozen randinit ckpt), iPSC test set. +# Control ablation — measures untrained model output for paper. +# References the frozen randinit.ckpt persisted by save_random_init_vscyto3d_ckpts.py +# so all 4 datasets (iPSC + 3 A549 plates) for this organelle reuse the same weights. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: randinit + predict_set: ipsc_confocal + model_name: fcmae_vscyto3d_pretrained_randinit + experiment_id: er__randinit__fcmae_vscyto3d_pretrained__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/randinit/sec61b/fcmae_vscyto3d_pretrained/checkpoints/randinit.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_fcmae_vscyto3d_pretrained_randinit.zarr + +launcher: + job_name: FCMAE_VSCyto3D_RandInit_PRED_SEC61B + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..ec7a7f60b --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,45 @@ +# FCMAE_VSCyto3D_Pretrained (VSCyto3D) predict: ER trained on a549_mantis (sec61b), +# predicting against a549-mantis-sec61b-denv test. +# Best val-loss checkpoint from job 31910356 (epoch 132, loss/validate=0.5716). +# Both iPSC and a549 manifests use `sec61b` for the ER target, so no +# dataset_ref override is needed (targets/er_sec61b.yml already sets it). +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_denv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: a549_mantis + predict_set: a549_mantis_sec61b_denv + model_name: fcmae_vscyto3d_pretrained + experiment_id: er__a549_mantis__fcmae_vscyto3d_pretrained__a549_mantis_sec61b_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/sec61b/fcmae_vscyto3d_pretrained_ws8500/checkpoints/epoch=132-step=22876.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_pretrained_a549trained_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_SEC61B_A549TR_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..0944b8208 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,45 @@ +# FCMAE_VSCyto3D_Pretrained (VSCyto3D) predict: ER trained on a549_mantis (sec61b), +# predicting against a549-mantis-sec61b-mock test. +# Best val-loss checkpoint from job 31910356 (epoch 132, loss/validate=0.5716). +# Both iPSC and a549 manifests use `sec61b` for the ER target, so no +# dataset_ref override is needed (targets/er_sec61b.yml already sets it). +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_mock.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: a549_mantis + predict_set: a549_mantis_sec61b_mock + model_name: fcmae_vscyto3d_pretrained + experiment_id: er__a549_mantis__fcmae_vscyto3d_pretrained__a549_mantis_sec61b_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/sec61b/fcmae_vscyto3d_pretrained_ws8500/checkpoints/epoch=132-step=22876.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_pretrained_a549trained_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_SEC61B_A549TR_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..36c7bbd0c --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,45 @@ +# FCMAE_VSCyto3D_Pretrained (VSCyto3D) predict: ER trained on a549_mantis (sec61b), +# predicting against a549-mantis-sec61b-zikv test. +# Best val-loss checkpoint from job 31910356 (epoch 132, loss/validate=0.5716). +# Both iPSC and a549 manifests use `sec61b` for the ER target, so no +# dataset_ref override is needed (targets/er_sec61b.yml already sets it). +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_zikv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: a549_mantis + predict_set: a549_mantis_sec61b_zikv + model_name: fcmae_vscyto3d_pretrained + experiment_id: er__a549_mantis__fcmae_vscyto3d_pretrained__a549_mantis_sec61b_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/sec61b/fcmae_vscyto3d_pretrained_ws8500/checkpoints/epoch=132-step=22876.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_pretrained_a549trained_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_SEC61B_A549TR_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..54a35eec3 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,43 @@ +# FCMAE_VSCyto3D_Pretrained (VSCyto3D) predict: ER trained on a549_mantis (sec61b), +# predicting against ipsc_confocal test_cropped. +# Best val-loss checkpoint from job 31910356 (epoch 132, loss/validate=0.5716). +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: a549_mantis + predict_set: ipsc_confocal + model_name: fcmae_vscyto3d_pretrained + experiment_id: er__a549_mantis__fcmae_vscyto3d_pretrained__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/sec61b/fcmae_vscyto3d_pretrained_ws8500/checkpoints/epoch=132-step=22876.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_fcmae_vscyto3d_pretrained_a549trained.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_SEC61B_A549TR_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/a549_mantis/train.yml new file mode 100644 index 000000000..c58b0ecd5 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/a549_mantis/train.yml @@ -0,0 +1,55 @@ +# FCMAE-class (FullyConvolutionalMAE, pretraining=False) with FCMAE- +# pretrained encoder init on ER/SEC61B. Companion to +# fcmae_vscyto3d_scratch.yml — the two leaves are identical except this +# one loads encoder weights from the published VSCyto3D FCMAE ckpt +# (400 ep on HEK + A549 + iPSC phase data). See vs_test/finetune_3d.py +# for the canonical recipe. +base: + - ../../../_internal/shared/model/train_sets/a549_mantis.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/data_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + train_set: a549_mantis + model_name: fcmae_vscyto3d_pretrained + experiment_id: er__a549_mantis__fcmae_vscyto3d_pretrained + +model: + init_args: + # Load only the encoder from the canonical VSCyto3D FCMAE ckpt — + # decoder/head stay at fresh init. Matches vs_test/finetune_3d.py:247. + encoder_only: true + ckpt_path: /hpc/projects/virtual_staining/models/mehta-lab/VSCyto3D/fcmae.ckpt + +trainer: + logger: + init_args: + name: FCMAE_VSCyto3D_Pretrained_A549_SEC61B_ws8500 + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/sec61b/fcmae_vscyto3d_pretrained_ws8500 + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/sec61b/fcmae_vscyto3d_pretrained_ws8500/checkpoints + +data: + init_args: + # A549 pooled store + target_channel — no resolver in this train_set. + target_channel: Structure + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/SEC61B_all.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_A549_SEC61B_ws8500 + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/sec61b/fcmae_vscyto3d_pretrained_ws8500 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 100644 index 000000000..b432a9581 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: ER (SEC61B) predicted by FCMAE_VSCyto3D_Pretrained on a549-mantis-sec61b-denv. +defaults: + - override /target: er_sec61b + - override /predict_set: a549_mantis_sec61b_denv + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_pretrained__sec61b_denv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_sec61b_fcmae_vscyto3d_pretrained__sec61b_denv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 100644 index 000000000..583b37267 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: ER (SEC61B) predicted by FCMAE_VSCyto3D_Pretrained on a549-mantis-sec61b-mock. +defaults: + - override /target: er_sec61b + - override /predict_set: a549_mantis_sec61b_mock + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_pretrained__sec61b_mock.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_sec61b_fcmae_vscyto3d_pretrained__sec61b_mock diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 100644 index 000000000..a1a8e80f6 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: ER (SEC61B) predicted by FCMAE_VSCyto3D_Pretrained on a549-mantis-sec61b-zikv. +defaults: + - override /target: er_sec61b + - override /predict_set: a549_mantis_sec61b_zikv + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_pretrained__sec61b_zikv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_sec61b_fcmae_vscyto3d_pretrained__sec61b_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..03dbbb139 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_denv.yml @@ -0,0 +1,50 @@ +# FCMAE_VSCyto3D_Pretrained predict: ER (SEC61B) trained on iPSC, +# predicting against a549_mantis_sec61b_denv test. +# +# TODO: replace ckpt_path once iPSC FCMAE pretrained ER training +# completes. Expected output (per fit leaf): +# /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/fcmae_vscyto3d_pretrained_ws8500/checkpoints/last.ckpt +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_denv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: ipsc_confocal + predict_set: a549_mantis_sec61b_denv + model_name: fcmae_vscyto3d_pretrained + experiment_id: er__ipsc_confocal__fcmae_vscyto3d_pretrained__a549_mantis_sec61b_denv + +model: + init_args: + # Best checkpoint from J31523022 (FCMAE_VSCyto3D_Pretrained_iPSC_SEC61B_ws8500): + # ep 123 / val_loss 0.40979 (49-epoch plateau, scancelled at 3d 1h elapsed). + # Hardlink alias at run_root; the underlying checkpoints/epoch=123-step=32736.ckpt + # is also preserved in checkpoints_frozen_ep123_20260501_004946/. + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/fcmae_vscyto3d_pretrained_ws8500/best_ep123_val0.40979.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_pretrained__sec61b_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_SEC61B_ON_A549_sec61b_denv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..7136cb42b --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_mock.yml @@ -0,0 +1,50 @@ +# FCMAE_VSCyto3D_Pretrained predict: ER (SEC61B) trained on iPSC, +# predicting against a549_mantis_sec61b_mock test. +# +# TODO: replace ckpt_path once iPSC FCMAE pretrained ER training +# completes. Expected output (per fit leaf): +# /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/fcmae_vscyto3d_pretrained_ws8500/checkpoints/last.ckpt +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_mock.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: ipsc_confocal + predict_set: a549_mantis_sec61b_mock + model_name: fcmae_vscyto3d_pretrained + experiment_id: er__ipsc_confocal__fcmae_vscyto3d_pretrained__a549_mantis_sec61b_mock + +model: + init_args: + # Best checkpoint from J31523022 (FCMAE_VSCyto3D_Pretrained_iPSC_SEC61B_ws8500): + # ep 123 / val_loss 0.40979 (49-epoch plateau, scancelled at 3d 1h elapsed). + # Hardlink alias at run_root; the underlying checkpoints/epoch=123-step=32736.ckpt + # is also preserved in checkpoints_frozen_ep123_20260501_004946/. + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/fcmae_vscyto3d_pretrained_ws8500/best_ep123_val0.40979.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_pretrained__sec61b_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_SEC61B_ON_A549_sec61b_mock + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..190b1d90e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_zikv.yml @@ -0,0 +1,50 @@ +# FCMAE_VSCyto3D_Pretrained predict: ER (SEC61B) trained on iPSC, +# predicting against a549_mantis_sec61b_zikv test. +# +# TODO: replace ckpt_path once iPSC FCMAE pretrained ER training +# completes. Expected output (per fit leaf): +# /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/fcmae_vscyto3d_pretrained_ws8500/checkpoints/last.ckpt +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_zikv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: ipsc_confocal + predict_set: a549_mantis_sec61b_zikv + model_name: fcmae_vscyto3d_pretrained + experiment_id: er__ipsc_confocal__fcmae_vscyto3d_pretrained__a549_mantis_sec61b_zikv + +model: + init_args: + # Best checkpoint from J31523022 (FCMAE_VSCyto3D_Pretrained_iPSC_SEC61B_ws8500): + # ep 123 / val_loss 0.40979 (49-epoch plateau, scancelled at 3d 1h elapsed). + # Hardlink alias at run_root; the underlying checkpoints/epoch=123-step=32736.ckpt + # is also preserved in checkpoints_frozen_ep123_20260501_004946/. + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/fcmae_vscyto3d_pretrained_ws8500/best_ep123_val0.40979.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_pretrained__sec61b_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_SEC61B_ON_A549_sec61b_zikv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__ipsc_confocal.yml new file mode 100644 index 000000000..920b3dfce --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__ipsc_confocal.yml @@ -0,0 +1,49 @@ +# FCMAE_VSCyto3D_Pretrained predict: ER (SEC61B) against ipsc_confocal test_cropped. +# +# TODO: replace ckpt_path with best-val ckpt once iPSC FCMAE pretrained +# ER training (J31523022, ws8500 variant) completes. Expected dir: +# /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/fcmae_vscyto3d_pretrained_ws8500/checkpoints/ +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: fcmae_vscyto3d_pretrained + experiment_id: er__ipsc_confocal__fcmae_vscyto3d_pretrained__ipsc_confocal + +model: + init_args: + # Best checkpoint from J31523022 (FCMAE_VSCyto3D_Pretrained_iPSC_SEC61B_ws8500): + # ep 123 / val_loss 0.40979 (49-epoch plateau, scancelled at 3d 1h elapsed). + # Hardlink alias at run_root; the underlying checkpoints/epoch=123-step=32736.ckpt + # is also preserved in checkpoints_frozen_ep123_20260501_004946/. + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/fcmae_vscyto3d_pretrained_ws8500/best_ep123_val0.40979.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_fcmae_vscyto3d_pretrained.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_SEC61B + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/ipsc_confocal/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/ipsc_confocal/train.yml new file mode 100644 index 000000000..d8e03ca03 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/ipsc_confocal/train.yml @@ -0,0 +1,49 @@ +# FCMAE-class (FullyConvolutionalMAE, pretraining=False) with FCMAE- +# pretrained encoder init on ER/SEC61B. Companion to +# fcmae_vscyto3d_scratch.yml — the two leaves are identical except this +# one loads encoder weights from the published VSCyto3D FCMAE ckpt +# (400 ep on HEK + A549 + iPSC phase data). See vs_test/finetune_3d.py +# for the canonical recipe. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/data_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + train_set: ipsc_confocal + model_name: fcmae_vscyto3d_pretrained + experiment_id: er__ipsc_confocal__fcmae_vscyto3d_pretrained + +model: + init_args: + # Load only the encoder from the canonical VSCyto3D FCMAE ckpt — + # decoder/head stay at fresh init. Matches vs_test/finetune_3d.py:247. + encoder_only: true + ckpt_path: /hpc/projects/virtual_staining/models/mehta-lab/VSCyto3D/fcmae.ckpt + +trainer: + logger: + init_args: + name: FCMAE_VSCyto3D_Pretrained_iPSC_SEC61B_ws8500 + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/fcmae_vscyto3d_pretrained_ws8500 + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/fcmae_vscyto3d_pretrained_ws8500/checkpoints + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_SEC61B_ws8500 + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/fcmae_vscyto3d_pretrained_ws8500 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..ad3322f0c --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,46 @@ +# FCMAE_VSCyto3D_Pretrained predict: ER trained on joint iPSC+A549, +# predicting against a549-mantis-sec61b-denv test. +# Best val-loss checkpoint from J31910331 (epoch 111, val 0.5164). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# Both iPSC and a549 manifests use `sec61b`; targets/er_sec61b.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_denv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_sec61b_denv + model_name: fcmae_vscyto3d_pretrained + experiment_id: er__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_pretrained__a549_mantis_sec61b_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/fcmae_vscyto3d_pretrained_ws8500/checkpoints/epoch=111-step=48496.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_pretrained_jointtrained_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_SEC61B_JOINTTR_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..53e93b9ee --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,46 @@ +# FCMAE_VSCyto3D_Pretrained predict: ER trained on joint iPSC+A549, +# predicting against a549-mantis-sec61b-mock test. +# Best val-loss checkpoint from J31910331 (epoch 111, val 0.5164). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# Both iPSC and a549 manifests use `sec61b`; targets/er_sec61b.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_mock.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_sec61b_mock + model_name: fcmae_vscyto3d_pretrained + experiment_id: er__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_pretrained__a549_mantis_sec61b_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/fcmae_vscyto3d_pretrained_ws8500/checkpoints/epoch=111-step=48496.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_pretrained_jointtrained_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_SEC61B_JOINTTR_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..3eb7bd188 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,46 @@ +# FCMAE_VSCyto3D_Pretrained predict: ER trained on joint iPSC+A549, +# predicting against a549-mantis-sec61b-zikv test. +# Best val-loss checkpoint from J31910331 (epoch 111, val 0.5164). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# Both iPSC and a549 manifests use `sec61b`; targets/er_sec61b.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_zikv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_sec61b_zikv + model_name: fcmae_vscyto3d_pretrained + experiment_id: er__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_pretrained__a549_mantis_sec61b_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/fcmae_vscyto3d_pretrained_ws8500/checkpoints/epoch=111-step=48496.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_pretrained_jointtrained_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_SEC61B_JOINTTR_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..1135ec504 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,51 @@ +# FCMAE_VSCyto3D_Pretrained (VSCyto3D) predict: ER (SEC61B) trained on joint +# iPSC+A549, predicting against ipsc_confocal test_cropped. +# Best val-loss checkpoint from J31910331 (epoch 111, loss/validate=0.5164). +# Wandb run 20260502-142508_FCMAE_VSCyto3D_Pretrained_JOINT_SEC61B_ws8500 +# (TIMEOUT @ 4d, wandb state=crashed; 60,489 steps; final val 0.5253 — drifted +# up from ep111 best). ws8500 = 8,500-step warmup variant. +# Both iPSC and a549 manifests use `sec61b`; targets/er_sec61b.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: ipsc_confocal + model_name: fcmae_vscyto3d_pretrained + experiment_id: er__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_pretrained__ipsc_confocal + +model: + init_args: + # ckpt lives under the _ws8500 training-output subdir; config namespace + # uses fcmae_vscyto3d_pretrained (no _ws8500 suffix) for consistency with + # iPSC + a549 single-set predict configs. + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/fcmae_vscyto3d_pretrained_ws8500/checkpoints/epoch=111-step=48496.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_fcmae_vscyto3d_pretrained_jointtrained.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_SEC61B_JOINTTR_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/train.yml new file mode 100644 index 000000000..44acd7050 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/train.yml @@ -0,0 +1,154 @@ +# FCMAE-class (FullyConvolutionalMAE, pretraining=False) with FCMAE- +# pretrained encoder init on er (SEC61B) — joint +# ipsc_confocal + a549_mantis pooled. Companion to +# fcmae_vscyto3d_scratch joint leaf — the two are identical except +# this one loads encoder weights from the published VSCyto3D FCMAE +# ckpt (400 ep on HEK + A549 + iPSC phase data). Mirrors +# er/fcmae_vscyto3d_pretrained/ipsc_confocal/train.yml on +# the joint train_set. +# +# Joint leaf per Stage 7 of A549_EXPANSION_ROADMAP.md. Uses +# BatchedConcatDataModule with two explicit HCSDataModule children +# (no benchmark.dataset_ref — joint leaves bypass the single-dataset +# resolver). Only model_overlays/fcmae_vscyto3d_fit.yml is composed; +# the data block is authored inline because joint hparams live on +# the children. +# +# Topology: 4-GPU DDP (inherited from +# model_overlays/fcmae_vscyto3d_fit.yml's ddp_4gpu base; the overlay +# also pins strategy=ddp_find_unused_parameters_true because +# FullyConvolutionalMAE has decoder/head params that only receive +# gradients on some forward paths). +base: + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + gene: SEC61B + target: er + target_id: er_sec61b + train_set: joint_ipsc_confocal_a549_mantis + model_name: fcmae_vscyto3d_pretrained + experiment_id: er__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_pretrained + +model: + init_args: + # Load only the encoder from the canonical VSCyto3D FCMAE ckpt — + # decoder/head stay at fresh init. Matches vs_test/finetune_3d.py:247. + encoder_only: true + ckpt_path: /hpc/projects/virtual_staining/models/mehta-lab/VSCyto3D/fcmae.ckpt + +trainer: + logger: + init_args: + name: FCMAE_VSCyto3D_Pretrained_JOINT_SEC61B_ws8500 + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/fcmae_vscyto3d_pretrained_ws8500 + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/fcmae_vscyto3d_pretrained_ws8500/checkpoints + +_hcs_init_args: &hcs_init_args + source_channel: Phase3D + target_channel: Structure + z_window_size: 20 + # See nucleus/fnet3d_paper/joint_*/train.yml for the rationale: joint + # mode does not divide batch_size by num_samples, so 8 * 4 = 32 GPU + # samples per DDP rank matches single-set effective batch. + batch_size: 8 + num_workers: 4 + yx_patch_size: [384, 384] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Structure] + level: fov_statistics + subtrahend: median + divisor: iqr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Structure] + w_key: Structure + spatial_size: [20, 600, 600] + num_samples: 4 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + # ipsc_confocal — aics-hipsc SEC61B train store + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/SEC61B.zarr + # a549_mantis — pooled SEC61B all-conditions train store + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/SEC61B_all.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_JOINT_SEC61B_ws8500 + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/fcmae_vscyto3d_pretrained_ws8500 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..782bf876b --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,46 @@ +# FCMAE_VSCyto3D_Scratch (UNeXt2) predict: ER trained on a549_mantis (sec61b), +# predicting against a549-mantis-sec61b-denv test. +# Best val-loss checkpoint from job 31910346 (epoch 137, val 0.6219). See +# predict__ipsc_confocal.yml in this dir for the wandb collision caveat. +# Both iPSC and a549 manifests use `sec61b`; targets/er_sec61b.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_denv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: a549_mantis + predict_set: a549_mantis_sec61b_denv + model_name: fcmae_vscyto3d_scratch + experiment_id: er__a549_mantis__fcmae_vscyto3d_scratch__a549_mantis_sec61b_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/sec61b/fcmae_vscyto3d_scratch/checkpoints/epoch=137-step=23736.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_scratch_a549trained_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_SEC61B_A549TR_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..c165bf89b --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,46 @@ +# FCMAE_VSCyto3D_Scratch (UNeXt2) predict: ER trained on a549_mantis (sec61b), +# predicting against a549-mantis-sec61b-mock test. +# Best val-loss checkpoint from job 31910346 (epoch 137, val 0.6219). See +# predict__ipsc_confocal.yml in this dir for the wandb collision caveat. +# Both iPSC and a549 manifests use `sec61b`; targets/er_sec61b.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_mock.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: a549_mantis + predict_set: a549_mantis_sec61b_mock + model_name: fcmae_vscyto3d_scratch + experiment_id: er__a549_mantis__fcmae_vscyto3d_scratch__a549_mantis_sec61b_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/sec61b/fcmae_vscyto3d_scratch/checkpoints/epoch=137-step=23736.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_scratch_a549trained_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_SEC61B_A549TR_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..2b9e69980 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,46 @@ +# FCMAE_VSCyto3D_Scratch (UNeXt2) predict: ER trained on a549_mantis (sec61b), +# predicting against a549-mantis-sec61b-zikv test. +# Best val-loss checkpoint from job 31910346 (epoch 137, val 0.6219). See +# predict__ipsc_confocal.yml in this dir for the wandb collision caveat. +# Both iPSC and a549 manifests use `sec61b`; targets/er_sec61b.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_zikv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: a549_mantis + predict_set: a549_mantis_sec61b_zikv + model_name: fcmae_vscyto3d_scratch + experiment_id: er__a549_mantis__fcmae_vscyto3d_scratch__a549_mantis_sec61b_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/sec61b/fcmae_vscyto3d_scratch/checkpoints/epoch=137-step=23736.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_scratch_a549trained_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_SEC61B_A549TR_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..5ab5951ce --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,50 @@ +# FCMAE_VSCyto3D_Scratch (UNeXt2) predict: ER trained on a549_mantis (sec61b), +# predicting against ipsc_confocal test_cropped. +# Best val-loss checkpoint from job 31910346 (epoch 137, val 0.6219). Job +# completed cleanly to ep199 at 2026-05-05T03:34:24 (elapsed 2d 6h 50m). +# NOTE: wandb run id `20260502-204536` collided with the simultaneous TOMM20 +# training (J31910360 on the same node gpu-f-5); the wandb dashboard for that +# run id shows the TOMM20 display name but the metrics actually belong to +# this SEC61B training (ep113 step=19607, ep137 step=23735 align with SEC61B's +# step counts, not TOMM20's). Both iPSC and a549 manifests use `sec61b`; +# targets/er_sec61b.yml handles both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: a549_mantis + predict_set: ipsc_confocal + model_name: fcmae_vscyto3d_scratch + experiment_id: er__a549_mantis__fcmae_vscyto3d_scratch__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/sec61b/fcmae_vscyto3d_scratch/checkpoints/epoch=137-step=23736.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_fcmae_vscyto3d_scratch_a549trained.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_SEC61B_A549TR_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/a549_mantis/train.yml new file mode 100644 index 000000000..fde2e8c5d --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/a549_mantis/train.yml @@ -0,0 +1,47 @@ +# FCMAE-class (FullyConvolutionalMAE, pretraining=False) random-init +# baseline on ER/SEC61B. Scratch control for the pretrained counterpart — +# the two leaves are identical except this one does NOT load pretrained +# encoder weights. See UNEXT2_VS_FCMAE_CLASSES.md for why this is the +# paper-adjacent scratch baseline (and not unext2.yml). +base: + - ../../../_internal/shared/model/train_sets/a549_mantis.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/data_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + train_set: a549_mantis + model_name: fcmae_vscyto3d_scratch + experiment_id: er__a549_mantis__fcmae_vscyto3d_scratch + +trainer: + logger: + init_args: + name: FCMAE_VSCyto3D_Scratch_A549_SEC61B + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/sec61b/fcmae_vscyto3d_scratch + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/sec61b/fcmae_vscyto3d_scratch/checkpoints + +data: + init_args: + # A549 pooled store + target_channel — no resolver in this train_set. + target_channel: Structure + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/SEC61B_all.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_A549_SEC61B + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/sec61b/fcmae_vscyto3d_scratch diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 100644 index 000000000..d1a899b82 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: ER (SEC61B) predicted by FCMAE_VSCyto3D_Scratch on a549-mantis-sec61b-denv. +defaults: + - override /target: er_sec61b + - override /predict_set: a549_mantis_sec61b_denv + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_scratch__sec61b_denv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_sec61b_fcmae_vscyto3d_scratch__sec61b_denv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 100644 index 000000000..0f6d13412 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: ER (SEC61B) predicted by FCMAE_VSCyto3D_Scratch on a549-mantis-sec61b-mock. +defaults: + - override /target: er_sec61b + - override /predict_set: a549_mantis_sec61b_mock + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_scratch__sec61b_mock.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_sec61b_fcmae_vscyto3d_scratch__sec61b_mock diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 100644 index 000000000..99f56a7fd --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: ER (SEC61B) predicted by FCMAE_VSCyto3D_Scratch on a549-mantis-sec61b-zikv. +defaults: + - override /target: er_sec61b + - override /predict_set: a549_mantis_sec61b_zikv + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_scratch__sec61b_zikv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_sec61b_fcmae_vscyto3d_scratch__sec61b_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..3eb26f308 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_denv.yml @@ -0,0 +1,47 @@ +# FCMAE_VSCyto3D_Scratch predict: ER (SEC61B) trained on iPSC, +# predicting against a549_mantis_sec61b_denv test. +# +# Pinned to best-val checkpoint from training run J31483778 +# (val 0.4119, epoch 122). Run cancelled at epoch 164 — val plateaued +# at epoch 122 and never recovered (~42 epochs without improvement, +# drifting up in last 5 epochs). +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_denv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: ipsc_confocal + predict_set: a549_mantis_sec61b_denv + model_name: fcmae_vscyto3d_scratch + experiment_id: er__ipsc_confocal__fcmae_vscyto3d_scratch__a549_mantis_sec61b_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/fcmae_vscyto3d_scratch/checkpoints/epoch=122-step=32472.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_scratch__sec61b_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_SEC61B_ON_A549_sec61b_denv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..e8564d6a1 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_mock.yml @@ -0,0 +1,47 @@ +# FCMAE_VSCyto3D_Scratch predict: ER (SEC61B) trained on iPSC, +# predicting against a549_mantis_sec61b_mock test. +# +# Pinned to best-val checkpoint from training run J31483778 +# (val 0.4119, epoch 122). Run cancelled at epoch 164 — val plateaued +# at epoch 122 and never recovered (~42 epochs without improvement, +# drifting up in last 5 epochs). +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_mock.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: ipsc_confocal + predict_set: a549_mantis_sec61b_mock + model_name: fcmae_vscyto3d_scratch + experiment_id: er__ipsc_confocal__fcmae_vscyto3d_scratch__a549_mantis_sec61b_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/fcmae_vscyto3d_scratch/checkpoints/epoch=122-step=32472.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_scratch__sec61b_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_SEC61B_ON_A549_sec61b_mock + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..5a0ab3eab --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_zikv.yml @@ -0,0 +1,47 @@ +# FCMAE_VSCyto3D_Scratch predict: ER (SEC61B) trained on iPSC, +# predicting against a549_mantis_sec61b_zikv test. +# +# Pinned to best-val checkpoint from training run J31483778 +# (val 0.4119, epoch 122). Run cancelled at epoch 164 — val plateaued +# at epoch 122 and never recovered (~42 epochs without improvement, +# drifting up in last 5 epochs). +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_zikv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: ipsc_confocal + predict_set: a549_mantis_sec61b_zikv + model_name: fcmae_vscyto3d_scratch + experiment_id: er__ipsc_confocal__fcmae_vscyto3d_scratch__a549_mantis_sec61b_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/fcmae_vscyto3d_scratch/checkpoints/epoch=122-step=32472.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_scratch__sec61b_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_SEC61B_ON_A549_sec61b_zikv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/ipsc_confocal/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/ipsc_confocal/predict__ipsc_confocal.yml new file mode 100644 index 000000000..bd862f614 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/ipsc_confocal/predict__ipsc_confocal.yml @@ -0,0 +1,46 @@ +# FCMAE_VSCyto3D_Scratch predict: ER (SEC61B) against ipsc_confocal test_cropped. +# +# Pinned to best-val checkpoint from training run J31483778 +# (val 0.4119, epoch 122). Run cancelled at epoch 164 — val plateaued +# at epoch 122 and never recovered (~42 epochs without improvement, +# drifting up in last 5 epochs). +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: fcmae_vscyto3d_scratch + experiment_id: er__ipsc_confocal__fcmae_vscyto3d_scratch__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/fcmae_vscyto3d_scratch/checkpoints/epoch=122-step=32472.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_fcmae_vscyto3d_scratch.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_SEC61B + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/ipsc_confocal/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/ipsc_confocal/train.yml new file mode 100644 index 000000000..f3f1cbe31 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/ipsc_confocal/train.yml @@ -0,0 +1,41 @@ +# FCMAE-class (FullyConvolutionalMAE, pretraining=False) random-init +# baseline on ER/SEC61B. Scratch control for the pretrained counterpart — +# the two leaves are identical except this one does NOT load pretrained +# encoder weights. See UNEXT2_VS_FCMAE_CLASSES.md for why this is the +# paper-adjacent scratch baseline (and not unext2.yml). +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/data_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + train_set: ipsc_confocal + model_name: fcmae_vscyto3d_scratch + experiment_id: er__ipsc_confocal__fcmae_vscyto3d_scratch + +trainer: + logger: + init_args: + name: FCMAE_VSCyto3D_Scratch_iPSC_SEC61B + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/fcmae_vscyto3d_scratch + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/fcmae_vscyto3d_scratch/checkpoints + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_SEC61B + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/fcmae_vscyto3d_scratch diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..8518bc2b6 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,46 @@ +# FCMAE_VSCyto3D_Scratch predict: ER trained on joint iPSC+A549, +# predicting against a549-mantis-sec61b-denv test. +# Best val-loss checkpoint from J31910330 (epoch 75, val 0.5234). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# Both iPSC and a549 manifests use `sec61b`; targets/er_sec61b.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_denv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_sec61b_denv + model_name: fcmae_vscyto3d_scratch + experiment_id: er__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_scratch__a549_mantis_sec61b_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/fcmae_vscyto3d_scratch/checkpoints/epoch=75-step=32908.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_scratch_jointtrained_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_SEC61B_JOINTTR_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..5db0b78a8 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,46 @@ +# FCMAE_VSCyto3D_Scratch predict: ER trained on joint iPSC+A549, +# predicting against a549-mantis-sec61b-mock test. +# Best val-loss checkpoint from J31910330 (epoch 75, val 0.5234). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# Both iPSC and a549 manifests use `sec61b`; targets/er_sec61b.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_mock.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_sec61b_mock + model_name: fcmae_vscyto3d_scratch + experiment_id: er__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_scratch__a549_mantis_sec61b_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/fcmae_vscyto3d_scratch/checkpoints/epoch=75-step=32908.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_scratch_jointtrained_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_SEC61B_JOINTTR_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..d8ff2a643 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,46 @@ +# FCMAE_VSCyto3D_Scratch predict: ER trained on joint iPSC+A549, +# predicting against a549-mantis-sec61b-zikv test. +# Best val-loss checkpoint from J31910330 (epoch 75, val 0.5234). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# Both iPSC and a549 manifests use `sec61b`; targets/er_sec61b.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_zikv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_sec61b_zikv + model_name: fcmae_vscyto3d_scratch + experiment_id: er__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_scratch__a549_mantis_sec61b_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/fcmae_vscyto3d_scratch/checkpoints/epoch=75-step=32908.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fcmae_vscyto3d_scratch_jointtrained_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_SEC61B_JOINTTR_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..2cbb9e39f --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,47 @@ +# FCMAE_VSCyto3D_Scratch predict: ER (SEC61B) trained on joint iPSC+A549, +# predicting against ipsc_confocal test_cropped. +# Best val-loss checkpoint from J31910330 (epoch 75, loss/validate=0.5234). +# Wandb run 20260502-133534_FCMAE_VSCyto3D_Scratch_JOINT_SEC61B (TIMEOUT @ 4d / +# 119 ep / 63,299 steps; final val 0.5313 — drifted up from ep75 best). +# Both iPSC and a549 manifests use `sec61b`; targets/er_sec61b.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: ipsc_confocal + model_name: fcmae_vscyto3d_scratch + experiment_id: er__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_scratch__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/fcmae_vscyto3d_scratch/checkpoints/epoch=75-step=32908.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_fcmae_vscyto3d_scratch_jointtrained.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_SEC61B_JOINTTR_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/train.yml new file mode 100644 index 000000000..282b99377 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/train.yml @@ -0,0 +1,142 @@ +# FCMAE-class (FullyConvolutionalMAE, pretraining=False) random-init +# baseline on er (SEC61B) — joint ipsc_confocal + +# a549_mantis pooled. Scratch control for the pretrained counterpart +# — the two leaves are identical except this one does NOT load +# pretrained encoder weights. Mirrors +# er/fcmae_vscyto3d_scratch/ipsc_confocal/train.yml on the +# joint train_set. +# +# Joint leaf per Stage 7 of A549_EXPANSION_ROADMAP.md. +# BatchedConcatDataModule + two explicit HCSDataModule children; +# only model_overlays/fcmae_vscyto3d_fit.yml is composed; data +# block inline. +# +# Topology: 4-GPU DDP +# (strategy=ddp_find_unused_parameters_true inherited from +# model_overlays/fcmae_vscyto3d_fit.yml). +base: + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + gene: SEC61B + target: er + target_id: er_sec61b + train_set: joint_ipsc_confocal_a549_mantis + model_name: fcmae_vscyto3d_scratch + experiment_id: er__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_scratch + +trainer: + logger: + init_args: + name: FCMAE_VSCyto3D_Scratch_JOINT_SEC61B + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/fcmae_vscyto3d_scratch + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/fcmae_vscyto3d_scratch/checkpoints + +_hcs_init_args: &hcs_init_args + source_channel: Phase3D + target_channel: Structure + z_window_size: 20 + # See nucleus/fnet3d_paper/joint_*/train.yml for the rationale: joint + # mode does not divide batch_size by num_samples, so 8 * 4 = 32 GPU + # samples per DDP rank matches single-set effective batch. + batch_size: 8 + num_workers: 4 + yx_patch_size: [384, 384] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Structure] + level: fov_statistics + subtrahend: median + divisor: iqr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Structure] + w_key: Structure + spatial_size: [20, 600, 600] + num_samples: 4 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + # ipsc_confocal — aics-hipsc SEC61B train store + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/SEC61B.zarr + # a549_mantis — pooled SEC61B all-conditions train store + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/SEC61B_all.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_JOINT_SEC61B + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/fcmae_vscyto3d_scratch diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/train_smoke.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/train_smoke.yml new file mode 100644 index 000000000..7a4b12c8b --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/train_smoke.yml @@ -0,0 +1,107 @@ +# FCMAE scratch joint smoke — minimal repro for the [N,2,48,640,960] +# val-shape mismatch hitting all 8 ER/MITO submissions (jobs 31857838-41 +# joint + 31858456-61 a549-only). Use: +# uv run python applications/dynacell/tools/submit_benchmark_job.py \ +# applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/train_smoke.yml \ +# --dry-run --print-resolved > /tmp/fcmae_er_smoke.yaml +# uv run python -m dynacell fit --config /tmp/fcmae_er_smoke.yaml \ +# --trainer.devices=1 --trainer.strategy=auto +base: + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + gene: SEC61B + target: er + target_id: er_sec61b + train_set: joint_ipsc_confocal_a549_mantis + model_name: fcmae_vscyto3d_scratch + experiment_id: er__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_scratch__smoke + +trainer: + devices: 1 + num_nodes: 1 + strategy: auto + max_steps: 2 + limit_val_batches: 1 + num_sanity_val_steps: 1 + logger: false + callbacks: + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + every_n_epochs: 1 + save_top_k: -1 + save_last: true + dirpath: /tmp/fcmae_er_smoke/checkpoints + +_hcs_init_args: &hcs_init_args + source_channel: Phase3D + target_channel: Structure + z_window_size: 20 + batch_size: 4 + num_workers: 0 + pin_memory: false + yx_patch_size: [384, 384] + split_ratio: 0.8 + mmap_preload: false + persistent_workers: false + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Structure] + level: fov_statistics + subtrahend: median + divisor: iqr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Structure] + w_key: Structure + spatial_size: [20, 600, 600] + num_samples: 4 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + # ipsc_confocal — aics-hipsc SEC61B test48 zarr (48 FOVs). + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/SEC61B_test48.zarr + # a549_mantis — pooled SEC61B all-conditions train store (T=7). + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/SEC61B_all.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_JOINT_SEC61B_SMOKE + run_root: /tmp/fcmae_er_smoke diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/train_smoke_4gpu.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/train_smoke_4gpu.yml new file mode 100644 index 000000000..de515c145 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/train_smoke_4gpu.yml @@ -0,0 +1,99 @@ +# FCMAE scratch joint smoke — 4-GPU DDP variant. Reproduces the +# [N,2,48,640,960] val-shape expand error from prod jobs 31857838-41 +# and 31858456-61. Single-GPU runs DO NOT reproduce; bug only appears +# under DDP. fast_dev_run-style: 2 train + 2 val + 2 sanity, no wandb. +# +# Use: +# uv run python applications/dynacell/tools/submit_benchmark_job.py \ +# applications/dynacell/configs/benchmarks/virtual_staining/er/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/train_smoke_4gpu.yml +base: + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + gene: SEC61B + target: er + target_id: er_sec61b + train_set: joint_ipsc_confocal_a549_mantis + model_name: fcmae_vscyto3d_scratch + experiment_id: er__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_scratch__smoke4gpu + +trainer: + max_steps: 2 + limit_train_batches: 2 + limit_val_batches: 2 + num_sanity_val_steps: 2 + enable_checkpointing: false + logger: false + callbacks: [] + +_hcs_init_args: &hcs_init_args + source_channel: Phase3D + target_channel: Structure + z_window_size: 20 + batch_size: 8 + num_workers: 2 + yx_patch_size: [384, 384] + split_ratio: 0.8 + mmap_preload: false + persistent_workers: false + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Structure] + level: fov_statistics + subtrahend: median + divisor: iqr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Structure] + w_key: Structure + spatial_size: [20, 600, 600] + num_samples: 4 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/SEC61B_test48.zarr + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/SEC61B_all.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_JOINT_SEC61B_SMOKE4GPU + run_root: /hpc/mydata/alex.kalinin/VisCy/.tmp/fcmae_er_smoke_4gpu + sbatch: + time: "00:30:00" diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/a549_mantis/train.yml new file mode 100644 index 000000000..1b822de85 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/a549_mantis/train.yml @@ -0,0 +1,51 @@ +# FNet3D paper-baseline fit on ER (SEC61B marker) — A549 mantis-lightsheet pooled (mock + DENV + ZIKV). +# Reproduces the trained run at +# /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/sec61b/fnet3d_paper/. +base: + - ../../../_internal/shared/model/train_sets/a549_mantis.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/data_overlays/fnet3d_paper_fit.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_gpu_any_long.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + train_set: a549_mantis + model_name: fnet3d_paper + experiment_id: er__a549_mantis__fnet3d_paper + +trainer: + logger: + init_args: + name: FNet3D_A549_SEC61B_paper + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/sec61b/fnet3d_paper + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/sec61b/fnet3d_paper/checkpoints + +data: + init_args: + # A549 pooled store + target_channel — no resolver in this train_set. + target_channel: Structure + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/SEC61B_all.zarr + +launcher: + job_name: FNet3DPaper_A549_SEC61B + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/sec61b/fnet3d_paper + # 512G to match the shared headroom convention across the fnet3d + # leaves on a549/joint workloads. mmap_preload after the BasicIndexer + # fix peaks at ~75 GB for SEC61B_all alone (single-set); 512G gives + # generous headroom. + sbatch: + mem: "512G" diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 100644 index 000000000..8c7725382 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: ER (SEC61B) predicted by FNet3DPaper on a549-mantis-sec61b-denv. +defaults: + - override /target: er_sec61b + - override /predict_set: a549_mantis_sec61b_denv + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fnet3d_paper__sec61b_denv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_sec61b_fnet3d_paper__sec61b_denv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 100644 index 000000000..b1459e28e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: ER (SEC61B) predicted by FNet3DPaper on a549-mantis-sec61b-mock. +defaults: + - override /target: er_sec61b + - override /predict_set: a549_mantis_sec61b_mock + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fnet3d_paper__sec61b_mock.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_sec61b_fnet3d_paper__sec61b_mock diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 100644 index 000000000..c8d2dae45 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: ER (SEC61B) predicted by FNet3DPaper on a549-mantis-sec61b-zikv. +defaults: + - override /target: er_sec61b + - override /predict_set: a549_mantis_sec61b_zikv + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fnet3d_paper__sec61b_zikv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_sec61b_fnet3d_paper__sec61b_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/ipsc_confocal/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/ipsc_confocal/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..522703d7e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/ipsc_confocal/predict__a549_mantis_denv.yml @@ -0,0 +1,42 @@ +# FNet3D paper-baseline predict: ER (SEC61B) trained on iPSC, predicting against a549_mantis_sec61b_denv test. +# Same iPSC best val-loss checkpoint as predict__ipsc_confocal.yml (epoch 183, loss/validate=0.5991). +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_denv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: ipsc_confocal + predict_set: a549_mantis_sec61b_denv + model_name: fnet3d_paper + experiment_id: er__ipsc_confocal__fnet3d_paper__a549_mantis_sec61b_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/fnet3d_paper/checkpoints/epoch=183-step=134688.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fnet3d_paper__sec61b_denv.zarr + +launcher: + job_name: FNet3DPaper_PRED_SEC61B_ON_A549_sec61b_denv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/ipsc_confocal/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/ipsc_confocal/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..01ba6b1b8 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/ipsc_confocal/predict__a549_mantis_mock.yml @@ -0,0 +1,42 @@ +# FNet3D paper-baseline predict: ER (SEC61B) trained on iPSC, predicting against a549_mantis_sec61b_mock test. +# Same iPSC best val-loss checkpoint as predict__ipsc_confocal.yml (epoch 183, loss/validate=0.5991). +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_mock.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: ipsc_confocal + predict_set: a549_mantis_sec61b_mock + model_name: fnet3d_paper + experiment_id: er__ipsc_confocal__fnet3d_paper__a549_mantis_sec61b_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/fnet3d_paper/checkpoints/epoch=183-step=134688.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fnet3d_paper__sec61b_mock.zarr + +launcher: + job_name: FNet3DPaper_PRED_SEC61B_ON_A549_sec61b_mock + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/ipsc_confocal/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/ipsc_confocal/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..e56f920d4 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/ipsc_confocal/predict__a549_mantis_zikv.yml @@ -0,0 +1,42 @@ +# FNet3D paper-baseline predict: ER (SEC61B) trained on iPSC, predicting against a549_mantis_sec61b_zikv test. +# Same iPSC best val-loss checkpoint as predict__ipsc_confocal.yml (epoch 183, loss/validate=0.5991). +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_zikv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: ipsc_confocal + predict_set: a549_mantis_sec61b_zikv + model_name: fnet3d_paper + experiment_id: er__ipsc_confocal__fnet3d_paper__a549_mantis_sec61b_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/fnet3d_paper/checkpoints/epoch=183-step=134688.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fnet3d_paper__sec61b_zikv.zarr + +launcher: + job_name: FNet3DPaper_PRED_SEC61B_ON_A549_sec61b_zikv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/ipsc_confocal/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/ipsc_confocal/predict__ipsc_confocal.yml new file mode 100644 index 000000000..e38bd6fde --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/ipsc_confocal/predict__ipsc_confocal.yml @@ -0,0 +1,42 @@ +# FNet3D paper-baseline predict: ER (SEC61B) against ipsc_confocal test_cropped. +# Uses best val-loss checkpoint (epoch 183, loss/validate=0.5991). +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: fnet3d_paper + experiment_id: er__ipsc_confocal__fnet3d_paper__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/fnet3d_paper/checkpoints/epoch=183-step=134688.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_fnet3d_paper.zarr + +launcher: + job_name: FNet3DPaper_PRED_SEC61B + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/ipsc_confocal/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/ipsc_confocal/train.yml new file mode 100644 index 000000000..3d55701de --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/ipsc_confocal/train.yml @@ -0,0 +1,39 @@ +# FNet3D paper-baseline fit on ER (SEC61B marker) — AICS iPSC confocal. +# Reproduces the trained run at +# /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/fnet3d_paper/. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/data_overlays/fnet3d_paper_fit.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_gpu_any_long.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + train_set: ipsc_confocal + model_name: fnet3d_paper + experiment_id: er__ipsc_confocal__fnet3d_paper + +trainer: + logger: + init_args: + name: FNet3D_iPSC_SEC61B_paper + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/fnet3d_paper + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/fnet3d_paper/checkpoints + +launcher: + job_name: FNet3DPaper_SEC61B + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/fnet3d_paper diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..e9b0a04a0 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,46 @@ +# FNet3D paper-baseline predict: ER trained on joint iPSC+A549, +# predicting against a549-mantis-sec61b-denv test. +# Best val-loss checkpoint from job 31962498 (epoch 106, val 0.6835). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# Both iPSC and a549 manifests use `sec61b`; targets/er_sec61b.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_denv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_sec61b_denv + model_name: fnet3d_paper + experiment_id: er__joint_ipsc_confocal_a549_mantis__fnet3d_paper__a549_mantis_sec61b_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/fnet3d_paper/checkpoints/epoch=106-step=135034.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fnet3d_paper_jointtrained_denv.zarr + +launcher: + job_name: FNet3DPaper_PRED_SEC61B_JOINTTR_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..7034a56b9 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,46 @@ +# FNet3D paper-baseline predict: ER trained on joint iPSC+A549, +# predicting against a549-mantis-sec61b-mock test. +# Best val-loss checkpoint from job 31962498 (epoch 106, val 0.6835). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# Both iPSC and a549 manifests use `sec61b`; targets/er_sec61b.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_mock.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_sec61b_mock + model_name: fnet3d_paper + experiment_id: er__joint_ipsc_confocal_a549_mantis__fnet3d_paper__a549_mantis_sec61b_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/fnet3d_paper/checkpoints/epoch=106-step=135034.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fnet3d_paper_jointtrained_mock.zarr + +launcher: + job_name: FNet3DPaper_PRED_SEC61B_JOINTTR_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..fb03c0e95 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,46 @@ +# FNet3D paper-baseline predict: ER trained on joint iPSC+A549, +# predicting against a549-mantis-sec61b-zikv test. +# Best val-loss checkpoint from job 31962498 (epoch 106, val 0.6835). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# Both iPSC and a549 manifests use `sec61b`; targets/er_sec61b.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_zikv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_sec61b_zikv + model_name: fnet3d_paper + experiment_id: er__joint_ipsc_confocal_a549_mantis__fnet3d_paper__a549_mantis_sec61b_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/fnet3d_paper/checkpoints/epoch=106-step=135034.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_fnet3d_paper_jointtrained_zikv.zarr + +launcher: + job_name: FNet3DPaper_PRED_SEC61B_JOINTTR_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..b3129cf8e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,47 @@ +# FNet3D paper-baseline predict: ER trained on joint iPSC+A549, +# predicting against ipsc_confocal test_cropped. +# Best val-loss checkpoint from job 31962498 (epoch 106, val 0.6835). +# Wandb run 20260503-181128_FNet3D_JOINT_SEC61B_paper (state=finished, +# 158 ep / 199,999 steps; final val 0.6951 — drifted up from ep106 best). +# Both iPSC and a549 manifests use `sec61b`; targets/er_sec61b.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: ipsc_confocal + model_name: fnet3d_paper + experiment_id: er__joint_ipsc_confocal_a549_mantis__fnet3d_paper__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/fnet3d_paper/checkpoints/epoch=106-step=135034.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_fnet3d_paper_jointtrained.zarr + +launcher: + job_name: FNet3DPaper_PRED_SEC61B_JOINTTR_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/joint_ipsc_confocal_a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/joint_ipsc_confocal_a549_mantis/train.yml new file mode 100644 index 000000000..b430d6771 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/joint_ipsc_confocal_a549_mantis/train.yml @@ -0,0 +1,123 @@ +# FNet3D paper-baseline fit on er (SEC61B) — joint +# ipsc_confocal + a549_mantis pooled. Mirrors +# er/fnet3d_paper/ipsc_confocal/train.yml on the joint +# train_set. +# +# Joint leaf per Stage 7 of A549_EXPANSION_ROADMAP.md. +# BatchedConcatDataModule + two explicit HCSDataModule children; +# only model_overlays/fnet3d_paper_fit.yml is composed; data block +# inline. Norms + 8-crops-per-FOV diverge from the CellDiff/UNetViT +# conventions: target channel uses mean/std (not median/iqr) and +# val augmentations are CPU CenterSpatialCropd on the raw keys (the +# baseline's training pipeline doesn't go through GPU val transforms). +# +# Topology: single GPU, any model, long wall — same as +# fnet3d_paper/ipsc_confocal/train.yml. The paper baseline is single-GPU +# and we keep that here so iPSC-only and joint runs are apples-to-apples. +base: + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_gpu_any_long.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + gene: SEC61B + target: er + target_id: er_sec61b + train_set: joint_ipsc_confocal_a549_mantis + model_name: fnet3d_paper + experiment_id: er__joint_ipsc_confocal_a549_mantis__fnet3d_paper + +trainer: + logger: + init_args: + name: FNet3D_JOINT_SEC61B_paper + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/fnet3d_paper + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/fnet3d_paper/checkpoints + +_hcs_init_args: &hcs_init_args + source_channel: Phase3D + target_channel: Structure + z_window_size: 32 + # See nucleus/fnet3d_paper/joint_*/train.yml for the rationale: joint + # mode does not divide batch_size by num_samples (unlike single-set), + # so 6 * num_samples=8 = 48 GPU samples matches single-set effective. + batch_size: 6 + num_workers: 8 + yx_patch_size: [64, 64] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Structure] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Structure] + w_key: Structure + spatial_size: [32, 64, 64] + num_samples: 8 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandFlipd + init_args: + keys: [source, target] + spatial_axes: [1] + prob: 0.5 + - class_path: viscy_transforms.BatchedRandFlipd + init_args: + keys: [source, target] + spatial_axes: [2] + prob: 0.5 + val_augmentations: + - class_path: viscy_transforms.CenterSpatialCropd + init_args: + keys: [Phase3D, Structure] + roi_size: [32, 64, 64] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + # ipsc_confocal — aics-hipsc SEC61B train store + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/SEC61B.zarr + # a549_mantis — pooled SEC61B all-conditions train store + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/SEC61B_all.zarr + +launcher: + job_name: FNet3DPaper_JOINT_SEC61B + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/fnet3d_paper + # Joint preloads two stores (iPSC + A549 pool) into /dev/shm; the default + # 256G cap is too tight (256G iPSC mem + ~50G A549 + worker peak OOMs). + # 512G is the smallest tier that fits joint preload + worker overhead. + sbatch: + mem: "512G" diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/joint_ipsc_confocal_a549_mantis/train_smoke.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/joint_ipsc_confocal_a549_mantis/train_smoke.yml new file mode 100644 index 000000000..8659579f3 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/joint_ipsc_confocal_a549_mantis/train_smoke.yml @@ -0,0 +1,122 @@ +# FNet3D paper-baseline joint smoke (single GPU, local interactive). +# Pairs the iPSC SEC61B_test12 zarr (12 FOVs, 2.4 GB) with the 4-FOV +# a549_mantis SEC61B store so a single A40 / H200 can iterate the joint +# loader end-to-end without the full ~250 GB iPSC SEC61B cache wait. +# +# Why a sibling leaf rather than --override at submit time: dotlist / +# bracket syntax (`data.init_args.data_modules.0.init_args.data_path=...`) +# does not index into list elements via submit_benchmark_job.py's override +# parser. Pre-swapping data_paths in a sibling leaf is the supported fix +# (same pattern as celldiff/joint_*/train_smoke.yml). +# +# Use: +# uv run python applications/dynacell/tools/submit_benchmark_job.py \ +# applications/dynacell/configs/benchmarks/virtual_staining/er/fnet3d_paper/joint_ipsc_confocal_a549_mantis/train_smoke.yml \ +# --dry-run --print-resolved > /tmp/fnet_joint_smoke.yaml +# uv run python -m dynacell fit --config /tmp/fnet_joint_smoke.yaml +base: + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + gene: SEC61B + target: er + target_id: er_sec61b + train_set: joint_ipsc_confocal_a549_mantis + model_name: fnet3d_paper + experiment_id: er__joint_ipsc_confocal_a549_mantis__fnet3d_paper__smoke + +trainer: + # Bound the run for a smoke; smoke doesn't need wandb logging. + max_steps: 10 + val_check_interval: 5 + limit_val_batches: 2 + logger: false + callbacks: + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + every_n_epochs: 1 + save_top_k: -1 + save_last: true + dirpath: /tmp/fnet_joint_smoke/checkpoints + +# `_`-prefixed top-level keys are stripped by load_composed_config. +_hcs_init_args: &hcs_init_args + source_channel: [Phase3D] + target_channel: [Structure] + z_window_size: 32 + # bs=8 conservatively fits a single A40 with FNet3D fp32 / 32×64×64 + # patches; bump to bs=16 or bs=32 if memory headroom is plentiful. + # bs % num_samples == 0 is required by HCSDataModule. + batch_size: 8 + # num_workers: 0 + pin_memory: false avoids fork-after-CUDA + pin-thread + # races that can deadlock the joint dataloader's first iter() on + # interactive GPU nodes. Plenty fast for a 10-step smoke; tune up + # (e.g. 2-4 workers, pin_memory=true) for full training. + num_workers: 0 + pin_memory: false + yx_patch_size: [64, 64] + split_ratio: 0.8 + mmap_preload: false + persistent_workers: false + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Structure] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Structure] + w_key: Structure + spatial_size: [32, 64, 64] + num_samples: 8 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandFlipd + init_args: + keys: [source, target] + spatial_axes: [1] + prob: 0.5 + - class_path: viscy_transforms.BatchedRandFlipd + init_args: + keys: [source, target] + spatial_axes: [2] + prob: 0.5 + val_augmentations: + - class_path: viscy_transforms.CenterSpatialCropd + init_args: + keys: [Phase3D, Structure] + roi_size: [32, 64, 64] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + # ipsc_confocal — aics-hipsc SEC61B test12 zarr (12 FOVs, 2.4 GB). + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/SEC61B_test12.zarr + # a549_mantis — 2024_11_07 SEC61B train store (only 4 FOVs). + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/SEC61B_all.zarr + +# launcher block kept minimal — local smoke isn't submitted via sbatch. +# Set so submit_benchmark_job.py --dry-run still composes successfully. +launcher: + job_name: FNet3DPaper_JOINT_SEC61B_SMOKE_LOCAL + run_root: /tmp/fnet_joint_smoke diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/a549_mantis/train.yml new file mode 100644 index 000000000..c6c16d163 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/a549_mantis/train.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit fit on ER (SEC61B marker) — A549 mantis-lightsheet pooled (mock + DENV + ZIKV). +base: + - ../../../_internal/shared/model/train_sets/a549_mantis.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/data_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + train_set: a549_mantis + model_name: pix2pix3d_unetvit + experiment_id: er__a549_mantis__pix2pix3d_unetvit + +trainer: + logger: + init_args: + name: pix2pix3d_unetvit_A549_SEC61B + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/sec61b/pix2pix3d_unetvit + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/sec61b/pix2pix3d_unetvit/checkpoints + +data: + init_args: + # A549 pooled store + target_channel — no resolver in this train_set. + target_channel: Structure + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/SEC61B_all.zarr + +launcher: + job_name: pix2pix3d_unetvit_A549_SEC61B + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/sec61b/pix2pix3d_unetvit diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 100644 index 000000000..49ea6646d --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: ER (SEC61B) predicted by pix2pix3d_unetvit on a549-mantis-sec61b-denv. +defaults: + - override /target: er_sec61b + - override /predict_set: a549_mantis_sec61b_denv + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_pix2pix3d_unetvit__sec61b_denv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_sec61b_pix2pix3d_unetvit__sec61b_denv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 100644 index 000000000..669276e73 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: ER (SEC61B) predicted by pix2pix3d_unetvit on a549-mantis-sec61b-mock. +defaults: + - override /target: er_sec61b + - override /predict_set: a549_mantis_sec61b_mock + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_pix2pix3d_unetvit__sec61b_mock.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_sec61b_pix2pix3d_unetvit__sec61b_mock diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 100644 index 000000000..6d06f42ef --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: ER (SEC61B) predicted by pix2pix3d_unetvit on a549-mantis-sec61b-zikv. +defaults: + - override /target: er_sec61b + - override /predict_set: a549_mantis_sec61b_zikv + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_pix2pix3d_unetvit__sec61b_zikv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_sec61b_pix2pix3d_unetvit__sec61b_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/eval__ipsc_confocal.yaml new file mode 100644 index 000000000..eb0dc067c --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/eval__ipsc_confocal.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: ER (SEC61B) predicted by pix2pix3d_unetvit on iPSC confocal. +defaults: + - override /target: er_sec61b + - override /predict_set: ipsc_confocal + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_pix2pix3d_unetvit.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/eval_sec61b_pix2pix3d_unetvit diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..b845c5f56 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_denv.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: ER (SEC61B) trained on iPSC, predicting against a549_mantis_sec61b_denv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_denv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: ipsc_confocal + predict_set: a549_mantis_sec61b_denv + model_name: pix2pix3d_unetvit + experiment_id: er__ipsc_confocal__pix2pix3d_unetvit__a549_mantis_sec61b_denv + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_pix2pix3d_unetvit__sec61b_denv.zarr + +launcher: + job_name: pix2pix3d_unetvit_PRED_SEC61B_ON_A549_sec61b_denv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..e70aa26c1 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_mock.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: ER (SEC61B) trained on iPSC, predicting against a549_mantis_sec61b_mock test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_mock.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: ipsc_confocal + predict_set: a549_mantis_sec61b_mock + model_name: pix2pix3d_unetvit + experiment_id: er__ipsc_confocal__pix2pix3d_unetvit__a549_mantis_sec61b_mock + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_pix2pix3d_unetvit__sec61b_mock.zarr + +launcher: + job_name: pix2pix3d_unetvit_PRED_SEC61B_ON_A549_sec61b_mock + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..ac88563ad --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_zikv.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: ER (SEC61B) trained on iPSC, predicting against a549_mantis_sec61b_zikv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_zikv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: ipsc_confocal + predict_set: a549_mantis_sec61b_zikv + model_name: pix2pix3d_unetvit + experiment_id: er__ipsc_confocal__pix2pix3d_unetvit__a549_mantis_sec61b_zikv + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_pix2pix3d_unetvit__sec61b_zikv.zarr + +launcher: + job_name: pix2pix3d_unetvit_PRED_SEC61B_ON_A549_sec61b_zikv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/predict__ipsc_confocal.yml new file mode 100644 index 000000000..b18947e37 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/predict__ipsc_confocal.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: ER (SEC61B) against ipsc_confocal test_cropped. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: pix2pix3d_unetvit + experiment_id: er__ipsc_confocal__pix2pix3d_unetvit__ipsc_confocal + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_pix2pix3d_unetvit.zarr + +launcher: + job_name: pix2pix3d_unetvit_PRED_SEC61B + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/train.yml new file mode 100644 index 000000000..9697d2cef --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/train.yml @@ -0,0 +1,37 @@ +# pix2pix3d_unetvit fit on ER (SEC61B marker) — AICS iPSC confocal. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/data_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + train_set: ipsc_confocal + model_name: pix2pix3d_unetvit + experiment_id: er__ipsc_confocal__pix2pix3d_unetvit + +trainer: + logger: + init_args: + name: pix2pix3d_unetvit_iPSC_SEC61B + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/pix2pix3d_unetvit + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/pix2pix3d_unetvit/checkpoints + +launcher: + job_name: pix2pix3d_unetvit_SEC61B + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/pix2pix3d_unetvit diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/train_4gpu.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/train_4gpu.yml new file mode 100644 index 000000000..8cfda5940 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/train_4gpu.yml @@ -0,0 +1,51 @@ +# pix2pix3d_unetvit fit on ER (SEC61B) — 4-GPU DDP production leaf. +# +# Sibling of train.yml that swaps the model overlay + hardware profile to +# the 4-GPU GAN DDP topology (find_unused_parameters=True). The DDP run is +# the canonical production run going forward; we keep `experiment_id` and +# `launcher.run_root` / `ModelCheckpoint.dirpath` IDENTICAL to train.yml +# so checkpoints land in the same canonical subtree (Phase 4 predict leaves +# reference that path). W&B run name and slurm job name are suffixed +# `_4gpu` to disambiguate from the in-flight single-GPU run sharing the +# same experiment_id. +# +# Per-GPU batch_size stays at 4 (inherited from data_overlays/unetvit3d_fit +# via single-GPU recipe). Effective batch = 16 across 4 ranks. lr_g/lr_d +# are NOT scaled by GPU count — we want to evaluate DDP at the same +# per-rank optimization settings as the single-GPU run first. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/data_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_fit_ddp.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + train_set: ipsc_confocal + model_name: pix2pix3d_unetvit + experiment_id: er__ipsc_confocal__pix2pix3d_unetvit + +trainer: + logger: + init_args: + name: pix2pix3d_unetvit_iPSC_SEC61B_4gpu + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/pix2pix3d_unetvit + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/pix2pix3d_unetvit/checkpoints + +launcher: + job_name: pix2pix3d_unetvit_SEC61B_4gpu + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/pix2pix3d_unetvit diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/train_4gpu_modernized.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/train_4gpu_modernized.yml new file mode 100644 index 000000000..6846d963a --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/train_4gpu_modernized.yml @@ -0,0 +1,51 @@ +# pix2pix3d_unetvit MODERNIZED fit on ER (SEC61B) — 4-GPU DDP production leaf. +# +# Run A of the modernization recipe (see PR +# https://github.com/mehta-lab/VisCy/pull/428). +# +# Sibling of train_4gpu.yml. Same experiment_id and W&B/save-dir structure, +# but composes the MODERNIZED model overlay (nonsat + R1 + EMA + equal LR) +# instead of the LSGAN baseline overlay. Checkpoints land in a distinct +# subtree so they cannot collide with the in-flight LSGAN run. +# +# W&B run name and slurm job name carry `_modernized` to make the recipe +# obvious in the dashboard. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/data_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_fit_ddp_modernized.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + train_set: ipsc_confocal + model_name: pix2pix3d_unetvit + experiment_id: er__ipsc_confocal__pix2pix3d_unetvit_modernized + +trainer: + logger: + init_args: + name: pix2pix3d_unetvit_iPSC_SEC61B_4gpu_modernized + save_dir: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/sec61b/pix2pix3d_unetvit_modernized + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + # Modernized recipe monitors the EMA generator's val loss — that's + # the inference path (predict_step uses _inference_generator which + # prefers EMA when available). + monitor: loss/validate_ema + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/sec61b/pix2pix3d_unetvit_modernized/checkpoints + +launcher: + job_name: pix2pix3d_unetvit_SEC61B_4gpu_modernized + run_root: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/sec61b/pix2pix3d_unetvit_modernized diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/train_smoke.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/train_smoke.yml new file mode 100644 index 000000000..3ec37992f --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/train_smoke.yml @@ -0,0 +1,70 @@ +# Single-GPU smoke for pix2pix3d_unetvit on the iPSC SEC61B test48 store. +# +# Purpose: validate compose / instantiate / training-loop end-to-end on a +# 48-FOV debug zarr without the full SEC61B.zarr mmap_preload that blows +# a smoke wall. Pair with `--override trainer.fast_dev_run=true` (or +# rely on the inline `trainer.max_steps: 20`) at submit time. +# +# Two constraints vs naive override of er/unetvit3d/ipsc_confocal/train.yml: +# (1) `targets/er_sec61b.yml` sets `benchmark.dataset_ref` so the compose +# hook would resolve data_path from the manifest and raise on the +# inline data_path below. We null `dataset_ref` to disable the +# resolver (`dataset_ref_from_dict` returns None for non-dict refs, +# so the resolver no-ops — see dynacell/data/resolver.py:66-81). +# (2) `targets/er_sec61b.yml` sets `augmentations[0].init_args.num_samples: 2`, +# and HCSDataModule._train_transform requires `batch_size % +# num_samples == 0` for non-BatchedConcat datamodules. We keep +# `num_samples: 2` and set `batch_size: 2` (the simplest way to +# satisfy 2 % 2 == 0; do not override the augmentation's num_samples +# from the smoke leaf). +# +# `source_channel` / `target_channel` are normally populated by the +# compose-hook resolver from the manifest entry; since we disable the +# resolver, they are authored inline here. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/data_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/wall_smoke.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + train_set: ipsc_confocal + model_name: pix2pix3d_unetvit + experiment_id: er__ipsc_confocal__pix2pix3d_unetvit__smoke + # Null disables the compose-hook resolver so the inline data_path / + # source_channel / target_channel below take effect without colliding. + dataset_ref: null + +data: + init_args: + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/SEC61B_test48.zarr + source_channel: [Phase3D] + target_channel: [Structure] + batch_size: 2 + +trainer: + # Smoke runs don't need a logger. `false` disables the recipe's WandbLogger + # so consumers don't have to remember --override trainer.logger=false. + # LearningRateMonitor (recipe default) raises without a logger AND + # manual schedulers don't play well with it under DynacellGAN — so the + # callbacks list is replaced with only ModelCheckpoint (lists replace + # wholesale under deep_merge). + logger: false + max_steps: 20 + max_epochs: 1 + callbacks: + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + save_last: true + every_n_train_steps: 10 + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/pix2pix3d_unetvit/smoke/checkpoints + +launcher: + job_name: pix2pix3d_unetvit_SEC61B_SMOKE + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/pix2pix3d_unetvit/smoke diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/train_smoke_4gpu.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/train_smoke_4gpu.yml new file mode 100644 index 000000000..005120b03 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/ipsc_confocal/train_smoke_4gpu.yml @@ -0,0 +1,64 @@ +# 4-GPU DDP smoke for pix2pix3d_unetvit on the iPSC SEC61B test48 store. +# +# Purpose: validate the GAN-aware DDP topology (find_unused_parameters=True) +# + HCSDataModule single-set DistributedSampler integration end-to-end on a +# 48-FOV debug zarr. The single-GPU `train_smoke.yml` already proved +# compose/instantiate/training-loop; this leaf isolates DDP behavior on +# the alternating two-optimizer training_step under 4 ranks. +# +# Mirrors train_smoke.yml's two compose constraints: +# (1) `targets/er_sec61b.yml` sets `benchmark.dataset_ref`; we null it so +# the resolver no-ops and the inline data_path/channels take effect. +# (2) `augmentations[0].init_args.num_samples: 2` and +# HCSDataModule._train_transform enforces batch_size % num_samples == 0, +# so we set `batch_size: 2`. +# +# Per-GPU batch_size stays at 2 (matches single-GPU smoke); effective batch +# under 4 GPUs is 8. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/data_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_fit_ddp.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/wall_smoke.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + train_set: ipsc_confocal + model_name: pix2pix3d_unetvit + experiment_id: er__ipsc_confocal__pix2pix3d_unetvit__smoke_4gpu + # Null disables the compose-hook resolver so the inline data_path / + # source_channel / target_channel below take effect without colliding. + dataset_ref: null + +data: + init_args: + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/SEC61B_test48.zarr + source_channel: [Phase3D] + target_channel: [Structure] + batch_size: 2 + +trainer: + # Smoke runs don't need a logger. `false` disables the recipe's WandbLogger + # so consumers don't have to remember --override trainer.logger=false. + # LearningRateMonitor (recipe default) raises without a logger AND + # manual schedulers don't play well with it under DynacellGAN — so the + # callbacks list is replaced with only ModelCheckpoint (lists replace + # wholesale under deep_merge). + logger: false + max_steps: 20 + max_epochs: 1 + callbacks: + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + save_last: true + every_n_train_steps: 10 + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/pix2pix3d_unetvit/smoke_4gpu/checkpoints + +launcher: + job_name: pix2pix3d_unetvit_SEC61B_SMOKE_4GPU + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/pix2pix3d_unetvit/smoke_4gpu diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..6d684c4c2 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: er (SEC61B marker) trained on joint iPSC+A549, predicting against a549_mantis_sec61b_denv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_denv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_sec61b_denv + model_name: pix2pix3d_unetvit + experiment_id: er__joint_ipsc_confocal_a549_mantis__pix2pix3d_unetvit__a549_mantis_sec61b_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/pix2pix3d_unetvit/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/sec61b_pix2pix3d_unetvit__sec61b_denv.zarr + +launcher: + job_name: pix2pix3d_unetvit_JOINT_PRED_SEC61B_ON_A549_sec61b_denv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..e72661697 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: er (SEC61B marker) trained on joint iPSC+A549, predicting against a549_mantis_sec61b_mock test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_mock.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_sec61b_mock + model_name: pix2pix3d_unetvit + experiment_id: er__joint_ipsc_confocal_a549_mantis__pix2pix3d_unetvit__a549_mantis_sec61b_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/pix2pix3d_unetvit/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/sec61b_pix2pix3d_unetvit__sec61b_mock.zarr + +launcher: + job_name: pix2pix3d_unetvit_JOINT_PRED_SEC61B_ON_A549_sec61b_mock + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..c3b848eaf --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: er (SEC61B marker) trained on joint iPSC+A549, predicting against a549_mantis_sec61b_zikv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_zikv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_sec61b_zikv + model_name: pix2pix3d_unetvit + experiment_id: er__joint_ipsc_confocal_a549_mantis__pix2pix3d_unetvit__a549_mantis_sec61b_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/pix2pix3d_unetvit/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/sec61b_pix2pix3d_unetvit__sec61b_zikv.zarr + +launcher: + job_name: pix2pix3d_unetvit_JOINT_PRED_SEC61B_ON_A549_sec61b_zikv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..ce065cb3c --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: er (SEC61B marker) trained on joint iPSC+A549, predicting against ipsc_confocal test. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: ipsc_confocal + model_name: pix2pix3d_unetvit + experiment_id: er__joint_ipsc_confocal_a549_mantis__pix2pix3d_unetvit__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/pix2pix3d_unetvit/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions/sec61b_pix2pix3d_unetvit.zarr + +launcher: + job_name: pix2pix3d_unetvit_JOINT_PRED_SEC61B_ON_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/train.yml new file mode 100644 index 000000000..5d25c3343 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/train.yml @@ -0,0 +1,147 @@ +# pix2pix3d_unetvit fit on er (SEC61B marker) — joint ipsc_confocal + a549_mantis pooled. +# +# Joint leaf. Uses BatchedConcatDataModule with two explicit HCSDataModule +# children (no benchmark.dataset_ref — joint leaves bypass the single-dataset +# resolver). Only model_overlays/pix2pix3d_unetvit_fit.yml is composed; the +# data block is authored inline because joint hparams live on the children. +# +# Normalization is NormalizeSampled (fov_statistics) to match the single-set +# pix2pix3d_unetvit leaves — divergent from the celldiff joint which uses +# MinMaxSampled. Per-organelle prior is to keep joint and single-set +# normalizations identical so ablations are apples-to-apples. +# +# Topology: single H200, single GPU — same as pix2pix3d_unetvit/ipsc_confocal/train.yml. +base: + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + gene: SEC61B + target: er + target_id: er_sec61b + train_set: joint_ipsc_confocal_a549_mantis + model_name: pix2pix3d_unetvit + experiment_id: er__joint_ipsc_confocal_a549_mantis__pix2pix3d_unetvit + +trainer: + logger: + init_args: + name: pix2pix3d_unetvit_JOINT_SEC61B + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/pix2pix3d_unetvit + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/pix2pix3d_unetvit/checkpoints + +# Child HCSDataModule init_args shared across both datasets (only data_path +# differs). `_`-prefixed top-level keys are stripped by load_composed_config +# before reaching LightningCLI; the merge expansion under `data:` survives. +_hcs_init_args: &hcs_init_args + source_channel: Phase3D + target_channel: Structure + z_window_size: 13 + # batch_size=2 + num_samples=2 → 4 GPU samples/step, matching the single-set + # pix2pix3d_unetvit (batch=4, num_samples=2). BatchedConcatDataModule does + # NOT divide by num_samples (see CLAUDE.md), so joint.batch_size = + # single_set.batch_size / num_samples. + batch_size: 2 + num_workers: 4 + yx_patch_size: [512, 512] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Structure] + level: fov_statistics + subtrahend: median + divisor: iqr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Structure] + w_key: Structure + spatial_size: [13, 624, 624] + num_samples: 2 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + safe_crop_size: [8, 512, 512] + safe_crop_coverage: 0.9 + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/SEC61B.zarr + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/SEC61B_all.zarr + +launcher: + job_name: pix2pix3d_unetvit_JOINT_SEC61B + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/pix2pix3d_unetvit + # Joint preloads two stores (iPSC + A549 pool) into /dev/shm; default 256G + # is too tight for the iPSC marker store + A549 pool + worker overhead. + sbatch: + mem: "512G" diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/a549_mantis/train.yml new file mode 100644 index 000000000..8667ebe44 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/a549_mantis/train.yml @@ -0,0 +1,43 @@ +# UNetViT3D fit on ER (SEC61B marker) — A549 mantis-lightsheet pooled (mock + DENV + ZIKV). +base: + - ../../../_internal/shared/model/train_sets/a549_mantis.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/data_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/model_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + train_set: a549_mantis + model_name: unetvit3d + experiment_id: er__a549_mantis__unetvit3d + +trainer: + logger: + init_args: + name: UNetViT3D_A549_SEC61B + save_dir: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/sec61b/unetvit3d + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/sec61b/unetvit3d/checkpoints + +data: + init_args: + # A549 pooled store + target_channel — no resolver in this train_set. + target_channel: Structure + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/SEC61B_all.zarr + +launcher: + job_name: UNetViT3D_A549_SEC61B + run_root: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/sec61b/unetvit3d diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 100644 index 000000000..62bd749e1 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: ER (SEC61B) predicted by UNetViT3D on a549-mantis-sec61b-denv. +defaults: + - override /target: er_sec61b + - override /predict_set: a549_mantis_sec61b_denv + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_unetvit3d__sec61b_denv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_sec61b_unetvit3d__sec61b_denv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 100644 index 000000000..fc376dfee --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: ER (SEC61B) predicted by UNetViT3D on a549-mantis-sec61b-mock. +defaults: + - override /target: er_sec61b + - override /predict_set: a549_mantis_sec61b_mock + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_unetvit3d__sec61b_mock.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_sec61b_unetvit3d__sec61b_mock diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 100644 index 000000000..beae75161 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: ER (SEC61B) predicted by UNetViT3D on a549-mantis-sec61b-zikv. +defaults: + - override /target: er_sec61b + - override /predict_set: a549_mantis_sec61b_zikv + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_unetvit3d__sec61b_zikv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_sec61b_unetvit3d__sec61b_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/ipsc_confocal/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/ipsc_confocal/eval__ipsc_confocal.yaml new file mode 100644 index 000000000..8f4329e08 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/ipsc_confocal/eval__ipsc_confocal.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: ER (SEC61B) predicted by UNetViT3D on iPSC confocal. +defaults: + - override /target: er_sec61b + - override /predict_set: ipsc_confocal + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_unetvit3d.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/eval_sec61b_unetvit3d diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/ipsc_confocal/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/ipsc_confocal/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..cfccde404 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/ipsc_confocal/predict__a549_mantis_denv.yml @@ -0,0 +1,43 @@ +# UNetViT3D predict: ER (SEC61B) trained on iPSC, predicting against a549_mantis_sec61b_denv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_denv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/unetvit3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: ipsc_confocal + predict_set: a549_mantis_sec61b_denv + model_name: unetvit3d + experiment_id: er__ipsc_confocal__unetvit3d__a549_mantis_sec61b_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/sec61b/unetvit3d/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_unetvit3d__sec61b_denv.zarr + +launcher: + job_name: UNetViT3D_PRED_SEC61B_ON_A549_sec61b_denv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/ipsc_confocal/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/ipsc_confocal/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..7391d1868 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/ipsc_confocal/predict__a549_mantis_mock.yml @@ -0,0 +1,43 @@ +# UNetViT3D predict: ER (SEC61B) trained on iPSC, predicting against a549_mantis_sec61b_mock test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_mock.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/unetvit3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: ipsc_confocal + predict_set: a549_mantis_sec61b_mock + model_name: unetvit3d + experiment_id: er__ipsc_confocal__unetvit3d__a549_mantis_sec61b_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/sec61b/unetvit3d/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_unetvit3d__sec61b_mock.zarr + +launcher: + job_name: UNetViT3D_PRED_SEC61B_ON_A549_sec61b_mock + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/ipsc_confocal/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/ipsc_confocal/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..c2cda1647 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/ipsc_confocal/predict__a549_mantis_zikv.yml @@ -0,0 +1,43 @@ +# UNetViT3D predict: ER (SEC61B) trained on iPSC, predicting against a549_mantis_sec61b_zikv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_sec61b_zikv.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/unetvit3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: ipsc_confocal + predict_set: a549_mantis_sec61b_zikv + model_name: unetvit3d + experiment_id: er__ipsc_confocal__unetvit3d__a549_mantis_sec61b_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/sec61b/unetvit3d/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/sec61b_unetvit3d__sec61b_zikv.zarr + +launcher: + job_name: UNetViT3D_PRED_SEC61B_ON_A549_sec61b_zikv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/ipsc_confocal/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/ipsc_confocal/predict__ipsc_confocal.yml new file mode 100644 index 000000000..1996d089d --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/ipsc_confocal/predict__ipsc_confocal.yml @@ -0,0 +1,43 @@ +# UNetViT3D predict: ER (SEC61B) against ipsc_confocal test_cropped. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/model_overlays/unetvit3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: unetvit3d + experiment_id: er__ipsc_confocal__unetvit3d__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/sec61b/unetvit3d/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_unetvit3d.zarr + +launcher: + job_name: UNetViT3D_PRED_SEC61B + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/ipsc_confocal/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/ipsc_confocal/train.yml new file mode 100644 index 000000000..d0b03dfd2 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/ipsc_confocal/train.yml @@ -0,0 +1,37 @@ +# UNetViT3D fit on ER (SEC61B marker) — AICS iPSC confocal. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/data_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/model_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + train_set: ipsc_confocal + model_name: unetvit3d + experiment_id: er__ipsc_confocal__unetvit3d + +trainer: + logger: + init_args: + name: UNetViT3D_iPSC_SEC61B + save_dir: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/sec61b/unetvit3d + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/sec61b/unetvit3d/checkpoints + +launcher: + job_name: UNetViT3D_SEC61B + run_root: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/sec61b/unetvit3d diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/joint_ipsc_confocal_a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/joint_ipsc_confocal_a549_mantis/train.yml new file mode 100644 index 000000000..7cc35bed1 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/unetvit3d/joint_ipsc_confocal_a549_mantis/train.yml @@ -0,0 +1,148 @@ +# UNetViT3D fit on ER (SEC61B) — joint ipsc_confocal + a549_mantis pooled. +# +# Joint leaf per Stage 7 of A549_EXPANSION_ROADMAP.md. Uses +# BatchedConcatDataModule with two explicit HCSDataModule children +# (no benchmark.dataset_ref — joint leaves bypass the single-dataset +# resolver). Only model_overlays/unetvit3d_fit.yml is composed; the data +# block is authored inline because joint hparams live on the children. +# +# Topology: single H200, single GPU — same as unetvit3d/ipsc_confocal/train.yml. +# The paper baseline pattern is single-GPU and we keep that here so +# iPSC-only and joint runs are apples-to-apples. +base: + - ../../../_internal/shared/model/model_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + gene: SEC61B + target: er + target_id: er_sec61b + train_set: joint_ipsc_confocal_a549_mantis + model_name: unetvit3d + experiment_id: er__joint_ipsc_confocal_a549_mantis__unetvit3d + +trainer: + logger: + init_args: + name: UNetViT3D_JOINT_SEC61B + save_dir: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/sec61b/unetvit3d + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/sec61b/unetvit3d/checkpoints + +# Child HCSDataModule init_args shared across both datasets (only data_path +# differs). Factored as a YAML anchor so the joint leaf stays auditable. +# +# Naming convention: top-level keys starting with `_` are private to the +# YAML compose layer and are stripped by `load_composed_config` before +# the dict reaches LightningCLI / jsonargparse (which would reject them +# as unknown options). The merge expansion under `data:` survives. +_hcs_init_args: &hcs_init_args + source_channel: Phase3D + target_channel: Structure + z_window_size: 13 + batch_size: 4 + num_workers: 4 + yx_patch_size: [512, 512] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Structure] + level: fov_statistics + subtrahend: median + divisor: iqr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Structure] + w_key: Structure + spatial_size: [13, 624, 624] + num_samples: 2 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + safe_crop_size: [8, 512, 512] + safe_crop_coverage: 0.9 + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + # ipsc_confocal — aics-hipsc SEC61B train store + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/SEC61B.zarr + # a549_mantis — pooled SEC61B all-conditions train store + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/SEC61B_all.zarr + +launcher: + job_name: UNetViT3D_JOINT_SEC61B + run_root: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/sec61b/unetvit3d + # Joint preloads two stores (iPSC + A549 pool) into /dev/shm; the default + # 256G cap is too tight (256G iPSC mem + ~50G A549 + worker peak OOMs). + # 512G is the smallest tier that fits joint preload + worker overhead. + sbatch: + mem: "512G" diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/unext2/a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/unext2/a549_mantis/train.yml new file mode 100644 index 000000000..3b0e02a52 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/unext2/a549_mantis/train.yml @@ -0,0 +1,53 @@ +# Timm-backed UNeXt2 (viscy_models.unet.unext2:UNeXt2) supervised scratch +# baseline on ER/SEC61B — i.e. NOT FullyConvolutionalMAE(pretraining=False). +# This answers "how does the dynacell UNeXt2 recipe train at all?" — it is +# NOT the apples-to-apples scratch control for FCMAE-pretrained init. The +# FCMAE paper-adjacent scratch baseline lives at fcmae_vscyto3d_scratch.yml +# and uses a different model class. See +# applications/dynacell/configs/benchmarks/UNEXT2_VS_FCMAE_CLASSES.md. +# +# Reproduces wandb run 20260409-020023_UNeXt2_iPSC_SEC61B (Dihan's Run 4, +# commit 46e4c79): lr=0.0004, batch_size=32, z_window_size=20, 4-GPU DDP. +# MixedLoss(L1 0.5 + DSSIM 0.5). max_epochs=200. +base: + - ../../../_internal/shared/model/train_sets/a549_mantis.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/data_overlays/unext2_fit.yml + - ../../../_internal/shared/model/model_overlays/unext2_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + train_set: a549_mantis + model_name: unext2_timm_scratch + experiment_id: er__a549_mantis__unext2_timm_scratch + +trainer: + logger: + init_args: + name: UNeXt2_A549_SEC61B + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/sec61b/unext2 + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/sec61b/unext2/checkpoints + +data: + init_args: + # A549 pooled store + target_channel — no resolver in this train_set. + target_channel: Structure + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/SEC61B_all.zarr + +launcher: + job_name: UNeXt2_A549_SEC61B + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/sec61b/unext2 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/unext2/ipsc_confocal/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/unext2/ipsc_confocal/train.yml new file mode 100644 index 000000000..9121f5692 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/unext2/ipsc_confocal/train.yml @@ -0,0 +1,47 @@ +# Timm-backed UNeXt2 (viscy_models.unet.unext2:UNeXt2) supervised scratch +# baseline on ER/SEC61B — i.e. NOT FullyConvolutionalMAE(pretraining=False). +# This answers "how does the dynacell UNeXt2 recipe train at all?" — it is +# NOT the apples-to-apples scratch control for FCMAE-pretrained init. The +# FCMAE paper-adjacent scratch baseline lives at fcmae_vscyto3d_scratch.yml +# and uses a different model class. See +# applications/dynacell/configs/benchmarks/UNEXT2_VS_FCMAE_CLASSES.md. +# +# Reproduces wandb run 20260409-020023_UNeXt2_iPSC_SEC61B (Dihan's Run 4, +# commit 46e4c79): lr=0.0004, batch_size=32, z_window_size=20, 4-GPU DDP. +# MixedLoss(L1 0.5 + DSSIM 0.5). max_epochs=200. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/er_sec61b.yml + - ../../../_internal/shared/model/data_overlays/unext2_fit.yml + - ../../../_internal/shared/model/model_overlays/unext2_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + train_set: ipsc_confocal + model_name: unext2_timm_scratch + experiment_id: er__ipsc_confocal__unext2_timm_scratch + +trainer: + logger: + init_args: + name: UNeXt2_iPSC_SEC61B + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/unext2 + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/unext2/checkpoints + +launcher: + job_name: UNeXt2_SEC61B + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/sec61b/unext2 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/er/unext2/joint_ipsc_confocal_a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/er/unext2/joint_ipsc_confocal_a549_mantis/train.yml new file mode 100644 index 000000000..e1b905ebb --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/er/unext2/joint_ipsc_confocal_a549_mantis/train.yml @@ -0,0 +1,142 @@ +# Timm-backed UNeXt2 (viscy_models.unet.unext2:UNeXt2) supervised +# scratch baseline on er (SEC61B) — joint ipsc_confocal + +# a549_mantis pooled. Mirrors +# er/unext2/ipsc_confocal/train.yml on the joint train_set. +# Reproduces Run 4 hparams (lr=0.0004, bs=32, z=20, 4-GPU DDP, +# MixedLoss(L1 0.5 + DSSIM 0.5), max_epochs=200) inherited from +# model_overlays/unext2_fit.yml. +# +# This is NOT the apples-to-apples scratch control for FCMAE-pretrained +# init. The FCMAE paper-adjacent scratch baseline lives at +# fcmae_vscyto3d_scratch/joint_*/train.yml and uses a different model +# class. See applications/dynacell/configs/benchmarks/UNEXT2_VS_FCMAE_CLASSES.md. +# +# Joint leaf per Stage 7 of A549_EXPANSION_ROADMAP.md. +# BatchedConcatDataModule + two explicit HCSDataModule children; +# only model_overlays/unext2_fit.yml is composed; data block inline. +# +# Topology: 4-GPU DDP (inherited from +# model_overlays/unext2_fit.yml's ddp_4gpu base). +base: + - ../../../_internal/shared/model/model_overlays/unext2_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: er + gene: SEC61B + target: er + target_id: er_sec61b + train_set: joint_ipsc_confocal_a549_mantis + model_name: unext2_timm_scratch + experiment_id: er__joint_ipsc_confocal_a549_mantis__unext2_timm_scratch + +trainer: + logger: + init_args: + name: UNeXt2_JOINT_SEC61B + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/unext2 + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/unext2/checkpoints + +_hcs_init_args: &hcs_init_args + source_channel: Phase3D + target_channel: Structure + z_window_size: 20 + batch_size: 32 + num_workers: 8 + yx_patch_size: [384, 384] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Structure] + level: fov_statistics + subtrahend: median + divisor: iqr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Structure] + w_key: Structure + spatial_size: [20, 600, 600] + num_samples: 4 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + # ipsc_confocal — aics-hipsc SEC61B train store + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/SEC61B.zarr + # a549_mantis — pooled SEC61B all-conditions train store + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/SEC61B_all.zarr + +launcher: + job_name: UNeXt2_JOINT_SEC61B + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/sec61b/unext2 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..7db172d62 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,45 @@ +# CellDiff r2 predict: membrane trained on A549 mantis, predicting against a549_mantis_caax_denv test. +# A549 manifest keys membrane by gene (`caax`); override the target_id so the resolver finds caax. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_denv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: a549_mantis + predict_set: a549_mantis_caax_denv + model_name: celldiff + experiment_id: membrane__a549_mantis__celldiff__a549_mantis_caax_denv + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/memb/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_celldiff_r2_a549trained_denv.zarr + +launcher: + job_name: CELLDiff_A549_PRED_MEMB_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..3442ccfcb --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,45 @@ +# CellDiff r2 predict: membrane trained on A549 mantis, predicting against a549_mantis_caax_mock test. +# A549 manifest keys membrane by gene (`caax`); override the target_id so the resolver finds caax. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_mock.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: a549_mantis + predict_set: a549_mantis_caax_mock + model_name: celldiff + experiment_id: membrane__a549_mantis__celldiff__a549_mantis_caax_mock + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/memb/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_celldiff_r2_a549trained_mock.zarr + +launcher: + job_name: CELLDiff_A549_PRED_MEMB_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..14da9590f --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,45 @@ +# CellDiff r2 predict: membrane trained on A549 mantis, predicting against a549_mantis_caax_zikv test. +# A549 manifest keys membrane by gene (`caax`); override the target_id so the resolver finds caax. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_zikv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: a549_mantis + predict_set: a549_mantis_caax_zikv + model_name: celldiff + experiment_id: membrane__a549_mantis__celldiff__a549_mantis_caax_zikv + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/memb/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_celldiff_r2_a549trained_zikv.zarr + +launcher: + job_name: CELLDiff_A549_PRED_MEMB_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..7081f488a --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: membrane trained on A549 mantis, predicting against ipsc_confocal test (OOD). +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: a549_mantis + predict_set: ipsc_confocal + model_name: celldiff + experiment_id: membrane__a549_mantis__celldiff__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/memb/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 40 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_celldiff_r2_a549trained.zarr + +launcher: + job_name: CELLDiff_A549_PRED_MEMB_ON_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/a549_mantis/train.yml new file mode 100644 index 000000000..4971450ef --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/a549_mantis/train.yml @@ -0,0 +1,42 @@ +# CellDiff fit on membrane (Membrane channel of cell.zarr) — A549 mantis-lightsheet pooled (mock + DENV + ZIKV). +base: + - ../../../_internal/shared/model/train_sets/a549_mantis.yml + - ../../../_internal/shared/model/targets/membrane_celldiff.yml + - ../../../_internal/shared/model/data_overlays/celldiff_fit.yml + - ../../../_internal/shared/model/model_overlays/celldiff_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + train_set: a549_mantis + model_name: celldiff + experiment_id: membrane__a549_mantis__celldiff + +trainer: + logger: + init_args: + name: CELLDiff_A549_MEMB + save_dir: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/memb/celldiff_r2 + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + every_n_epochs: 1 + save_top_k: -1 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/memb/celldiff_r2/checkpoints + +data: + init_args: + # A549 pooled store + target_channel — no resolver in this train_set. + target_channel: Membrane + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/CAAX_all.zarr + +launcher: + job_name: CELLDiff_A549_MEMB + run_root: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/memb/celldiff_r2 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 100644 index 000000000..d0186125d --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Membrane (CAAX) predicted by CellDiff on a549-mantis-caax-denv. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from the target group so the resolver finds caax on a549-mantis-caax-denv. +defaults: + - override /target: membrane + - override /predict_set: a549_mantis_caax_denv + +benchmark: + dataset_ref: + target: caax + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_celldiff_sliding_window_denv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_memb_celldiff_sliding_window_denv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 100644 index 000000000..8c7318119 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Membrane (CAAX) predicted by CellDiff on a549-mantis-caax-mock. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from the target group so the resolver finds caax on a549-mantis-caax-mock. +defaults: + - override /target: membrane + - override /predict_set: a549_mantis_caax_mock + +benchmark: + dataset_ref: + target: caax + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_celldiff_sliding_window_mock.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_memb_celldiff_sliding_window_mock diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 100644 index 000000000..79b0b7fc1 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Membrane (CAAX) predicted by CellDiff on a549-mantis-caax-zikv. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from the target group so the resolver finds caax on a549-mantis-caax-zikv. +defaults: + - override /target: membrane + - override /predict_set: a549_mantis_caax_zikv + +benchmark: + dataset_ref: + target: caax + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_celldiff_sliding_window_zikv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_memb_celldiff_sliding_window_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/eval__ipsc_confocal.yaml new file mode 100644 index 000000000..74852e701 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/eval__ipsc_confocal.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: Membrane predicted by CellDiff on iPSC confocal. +defaults: + - override /target: membrane + - override /predict_set: ipsc_confocal + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_celldiff_sliding_window.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/eval_memb_celldiff_sliding_window diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..3641144d8 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/predict__a549_mantis_denv.yml @@ -0,0 +1,48 @@ +# CellDiff predict: membrane trained on iPSC, predicting against a549-mantis-caax-denv test. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-denv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_denv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: ipsc_confocal + predict_set: a549_mantis_caax_denv + model_name: celldiff + experiment_id: membrane__ipsc_confocal__celldiff__a549_mantis_caax_denv + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/memb/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative # denoise, generate, sliding_window, or iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 # 8 for denoise and generate, 40 for iterative and sliding_window. + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_celldiff_r2_iterative_denv.zarr + +launcher: + job_name: CELLDiff_PRED_MEMB_ON_A549_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..96b95ebd2 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/predict__a549_mantis_mock.yml @@ -0,0 +1,48 @@ +# CellDiff predict: membrane trained on iPSC, predicting against a549-mantis-caax-mock test. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-mock. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_mock.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: ipsc_confocal + predict_set: a549_mantis_caax_mock + model_name: celldiff + experiment_id: membrane__ipsc_confocal__celldiff__a549_mantis_caax_mock + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/memb/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative # denoise, generate, sliding_window, or iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 # 8 for denoise and generate, 40 for iterative and sliding_window. + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_celldiff_r2_iterative_mock.zarr + +launcher: + job_name: CELLDiff_PRED_MEMB_ON_A549_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..075bf03b2 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/predict__a549_mantis_zikv.yml @@ -0,0 +1,48 @@ +# CellDiff predict: membrane trained on iPSC, predicting against a549-mantis-caax-zikv test. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-zikv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_zikv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: ipsc_confocal + predict_set: a549_mantis_caax_zikv + model_name: celldiff + experiment_id: membrane__ipsc_confocal__celldiff__a549_mantis_caax_zikv + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/memb/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative # denoise, generate, sliding_window, or iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 # 8 for denoise and generate, 40 for iterative and sliding_window. + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_celldiff_r2_iterative_zikv.zarr + +launcher: + job_name: CELLDiff_PRED_MEMB_ON_A549_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/predict__ipsc_confocal__denoise.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/predict__ipsc_confocal__denoise.yml new file mode 100644 index 000000000..a320f0bf6 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/predict__ipsc_confocal__denoise.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: membrane on ipsc_confocal — denoise method. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: celldiff + experiment_id: membrane__ipsc_confocal__celldiff__ipsc_confocal__denoise + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/memb/celldiff_r2/checkpoints/last.ckpt + predict_method: denoise + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 8 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_celldiff_r2_denoise.zarr + +launcher: + job_name: CELLDiff_PRED_MEMB_DN + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/predict__ipsc_confocal__iterative.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/predict__ipsc_confocal__iterative.yml new file mode 100644 index 000000000..1281387ea --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/predict__ipsc_confocal__iterative.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: membrane on ipsc_confocal — iterative method. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: celldiff + experiment_id: membrane__ipsc_confocal__celldiff__ipsc_confocal__iterative + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/memb/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 40 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_celldiff_r2_iterative.zarr + +launcher: + job_name: CELLDiff_PRED_MEMB_ITER + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/predict__ipsc_confocal__sliding_window.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/predict__ipsc_confocal__sliding_window.yml new file mode 100644 index 000000000..8278e5311 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/predict__ipsc_confocal__sliding_window.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: membrane on ipsc_confocal — sliding_window method. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: celldiff + experiment_id: membrane__ipsc_confocal__celldiff__ipsc_confocal__sliding_window + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/memb/celldiff_r2/checkpoints/last.ckpt + predict_method: sliding_window + predict_overlap: [0, 0, 0] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 40 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_celldiff_r2_sliding_window.zarr + +launcher: + job_name: CELLDiff_PRED_MEMB_SW + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/train.yml new file mode 100644 index 000000000..3f3fb6029 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/ipsc_confocal/train.yml @@ -0,0 +1,36 @@ +# CellDiff fit on membrane (Membrane channel of cell.zarr) — AICS iPSC confocal. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/membrane_celldiff.yml + - ../../../_internal/shared/model/data_overlays/celldiff_fit.yml + - ../../../_internal/shared/model/model_overlays/celldiff_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + train_set: ipsc_confocal + model_name: celldiff + experiment_id: membrane__ipsc_confocal__celldiff + +trainer: + logger: + init_args: + name: CELLDiff_iPSC_MEMB + save_dir: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/memb/celldiff_r2 + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + every_n_epochs: 1 + save_top_k: -1 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/memb/celldiff_r2/checkpoints + +launcher: + job_name: CELLDiff_MEMB + run_root: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/memb/celldiff_r2 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..90dde7675 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,48 @@ +# CellDiff r2 predict: membrane trained on joint iPSC+A549, predicting against a549-mantis-caax-denv test. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-denv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_denv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_caax_denv + model_name: celldiff + experiment_id: membrane__joint_ipsc_confocal_a549_mantis__celldiff__a549_mantis_caax_denv + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/memb/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/memb_celldiff_r2_denv.zarr + +launcher: + job_name: CELLDiff_JOINT_PRED_MEMB_ON_A549_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..708acdcf1 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,48 @@ +# CellDiff r2 predict: membrane trained on joint iPSC+A549, predicting against a549-mantis-caax-mock test. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-mock. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_mock.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_caax_mock + model_name: celldiff + experiment_id: membrane__joint_ipsc_confocal_a549_mantis__celldiff__a549_mantis_caax_mock + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/memb/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/memb_celldiff_r2_mock.zarr + +launcher: + job_name: CELLDiff_JOINT_PRED_MEMB_ON_A549_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_movie.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_movie.yml new file mode 100644 index 000000000..ff1c756c5 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_movie.yml @@ -0,0 +1,49 @@ +# CellDiff predict: membrane trained on joint iPSC+A549, predicting on the +# cropped A549 TOMM20 DENV movie zarr (125 timepoints). +# No dataset_ref — data path is set directly to bypass the manifest system. +base: + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_movie + model_name: celldiff + experiment_id: membrane__joint_ipsc_confocal_a549_mantis__celldiff__a549_mantis_movie + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/memb/celldiff/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/movie/2024_11_21_A549_TOMM20_DENV_crop.zarr + source_channel: Phase3D + target_channel: Membrane + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + z_window_size: 48 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/movie_predictions/memb_celldiff_joint.zarr + +launcher: + job_name: CELLDiff_JOINT_PRED_MEMB_ON_A549_MOVIE + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/movie_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..bd5007897 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,48 @@ +# CellDiff r2 predict: membrane trained on joint iPSC+A549, predicting against a549-mantis-caax-zikv test. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-zikv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_zikv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_caax_zikv + model_name: celldiff + experiment_id: membrane__joint_ipsc_confocal_a549_mantis__celldiff__a549_mantis_caax_zikv + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/memb/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/memb_celldiff_r2_zikv.zarr + +launcher: + job_name: CELLDiff_JOINT_PRED_MEMB_ON_A549_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..9f6b68d19 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: membrane trained on joint iPSC+A549, predicting against ipsc_confocal test. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: ipsc_confocal + model_name: celldiff + experiment_id: membrane__joint_ipsc_confocal_a549_mantis__celldiff__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/memb/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 40 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions/memb_celldiff_r2.zarr + +launcher: + job_name: CELLDiff_JOINT_PRED_MEMB_ON_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/joint_ipsc_confocal_a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/joint_ipsc_confocal_a549_mantis/train.yml new file mode 100644 index 000000000..9f0b7aa8f --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/celldiff/joint_ipsc_confocal_a549_mantis/train.yml @@ -0,0 +1,138 @@ +# CellDiff fit on membrane (Membrane) — joint ipsc_confocal + a549_mantis pooled. +# +# Joint leaf per Stage 7 of A549_EXPANSION_ROADMAP.md. Uses +# BatchedConcatDataModule with two explicit HCSDataModule children +# (no benchmark.dataset_ref — joint leaves bypass the single-dataset +# resolver). Only model_overlays/celldiff_fit.yml is composed; the data +# block is authored inline because joint hparams live on the children. +# +# iPSC source is the multi-marker cell.zarr (Brightfield, Nuclei, +# Membrane, Phase3D); A549 source is the CAAX-marker pooled store +# CAAX_all.zarr. The shared target_channel name is `Membrane` in both. +# +# Topology: single H200, single GPU — same as celldiff/ipsc_confocal/train.yml. +# The paper baseline pattern is single-GPU and we keep that here so +# iPSC-only and joint runs are apples-to-apples. +base: + - ../../../_internal/shared/model/model_overlays/celldiff_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + gene: Membrane + target: membrane + target_id: membrane + train_set: joint_ipsc_confocal_a549_mantis + model_name: celldiff + experiment_id: membrane__joint_ipsc_confocal_a549_mantis__celldiff + +trainer: + logger: + init_args: + name: CELLDiff_JOINT_MEMB + save_dir: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/memb/celldiff_r2 + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + every_n_epochs: 1 + save_top_k: -1 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/memb/celldiff_r2/checkpoints + +_hcs_init_args: &hcs_init_args + source_channel: Phase3D + target_channel: Membrane + z_window_size: 13 + batch_size: 2 + num_workers: 4 + yx_patch_size: [512, 512] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Membrane] + level: timepoint_statistics + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Membrane] + w_key: Membrane + spatial_size: [13, 624, 624] + num_samples: 2 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + safe_crop_size: [8, 512, 512] + safe_crop_coverage: 0.9 + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/cell.zarr + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/CAAX_all.zarr + +launcher: + job_name: CELLDiff_JOINT_MEMB + run_root: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/memb/celldiff_r2 + # Joint preloads two stores (iPSC + A549 pool) into /dev/shm; the default + # 256G cap is too tight (256G iPSC mem + ~50G A549 + worker peak OOMs). + # 512G is the smallest tier that fits joint preload + worker overhead. + sbatch: + mem: "512G" diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..7af92f328 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_denv.yml @@ -0,0 +1,46 @@ +# VSCyto3D random-init predict: membrane (frozen randinit ckpt), A549 denv plate. +# Control ablation. A549 manifest keys membrane by gene (`caax`); override the +# iPSC-side `membrane` target_id from targets/membrane.yml so the resolver finds the +# caax target on a549-mantis-caax-denv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_denv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: randinit + predict_set: a549_mantis_caax_denv + model_name: fcmae_vscyto3d_pretrained_randinit + experiment_id: membrane__randinit__fcmae_vscyto3d_pretrained__a549_mantis_caax_denv + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/randinit/memb/fcmae_vscyto3d_pretrained/checkpoints/randinit.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_pretrained_randinit_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_RandInit_PRED_MEMB_ON_A549_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..f79048aa2 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_mock.yml @@ -0,0 +1,46 @@ +# VSCyto3D random-init predict: membrane (frozen randinit ckpt), A549 mock plate. +# Control ablation. A549 manifest keys membrane by gene (`caax`); override the +# iPSC-side `membrane` target_id from targets/membrane.yml so the resolver finds the +# caax target on a549-mantis-caax-mock. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_mock.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: randinit + predict_set: a549_mantis_caax_mock + model_name: fcmae_vscyto3d_pretrained_randinit + experiment_id: membrane__randinit__fcmae_vscyto3d_pretrained__a549_mantis_caax_mock + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/randinit/memb/fcmae_vscyto3d_pretrained/checkpoints/randinit.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_pretrained_randinit_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_RandInit_PRED_MEMB_ON_A549_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..9669ee930 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_zikv.yml @@ -0,0 +1,46 @@ +# VSCyto3D random-init predict: membrane (frozen randinit ckpt), A549 zikv plate. +# Control ablation. A549 manifest keys membrane by gene (`caax`); override the +# iPSC-side `membrane` target_id from targets/membrane.yml so the resolver finds the +# caax target on a549-mantis-caax-zikv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_zikv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: randinit + predict_set: a549_mantis_caax_zikv + model_name: fcmae_vscyto3d_pretrained_randinit + experiment_id: membrane__randinit__fcmae_vscyto3d_pretrained__a549_mantis_caax_zikv + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/randinit/memb/fcmae_vscyto3d_pretrained/checkpoints/randinit.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_pretrained_randinit_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_RandInit_PRED_MEMB_ON_A549_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__ipsc_confocal.yml new file mode 100644 index 000000000..3f66b5c62 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__ipsc_confocal.yml @@ -0,0 +1,44 @@ +# VSCyto3D random-init predict: membrane (frozen randinit ckpt), iPSC test set. +# Control ablation — measures untrained model output for paper. +# References the frozen randinit.ckpt persisted by save_random_init_vscyto3d_ckpts.py +# so all 4 datasets (iPSC + 3 A549 plates) for this organelle reuse the same weights. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: randinit + predict_set: ipsc_confocal + model_name: fcmae_vscyto3d_pretrained_randinit + experiment_id: membrane__randinit__fcmae_vscyto3d_pretrained__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/randinit/memb/fcmae_vscyto3d_pretrained/checkpoints/randinit.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_fcmae_vscyto3d_pretrained_randinit.zarr + +launcher: + job_name: FCMAE_VSCyto3D_RandInit_PRED_MEMB + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..19d3a879b --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,51 @@ +# FCMAE_VSCyto3D_Pretrained (VSCyto3D) predict: membrane trained on a549_mantis (caax), +# predicting against a549-mantis-caax-denv test. +# Best val-loss checkpoint from run 20260522-120713 (epoch 154, loss/validate=0.26533). +# Training reached max_epochs=200; epoch 154 is the global-best val loss (epochs +# 155-199 plateaued higher), so save_top_k kept it as the newest checkpoint. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-denv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_denv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: a549_mantis + predict_set: a549_mantis_caax_denv + model_name: fcmae_vscyto3d_pretrained + experiment_id: membrane__a549_mantis__fcmae_vscyto3d_pretrained__a549_mantis_caax_denv + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/memb/fcmae_vscyto3d_pretrained/checkpoints/epoch=154-step=33635.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_pretrained_a549trained_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_MEMB_A549TR_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..5c5c994d7 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,51 @@ +# FCMAE_VSCyto3D_Pretrained (VSCyto3D) predict: membrane trained on a549_mantis (caax), +# predicting against a549-mantis-caax-mock test. +# Best val-loss checkpoint from run 20260522-120713 (epoch 154, loss/validate=0.26533). +# Training reached max_epochs=200; epoch 154 is the global-best val loss (epochs +# 155-199 plateaued higher), so save_top_k kept it as the newest checkpoint. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-mock. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_mock.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: a549_mantis + predict_set: a549_mantis_caax_mock + model_name: fcmae_vscyto3d_pretrained + experiment_id: membrane__a549_mantis__fcmae_vscyto3d_pretrained__a549_mantis_caax_mock + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/memb/fcmae_vscyto3d_pretrained/checkpoints/epoch=154-step=33635.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_pretrained_a549trained_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_MEMB_A549TR_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..4248e142f --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,51 @@ +# FCMAE_VSCyto3D_Pretrained (VSCyto3D) predict: membrane trained on a549_mantis (caax), +# predicting against a549-mantis-caax-zikv test. +# Best val-loss checkpoint from run 20260522-120713 (epoch 154, loss/validate=0.26533). +# Training reached max_epochs=200; epoch 154 is the global-best val loss (epochs +# 155-199 plateaued higher), so save_top_k kept it as the newest checkpoint. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-zikv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_zikv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: a549_mantis + predict_set: a549_mantis_caax_zikv + model_name: fcmae_vscyto3d_pretrained + experiment_id: membrane__a549_mantis__fcmae_vscyto3d_pretrained__a549_mantis_caax_zikv + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/memb/fcmae_vscyto3d_pretrained/checkpoints/epoch=154-step=33635.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_pretrained_a549trained_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_MEMB_A549TR_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..ab1415ab2 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,45 @@ +# FCMAE_VSCyto3D_Pretrained (VSCyto3D) predict: membrane trained on a549_mantis (caax), +# predicting against ipsc_confocal test_cropped. +# Best val-loss checkpoint from run 20260522-120713 (epoch 154, loss/validate=0.26533). +# Training reached max_epochs=200; epoch 154 is the global-best val loss (epochs +# 155-199 plateaued higher), so save_top_k kept it as the newest checkpoint. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: a549_mantis + predict_set: ipsc_confocal + model_name: fcmae_vscyto3d_pretrained + experiment_id: membrane__a549_mantis__fcmae_vscyto3d_pretrained__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/memb/fcmae_vscyto3d_pretrained/checkpoints/epoch=154-step=33635.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_fcmae_vscyto3d_pretrained_a549trained.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_MEMB_A549TR_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/a549_mantis/train.yml new file mode 100644 index 000000000..af89bcacb --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/a549_mantis/train.yml @@ -0,0 +1,67 @@ +# FCMAE-class (FullyConvolutionalMAE, pretraining=False) with FCMAE- +# pretrained encoder init on membrane (Membrane marker). Companion to +# fcmae_vscyto3d_scratch.yml — the two leaves are identical except this +# one loads encoder weights from the published VSCyto3D FCMAE ckpt +# (400 ep on HEK + A549 + iPSC phase data). See vs_test/finetune_3d.py +# for the canonical recipe. +base: + - ../../../_internal/shared/model/train_sets/a549_mantis.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/data_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + train_set: a549_mantis + model_name: fcmae_vscyto3d_pretrained + experiment_id: membrane__a549_mantis__fcmae_vscyto3d_pretrained + +# Override the FCMAE data overlay's hardcoded `Structure` augmentation +# keys (the overlay was authored for ER/Mito where target_channel == +# "Structure"). RandWeightedCropd needs the actual membrane channel +# name in keys/w_key. spatial_size + num_samples kept identical to the +# FCMAE overlay so the augmentation policy matches ER/Mito. +data: + init_args: + # A549 pooled store + target_channel — no resolver in this train_set. + target_channel: Membrane + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/CAAX_all.zarr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Membrane] + w_key: Membrane + spatial_size: [20, 600, 600] + num_samples: 4 + +model: + init_args: + # Load only the encoder from the canonical VSCyto3D FCMAE ckpt — + # decoder/head stay at fresh init. Matches vs_test/finetune_3d.py:247. + encoder_only: true + ckpt_path: /hpc/projects/virtual_staining/models/mehta-lab/VSCyto3D/fcmae.ckpt + +trainer: + logger: + init_args: + name: FCMAE_VSCyto3D_Pretrained_A549_Membrane + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/memb/fcmae_vscyto3d_pretrained + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/memb/fcmae_vscyto3d_pretrained/checkpoints + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_A549_Membrane + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/memb/fcmae_vscyto3d_pretrained diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 100644 index 000000000..c2e04cb76 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Membrane (CAAX) predicted by FCMAE_VSCyto3D_Pretrained on a549-mantis-caax-denv. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from the target group so the resolver finds caax on a549-mantis-caax-denv. +defaults: + - override /target: membrane + - override /predict_set: a549_mantis_caax_denv + +benchmark: + dataset_ref: + target: caax + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_pretrained_denv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_memb_fcmae_vscyto3d_pretrained_denv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 100644 index 000000000..e6c9da7f2 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Membrane (CAAX) predicted by FCMAE_VSCyto3D_Pretrained on a549-mantis-caax-mock. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from the target group so the resolver finds caax on a549-mantis-caax-mock. +defaults: + - override /target: membrane + - override /predict_set: a549_mantis_caax_mock + +benchmark: + dataset_ref: + target: caax + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_pretrained_mock.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_memb_fcmae_vscyto3d_pretrained_mock diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 100644 index 000000000..7f7fbfb5d --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Membrane (CAAX) predicted by FCMAE_VSCyto3D_Pretrained on a549-mantis-caax-zikv. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from the target group so the resolver finds caax on a549-mantis-caax-zikv. +defaults: + - override /target: membrane + - override /predict_set: a549_mantis_caax_zikv + +benchmark: + dataset_ref: + target: caax + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_pretrained_zikv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_memb_fcmae_vscyto3d_pretrained_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..2ddbbc193 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_denv.yml @@ -0,0 +1,48 @@ +# FCMAE_VSCyto3D_Pretrained predict: membrane trained on iPSC, +# predicting against a549-mantis-caax-denv test. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side +# `membrane` target_id from targets/membrane.yml so the resolver finds +# the caax target on a549-mantis-caax-denv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_denv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: ipsc_confocal + predict_set: a549_mantis_caax_denv + model_name: fcmae_vscyto3d_pretrained + experiment_id: membrane__ipsc_confocal__fcmae_vscyto3d_pretrained__a549_mantis_caax_denv + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/memb/fcmae_vscyto3d_pretrained/checkpoints/epoch=189-step=59280.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_pretrained_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_MEMB_ON_A549_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..61cffffc1 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_mock.yml @@ -0,0 +1,48 @@ +# FCMAE_VSCyto3D_Pretrained predict: membrane trained on iPSC, +# predicting against a549-mantis-caax-mock test. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side +# `membrane` target_id from targets/membrane.yml so the resolver finds +# the caax target on a549-mantis-caax-mock. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_mock.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: ipsc_confocal + predict_set: a549_mantis_caax_mock + model_name: fcmae_vscyto3d_pretrained + experiment_id: membrane__ipsc_confocal__fcmae_vscyto3d_pretrained__a549_mantis_caax_mock + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/memb/fcmae_vscyto3d_pretrained/checkpoints/epoch=189-step=59280.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_pretrained_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_MEMB_ON_A549_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..844934d41 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_zikv.yml @@ -0,0 +1,48 @@ +# FCMAE_VSCyto3D_Pretrained predict: membrane trained on iPSC, +# predicting against a549-mantis-caax-zikv test. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side +# `membrane` target_id from targets/membrane.yml so the resolver finds +# the caax target on a549-mantis-caax-zikv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_zikv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: ipsc_confocal + predict_set: a549_mantis_caax_zikv + model_name: fcmae_vscyto3d_pretrained + experiment_id: membrane__ipsc_confocal__fcmae_vscyto3d_pretrained__a549_mantis_caax_zikv + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/memb/fcmae_vscyto3d_pretrained/checkpoints/epoch=189-step=59280.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_pretrained_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_MEMB_ON_A549_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__ipsc_confocal.yml new file mode 100644 index 000000000..32093aacd --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__ipsc_confocal.yml @@ -0,0 +1,41 @@ +# FCMAE_VSCyto3D_Pretrained predict: membrane (CAAX) against ipsc_confocal test_cropped. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: fcmae_vscyto3d_pretrained + experiment_id: membrane__ipsc_confocal__fcmae_vscyto3d_pretrained__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/memb/fcmae_vscyto3d_pretrained/checkpoints/epoch=189-step=59280.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_fcmae_vscyto3d_pretrained.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_MEMB + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/train.yml new file mode 100644 index 000000000..4fb87dd32 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/train.yml @@ -0,0 +1,64 @@ +# FCMAE-class (FullyConvolutionalMAE, pretraining=False) with FCMAE- +# pretrained encoder init on membrane (Membrane marker). Companion to +# fcmae_vscyto3d_scratch.yml — the two leaves are identical except this +# one loads encoder weights from the published VSCyto3D FCMAE ckpt +# (400 ep on HEK + A549 + iPSC phase data). See vs_test/finetune_3d.py +# for the canonical recipe. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/data_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + train_set: ipsc_confocal + model_name: fcmae_vscyto3d_pretrained + experiment_id: membrane__ipsc_confocal__fcmae_vscyto3d_pretrained + +# Override the FCMAE data overlay's hardcoded `Structure` augmentation +# keys (the overlay was authored for ER/Mito where target_channel == +# "Structure"). RandWeightedCropd needs the actual membrane channel +# name in keys/w_key. spatial_size + num_samples kept identical to the +# FCMAE overlay so the augmentation policy matches ER/Mito. +data: + init_args: + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Membrane] + w_key: Membrane + spatial_size: [20, 600, 600] + num_samples: 4 + +model: + init_args: + # Load only the encoder from the canonical VSCyto3D FCMAE ckpt — + # decoder/head stay at fresh init. Matches vs_test/finetune_3d.py:247. + encoder_only: true + ckpt_path: /hpc/projects/virtual_staining/models/mehta-lab/VSCyto3D/fcmae.ckpt + +trainer: + logger: + init_args: + name: FCMAE_VSCyto3D_Pretrained_iPSC_Membrane + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/memb/fcmae_vscyto3d_pretrained + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/memb/fcmae_vscyto3d_pretrained/checkpoints + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_Membrane + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/memb/fcmae_vscyto3d_pretrained diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..60fe4a0f1 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,49 @@ +# FCMAE_VSCyto3D_Pretrained (VSCyto3D) predict: cell membrane trained on joint +# iPSC+A549, predicting against a549-mantis-caax-denv test. +# Best val-loss checkpoint from job 31822529 (epoch 111, loss/validate=0.3754). +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-denv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_denv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_caax_denv + model_name: fcmae_vscyto3d_pretrained + experiment_id: membrane__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_pretrained__a549_mantis_caax_denv + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/memb/fcmae_vscyto3d_pretrained/checkpoints/epoch=111-step=59360.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_pretrained_jointtrained_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_MEMB_JOINTTR_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..176cf777b --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,49 @@ +# FCMAE_VSCyto3D_Pretrained (VSCyto3D) predict: cell membrane trained on joint +# iPSC+A549, predicting against a549-mantis-caax-mock test. +# Best val-loss checkpoint from job 31822529 (epoch 111, loss/validate=0.3754). +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-mock. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_mock.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_caax_mock + model_name: fcmae_vscyto3d_pretrained + experiment_id: membrane__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_pretrained__a549_mantis_caax_mock + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/memb/fcmae_vscyto3d_pretrained/checkpoints/epoch=111-step=59360.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_pretrained_jointtrained_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_MEMB_JOINTTR_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..1e3ae6171 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,49 @@ +# FCMAE_VSCyto3D_Pretrained (VSCyto3D) predict: cell membrane trained on joint +# iPSC+A549, predicting against a549-mantis-caax-zikv test. +# Best val-loss checkpoint from job 31822529 (epoch 111, loss/validate=0.3754). +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-zikv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_zikv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_caax_zikv + model_name: fcmae_vscyto3d_pretrained + experiment_id: membrane__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_pretrained__a549_mantis_caax_zikv + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/memb/fcmae_vscyto3d_pretrained/checkpoints/epoch=111-step=59360.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_pretrained_jointtrained_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_MEMB_JOINTTR_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..babf7d9e9 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,45 @@ +# FCMAE_VSCyto3D_Pretrained (VSCyto3D) predict: cell membrane trained on joint +# iPSC+A549, predicting against ipsc_confocal test_cropped. +# Best val-loss checkpoint from job 31822529 (epoch 111, loss/validate=0.3754). +# Wandb run 20260501-004706_FCMAE_VSCyto3D_Pretrained_JOINT_MEMB (state=finished, +# 119 ep / 63,179 steps). +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: ipsc_confocal + model_name: fcmae_vscyto3d_pretrained + experiment_id: membrane__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_pretrained__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/memb/fcmae_vscyto3d_pretrained/checkpoints/epoch=111-step=59360.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_fcmae_vscyto3d_pretrained_jointtrained.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_MEMB_JOINTTR_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/train.yml new file mode 100644 index 000000000..9e543f5b1 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/train.yml @@ -0,0 +1,154 @@ +# FCMAE-class (FullyConvolutionalMAE, pretraining=False) with FCMAE- +# pretrained encoder init on membrane (MEMB) — joint +# ipsc_confocal + a549_mantis pooled. Companion to +# fcmae_vscyto3d_scratch joint leaf — the two are identical except +# this one loads encoder weights from the published VSCyto3D FCMAE +# ckpt (400 ep on HEK + A549 + iPSC phase data). Mirrors +# membrane/fcmae_vscyto3d_pretrained/ipsc_confocal/train.yml on +# the joint train_set. +# +# Joint leaf per Stage 7 of A549_EXPANSION_ROADMAP.md. Uses +# BatchedConcatDataModule with two explicit HCSDataModule children +# (no benchmark.dataset_ref — joint leaves bypass the single-dataset +# resolver). Only model_overlays/fcmae_vscyto3d_fit.yml is composed; +# the data block is authored inline because joint hparams live on +# the children. +# +# Topology: 4-GPU DDP (inherited from +# model_overlays/fcmae_vscyto3d_fit.yml's ddp_4gpu base; the overlay +# also pins strategy=ddp_find_unused_parameters_true because +# FullyConvolutionalMAE has decoder/head params that only receive +# gradients on some forward paths). +base: + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + gene: Membrane + target: membrane + target_id: membrane + train_set: joint_ipsc_confocal_a549_mantis + model_name: fcmae_vscyto3d_pretrained + experiment_id: membrane__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_pretrained + +model: + init_args: + # Load only the encoder from the canonical VSCyto3D FCMAE ckpt — + # decoder/head stay at fresh init. Matches vs_test/finetune_3d.py:247. + encoder_only: true + ckpt_path: /hpc/projects/virtual_staining/models/mehta-lab/VSCyto3D/fcmae.ckpt + +trainer: + logger: + init_args: + name: FCMAE_VSCyto3D_Pretrained_JOINT_MEMB + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/memb/fcmae_vscyto3d_pretrained + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/memb/fcmae_vscyto3d_pretrained/checkpoints + +_hcs_init_args: &hcs_init_args + source_channel: Phase3D + target_channel: Membrane + z_window_size: 20 + # See nucleus/fnet3d_paper/joint_*/train.yml for the rationale: joint + # mode does not divide batch_size by num_samples, so 8 * 4 = 32 GPU + # samples per DDP rank matches single-set effective batch. + batch_size: 8 + num_workers: 4 + yx_patch_size: [384, 384] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Membrane] + level: fov_statistics + subtrahend: median + divisor: iqr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Membrane] + w_key: Membrane + spatial_size: [20, 600, 600] + num_samples: 4 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + # ipsc_confocal — aics-hipsc multi-marker cell.zarr (Membrane channel) + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/cell.zarr + # a549_mantis — pooled CAAX all-conditions train store (Membrane channel) + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/CAAX_all.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_JOINT_MEMB + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/memb/fcmae_vscyto3d_pretrained diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..be27c8afc --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,49 @@ +# FCMAE_VSCyto3D_Scratch (UNeXt2) predict: membrane trained on a549_mantis (caax), +# predicting against a549-mantis-caax-denv test. +# Best val-loss checkpoint from job 31822574 (epoch 119, loss/validate=0.2722). +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-denv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_denv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: a549_mantis + predict_set: a549_mantis_caax_denv + model_name: fcmae_vscyto3d_scratch + experiment_id: membrane__a549_mantis__fcmae_vscyto3d_scratch__a549_mantis_caax_denv + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/memb/fcmae_vscyto3d_scratch/checkpoints/epoch=119-step=26040.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_scratch_a549trained_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_MEMB_A549TR_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..d63edd3ea --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,49 @@ +# FCMAE_VSCyto3D_Scratch (UNeXt2) predict: membrane trained on a549_mantis (caax), +# predicting against a549-mantis-caax-mock test. +# Best val-loss checkpoint from job 31822574 (epoch 119, loss/validate=0.2722). +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-mock. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_mock.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: a549_mantis + predict_set: a549_mantis_caax_mock + model_name: fcmae_vscyto3d_scratch + experiment_id: membrane__a549_mantis__fcmae_vscyto3d_scratch__a549_mantis_caax_mock + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/memb/fcmae_vscyto3d_scratch/checkpoints/epoch=119-step=26040.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_scratch_a549trained_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_MEMB_A549TR_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..5521558ae --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,49 @@ +# FCMAE_VSCyto3D_Scratch (UNeXt2) predict: membrane trained on a549_mantis (caax), +# predicting against a549-mantis-caax-zikv test. +# Best val-loss checkpoint from job 31822574 (epoch 119, loss/validate=0.2722). +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-zikv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_zikv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: a549_mantis + predict_set: a549_mantis_caax_zikv + model_name: fcmae_vscyto3d_scratch + experiment_id: membrane__a549_mantis__fcmae_vscyto3d_scratch__a549_mantis_caax_zikv + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/memb/fcmae_vscyto3d_scratch/checkpoints/epoch=119-step=26040.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_scratch_a549trained_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_MEMB_A549TR_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..821ab2335 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,43 @@ +# FCMAE_VSCyto3D_Scratch (UNeXt2) predict: membrane trained on a549_mantis (caax), +# predicting against ipsc_confocal test_cropped. +# Best val-loss checkpoint from job 31822574 (epoch 119, loss/validate=0.2722). +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: a549_mantis + predict_set: ipsc_confocal + model_name: fcmae_vscyto3d_scratch + experiment_id: membrane__a549_mantis__fcmae_vscyto3d_scratch__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/memb/fcmae_vscyto3d_scratch/checkpoints/epoch=119-step=26040.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_fcmae_vscyto3d_scratch_a549trained.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_MEMB_A549TR_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/a549_mantis/train.yml new file mode 100644 index 000000000..a01a7175d --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/a549_mantis/train.yml @@ -0,0 +1,59 @@ +# FCMAE-class (FullyConvolutionalMAE, pretraining=False) random-init +# baseline on membrane (Membrane marker). Scratch control for the +# pretrained counterpart — the two leaves are identical except this one +# does NOT load pretrained encoder weights. See UNEXT2_VS_FCMAE_CLASSES.md +# for why this is the paper-adjacent scratch baseline (and not unext2.yml). +base: + - ../../../_internal/shared/model/train_sets/a549_mantis.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/data_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + train_set: a549_mantis + model_name: fcmae_vscyto3d_scratch + experiment_id: membrane__a549_mantis__fcmae_vscyto3d_scratch + +# Override the FCMAE data overlay's hardcoded `Structure` augmentation +# keys (the overlay was authored for ER/Mito where target_channel == +# "Structure"). RandWeightedCropd needs the actual membrane channel +# name in keys/w_key. spatial_size + num_samples kept identical to the +# FCMAE overlay so the augmentation policy matches ER/Mito. +data: + init_args: + # A549 pooled store + target_channel — no resolver in this train_set. + target_channel: Membrane + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/CAAX_all.zarr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Membrane] + w_key: Membrane + spatial_size: [20, 600, 600] + num_samples: 4 + +trainer: + logger: + init_args: + name: FCMAE_VSCyto3D_Scratch_A549_Membrane + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/memb/fcmae_vscyto3d_scratch + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/memb/fcmae_vscyto3d_scratch/checkpoints + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_A549_Membrane + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/memb/fcmae_vscyto3d_scratch diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 100644 index 000000000..8d6185031 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Membrane (CAAX) predicted by FCMAE_VSCyto3D_Scratch on a549-mantis-caax-denv. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from the target group so the resolver finds caax on a549-mantis-caax-denv. +defaults: + - override /target: membrane + - override /predict_set: a549_mantis_caax_denv + +benchmark: + dataset_ref: + target: caax + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_scratch_denv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_memb_fcmae_vscyto3d_scratch_denv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 100644 index 000000000..b42b6d474 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Membrane (CAAX) predicted by FCMAE_VSCyto3D_Scratch on a549-mantis-caax-mock. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from the target group so the resolver finds caax on a549-mantis-caax-mock. +defaults: + - override /target: membrane + - override /predict_set: a549_mantis_caax_mock + +benchmark: + dataset_ref: + target: caax + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_scratch_mock.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_memb_fcmae_vscyto3d_scratch_mock diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 100644 index 000000000..e8de39923 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Membrane (CAAX) predicted by FCMAE_VSCyto3D_Scratch on a549-mantis-caax-zikv. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from the target group so the resolver finds caax on a549-mantis-caax-zikv. +defaults: + - override /target: membrane + - override /predict_set: a549_mantis_caax_zikv + +benchmark: + dataset_ref: + target: caax + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_scratch_zikv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_memb_fcmae_vscyto3d_scratch_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..3f975a14b --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_denv.yml @@ -0,0 +1,58 @@ +# FCMAE_VSCyto3D_Scratch predict: membrane trained on iPSC, +# predicting against a549-mantis-caax-denv test. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side +# `membrane` target_id from targets/membrane.yml so the resolver finds +# the caax target on a549-mantis-caax-denv. +# +# TODO: replace ckpt_path once iPSC FCMAE scratch membrane training +# completes. Expected output (per fit leaf): +# /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/memb/fcmae_vscyto3d_scratch/checkpoints/last.ckpt +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_denv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: ipsc_confocal + predict_set: a549_mantis_caax_denv + model_name: fcmae_vscyto3d_scratch + experiment_id: membrane__ipsc_confocal__fcmae_vscyto3d_scratch__a549_mantis_caax_denv + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + # Best checkpoint from J31710718 (FCMAE_VSCyto3D_Scratch_iPSC_Membrane): + # ep 136 / val_loss 0.39590 (27-epoch plateau, scancelled at 1d 11h elapsed). + # Note: pretrained variant (J31795524, ep 194 = 0.37878) outperforms scratch + # by 4.3%; prefer the pretrained predict configs for downstream eval. + # Hardlink alias at run_root; underlying epoch=136-step=42744.ckpt also + # preserved in checkpoints_frozen_ep136_20260501_005505/. + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/memb/fcmae_vscyto3d_scratch/best_ep136_val0.39590.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_scratch_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_MEMB_ON_A549_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..e7a79de69 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_mock.yml @@ -0,0 +1,58 @@ +# FCMAE_VSCyto3D_Scratch predict: membrane trained on iPSC, +# predicting against a549-mantis-caax-mock test. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side +# `membrane` target_id from targets/membrane.yml so the resolver finds +# the caax target on a549-mantis-caax-mock. +# +# TODO: replace ckpt_path once iPSC FCMAE scratch membrane training +# completes. Expected output (per fit leaf): +# /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/memb/fcmae_vscyto3d_scratch/checkpoints/last.ckpt +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_mock.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: ipsc_confocal + predict_set: a549_mantis_caax_mock + model_name: fcmae_vscyto3d_scratch + experiment_id: membrane__ipsc_confocal__fcmae_vscyto3d_scratch__a549_mantis_caax_mock + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + # Best checkpoint from J31710718 (FCMAE_VSCyto3D_Scratch_iPSC_Membrane): + # ep 136 / val_loss 0.39590 (27-epoch plateau, scancelled at 1d 11h elapsed). + # Note: pretrained variant (J31795524, ep 194 = 0.37878) outperforms scratch + # by 4.3%; prefer the pretrained predict configs for downstream eval. + # Hardlink alias at run_root; underlying epoch=136-step=42744.ckpt also + # preserved in checkpoints_frozen_ep136_20260501_005505/. + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/memb/fcmae_vscyto3d_scratch/best_ep136_val0.39590.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_scratch_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_MEMB_ON_A549_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..53a090a83 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_zikv.yml @@ -0,0 +1,58 @@ +# FCMAE_VSCyto3D_Scratch predict: membrane trained on iPSC, +# predicting against a549-mantis-caax-zikv test. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side +# `membrane` target_id from targets/membrane.yml so the resolver finds +# the caax target on a549-mantis-caax-zikv. +# +# TODO: replace ckpt_path once iPSC FCMAE scratch membrane training +# completes. Expected output (per fit leaf): +# /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/memb/fcmae_vscyto3d_scratch/checkpoints/last.ckpt +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_zikv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: ipsc_confocal + predict_set: a549_mantis_caax_zikv + model_name: fcmae_vscyto3d_scratch + experiment_id: membrane__ipsc_confocal__fcmae_vscyto3d_scratch__a549_mantis_caax_zikv + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + # Best checkpoint from J31710718 (FCMAE_VSCyto3D_Scratch_iPSC_Membrane): + # ep 136 / val_loss 0.39590 (27-epoch plateau, scancelled at 1d 11h elapsed). + # Note: pretrained variant (J31795524, ep 194 = 0.37878) outperforms scratch + # by 4.3%; prefer the pretrained predict configs for downstream eval. + # Hardlink alias at run_root; underlying epoch=136-step=42744.ckpt also + # preserved in checkpoints_frozen_ep136_20260501_005505/. + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/memb/fcmae_vscyto3d_scratch/best_ep136_val0.39590.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_scratch_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_MEMB_ON_A549_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/predict__ipsc_confocal.yml new file mode 100644 index 000000000..1d430ba9e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/predict__ipsc_confocal.yml @@ -0,0 +1,51 @@ +# FCMAE_VSCyto3D_Scratch predict: membrane (CAAX) against ipsc_confocal test_cropped. +# +# TODO: replace ckpt_path with best-val ckpt once iPSC FCMAE scratch +# membrane training (J31710718, resumed from J31475106) completes. Expected dir: +# /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/memb/fcmae_vscyto3d_scratch/checkpoints/ +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: fcmae_vscyto3d_scratch + experiment_id: membrane__ipsc_confocal__fcmae_vscyto3d_scratch__ipsc_confocal + +model: + init_args: + # Best checkpoint from J31710718 (FCMAE_VSCyto3D_Scratch_iPSC_Membrane): + # ep 136 / val_loss 0.39590 (27-epoch plateau, scancelled at 1d 11h elapsed). + # Note: pretrained variant (J31795524, ep 194 = 0.37878) outperforms scratch + # by 4.3%; prefer the pretrained predict configs for downstream eval. + # Hardlink alias at run_root; underlying epoch=136-step=42744.ckpt also + # preserved in checkpoints_frozen_ep136_20260501_005505/. + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/memb/fcmae_vscyto3d_scratch/best_ep136_val0.39590.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_fcmae_vscyto3d_scratch.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_MEMB + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/train.yml new file mode 100644 index 000000000..5b5c2866e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/ipsc_confocal/train.yml @@ -0,0 +1,56 @@ +# FCMAE-class (FullyConvolutionalMAE, pretraining=False) random-init +# baseline on membrane (Membrane marker). Scratch control for the +# pretrained counterpart — the two leaves are identical except this one +# does NOT load pretrained encoder weights. See UNEXT2_VS_FCMAE_CLASSES.md +# for why this is the paper-adjacent scratch baseline (and not unext2.yml). +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/data_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + train_set: ipsc_confocal + model_name: fcmae_vscyto3d_scratch + experiment_id: membrane__ipsc_confocal__fcmae_vscyto3d_scratch + +# Override the FCMAE data overlay's hardcoded `Structure` augmentation +# keys (the overlay was authored for ER/Mito where target_channel == +# "Structure"). RandWeightedCropd needs the actual membrane channel +# name in keys/w_key. spatial_size + num_samples kept identical to the +# FCMAE overlay so the augmentation policy matches ER/Mito. +data: + init_args: + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Membrane] + w_key: Membrane + spatial_size: [20, 600, 600] + num_samples: 4 + +trainer: + logger: + init_args: + name: FCMAE_VSCyto3D_Scratch_iPSC_Membrane + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/memb/fcmae_vscyto3d_scratch + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/memb/fcmae_vscyto3d_scratch/checkpoints + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_Membrane + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/memb/fcmae_vscyto3d_scratch diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..fd69b738d --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,49 @@ +# FCMAE_VSCyto3D_Scratch (UNeXt2) predict: cell membrane trained on joint +# iPSC+A549, predicting against a549-mantis-caax-denv test. +# Best val-loss checkpoint from job 31822536 (epoch 112, loss/validate=0.3859). +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-denv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_denv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_caax_denv + model_name: fcmae_vscyto3d_scratch + experiment_id: membrane__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_scratch__a549_mantis_caax_denv + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/memb/fcmae_vscyto3d_scratch/checkpoints/epoch=112-step=59890.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_scratch_jointtrained_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_MEMB_JOINTTR_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..3dc88f5a9 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,49 @@ +# FCMAE_VSCyto3D_Scratch (UNeXt2) predict: cell membrane trained on joint +# iPSC+A549, predicting against a549-mantis-caax-mock test. +# Best val-loss checkpoint from job 31822536 (epoch 112, loss/validate=0.3859). +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-mock. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_mock.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_caax_mock + model_name: fcmae_vscyto3d_scratch + experiment_id: membrane__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_scratch__a549_mantis_caax_mock + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/memb/fcmae_vscyto3d_scratch/checkpoints/epoch=112-step=59890.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_scratch_jointtrained_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_MEMB_JOINTTR_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..2fc00ab64 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,49 @@ +# FCMAE_VSCyto3D_Scratch (UNeXt2) predict: cell membrane trained on joint +# iPSC+A549, predicting against a549-mantis-caax-zikv test. +# Best val-loss checkpoint from job 31822536 (epoch 112, loss/validate=0.3859). +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-zikv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_zikv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_caax_zikv + model_name: fcmae_vscyto3d_scratch + experiment_id: membrane__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_scratch__a549_mantis_caax_zikv + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/memb/fcmae_vscyto3d_scratch/checkpoints/epoch=112-step=59890.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fcmae_vscyto3d_scratch_jointtrained_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_MEMB_JOINTTR_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..2cfeaa4a8 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,45 @@ +# FCMAE_VSCyto3D_Scratch (UNeXt2) predict: cell membrane trained on joint +# iPSC+A549, predicting against ipsc_confocal test_cropped. +# Best val-loss checkpoint from job 31822536 (epoch 112, loss/validate=0.3859). +# Wandb run 20260501-011350_FCMAE_VSCyto3D_Scratch_JOINT_MEMB (state=finished, +# 118 ep / 62,799 steps). +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: ipsc_confocal + model_name: fcmae_vscyto3d_scratch + experiment_id: membrane__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_scratch__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/memb/fcmae_vscyto3d_scratch/checkpoints/epoch=112-step=59890.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_fcmae_vscyto3d_scratch_jointtrained.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_MEMB_JOINTTR_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/train.yml new file mode 100644 index 000000000..15d247bf7 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/train.yml @@ -0,0 +1,142 @@ +# FCMAE-class (FullyConvolutionalMAE, pretraining=False) random-init +# baseline on membrane (MEMB) — joint ipsc_confocal + +# a549_mantis pooled. Scratch control for the pretrained counterpart +# — the two leaves are identical except this one does NOT load +# pretrained encoder weights. Mirrors +# membrane/fcmae_vscyto3d_scratch/ipsc_confocal/train.yml on the +# joint train_set. +# +# Joint leaf per Stage 7 of A549_EXPANSION_ROADMAP.md. +# BatchedConcatDataModule + two explicit HCSDataModule children; +# only model_overlays/fcmae_vscyto3d_fit.yml is composed; data +# block inline. +# +# Topology: 4-GPU DDP +# (strategy=ddp_find_unused_parameters_true inherited from +# model_overlays/fcmae_vscyto3d_fit.yml). +base: + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + gene: Membrane + target: membrane + target_id: membrane + train_set: joint_ipsc_confocal_a549_mantis + model_name: fcmae_vscyto3d_scratch + experiment_id: membrane__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_scratch + +trainer: + logger: + init_args: + name: FCMAE_VSCyto3D_Scratch_JOINT_MEMB + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/memb/fcmae_vscyto3d_scratch + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/memb/fcmae_vscyto3d_scratch/checkpoints + +_hcs_init_args: &hcs_init_args + source_channel: Phase3D + target_channel: Membrane + z_window_size: 20 + # See nucleus/fnet3d_paper/joint_*/train.yml for the rationale: joint + # mode does not divide batch_size by num_samples, so 8 * 4 = 32 GPU + # samples per DDP rank matches single-set effective batch. + batch_size: 8 + num_workers: 4 + yx_patch_size: [384, 384] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Membrane] + level: fov_statistics + subtrahend: median + divisor: iqr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Membrane] + w_key: Membrane + spatial_size: [20, 600, 600] + num_samples: 4 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + # ipsc_confocal — aics-hipsc multi-marker cell.zarr (Membrane channel) + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/cell.zarr + # a549_mantis — pooled CAAX all-conditions train store (Membrane channel) + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/CAAX_all.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_JOINT_MEMB + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/memb/fcmae_vscyto3d_scratch diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..dda0e1612 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,49 @@ +# FNet3D paper-baseline predict: membrane trained on a549_mantis (caax), +# predicting against a549-mantis-caax-denv test. +# Best val-loss checkpoint from job 31858488 (epoch 281, loss/validate=0.3143). +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-denv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_denv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: a549_mantis + predict_set: a549_mantis_caax_denv + model_name: fnet3d_paper + experiment_id: membrane__a549_mantis__fnet3d_paper__a549_mantis_caax_denv + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/memb/fnet3d_paper/checkpoints/epoch=281-step=191760.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fnet3d_paper_a549trained_denv.zarr + +launcher: + job_name: FNet3DPaper_PRED_MEMB_A549TR_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..a6a0299a8 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,49 @@ +# FNet3D paper-baseline predict: membrane trained on a549_mantis (caax), +# predicting against a549-mantis-caax-mock test. +# Best val-loss checkpoint from job 31858488 (epoch 281, loss/validate=0.3143). +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-mock. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_mock.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: a549_mantis + predict_set: a549_mantis_caax_mock + model_name: fnet3d_paper + experiment_id: membrane__a549_mantis__fnet3d_paper__a549_mantis_caax_mock + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/memb/fnet3d_paper/checkpoints/epoch=281-step=191760.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fnet3d_paper_a549trained_mock.zarr + +launcher: + job_name: FNet3DPaper_PRED_MEMB_A549TR_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..f7bad7715 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,49 @@ +# FNet3D paper-baseline predict: membrane trained on a549_mantis (caax), +# predicting against a549-mantis-caax-zikv test. +# Best val-loss checkpoint from job 31858488 (epoch 281, loss/validate=0.3143). +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-zikv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_zikv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: a549_mantis + predict_set: a549_mantis_caax_zikv + model_name: fnet3d_paper + experiment_id: membrane__a549_mantis__fnet3d_paper__a549_mantis_caax_zikv + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/memb/fnet3d_paper/checkpoints/epoch=281-step=191760.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fnet3d_paper_a549trained_zikv.zarr + +launcher: + job_name: FNet3DPaper_PRED_MEMB_A549TR_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..3f24aba59 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,43 @@ +# FNet3D paper-baseline predict: membrane trained on a549_mantis (caax), +# predicting against ipsc_confocal test_cropped. +# Best val-loss checkpoint from job 31858488 (epoch 281, loss/validate=0.3143). +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: a549_mantis + predict_set: ipsc_confocal + model_name: fnet3d_paper + experiment_id: membrane__a549_mantis__fnet3d_paper__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/memb/fnet3d_paper/checkpoints/epoch=281-step=191760.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_fnet3d_paper_a549trained.zarr + +launcher: + job_name: FNet3DPaper_PRED_MEMB_A549TR_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/a549_mantis/train.yml new file mode 100644 index 000000000..f1ccc4a5f --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/a549_mantis/train.yml @@ -0,0 +1,77 @@ +# FNet3D paper-baseline fit on membrane (Membrane channel of cell.zarr) — A549 mantis-lightsheet pooled (mock + DENV + ZIKV). +# The overlay's norm/aug/val_aug are keyed on Structure (the SEC61B/TOMM20 target +# channel). Membrane target_channel is Membrane, so we list-replace those three +# lists here to re-key them. +base: + - ../../../_internal/shared/model/train_sets/a549_mantis.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/data_overlays/fnet3d_paper_fit.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_gpu_any_long.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + train_set: a549_mantis + model_name: fnet3d_paper + experiment_id: membrane__a549_mantis__fnet3d_paper + +data: + init_args: + # A549 pooled store + target_channel — no resolver in this train_set. + target_channel: Membrane + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/CAAX_all.zarr + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Membrane] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Membrane] + w_key: Membrane + spatial_size: [32, 64, 64] + num_samples: 8 + val_augmentations: + - class_path: viscy_transforms.CenterSpatialCropd + init_args: + keys: [Phase3D, Membrane] + roi_size: [32, 64, 64] + +trainer: + logger: + init_args: + name: FNet3D_A549_MEMB_paper + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/memb/fnet3d_paper + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/memb/fnet3d_paper/checkpoints + +launcher: + job_name: FNet3DPaper_A549_MEMB + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/memb/fnet3d_paper + # 512G to match the shared headroom convention across the fnet3d + # leaves on a549/joint workloads. mmap_preload after the BasicIndexer + # fix peaks at ~75 GB for CAAX_all alone (single-set); 512G gives + # generous headroom. + sbatch: + mem: "512G" diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 100644 index 000000000..7c0b1a626 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Membrane (CAAX) predicted by FNet3DPaper on a549-mantis-caax-denv. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from the target group so the resolver finds caax on a549-mantis-caax-denv. +defaults: + - override /target: membrane + - override /predict_set: a549_mantis_caax_denv + +benchmark: + dataset_ref: + target: caax + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fnet3d_paper_denv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_memb_fnet3d_paper_denv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 100644 index 000000000..a25066d99 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Membrane (CAAX) predicted by FNet3DPaper on a549-mantis-caax-mock. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from the target group so the resolver finds caax on a549-mantis-caax-mock. +defaults: + - override /target: membrane + - override /predict_set: a549_mantis_caax_mock + +benchmark: + dataset_ref: + target: caax + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fnet3d_paper_mock.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_memb_fnet3d_paper_mock diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 100644 index 000000000..b2333dc20 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Membrane (CAAX) predicted by FNet3DPaper on a549-mantis-caax-zikv. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from the target group so the resolver finds caax on a549-mantis-caax-zikv. +defaults: + - override /target: membrane + - override /predict_set: a549_mantis_caax_zikv + +benchmark: + dataset_ref: + target: caax + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fnet3d_paper_zikv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_memb_fnet3d_paper_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/ipsc_confocal/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/ipsc_confocal/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..a7aef7680 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/ipsc_confocal/predict__a549_mantis_denv.yml @@ -0,0 +1,48 @@ +# FNet3D paper-baseline predict: membrane trained on iPSC, predicting against a549-mantis-caax-denv test. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-denv. +# Same iPSC best val-loss checkpoint as predict__ipsc_confocal.yml (epoch 181, loss/validate=0.6214). +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_denv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: ipsc_confocal + predict_set: a549_mantis_caax_denv + model_name: fnet3d_paper + experiment_id: membrane__ipsc_confocal__fnet3d_paper__a549_mantis_caax_denv + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/memb/fnet3d_paper/checkpoints/epoch=181-step=157612.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fnet3d_paper_denv.zarr + +launcher: + job_name: FNet3DPaper_PRED_MEMB_ON_A549_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/ipsc_confocal/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/ipsc_confocal/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..515c2d253 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/ipsc_confocal/predict__a549_mantis_mock.yml @@ -0,0 +1,48 @@ +# FNet3D paper-baseline predict: membrane trained on iPSC, predicting against a549-mantis-caax-mock test. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-mock. +# Same iPSC best val-loss checkpoint as predict__ipsc_confocal.yml (epoch 181, loss/validate=0.6214). +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_mock.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: ipsc_confocal + predict_set: a549_mantis_caax_mock + model_name: fnet3d_paper + experiment_id: membrane__ipsc_confocal__fnet3d_paper__a549_mantis_caax_mock + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/memb/fnet3d_paper/checkpoints/epoch=181-step=157612.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fnet3d_paper_mock.zarr + +launcher: + job_name: FNet3DPaper_PRED_MEMB_ON_A549_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/ipsc_confocal/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/ipsc_confocal/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..9af57720b --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/ipsc_confocal/predict__a549_mantis_zikv.yml @@ -0,0 +1,48 @@ +# FNet3D paper-baseline predict: membrane trained on iPSC, predicting against a549-mantis-caax-zikv test. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-zikv. +# Same iPSC best val-loss checkpoint as predict__ipsc_confocal.yml (epoch 181, loss/validate=0.6214). +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_zikv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: ipsc_confocal + predict_set: a549_mantis_caax_zikv + model_name: fnet3d_paper + experiment_id: membrane__ipsc_confocal__fnet3d_paper__a549_mantis_caax_zikv + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/memb/fnet3d_paper/checkpoints/epoch=181-step=157612.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fnet3d_paper_zikv.zarr + +launcher: + job_name: FNet3DPaper_PRED_MEMB_ON_A549_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/ipsc_confocal/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/ipsc_confocal/predict__ipsc_confocal.yml new file mode 100644 index 000000000..131b53480 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/ipsc_confocal/predict__ipsc_confocal.yml @@ -0,0 +1,42 @@ +# FNet3D paper-baseline predict: membrane against ipsc_confocal test_cropped. +# Uses best val-loss checkpoint (epoch 181, loss/validate=0.6214). +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: fnet3d_paper + experiment_id: membrane__ipsc_confocal__fnet3d_paper__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/memb/fnet3d_paper/checkpoints/epoch=181-step=157612.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_fnet3d_paper.zarr + +launcher: + job_name: FNet3DPaper_PRED_MEMB + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/ipsc_confocal/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/ipsc_confocal/train.yml new file mode 100644 index 000000000..196645011 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/ipsc_confocal/train.yml @@ -0,0 +1,72 @@ +# FNet3D paper-baseline fit on membrane (Membrane channel of cell.zarr) — AICS iPSC confocal. +# The overlay's norm/aug/val_aug are keyed on Structure (the SEC61B/TOMM20 target +# channel). Membrane target_channel is Membrane, so we list-replace those three +# lists here to re-key them. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/data_overlays/fnet3d_paper_fit.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_gpu_any_long.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + train_set: ipsc_confocal + model_name: fnet3d_paper + experiment_id: membrane__ipsc_confocal__fnet3d_paper + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Membrane] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Membrane] + w_key: Membrane + spatial_size: [32, 64, 64] + num_samples: 8 + val_augmentations: + - class_path: viscy_transforms.CenterSpatialCropd + init_args: + keys: [Phase3D, Membrane] + roi_size: [32, 64, 64] + +trainer: + logger: + init_args: + name: FNet3D_iPSC_MEMB_paper + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/memb/fnet3d_paper + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/memb/fnet3d_paper/checkpoints + +launcher: + job_name: FNet3DPaper_MEMB + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/memb/fnet3d_paper + # cell.zarr-backed preload (same plate as nucleus) puts MaxVMSize over + # the shared 256G cap; bump to match nucleus. + sbatch: + mem: "512G" diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..6a7323266 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,50 @@ +# FNet3D paper-baseline predict: cell membrane trained on joint iPSC+A549, +# predicting against a549-mantis-caax-denv test. +# Best val-loss checkpoint from job 31962519 (epoch 116, val 0.5759). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-denv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_denv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_caax_denv + model_name: fnet3d_paper + experiment_id: membrane__joint_ipsc_confocal_a549_mantis__fnet3d_paper__a549_mantis_caax_denv + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/memb/fnet3d_paper/checkpoints/epoch=116-step=180882.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fnet3d_paper_jointtrained_denv.zarr + +launcher: + job_name: FNet3DPaper_PRED_MEMB_JOINTTR_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..0f08c06fc --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,50 @@ +# FNet3D paper-baseline predict: cell membrane trained on joint iPSC+A549, +# predicting against a549-mantis-caax-mock test. +# Best val-loss checkpoint from job 31962519 (epoch 116, val 0.5759). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-mock. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_mock.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_caax_mock + model_name: fnet3d_paper + experiment_id: membrane__joint_ipsc_confocal_a549_mantis__fnet3d_paper__a549_mantis_caax_mock + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/memb/fnet3d_paper/checkpoints/epoch=116-step=180882.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fnet3d_paper_jointtrained_mock.zarr + +launcher: + job_name: FNet3DPaper_PRED_MEMB_JOINTTR_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..2c10aa869 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,50 @@ +# FNet3D paper-baseline predict: cell membrane trained on joint iPSC+A549, +# predicting against a549-mantis-caax-zikv test. +# Best val-loss checkpoint from job 31962519 (epoch 116, val 0.5759). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-zikv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_zikv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_caax_zikv + model_name: fnet3d_paper + experiment_id: membrane__joint_ipsc_confocal_a549_mantis__fnet3d_paper__a549_mantis_caax_zikv + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/memb/fnet3d_paper/checkpoints/epoch=116-step=180882.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fnet3d_paper_jointtrained_zikv.zarr + +launcher: + job_name: FNet3DPaper_PRED_MEMB_JOINTTR_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..90ebcd991 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,45 @@ +# FNet3D paper-baseline predict: cell membrane trained on joint iPSC+A549, +# predicting against ipsc_confocal test_cropped. +# Best val-loss checkpoint from job 31962519 (epoch 116, val 0.5759). +# Wandb run 20260503-181142_FNet3D_JOINT_MEMB_paper (state=finished, 129 ep / +# 199,999 steps; final val 0.6751 — drifted slightly past ep116 best). +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: ipsc_confocal + model_name: fnet3d_paper + experiment_id: membrane__joint_ipsc_confocal_a549_mantis__fnet3d_paper__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/memb/fnet3d_paper/checkpoints/epoch=116-step=180882.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_fnet3d_paper_jointtrained.zarr + +launcher: + job_name: FNet3DPaper_PRED_MEMB_JOINTTR_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/joint_ipsc_confocal_a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/joint_ipsc_confocal_a549_mantis/train.yml new file mode 100644 index 000000000..e3426b997 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/fnet3d_paper/joint_ipsc_confocal_a549_mantis/train.yml @@ -0,0 +1,124 @@ +# FNet3D paper-baseline fit on membrane (MEMB) — joint +# ipsc_confocal + a549_mantis pooled. Mirrors +# membrane/fnet3d_paper/ipsc_confocal/train.yml on the joint +# train_set. +# +# Joint leaf per Stage 7 of A549_EXPANSION_ROADMAP.md. +# BatchedConcatDataModule + two explicit HCSDataModule children; +# only model_overlays/fnet3d_paper_fit.yml is composed; data block +# inline. Norms + 8-crops-per-FOV diverge from the CellDiff/UNetViT +# conventions: target channel uses mean/std (not median/iqr) and +# val augmentations are CPU CenterSpatialCropd on the raw keys (the +# baseline's training pipeline doesn't go through GPU val transforms). +# +# Topology: single GPU, any model, long wall — same as +# fnet3d_paper/ipsc_confocal/train.yml. The paper baseline is single-GPU +# and we keep that here so iPSC-only and joint runs are apples-to-apples. +base: + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_gpu_any_long.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + gene: Membrane + target: membrane + target_id: membrane + train_set: joint_ipsc_confocal_a549_mantis + model_name: fnet3d_paper + experiment_id: membrane__joint_ipsc_confocal_a549_mantis__fnet3d_paper + +trainer: + logger: + init_args: + name: FNet3D_JOINT_MEMB_paper + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/memb/fnet3d_paper + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/memb/fnet3d_paper/checkpoints + +_hcs_init_args: &hcs_init_args + source_channel: Phase3D + target_channel: Membrane + z_window_size: 32 + # See nucleus/fnet3d_paper/joint_*/train.yml for the rationale: joint + # mode does not divide batch_size by num_samples (unlike single-set), + # so 6 * num_samples=8 = 48 GPU samples matches single-set effective. + batch_size: 6 + num_workers: 8 + yx_patch_size: [64, 64] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Membrane] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Membrane] + w_key: Membrane + spatial_size: [32, 64, 64] + num_samples: 8 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandFlipd + init_args: + keys: [source, target] + spatial_axes: [1] + prob: 0.5 + - class_path: viscy_transforms.BatchedRandFlipd + init_args: + keys: [source, target] + spatial_axes: [2] + prob: 0.5 + val_augmentations: + - class_path: viscy_transforms.CenterSpatialCropd + init_args: + keys: [Phase3D, Membrane] + roi_size: [32, 64, 64] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + # ipsc_confocal — aics-hipsc multi-marker cell.zarr (Membrane channel) + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/cell.zarr + # a549_mantis — pooled CAAX all-conditions train store (Membrane channel) + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/CAAX_all.zarr + +launcher: + job_name: FNet3DPaper_JOINT_MEMB + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/memb/fnet3d_paper + # 512G to match the shared headroom convention across the fnet3d + # leaves on a549/joint workloads. mmap_preload after the BasicIndexer + # fix peaks at ~185 GB for joint cell.zarr + CAAX_all; 512G gives + # generous headroom for worker buffers and validation transients. + sbatch: + mem: "512G" diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/a549_mantis/train.yml new file mode 100644 index 000000000..e1641459f --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/a549_mantis/train.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit fit on membrane (Membrane channel of cell.zarr) — A549 mantis-lightsheet pooled (mock + DENV + ZIKV). +base: + - ../../../_internal/shared/model/train_sets/a549_mantis.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/data_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + train_set: a549_mantis + model_name: pix2pix3d_unetvit + experiment_id: membrane__a549_mantis__pix2pix3d_unetvit + +trainer: + logger: + init_args: + name: pix2pix3d_unetvit_A549_MEMB + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/memb/pix2pix3d_unetvit + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/memb/pix2pix3d_unetvit/checkpoints + +data: + init_args: + # A549 pooled store + target_channel — no resolver in this train_set. + target_channel: Membrane + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/CAAX_all.zarr + +launcher: + job_name: pix2pix3d_unetvit_A549_MEMB + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/memb/pix2pix3d_unetvit diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 100644 index 000000000..e5139dea8 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: membrane (Membrane) predicted by pix2pix3d_unetvit on a549-mantis-caax-denv. +defaults: + - override /target: membrane + - override /predict_set: a549_mantis_caax_denv + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_pix2pix3d_unetvit__caax_denv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_memb_pix2pix3d_unetvit__caax_denv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 100644 index 000000000..f93b7f83c --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: membrane (Membrane) predicted by pix2pix3d_unetvit on a549-mantis-caax-mock. +defaults: + - override /target: membrane + - override /predict_set: a549_mantis_caax_mock + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_pix2pix3d_unetvit__caax_mock.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_memb_pix2pix3d_unetvit__caax_mock diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 100644 index 000000000..8c4a62a9a --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: membrane (Membrane) predicted by pix2pix3d_unetvit on a549-mantis-caax-zikv. +defaults: + - override /target: membrane + - override /predict_set: a549_mantis_caax_zikv + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_pix2pix3d_unetvit__caax_zikv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_memb_pix2pix3d_unetvit__caax_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/ipsc_confocal/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/ipsc_confocal/eval__ipsc_confocal.yaml new file mode 100644 index 000000000..3d4a89595 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/ipsc_confocal/eval__ipsc_confocal.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: membrane (Membrane) predicted by pix2pix3d_unetvit on iPSC confocal. +defaults: + - override /target: membrane + - override /predict_set: ipsc_confocal + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_pix2pix3d_unetvit.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/eval_memb_pix2pix3d_unetvit diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..7309ac18e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_denv.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: membrane (Membrane marker) trained on iPSC, predicting against a549_mantis_caax_denv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_denv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: ipsc_confocal + predict_set: a549_mantis_caax_denv + model_name: pix2pix3d_unetvit + experiment_id: membrane__ipsc_confocal__pix2pix3d_unetvit__a549_mantis_caax_denv + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_pix2pix3d_unetvit__caax_denv.zarr + +launcher: + job_name: pix2pix3d_unetvit_PRED_MEMB_ON_A549_caax_denv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..86c2138da --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_mock.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: membrane (Membrane marker) trained on iPSC, predicting against a549_mantis_caax_mock test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_mock.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: ipsc_confocal + predict_set: a549_mantis_caax_mock + model_name: pix2pix3d_unetvit + experiment_id: membrane__ipsc_confocal__pix2pix3d_unetvit__a549_mantis_caax_mock + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_pix2pix3d_unetvit__caax_mock.zarr + +launcher: + job_name: pix2pix3d_unetvit_PRED_MEMB_ON_A549_caax_mock + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..4c8a72300 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_zikv.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: membrane (Membrane marker) trained on iPSC, predicting against a549_mantis_caax_zikv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_zikv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: ipsc_confocal + predict_set: a549_mantis_caax_zikv + model_name: pix2pix3d_unetvit + experiment_id: membrane__ipsc_confocal__pix2pix3d_unetvit__a549_mantis_caax_zikv + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_pix2pix3d_unetvit__caax_zikv.zarr + +launcher: + job_name: pix2pix3d_unetvit_PRED_MEMB_ON_A549_caax_zikv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/ipsc_confocal/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/ipsc_confocal/predict__ipsc_confocal.yml new file mode 100644 index 000000000..b6f95b837 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/ipsc_confocal/predict__ipsc_confocal.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: membrane (Membrane marker) against ipsc_confocal test_cropped. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: pix2pix3d_unetvit + experiment_id: membrane__ipsc_confocal__pix2pix3d_unetvit__ipsc_confocal + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_pix2pix3d_unetvit.zarr + +launcher: + job_name: pix2pix3d_unetvit_PRED_MEMB + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/ipsc_confocal/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/ipsc_confocal/train.yml new file mode 100644 index 000000000..65fa7bfca --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/ipsc_confocal/train.yml @@ -0,0 +1,37 @@ +# pix2pix3d_unetvit fit on membrane (Membrane channel of cell.zarr) — AICS iPSC confocal. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/data_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + train_set: ipsc_confocal + model_name: pix2pix3d_unetvit + experiment_id: membrane__ipsc_confocal__pix2pix3d_unetvit + +trainer: + logger: + init_args: + name: pix2pix3d_unetvit_iPSC_MEMB + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/memb/pix2pix3d_unetvit + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/memb/pix2pix3d_unetvit/checkpoints + +launcher: + job_name: pix2pix3d_unetvit_MEMB + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/memb/pix2pix3d_unetvit diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..3b6ff0c9d --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: membrane (Membrane marker) trained on joint iPSC+A549, predicting against a549_mantis_caax_denv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_denv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_caax_denv + model_name: pix2pix3d_unetvit + experiment_id: membrane__joint_ipsc_confocal_a549_mantis__pix2pix3d_unetvit__a549_mantis_caax_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/memb/pix2pix3d_unetvit/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/memb_pix2pix3d_unetvit__caax_denv.zarr + +launcher: + job_name: pix2pix3d_unetvit_JOINT_PRED_MEMB_ON_A549_caax_denv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..8e8354f6f --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: membrane (Membrane marker) trained on joint iPSC+A549, predicting against a549_mantis_caax_mock test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_mock.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_caax_mock + model_name: pix2pix3d_unetvit + experiment_id: membrane__joint_ipsc_confocal_a549_mantis__pix2pix3d_unetvit__a549_mantis_caax_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/memb/pix2pix3d_unetvit/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/memb_pix2pix3d_unetvit__caax_mock.zarr + +launcher: + job_name: pix2pix3d_unetvit_JOINT_PRED_MEMB_ON_A549_caax_mock + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..c1c457796 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: membrane (Membrane marker) trained on joint iPSC+A549, predicting against a549_mantis_caax_zikv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_zikv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_caax_zikv + model_name: pix2pix3d_unetvit + experiment_id: membrane__joint_ipsc_confocal_a549_mantis__pix2pix3d_unetvit__a549_mantis_caax_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/memb/pix2pix3d_unetvit/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/memb_pix2pix3d_unetvit__caax_zikv.zarr + +launcher: + job_name: pix2pix3d_unetvit_JOINT_PRED_MEMB_ON_A549_caax_zikv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..a0aa105fd --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: membrane (Membrane marker) trained on joint iPSC+A549, predicting against ipsc_confocal test. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: ipsc_confocal + model_name: pix2pix3d_unetvit + experiment_id: membrane__joint_ipsc_confocal_a549_mantis__pix2pix3d_unetvit__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/memb/pix2pix3d_unetvit/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions/memb_pix2pix3d_unetvit.zarr + +launcher: + job_name: pix2pix3d_unetvit_JOINT_PRED_MEMB_ON_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/train.yml new file mode 100644 index 000000000..f1dbc7eae --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/train.yml @@ -0,0 +1,147 @@ +# pix2pix3d_unetvit fit on membrane (Membrane marker) — joint ipsc_confocal + a549_mantis pooled. +# +# Joint leaf. Uses BatchedConcatDataModule with two explicit HCSDataModule +# children (no benchmark.dataset_ref — joint leaves bypass the single-dataset +# resolver). Only model_overlays/pix2pix3d_unetvit_fit.yml is composed; the +# data block is authored inline because joint hparams live on the children. +# +# Normalization is NormalizeSampled (fov_statistics) to match the single-set +# pix2pix3d_unetvit leaves — divergent from the celldiff joint which uses +# MinMaxSampled. Per-organelle prior is to keep joint and single-set +# normalizations identical so ablations are apples-to-apples. +# +# Topology: single H200, single GPU — same as pix2pix3d_unetvit/ipsc_confocal/train.yml. +base: + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + gene: Membrane + target: membrane + target_id: membrane + train_set: joint_ipsc_confocal_a549_mantis + model_name: pix2pix3d_unetvit + experiment_id: membrane__joint_ipsc_confocal_a549_mantis__pix2pix3d_unetvit + +trainer: + logger: + init_args: + name: pix2pix3d_unetvit_JOINT_MEMB + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/memb/pix2pix3d_unetvit + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/memb/pix2pix3d_unetvit/checkpoints + +# Child HCSDataModule init_args shared across both datasets (only data_path +# differs). `_`-prefixed top-level keys are stripped by load_composed_config +# before reaching LightningCLI; the merge expansion under `data:` survives. +_hcs_init_args: &hcs_init_args + source_channel: Phase3D + target_channel: Membrane + z_window_size: 13 + # batch_size=2 + num_samples=2 → 4 GPU samples/step, matching the single-set + # pix2pix3d_unetvit (batch=4, num_samples=2). BatchedConcatDataModule does + # NOT divide by num_samples (see CLAUDE.md), so joint.batch_size = + # single_set.batch_size / num_samples. + batch_size: 2 + num_workers: 4 + yx_patch_size: [512, 512] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Membrane] + level: fov_statistics + subtrahend: median + divisor: iqr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Membrane] + w_key: Membrane + spatial_size: [13, 624, 624] + num_samples: 2 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + safe_crop_size: [8, 512, 512] + safe_crop_coverage: 0.9 + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/cell.zarr + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/CAAX_all.zarr + +launcher: + job_name: pix2pix3d_unetvit_JOINT_MEMB + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/memb/pix2pix3d_unetvit + # Joint preloads two stores (iPSC + A549 pool) into /dev/shm; default 256G + # is too tight for the iPSC marker store + A549 pool + worker overhead. + sbatch: + mem: "512G" diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/a549_mantis/train.yml new file mode 100644 index 000000000..8bca97a50 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/a549_mantis/train.yml @@ -0,0 +1,43 @@ +# UNetViT3D fit on membrane (Membrane channel of cell.zarr) — A549 mantis-lightsheet pooled (mock + DENV + ZIKV). +base: + - ../../../_internal/shared/model/train_sets/a549_mantis.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/data_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/model_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + train_set: a549_mantis + model_name: unetvit3d + experiment_id: membrane__a549_mantis__unetvit3d + +trainer: + logger: + init_args: + name: UNetViT3D_A549_MEMB + save_dir: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/memb_temp/unetvit3d + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/memb_temp/unetvit3d/checkpoints + +data: + init_args: + # A549 pooled store + target_channel — no resolver in this train_set. + target_channel: Membrane + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/CAAX_all.zarr + +launcher: + job_name: UNetViT3D_A549_MEMB + run_root: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/memb_temp/unetvit3d diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 100644 index 000000000..3165b49cd --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Membrane (CAAX) predicted by UNetViT3D on a549-mantis-caax-denv. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from the target group so the resolver finds caax on a549-mantis-caax-denv. +defaults: + - override /target: membrane + - override /predict_set: a549_mantis_caax_denv + +benchmark: + dataset_ref: + target: caax + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_unetvit3d_denv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_memb_unetvit3d_denv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 100644 index 000000000..2e3aa496c --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Membrane (CAAX) predicted by UNetViT3D on a549-mantis-caax-mock. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from the target group so the resolver finds caax on a549-mantis-caax-mock. +defaults: + - override /target: membrane + - override /predict_set: a549_mantis_caax_mock + +benchmark: + dataset_ref: + target: caax + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_unetvit3d_mock.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_memb_unetvit3d_mock diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 100644 index 000000000..dcde8c662 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Membrane (CAAX) predicted by UNetViT3D on a549-mantis-caax-zikv. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from the target group so the resolver finds caax on a549-mantis-caax-zikv. +defaults: + - override /target: membrane + - override /predict_set: a549_mantis_caax_zikv + +benchmark: + dataset_ref: + target: caax + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_unetvit3d_zikv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_memb_unetvit3d_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/ipsc_confocal/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/ipsc_confocal/eval__ipsc_confocal.yaml new file mode 100644 index 000000000..12c736435 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/ipsc_confocal/eval__ipsc_confocal.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: Membrane predicted by UNetViT3D on iPSC confocal. +defaults: + - override /target: membrane + - override /predict_set: ipsc_confocal + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_unetvit3d.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/eval_memb_unetvit3d diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/ipsc_confocal/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/ipsc_confocal/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..1aa4ee4fb --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/ipsc_confocal/predict__a549_mantis_denv.yml @@ -0,0 +1,49 @@ +# UNetViT3D predict: membrane trained on iPSC, predicting against a549-mantis-caax-denv test. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-denv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_denv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/unetvit3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: ipsc_confocal + predict_set: a549_mantis_caax_denv + model_name: unetvit3d + experiment_id: membrane__ipsc_confocal__unetvit3d__a549_mantis_caax_denv + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/memb/unetvit3d/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_unetvit3d_denv.zarr + +launcher: + job_name: UNetViT3D_PRED_MEMB_ON_A549_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/ipsc_confocal/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/ipsc_confocal/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..18a214e83 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/ipsc_confocal/predict__a549_mantis_mock.yml @@ -0,0 +1,49 @@ +# UNetViT3D predict: membrane trained on iPSC, predicting against a549-mantis-caax-mock test. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-mock. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_mock.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/unetvit3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: ipsc_confocal + predict_set: a549_mantis_caax_mock + model_name: unetvit3d + experiment_id: membrane__ipsc_confocal__unetvit3d__a549_mantis_caax_mock + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/memb/unetvit3d/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_unetvit3d_mock.zarr + +launcher: + job_name: UNetViT3D_PRED_MEMB_ON_A549_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/ipsc_confocal/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/ipsc_confocal/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..c138f4239 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/ipsc_confocal/predict__a549_mantis_zikv.yml @@ -0,0 +1,49 @@ +# UNetViT3D predict: membrane trained on iPSC, predicting against a549-mantis-caax-zikv test. +# A549 manifest keys membrane by gene (`caax`); override the iPSC-side `membrane` +# target_id from targets/membrane.yml so the resolver finds the caax target on +# a549-mantis-caax-zikv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_caax_zikv.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/unetvit3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: ipsc_confocal + predict_set: a549_mantis_caax_zikv + model_name: unetvit3d + experiment_id: membrane__ipsc_confocal__unetvit3d__a549_mantis_caax_zikv + # Override the iPSC-side `membrane` target to a549's gene-keyed `caax`. + dataset_ref: + target: caax + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/memb/unetvit3d/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_unetvit3d_zikv.zarr + +launcher: + job_name: UNetViT3D_PRED_MEMB_ON_A549_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/ipsc_confocal/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/ipsc_confocal/predict__ipsc_confocal.yml new file mode 100644 index 000000000..d4a42d556 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/ipsc_confocal/predict__ipsc_confocal.yml @@ -0,0 +1,43 @@ +# UNetViT3D predict: membrane against ipsc_confocal test_cropped. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/model_overlays/unetvit3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: unetvit3d + experiment_id: membrane__ipsc_confocal__unetvit3d__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/memb/unetvit3d/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_unetvit3d.zarr + +launcher: + job_name: UNetViT3D_PRED_MEMB + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/ipsc_confocal/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/ipsc_confocal/train.yml new file mode 100644 index 000000000..daf2651d4 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/ipsc_confocal/train.yml @@ -0,0 +1,37 @@ +# UNetViT3D fit on membrane (Membrane channel of cell.zarr) — AICS iPSC confocal. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/membrane.yml + - ../../../_internal/shared/model/data_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/model_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + train_set: ipsc_confocal + model_name: unetvit3d + experiment_id: membrane__ipsc_confocal__unetvit3d + +trainer: + logger: + init_args: + name: UNetViT3D_iPSC_MEMB + save_dir: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/memb_temp/unetvit3d + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/memb_temp/unetvit3d/checkpoints + +launcher: + job_name: UNetViT3D_MEMB + run_root: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/memb_temp/unetvit3d diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/joint_ipsc_confocal_a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/joint_ipsc_confocal_a549_mantis/train.yml new file mode 100644 index 000000000..79679628f --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/membrane/unetvit3d/joint_ipsc_confocal_a549_mantis/train.yml @@ -0,0 +1,143 @@ +# UNetViT3D fit on membrane (Membrane) — joint ipsc_confocal + a549_mantis pooled. +# +# Joint leaf per Stage 7 of A549_EXPANSION_ROADMAP.md. Uses +# BatchedConcatDataModule with two explicit HCSDataModule children +# (no benchmark.dataset_ref — joint leaves bypass the single-dataset +# resolver). Only model_overlays/unetvit3d_fit.yml is composed; the data +# block is authored inline because joint hparams live on the children. +# +# iPSC source is the multi-marker cell.zarr (Brightfield, Nuclei, +# Membrane, Phase3D); A549 source is the CAAX-marker pooled store +# CAAX_all.zarr. The shared target_channel name is `Membrane` in both. +# +# Topology: single H200, single GPU — same as unetvit3d/ipsc_confocal/train.yml. +# The paper baseline pattern is single-GPU and we keep that here so +# iPSC-only and joint runs are apples-to-apples. +base: + - ../../../_internal/shared/model/model_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: membrane + gene: Membrane + target: membrane + target_id: membrane + train_set: joint_ipsc_confocal_a549_mantis + model_name: unetvit3d + experiment_id: membrane__joint_ipsc_confocal_a549_mantis__unetvit3d + +trainer: + logger: + init_args: + name: UNetViT3D_JOINT_MEMB + save_dir: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/memb/unetvit3d + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/memb/unetvit3d/checkpoints + +_hcs_init_args: &hcs_init_args + source_channel: Phase3D + target_channel: Membrane + z_window_size: 13 + batch_size: 4 + num_workers: 4 + yx_patch_size: [512, 512] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Membrane] + level: fov_statistics + subtrahend: median + divisor: iqr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Membrane] + w_key: Membrane + spatial_size: [13, 624, 624] + num_samples: 2 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + safe_crop_size: [8, 512, 512] + safe_crop_coverage: 0.9 + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/cell.zarr + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/CAAX_all.zarr + +launcher: + job_name: UNetViT3D_JOINT_MEMB + run_root: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/memb/unetvit3d + # Joint preloads two stores (iPSC + A549 pool) into /dev/shm; the default + # 256G cap is too tight (256G iPSC mem + ~50G A549 + worker peak OOMs). + # 512G is the smallest tier that fits joint preload + worker overhead. + sbatch: + mem: "512G" diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..aa4bd17be --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: mito (TOMM20) trained on A549 mantis, predicting against a549_mantis_tomm20_denv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_denv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: a549_mantis + predict_set: a549_mantis_tomm20_denv + model_name: celldiff + experiment_id: mito__a549_mantis__celldiff__a549_mantis_tomm20_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/tomm20/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_celldiff_r2_a549trained_denv.zarr + +launcher: + job_name: CELLDiff_A549_PRED_TOMM20_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..9a92e0cbe --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: mito (TOMM20) trained on A549 mantis, predicting against a549_mantis_tomm20_mock test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_mock.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: a549_mantis + predict_set: a549_mantis_tomm20_mock + model_name: celldiff + experiment_id: mito__a549_mantis__celldiff__a549_mantis_tomm20_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/tomm20/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_celldiff_r2_a549trained_mock.zarr + +launcher: + job_name: CELLDiff_A549_PRED_TOMM20_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..25cd1676b --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: mito (TOMM20) trained on A549 mantis, predicting against a549_mantis_tomm20_zikv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_zikv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: a549_mantis + predict_set: a549_mantis_tomm20_zikv + model_name: celldiff + experiment_id: mito__a549_mantis__celldiff__a549_mantis_tomm20_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/tomm20/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_celldiff_r2_a549trained_zikv.zarr + +launcher: + job_name: CELLDiff_A549_PRED_TOMM20_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..b6ce4cd92 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: mito (TOMM20) trained on A549 mantis, predicting against ipsc_confocal test (OOD). +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: a549_mantis + predict_set: ipsc_confocal + model_name: celldiff + experiment_id: mito__a549_mantis__celldiff__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/tomm20/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 40 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_celldiff_r2_a549trained.zarr + +launcher: + job_name: CELLDiff_A549_PRED_TOMM20_ON_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/a549_mantis/train.yml new file mode 100644 index 000000000..f26b1edf9 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/a549_mantis/train.yml @@ -0,0 +1,42 @@ +# CellDiff fit on mitochondria (TOMM20 marker) — A549 mantis-lightsheet pooled (mock + DENV + ZIKV). +base: + - ../../../_internal/shared/model/train_sets/a549_mantis.yml + - ../../../_internal/shared/model/targets/mito_tomm20_celldiff.yml + - ../../../_internal/shared/model/data_overlays/celldiff_fit.yml + - ../../../_internal/shared/model/model_overlays/celldiff_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + train_set: a549_mantis + model_name: celldiff + experiment_id: mito__a549_mantis__celldiff + +trainer: + logger: + init_args: + name: CELLDiff_A549_TOMM20 + save_dir: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/tomm20/celldiff_r2 + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + every_n_epochs: 1 + save_top_k: -1 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/tomm20/celldiff_r2/checkpoints + +data: + init_args: + # A549 pooled store + target_channel — no resolver in this train_set. + target_channel: Structure + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/TOMM20_all.zarr + +launcher: + job_name: CELLDiff_A549_TOMM20 + run_root: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/tomm20/celldiff_r2 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 100644 index 000000000..9a8aec1a4 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: Mitochondria (TOMM20) predicted by CellDiff on a549-mantis-tomm20-denv. +defaults: + - override /target: mito_tomm20 + - override /predict_set: a549_mantis_tomm20_denv + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_celldiff_iterative__tomm20_denv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_tomm20_celldiff_iterative__tomm20_denv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 100644 index 000000000..6177c7214 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: Mitochondria (TOMM20) predicted by CellDiff on a549-mantis-tomm20-mock. +defaults: + - override /target: mito_tomm20 + - override /predict_set: a549_mantis_tomm20_mock + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_celldiff_iterative__tomm20_mock.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_tomm20_celldiff_iterative__tomm20_mock diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 100644 index 000000000..0f28893fd --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: Mitochondria (TOMM20) predicted by CellDiff on a549-mantis-tomm20-zikv. +defaults: + - override /target: mito_tomm20 + - override /predict_set: a549_mantis_tomm20_zikv + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_celldiff_iterative__tomm20_zikv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_tomm20_celldiff_iterative__tomm20_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/eval__ipsc_confocal.yaml new file mode 100644 index 000000000..7d72d571c --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/eval__ipsc_confocal.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: Mitochondria (TOMM20) predicted by CellDiff on iPSC confocal. +defaults: + - override /target: mito_tomm20 + - override /predict_set: ipsc_confocal + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_celldiff_iterative.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/eval_tomm20_celldiff_iterative diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..c9ee79680 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/predict__a549_mantis_denv.yml @@ -0,0 +1,42 @@ +# CellDiff predict: mito (TOMM20) trained on iPSC, predicting against a549_mantis_tomm20_denv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_denv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: ipsc_confocal + predict_set: a549_mantis_tomm20_denv + model_name: celldiff + experiment_id: mito__ipsc_confocal__celldiff__a549_mantis_tomm20_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/tomm20/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative # denoise, generate, sliding_window, or iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 # 8 for denoise and generate, 40 for iterative and sliding_window. + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_celldiff_r2_iterative__tomm20_denv.zarr + +launcher: + job_name: CELLDiff_PRED_TOMM20_ON_A549_tomm20_denv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..98e3ce246 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/predict__a549_mantis_mock.yml @@ -0,0 +1,42 @@ +# CellDiff predict: mito (TOMM20) trained on iPSC, predicting against a549_mantis_tomm20_mock test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_mock.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: ipsc_confocal + predict_set: a549_mantis_tomm20_mock + model_name: celldiff + experiment_id: mito__ipsc_confocal__celldiff__a549_mantis_tomm20_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/tomm20/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative # denoise, generate, sliding_window, or iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 # 8 for denoise and generate, 40 for iterative and sliding_window. + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_celldiff_r2_iterative__tomm20_mock.zarr + +launcher: + job_name: CELLDiff_PRED_TOMM20_ON_A549_tomm20_mock + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..de87c0bf0 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/predict__a549_mantis_zikv.yml @@ -0,0 +1,42 @@ +# CellDiff predict: mito (TOMM20) trained on iPSC, predicting against a549_mantis_tomm20_zikv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_zikv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: ipsc_confocal + predict_set: a549_mantis_tomm20_zikv + model_name: celldiff + experiment_id: mito__ipsc_confocal__celldiff__a549_mantis_tomm20_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/tomm20/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative # denoise, generate, sliding_window, or iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 # 8 for denoise and generate, 40 for iterative and sliding_window. + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_celldiff_r2_iterative__tomm20_zikv.zarr + +launcher: + job_name: CELLDiff_PRED_TOMM20_ON_A549_tomm20_zikv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/predict__ipsc_confocal__denoise.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/predict__ipsc_confocal__denoise.yml new file mode 100644 index 000000000..dc4c76c2d --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/predict__ipsc_confocal__denoise.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: mito (TOMM20) on ipsc_confocal — denoise method. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: celldiff + experiment_id: mito__ipsc_confocal__celldiff__ipsc_confocal__denoise + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/tomm20/celldiff_r2/checkpoints/last.ckpt + predict_method: denoise + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 8 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_celldiff_r2_denoise.zarr + +launcher: + job_name: CELLDiff_PRED_TOMM20_DN + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/predict__ipsc_confocal__iterative.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/predict__ipsc_confocal__iterative.yml new file mode 100644 index 000000000..2c1bc0b6b --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/predict__ipsc_confocal__iterative.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: mito (TOMM20) on ipsc_confocal — iterative method. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: celldiff + experiment_id: mito__ipsc_confocal__celldiff__ipsc_confocal__iterative + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/tomm20/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 40 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_celldiff_r2_iterative.zarr + +launcher: + job_name: CELLDiff_PRED_TOMM20_ITER + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/predict__ipsc_confocal__sliding_window.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/predict__ipsc_confocal__sliding_window.yml new file mode 100644 index 000000000..2a3c91be4 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/predict__ipsc_confocal__sliding_window.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: mito (TOMM20) on ipsc_confocal — sliding_window method. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: celldiff + experiment_id: mito__ipsc_confocal__celldiff__ipsc_confocal__sliding_window + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/tomm20/celldiff_r2/checkpoints/last.ckpt + predict_method: sliding_window + predict_overlap: [0, 0, 0] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 40 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_celldiff_r2_sliding_window.zarr + +launcher: + job_name: CELLDiff_PRED_TOMM20_SW + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/train.yml new file mode 100644 index 000000000..5eff94452 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/ipsc_confocal/train.yml @@ -0,0 +1,36 @@ +# CellDiff fit on mitochondria (TOMM20 marker) — AICS iPSC confocal. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/mito_tomm20_celldiff.yml + - ../../../_internal/shared/model/data_overlays/celldiff_fit.yml + - ../../../_internal/shared/model/model_overlays/celldiff_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + train_set: ipsc_confocal + model_name: celldiff + experiment_id: mito__ipsc_confocal__celldiff + +trainer: + logger: + init_args: + name: CELLDiff_iPSC_TOMM20 + save_dir: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/tomm20/celldiff_r2 + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + every_n_epochs: 1 + save_top_k: -1 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/tomm20/celldiff_r2/checkpoints + +launcher: + job_name: CELLDiff_TOMM20 + run_root: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/tomm20/celldiff_r2 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..6d5cb383e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: mito (TOMM20) trained on joint iPSC+A549, predicting against a549_mantis_tomm20_denv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_denv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_tomm20_denv + model_name: celldiff + experiment_id: mito__joint_ipsc_confocal_a549_mantis__celldiff__a549_mantis_tomm20_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/tomm20/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/tomm20_celldiff_r2_denv.zarr + +launcher: + job_name: CELLDiff_JOINT_PRED_TOMM20_ON_A549_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..2ea6c3e5a --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: mito (TOMM20) trained on joint iPSC+A549, predicting against a549_mantis_tomm20_mock test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_mock.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_tomm20_mock + model_name: celldiff + experiment_id: mito__joint_ipsc_confocal_a549_mantis__celldiff__a549_mantis_tomm20_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/tomm20/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/tomm20_celldiff_r2_mock.zarr + +launcher: + job_name: CELLDiff_JOINT_PRED_TOMM20_ON_A549_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..c27154644 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: mito (TOMM20) trained on joint iPSC+A549, predicting against a549_mantis_tomm20_zikv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_zikv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_tomm20_zikv + model_name: celldiff + experiment_id: mito__joint_ipsc_confocal_a549_mantis__celldiff__a549_mantis_tomm20_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/tomm20/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/tomm20_celldiff_r2_zikv.zarr + +launcher: + job_name: CELLDiff_JOINT_PRED_TOMM20_ON_A549_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..6e308b8d7 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: mito (TOMM20) trained on joint iPSC+A549, predicting against ipsc_confocal test. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: ipsc_confocal + model_name: celldiff + experiment_id: mito__joint_ipsc_confocal_a549_mantis__celldiff__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/tomm20/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 40 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions/tomm20_celldiff_r2.zarr + +launcher: + job_name: CELLDiff_JOINT_PRED_TOMM20_ON_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/joint_ipsc_confocal_a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/joint_ipsc_confocal_a549_mantis/train.yml new file mode 100644 index 000000000..7c4f3ac29 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/celldiff/joint_ipsc_confocal_a549_mantis/train.yml @@ -0,0 +1,141 @@ +# CellDiff fit on mitochondria (TOMM20) — joint ipsc_confocal + a549_mantis pooled. +# +# Joint leaf per Stage 7 of A549_EXPANSION_ROADMAP.md. Uses +# BatchedConcatDataModule with two explicit HCSDataModule children +# (no benchmark.dataset_ref — joint leaves bypass the single-dataset +# resolver). Only model_overlays/celldiff_fit.yml is composed; the data +# block is authored inline because joint hparams live on the children. +# +# Topology: single H200, single GPU — same as celldiff/ipsc_confocal/train.yml. +# The paper baseline pattern is single-GPU and we keep that here so +# iPSC-only and joint runs are apples-to-apples. +base: + - ../../../_internal/shared/model/model_overlays/celldiff_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + gene: TOMM20 + target: mito + target_id: mito_tomm20 + train_set: joint_ipsc_confocal_a549_mantis + model_name: celldiff + experiment_id: mito__joint_ipsc_confocal_a549_mantis__celldiff + +trainer: + logger: + init_args: + name: CELLDiff_JOINT_TOMM20 + save_dir: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/tomm20/celldiff_r2 + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + every_n_epochs: 1 + save_top_k: -1 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/tomm20/celldiff_r2/checkpoints + +# `_`-prefixed top-level keys are stripped by load_composed_config; see +# er/celldiff_r2/joint_*/train.yml for the full anchor-convention rationale. +_hcs_init_args: &hcs_init_args + source_channel: Phase3D + target_channel: Structure + z_window_size: 13 + # batch_size=2 (not the celldiff_fit.yml default of 4): BatchedConcatDataModule + # does NOT divide by num_samples (see CLAUDE.md), so 4 × num_samples=2 = 8 GPU + # samples/step OOMs H200 140 GiB at unet/blocks.py:187 (h + res_conv(x)). + batch_size: 2 + num_workers: 4 + yx_patch_size: [512, 512] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Structure] + level: timepoint_statistics + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Structure] + w_key: Structure + spatial_size: [13, 624, 624] + num_samples: 2 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + safe_crop_size: [8, 512, 512] + safe_crop_coverage: 0.9 + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + # ipsc_confocal — aics-hipsc TOMM20 train store + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/TOMM20.zarr + # a549_mantis — pooled TOMM20 all-conditions train store + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/TOMM20_all.zarr + +launcher: + job_name: CELLDiff_JOINT_TOMM20 + run_root: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/tomm20/celldiff_r2 + # Joint preloads two stores (iPSC + A549 pool) into /dev/shm; the default + # 256G cap is too tight (256G iPSC mem + ~50G A549 + worker peak OOMs). + # 512G is the smallest tier that fits joint preload + worker overhead. + sbatch: + mem: "512G" diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..736ada20d --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_denv.yml @@ -0,0 +1,46 @@ +# VSCyto3D random-init predict: mito (frozen randinit ckpt), A549 denv plate. +# Control ablation. A549 manifest keys mito by gene (`tomm20`); override the +# iPSC-side `mito_tomm20` target_id from targets/mito_tomm20.yml so the resolver finds the +# tomm20 target on a549-mantis-tomm20-denv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_denv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: randinit + predict_set: a549_mantis_tomm20_denv + model_name: fcmae_vscyto3d_pretrained_randinit + experiment_id: mito__randinit__fcmae_vscyto3d_pretrained__a549_mantis_tomm20_denv + dataset_ref: + target: tomm20 + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/randinit/tomm20/fcmae_vscyto3d_pretrained/checkpoints/randinit.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_pretrained_randinit_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_RandInit_PRED_TOMM20_ON_A549_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..461364208 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_mock.yml @@ -0,0 +1,46 @@ +# VSCyto3D random-init predict: mito (frozen randinit ckpt), A549 mock plate. +# Control ablation. A549 manifest keys mito by gene (`tomm20`); override the +# iPSC-side `mito_tomm20` target_id from targets/mito_tomm20.yml so the resolver finds the +# tomm20 target on a549-mantis-tomm20-mock. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_mock.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: randinit + predict_set: a549_mantis_tomm20_mock + model_name: fcmae_vscyto3d_pretrained_randinit + experiment_id: mito__randinit__fcmae_vscyto3d_pretrained__a549_mantis_tomm20_mock + dataset_ref: + target: tomm20 + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/randinit/tomm20/fcmae_vscyto3d_pretrained/checkpoints/randinit.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_pretrained_randinit_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_RandInit_PRED_TOMM20_ON_A549_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..d94a378d1 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_zikv.yml @@ -0,0 +1,46 @@ +# VSCyto3D random-init predict: mito (frozen randinit ckpt), A549 zikv plate. +# Control ablation. A549 manifest keys mito by gene (`tomm20`); override the +# iPSC-side `mito_tomm20` target_id from targets/mito_tomm20.yml so the resolver finds the +# tomm20 target on a549-mantis-tomm20-zikv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_zikv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: randinit + predict_set: a549_mantis_tomm20_zikv + model_name: fcmae_vscyto3d_pretrained_randinit + experiment_id: mito__randinit__fcmae_vscyto3d_pretrained__a549_mantis_tomm20_zikv + dataset_ref: + target: tomm20 + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/randinit/tomm20/fcmae_vscyto3d_pretrained/checkpoints/randinit.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_pretrained_randinit_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_RandInit_PRED_TOMM20_ON_A549_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__ipsc_confocal.yml new file mode 100644 index 000000000..bb39acbf3 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__ipsc_confocal.yml @@ -0,0 +1,44 @@ +# VSCyto3D random-init predict: mito (frozen randinit ckpt), iPSC test set. +# Control ablation — measures untrained model output for paper. +# References the frozen randinit.ckpt persisted by save_random_init_vscyto3d_ckpts.py +# so all 4 datasets (iPSC + 3 A549 plates) for this organelle reuse the same weights. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: randinit + predict_set: ipsc_confocal + model_name: fcmae_vscyto3d_pretrained_randinit + experiment_id: mito__randinit__fcmae_vscyto3d_pretrained__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/randinit/tomm20/fcmae_vscyto3d_pretrained/checkpoints/randinit.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_fcmae_vscyto3d_pretrained_randinit.zarr + +launcher: + job_name: FCMAE_VSCyto3D_RandInit_PRED_TOMM20 + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..ba8a8e906 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,47 @@ +# FCMAE_VSCyto3D_Pretrained (VSCyto3D) predict: mito trained on a549_mantis (tomm20), +# predicting against a549-mantis-tomm20-denv test. +# Best val-loss checkpoint epoch=139-step=22120 (loss/validate=0.71857), from the +# ws8500 training run (save dir suffix _ws8500; model_name stays +# fcmae_vscyto3d_pretrained). Last epoch was 150 but ep139 is best-val. +# Both iPSC and a549 manifests use `tomm20`; targets/mito_tomm20.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_denv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: a549_mantis + predict_set: a549_mantis_tomm20_denv + model_name: fcmae_vscyto3d_pretrained + experiment_id: mito__a549_mantis__fcmae_vscyto3d_pretrained__a549_mantis_tomm20_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/tomm20/fcmae_vscyto3d_pretrained_ws8500/checkpoints/epoch=139-step=22120.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_pretrained_a549trained_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_TOMM20_A549TR_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..bf536a7a7 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,47 @@ +# FCMAE_VSCyto3D_Pretrained (VSCyto3D) predict: mito trained on a549_mantis (tomm20), +# predicting against a549-mantis-tomm20-mock test. +# Best val-loss checkpoint epoch=139-step=22120 (loss/validate=0.71857), from the +# ws8500 training run (save dir suffix _ws8500; model_name stays +# fcmae_vscyto3d_pretrained). Last epoch was 150 but ep139 is best-val. +# Both iPSC and a549 manifests use `tomm20`; targets/mito_tomm20.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_mock.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: a549_mantis + predict_set: a549_mantis_tomm20_mock + model_name: fcmae_vscyto3d_pretrained + experiment_id: mito__a549_mantis__fcmae_vscyto3d_pretrained__a549_mantis_tomm20_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/tomm20/fcmae_vscyto3d_pretrained_ws8500/checkpoints/epoch=139-step=22120.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_pretrained_a549trained_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_TOMM20_A549TR_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..92dbdd60b --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,47 @@ +# FCMAE_VSCyto3D_Pretrained (VSCyto3D) predict: mito trained on a549_mantis (tomm20), +# predicting against a549-mantis-tomm20-zikv test. +# Best val-loss checkpoint epoch=139-step=22120 (loss/validate=0.71857), from the +# ws8500 training run (save dir suffix _ws8500; model_name stays +# fcmae_vscyto3d_pretrained). Last epoch was 150 but ep139 is best-val. +# Both iPSC and a549 manifests use `tomm20`; targets/mito_tomm20.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_zikv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: a549_mantis + predict_set: a549_mantis_tomm20_zikv + model_name: fcmae_vscyto3d_pretrained + experiment_id: mito__a549_mantis__fcmae_vscyto3d_pretrained__a549_mantis_tomm20_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/tomm20/fcmae_vscyto3d_pretrained_ws8500/checkpoints/epoch=139-step=22120.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_pretrained_a549trained_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_TOMM20_A549TR_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..dee598e4f --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,47 @@ +# FCMAE_VSCyto3D_Pretrained (VSCyto3D) predict: mito trained on a549_mantis (tomm20), +# predicting against ipsc_confocal test_cropped. +# Best val-loss checkpoint epoch=139-step=22120 (loss/validate=0.71857), from the +# ws8500 training run (save dir suffix _ws8500; model_name stays +# fcmae_vscyto3d_pretrained). Last epoch was 150 but ep139 is best-val. +# Both iPSC and a549 manifests use `tomm20`; targets/mito_tomm20.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: a549_mantis + predict_set: ipsc_confocal + model_name: fcmae_vscyto3d_pretrained + experiment_id: mito__a549_mantis__fcmae_vscyto3d_pretrained__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/tomm20/fcmae_vscyto3d_pretrained_ws8500/checkpoints/epoch=139-step=22120.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_fcmae_vscyto3d_pretrained_a549trained.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_TOMM20_A549TR_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/a549_mantis/train.yml new file mode 100644 index 000000000..f19f0f1a6 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/a549_mantis/train.yml @@ -0,0 +1,55 @@ +# FCMAE-class (FullyConvolutionalMAE, pretraining=False) with FCMAE- +# pretrained encoder init on mito/TOMM20. Companion to +# fcmae_vscyto3d_scratch.yml — the two leaves are identical except this +# one loads encoder weights from the published VSCyto3D FCMAE ckpt +# (400 ep on HEK + A549 + iPSC phase data). Mirrors +# er/ipsc_confocal/fcmae_vscyto3d_pretrained.yml. +base: + - ../../../_internal/shared/model/train_sets/a549_mantis.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/data_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + train_set: a549_mantis + model_name: fcmae_vscyto3d_pretrained + experiment_id: mito__a549_mantis__fcmae_vscyto3d_pretrained + +model: + init_args: + # Load only the encoder from the canonical VSCyto3D FCMAE ckpt — + # decoder/head stay at fresh init. Matches vs_test/finetune_3d.py:247. + encoder_only: true + ckpt_path: /hpc/projects/virtual_staining/models/mehta-lab/VSCyto3D/fcmae.ckpt + +trainer: + logger: + init_args: + name: FCMAE_VSCyto3D_Pretrained_A549_TOMM20_ws8500 + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/tomm20/fcmae_vscyto3d_pretrained_ws8500 + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/tomm20/fcmae_vscyto3d_pretrained_ws8500/checkpoints + +data: + init_args: + # A549 pooled store + target_channel — no resolver in this train_set. + target_channel: Structure + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/TOMM20_all.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_A549_TOMM20_ws8500 + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/tomm20/fcmae_vscyto3d_pretrained_ws8500 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 100644 index 000000000..90120fc4d --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: Mitochondria (TOMM20) predicted by FCMAE_VSCyto3D_Pretrained on a549-mantis-tomm20-denv. +defaults: + - override /target: mito_tomm20 + - override /predict_set: a549_mantis_tomm20_denv + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_pretrained__tomm20_denv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_tomm20_fcmae_vscyto3d_pretrained__tomm20_denv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 100644 index 000000000..6b6619ebf --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: Mitochondria (TOMM20) predicted by FCMAE_VSCyto3D_Pretrained on a549-mantis-tomm20-mock. +defaults: + - override /target: mito_tomm20 + - override /predict_set: a549_mantis_tomm20_mock + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_pretrained__tomm20_mock.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_tomm20_fcmae_vscyto3d_pretrained__tomm20_mock diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 100644 index 000000000..d20c1d11d --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: Mitochondria (TOMM20) predicted by FCMAE_VSCyto3D_Pretrained on a549-mantis-tomm20-zikv. +defaults: + - override /target: mito_tomm20 + - override /predict_set: a549_mantis_tomm20_zikv + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_pretrained__tomm20_zikv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_tomm20_fcmae_vscyto3d_pretrained__tomm20_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..9690f1f06 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_denv.yml @@ -0,0 +1,47 @@ +# FCMAE_VSCyto3D_Pretrained predict: mito (TOMM20) trained on iPSC, +# predicting against a549_mantis_tomm20_denv test. +# +# Pinned to best-val checkpoint from training run J31523064 (ws8500 +# variant; val 0.5543, epoch 58). Run cancelled at epoch 92 — val +# plateaued at epoch 58 and never recovered (~34 epochs without +# improvement, drifting up in last 5 epochs). +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_denv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: ipsc_confocal + predict_set: a549_mantis_tomm20_denv + model_name: fcmae_vscyto3d_pretrained + experiment_id: mito__ipsc_confocal__fcmae_vscyto3d_pretrained__a549_mantis_tomm20_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/tomm20/fcmae_vscyto3d_pretrained_ws8500/checkpoints/epoch=58-step=18408.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_pretrained__tomm20_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_TOMM20_ON_A549_tomm20_denv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..3f50d6514 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_mock.yml @@ -0,0 +1,47 @@ +# FCMAE_VSCyto3D_Pretrained predict: mito (TOMM20) trained on iPSC, +# predicting against a549_mantis_tomm20_mock test. +# +# Pinned to best-val checkpoint from training run J31523064 (ws8500 +# variant; val 0.5543, epoch 58). Run cancelled at epoch 92 — val +# plateaued at epoch 58 and never recovered (~34 epochs without +# improvement, drifting up in last 5 epochs). +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_mock.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: ipsc_confocal + predict_set: a549_mantis_tomm20_mock + model_name: fcmae_vscyto3d_pretrained + experiment_id: mito__ipsc_confocal__fcmae_vscyto3d_pretrained__a549_mantis_tomm20_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/tomm20/fcmae_vscyto3d_pretrained_ws8500/checkpoints/epoch=58-step=18408.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_pretrained__tomm20_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_TOMM20_ON_A549_tomm20_mock + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..010cd8c66 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_zikv.yml @@ -0,0 +1,47 @@ +# FCMAE_VSCyto3D_Pretrained predict: mito (TOMM20) trained on iPSC, +# predicting against a549_mantis_tomm20_zikv test. +# +# Pinned to best-val checkpoint from training run J31523064 (ws8500 +# variant; val 0.5543, epoch 58). Run cancelled at epoch 92 — val +# plateaued at epoch 58 and never recovered (~34 epochs without +# improvement, drifting up in last 5 epochs). +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_zikv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: ipsc_confocal + predict_set: a549_mantis_tomm20_zikv + model_name: fcmae_vscyto3d_pretrained + experiment_id: mito__ipsc_confocal__fcmae_vscyto3d_pretrained__a549_mantis_tomm20_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/tomm20/fcmae_vscyto3d_pretrained_ws8500/checkpoints/epoch=58-step=18408.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_pretrained__tomm20_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_TOMM20_ON_A549_tomm20_zikv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__ipsc_confocal.yml new file mode 100644 index 000000000..d5d15fc0e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__ipsc_confocal.yml @@ -0,0 +1,46 @@ +# FCMAE_VSCyto3D_Pretrained predict: mito (TOMM20) against ipsc_confocal test_cropped. +# +# Pinned to best-val checkpoint from training run J31523064 (ws8500 +# variant; val 0.5543, epoch 58). Run cancelled at epoch 92 — val +# plateaued at epoch 58 and never recovered (~34 epochs without +# improvement, drifting up in last 5 epochs). +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: fcmae_vscyto3d_pretrained + experiment_id: mito__ipsc_confocal__fcmae_vscyto3d_pretrained__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/tomm20/fcmae_vscyto3d_pretrained_ws8500/checkpoints/epoch=58-step=18408.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_fcmae_vscyto3d_pretrained.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_TOMM20 + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/train.yml new file mode 100644 index 000000000..57c1002ff --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/ipsc_confocal/train.yml @@ -0,0 +1,49 @@ +# FCMAE-class (FullyConvolutionalMAE, pretraining=False) with FCMAE- +# pretrained encoder init on mito/TOMM20. Companion to +# fcmae_vscyto3d_scratch.yml — the two leaves are identical except this +# one loads encoder weights from the published VSCyto3D FCMAE ckpt +# (400 ep on HEK + A549 + iPSC phase data). Mirrors +# er/ipsc_confocal/fcmae_vscyto3d_pretrained.yml. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/data_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + train_set: ipsc_confocal + model_name: fcmae_vscyto3d_pretrained + experiment_id: mito__ipsc_confocal__fcmae_vscyto3d_pretrained + +model: + init_args: + # Load only the encoder from the canonical VSCyto3D FCMAE ckpt — + # decoder/head stay at fresh init. Matches vs_test/finetune_3d.py:247. + encoder_only: true + ckpt_path: /hpc/projects/virtual_staining/models/mehta-lab/VSCyto3D/fcmae.ckpt + +trainer: + logger: + init_args: + name: FCMAE_VSCyto3D_Pretrained_iPSC_TOMM20_ws8500 + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/tomm20/fcmae_vscyto3d_pretrained_ws8500 + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/tomm20/fcmae_vscyto3d_pretrained_ws8500/checkpoints + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_TOMM20_ws8500 + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/tomm20/fcmae_vscyto3d_pretrained_ws8500 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..502dffeea --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,46 @@ +# FCMAE_VSCyto3D_Pretrained predict: Mito trained on joint iPSC+A549, +# predicting against a549-mantis-tomm20-denv test. +# Best val-loss checkpoint from J31910345 (epoch 43, val 0.6016). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# Both iPSC and a549 manifests use `tomm20`; targets/mito_tomm20.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_denv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_tomm20_denv + model_name: fcmae_vscyto3d_pretrained + experiment_id: mito__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_pretrained__a549_mantis_tomm20_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/tomm20/fcmae_vscyto3d_pretrained_ws8500/checkpoints/epoch=43-step=21120.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_pretrained_jointtrained_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_TOMM20_JOINTTR_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..7ef48b025 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,46 @@ +# FCMAE_VSCyto3D_Pretrained predict: Mito trained on joint iPSC+A549, +# predicting against a549-mantis-tomm20-mock test. +# Best val-loss checkpoint from J31910345 (epoch 43, val 0.6016). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# Both iPSC and a549 manifests use `tomm20`; targets/mito_tomm20.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_mock.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_tomm20_mock + model_name: fcmae_vscyto3d_pretrained + experiment_id: mito__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_pretrained__a549_mantis_tomm20_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/tomm20/fcmae_vscyto3d_pretrained_ws8500/checkpoints/epoch=43-step=21120.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_pretrained_jointtrained_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_TOMM20_JOINTTR_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..f7444295d --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,46 @@ +# FCMAE_VSCyto3D_Pretrained predict: Mito trained on joint iPSC+A549, +# predicting against a549-mantis-tomm20-zikv test. +# Best val-loss checkpoint from J31910345 (epoch 43, val 0.6016). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# Both iPSC and a549 manifests use `tomm20`; targets/mito_tomm20.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_zikv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_tomm20_zikv + model_name: fcmae_vscyto3d_pretrained + experiment_id: mito__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_pretrained__a549_mantis_tomm20_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/tomm20/fcmae_vscyto3d_pretrained_ws8500/checkpoints/epoch=43-step=21120.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_pretrained_jointtrained_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_TOMM20_JOINTTR_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..0ed74c32d --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,51 @@ +# FCMAE_VSCyto3D_Pretrained (VSCyto3D) predict: Mito (TOMM20) trained on joint +# iPSC+A549, predicting against ipsc_confocal test_cropped. +# Best val-loss checkpoint from J31910345 (epoch 43, loss/validate=0.6016). +# Wandb run 20260502-150402_FCMAE_VSCyto3D_Pretrained_JOINT_TOMM20_ws8500 +# (TIMEOUT @ 4d / 62,719 steps; final val 0.6080 — drifted up from ep43 best). +# ws8500 = 8,500-step warmup variant. +# Both iPSC and a549 manifests use `tomm20`; targets/mito_tomm20.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: ipsc_confocal + model_name: fcmae_vscyto3d_pretrained + experiment_id: mito__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_pretrained__ipsc_confocal + +model: + init_args: + # ckpt lives under the _ws8500 training-output subdir; config namespace + # uses fcmae_vscyto3d_pretrained (no _ws8500 suffix) for consistency with + # iPSC + a549 single-set predict configs. + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/tomm20/fcmae_vscyto3d_pretrained_ws8500/checkpoints/epoch=43-step=21120.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_fcmae_vscyto3d_pretrained_jointtrained.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_TOMM20_JOINTTR_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/train.yml new file mode 100644 index 000000000..d7241e6bd --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/train.yml @@ -0,0 +1,154 @@ +# FCMAE-class (FullyConvolutionalMAE, pretraining=False) with FCMAE- +# pretrained encoder init on mito (TOMM20) — joint +# ipsc_confocal + a549_mantis pooled. Companion to +# fcmae_vscyto3d_scratch joint leaf — the two are identical except +# this one loads encoder weights from the published VSCyto3D FCMAE +# ckpt (400 ep on HEK + A549 + iPSC phase data). Mirrors +# mito/fcmae_vscyto3d_pretrained/ipsc_confocal/train.yml on +# the joint train_set. +# +# Joint leaf per Stage 7 of A549_EXPANSION_ROADMAP.md. Uses +# BatchedConcatDataModule with two explicit HCSDataModule children +# (no benchmark.dataset_ref — joint leaves bypass the single-dataset +# resolver). Only model_overlays/fcmae_vscyto3d_fit.yml is composed; +# the data block is authored inline because joint hparams live on +# the children. +# +# Topology: 4-GPU DDP (inherited from +# model_overlays/fcmae_vscyto3d_fit.yml's ddp_4gpu base; the overlay +# also pins strategy=ddp_find_unused_parameters_true because +# FullyConvolutionalMAE has decoder/head params that only receive +# gradients on some forward paths). +base: + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + gene: TOMM20 + target: mito + target_id: mito_tomm20 + train_set: joint_ipsc_confocal_a549_mantis + model_name: fcmae_vscyto3d_pretrained + experiment_id: mito__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_pretrained + +model: + init_args: + # Load only the encoder from the canonical VSCyto3D FCMAE ckpt — + # decoder/head stay at fresh init. Matches vs_test/finetune_3d.py:247. + encoder_only: true + ckpt_path: /hpc/projects/virtual_staining/models/mehta-lab/VSCyto3D/fcmae.ckpt + +trainer: + logger: + init_args: + name: FCMAE_VSCyto3D_Pretrained_JOINT_TOMM20_ws8500 + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/tomm20/fcmae_vscyto3d_pretrained_ws8500 + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/tomm20/fcmae_vscyto3d_pretrained_ws8500/checkpoints + +_hcs_init_args: &hcs_init_args + source_channel: Phase3D + target_channel: Structure + z_window_size: 20 + # See nucleus/fnet3d_paper/joint_*/train.yml for the rationale: joint + # mode does not divide batch_size by num_samples, so 8 * 4 = 32 GPU + # samples per DDP rank matches single-set effective batch. + batch_size: 8 + num_workers: 4 + yx_patch_size: [384, 384] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Structure] + level: fov_statistics + subtrahend: median + divisor: iqr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Structure] + w_key: Structure + spatial_size: [20, 600, 600] + num_samples: 4 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + # ipsc_confocal — aics-hipsc TOMM20 train store + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/TOMM20.zarr + # a549_mantis — pooled TOMM20 all-conditions train store + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/TOMM20_all.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_JOINT_TOMM20_ws8500 + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/tomm20/fcmae_vscyto3d_pretrained_ws8500 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..6e7673faa --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,48 @@ +# FCMAE_VSCyto3D_Scratch (UNeXt2) predict: mito trained on a549_mantis (tomm20), +# predicting against a549-mantis-tomm20-denv test. +# Pinned to checkpoint epoch=113-step=18012 (val 0.7329 per resume's +# re-evaluation; wandb metrics for the original run are unrecoverable due +# to a run-id collision with the SEC61B training). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# Both iPSC and a549 manifests use `tomm20`; targets/mito_tomm20.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_denv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: a549_mantis + predict_set: a549_mantis_tomm20_denv + model_name: fcmae_vscyto3d_scratch + experiment_id: mito__a549_mantis__fcmae_vscyto3d_scratch__a549_mantis_tomm20_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/tomm20/fcmae_vscyto3d_scratch/checkpoints/epoch=113-step=18012.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_scratch_a549trained_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_TOMM20_A549TR_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..0d387ad05 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,48 @@ +# FCMAE_VSCyto3D_Scratch (UNeXt2) predict: mito trained on a549_mantis (tomm20), +# predicting against a549-mantis-tomm20-mock test. +# Pinned to checkpoint epoch=113-step=18012 (val 0.7329 per resume's +# re-evaluation; wandb metrics for the original run are unrecoverable due +# to a run-id collision with the SEC61B training). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# Both iPSC and a549 manifests use `tomm20`; targets/mito_tomm20.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_mock.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: a549_mantis + predict_set: a549_mantis_tomm20_mock + model_name: fcmae_vscyto3d_scratch + experiment_id: mito__a549_mantis__fcmae_vscyto3d_scratch__a549_mantis_tomm20_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/tomm20/fcmae_vscyto3d_scratch/checkpoints/epoch=113-step=18012.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_scratch_a549trained_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_TOMM20_A549TR_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..f6c59d636 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,48 @@ +# FCMAE_VSCyto3D_Scratch (UNeXt2) predict: mito trained on a549_mantis (tomm20), +# predicting against a549-mantis-tomm20-zikv test. +# Pinned to checkpoint epoch=113-step=18012 (val 0.7329 per resume's +# re-evaluation; wandb metrics for the original run are unrecoverable due +# to a run-id collision with the SEC61B training). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# Both iPSC and a549 manifests use `tomm20`; targets/mito_tomm20.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_zikv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: a549_mantis + predict_set: a549_mantis_tomm20_zikv + model_name: fcmae_vscyto3d_scratch + experiment_id: mito__a549_mantis__fcmae_vscyto3d_scratch__a549_mantis_tomm20_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/tomm20/fcmae_vscyto3d_scratch/checkpoints/epoch=113-step=18012.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_scratch_a549trained_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_TOMM20_A549TR_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..ec3830537 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,53 @@ +# FCMAE_VSCyto3D_Scratch (UNeXt2) predict: mito trained on a549_mantis (tomm20), +# predicting against ipsc_confocal test_cropped. +# Pinned to checkpoint epoch=113-step=18012 from the original training +# (J31910360, wandb display 20260502-204546_FCMAE_VSCyto3D_Scratch_A549_TOMM20). +# Best-val ckpt per resume's re-evaluation: 0.7329 (ep113) — virtually tied +# with ep118 at 0.7327. NOTE: the wandb run id `20260502-204536` collided with +# the simultaneous SEC61B training (J31910346 on the same node gpu-f-5); +# wandb's history values for that run are SEC61B's metrics, not TOMM20's, so +# the original-training's true val trajectory is not recoverable from wandb. +# The 0.7327/0.7329 figures come from the resume Lightning trainer's local +# best_k_models block in last.ckpt — that is the only TOMM20-pipeline number +# we have. Both iPSC and a549 manifests use `tomm20`; targets/mito_tomm20.yml +# handles both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: a549_mantis + predict_set: ipsc_confocal + model_name: fcmae_vscyto3d_scratch + experiment_id: mito__a549_mantis__fcmae_vscyto3d_scratch__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/tomm20/fcmae_vscyto3d_scratch/checkpoints/epoch=113-step=18012.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_fcmae_vscyto3d_scratch_a549trained.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_TOMM20_A549TR_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/a549_mantis/train.yml new file mode 100644 index 000000000..98e99984a --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/a549_mantis/train.yml @@ -0,0 +1,46 @@ +# FCMAE-class (FullyConvolutionalMAE, pretraining=False) random-init +# baseline on mito/TOMM20. Scratch control for the pretrained counterpart — +# the two leaves are identical except this one does NOT load pretrained +# encoder weights. Mirrors er/ipsc_confocal/fcmae_vscyto3d_scratch.yml. +base: + - ../../../_internal/shared/model/train_sets/a549_mantis.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/data_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + train_set: a549_mantis + model_name: fcmae_vscyto3d_scratch + experiment_id: mito__a549_mantis__fcmae_vscyto3d_scratch + +trainer: + logger: + init_args: + name: FCMAE_VSCyto3D_Scratch_A549_TOMM20 + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/tomm20/fcmae_vscyto3d_scratch + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/tomm20/fcmae_vscyto3d_scratch/checkpoints + +data: + init_args: + # A549 pooled store + target_channel — no resolver in this train_set. + target_channel: Structure + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/TOMM20_all.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_A549_TOMM20 + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/tomm20/fcmae_vscyto3d_scratch diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 100644 index 000000000..a0e9ba367 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: Mitochondria (TOMM20) predicted by FCMAE_VSCyto3D_Scratch on a549-mantis-tomm20-denv. +defaults: + - override /target: mito_tomm20 + - override /predict_set: a549_mantis_tomm20_denv + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_scratch__tomm20_denv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_tomm20_fcmae_vscyto3d_scratch__tomm20_denv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 100644 index 000000000..2087a8dde --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: Mitochondria (TOMM20) predicted by FCMAE_VSCyto3D_Scratch on a549-mantis-tomm20-mock. +defaults: + - override /target: mito_tomm20 + - override /predict_set: a549_mantis_tomm20_mock + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_scratch__tomm20_mock.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_tomm20_fcmae_vscyto3d_scratch__tomm20_mock diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 100644 index 000000000..70b6ad214 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: Mitochondria (TOMM20) predicted by FCMAE_VSCyto3D_Scratch on a549-mantis-tomm20-zikv. +defaults: + - override /target: mito_tomm20 + - override /predict_set: a549_mantis_tomm20_zikv + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_scratch__tomm20_zikv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_tomm20_fcmae_vscyto3d_scratch__tomm20_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..c77d7afc7 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_denv.yml @@ -0,0 +1,46 @@ +# FCMAE_VSCyto3D_Scratch predict: mito (TOMM20) trained on iPSC, +# predicting against a549_mantis_tomm20_denv test. +# +# Pinned to best-val checkpoint from training run J31475715 +# (val 0.5527, epoch 69). Run cancelled at epoch 164 — val plateaued +# at epoch ~51 and never recovered, so later epochs are not better. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_denv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: ipsc_confocal + predict_set: a549_mantis_tomm20_denv + model_name: fcmae_vscyto3d_scratch + experiment_id: mito__ipsc_confocal__fcmae_vscyto3d_scratch__a549_mantis_tomm20_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/tomm20/fcmae_vscyto3d_scratch/checkpoints/epoch=69-step=21840.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_scratch__tomm20_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_TOMM20_ON_A549_tomm20_denv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..d56fec22a --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_mock.yml @@ -0,0 +1,46 @@ +# FCMAE_VSCyto3D_Scratch predict: mito (TOMM20) trained on iPSC, +# predicting against a549_mantis_tomm20_mock test. +# +# Pinned to best-val checkpoint from training run J31475715 +# (val 0.5527, epoch 69). Run cancelled at epoch 164 — val plateaued +# at epoch ~51 and never recovered, so later epochs are not better. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_mock.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: ipsc_confocal + predict_set: a549_mantis_tomm20_mock + model_name: fcmae_vscyto3d_scratch + experiment_id: mito__ipsc_confocal__fcmae_vscyto3d_scratch__a549_mantis_tomm20_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/tomm20/fcmae_vscyto3d_scratch/checkpoints/epoch=69-step=21840.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_scratch__tomm20_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_TOMM20_ON_A549_tomm20_mock + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..eb1867898 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_zikv.yml @@ -0,0 +1,46 @@ +# FCMAE_VSCyto3D_Scratch predict: mito (TOMM20) trained on iPSC, +# predicting against a549_mantis_tomm20_zikv test. +# +# Pinned to best-val checkpoint from training run J31475715 +# (val 0.5527, epoch 69). Run cancelled at epoch 164 — val plateaued +# at epoch ~51 and never recovered, so later epochs are not better. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_zikv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: ipsc_confocal + predict_set: a549_mantis_tomm20_zikv + model_name: fcmae_vscyto3d_scratch + experiment_id: mito__ipsc_confocal__fcmae_vscyto3d_scratch__a549_mantis_tomm20_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/tomm20/fcmae_vscyto3d_scratch/checkpoints/epoch=69-step=21840.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_scratch__tomm20_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_TOMM20_ON_A549_tomm20_zikv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/ipsc_confocal/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/ipsc_confocal/predict__ipsc_confocal.yml new file mode 100644 index 000000000..f422eb471 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/ipsc_confocal/predict__ipsc_confocal.yml @@ -0,0 +1,45 @@ +# FCMAE_VSCyto3D_Scratch predict: mito (TOMM20) against ipsc_confocal test_cropped. +# +# Pinned to best-val checkpoint from training run J31475715 +# (val 0.5527, epoch 69). Run cancelled at epoch 164 — val plateaued +# at epoch ~51 and never recovered, so later epochs are not better. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: fcmae_vscyto3d_scratch + experiment_id: mito__ipsc_confocal__fcmae_vscyto3d_scratch__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/tomm20/fcmae_vscyto3d_scratch/checkpoints/epoch=69-step=21840.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_fcmae_vscyto3d_scratch.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_TOMM20 + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/ipsc_confocal/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/ipsc_confocal/train.yml new file mode 100644 index 000000000..5f53c3e9d --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/ipsc_confocal/train.yml @@ -0,0 +1,40 @@ +# FCMAE-class (FullyConvolutionalMAE, pretraining=False) random-init +# baseline on mito/TOMM20. Scratch control for the pretrained counterpart — +# the two leaves are identical except this one does NOT load pretrained +# encoder weights. Mirrors er/ipsc_confocal/fcmae_vscyto3d_scratch.yml. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/data_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + train_set: ipsc_confocal + model_name: fcmae_vscyto3d_scratch + experiment_id: mito__ipsc_confocal__fcmae_vscyto3d_scratch + +trainer: + logger: + init_args: + name: FCMAE_VSCyto3D_Scratch_iPSC_TOMM20 + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/tomm20/fcmae_vscyto3d_scratch + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/tomm20/fcmae_vscyto3d_scratch/checkpoints + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_TOMM20 + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/tomm20/fcmae_vscyto3d_scratch diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..3e35ecc64 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,46 @@ +# FCMAE_VSCyto3D_Scratch predict: Mito trained on joint iPSC+A549, +# predicting against a549-mantis-tomm20-denv test. +# Best val-loss checkpoint from J31910343 (epoch 63, val 0.6063). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# Both iPSC and a549 manifests use `tomm20`; targets/mito_tomm20.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_denv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_tomm20_denv + model_name: fcmae_vscyto3d_scratch + experiment_id: mito__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_scratch__a549_mantis_tomm20_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/tomm20/fcmae_vscyto3d_scratch/checkpoints/epoch=63-step=30720.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_scratch_jointtrained_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_TOMM20_JOINTTR_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..a13a27eb3 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,46 @@ +# FCMAE_VSCyto3D_Scratch predict: Mito trained on joint iPSC+A549, +# predicting against a549-mantis-tomm20-mock test. +# Best val-loss checkpoint from J31910343 (epoch 63, val 0.6063). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# Both iPSC and a549 manifests use `tomm20`; targets/mito_tomm20.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_mock.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_tomm20_mock + model_name: fcmae_vscyto3d_scratch + experiment_id: mito__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_scratch__a549_mantis_tomm20_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/tomm20/fcmae_vscyto3d_scratch/checkpoints/epoch=63-step=30720.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_scratch_jointtrained_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_TOMM20_JOINTTR_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..89839646a --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,46 @@ +# FCMAE_VSCyto3D_Scratch predict: Mito trained on joint iPSC+A549, +# predicting against a549-mantis-tomm20-zikv test. +# Best val-loss checkpoint from J31910343 (epoch 63, val 0.6063). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# Both iPSC and a549 manifests use `tomm20`; targets/mito_tomm20.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_zikv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_tomm20_zikv + model_name: fcmae_vscyto3d_scratch + experiment_id: mito__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_scratch__a549_mantis_tomm20_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/tomm20/fcmae_vscyto3d_scratch/checkpoints/epoch=63-step=30720.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fcmae_vscyto3d_scratch_jointtrained_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_TOMM20_JOINTTR_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..14a65fd16 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,47 @@ +# FCMAE_VSCyto3D_Scratch predict: Mito (TOMM20) trained on joint iPSC+A549, +# predicting against ipsc_confocal test_cropped. +# Best val-loss checkpoint from J31910343 (epoch 63, loss/validate=0.6063). +# Wandb run 20260502-143633_FCMAE_VSCyto3D_Scratch_JOINT_TOMM20 (TIMEOUT @ 4d / +# 62,309 steps; final val 0.6110 — drifted up from ep63 best). +# Both iPSC and a549 manifests use `tomm20`; targets/mito_tomm20.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: ipsc_confocal + model_name: fcmae_vscyto3d_scratch + experiment_id: mito__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_scratch__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/tomm20/fcmae_vscyto3d_scratch/checkpoints/epoch=63-step=30720.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_fcmae_vscyto3d_scratch_jointtrained.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_TOMM20_JOINTTR_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/train.yml new file mode 100644 index 000000000..27bb8d0e3 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/train.yml @@ -0,0 +1,142 @@ +# FCMAE-class (FullyConvolutionalMAE, pretraining=False) random-init +# baseline on mito (TOMM20) — joint ipsc_confocal + +# a549_mantis pooled. Scratch control for the pretrained counterpart +# — the two leaves are identical except this one does NOT load +# pretrained encoder weights. Mirrors +# mito/fcmae_vscyto3d_scratch/ipsc_confocal/train.yml on the +# joint train_set. +# +# Joint leaf per Stage 7 of A549_EXPANSION_ROADMAP.md. +# BatchedConcatDataModule + two explicit HCSDataModule children; +# only model_overlays/fcmae_vscyto3d_fit.yml is composed; data +# block inline. +# +# Topology: 4-GPU DDP +# (strategy=ddp_find_unused_parameters_true inherited from +# model_overlays/fcmae_vscyto3d_fit.yml). +base: + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + gene: TOMM20 + target: mito + target_id: mito_tomm20 + train_set: joint_ipsc_confocal_a549_mantis + model_name: fcmae_vscyto3d_scratch + experiment_id: mito__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_scratch + +trainer: + logger: + init_args: + name: FCMAE_VSCyto3D_Scratch_JOINT_TOMM20 + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/tomm20/fcmae_vscyto3d_scratch + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/tomm20/fcmae_vscyto3d_scratch/checkpoints + +_hcs_init_args: &hcs_init_args + source_channel: Phase3D + target_channel: Structure + z_window_size: 20 + # See nucleus/fnet3d_paper/joint_*/train.yml for the rationale: joint + # mode does not divide batch_size by num_samples, so 8 * 4 = 32 GPU + # samples per DDP rank matches single-set effective batch. + batch_size: 8 + num_workers: 4 + yx_patch_size: [384, 384] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Structure] + level: fov_statistics + subtrahend: median + divisor: iqr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Structure] + w_key: Structure + spatial_size: [20, 600, 600] + num_samples: 4 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + # ipsc_confocal — aics-hipsc TOMM20 train store + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/TOMM20.zarr + # a549_mantis — pooled TOMM20 all-conditions train store + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/TOMM20_all.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_JOINT_TOMM20 + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/tomm20/fcmae_vscyto3d_scratch diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..218578537 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,46 @@ +# FNet3D paper-baseline predict: mito trained on a549_mantis (tomm20), +# predicting against a549-mantis-tomm20-denv test. +# Best val-loss checkpoint from job 31965119 (epoch 248, val 0.8291). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# Both iPSC and a549 manifests use `tomm20`; targets/mito_tomm20.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_denv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: a549_mantis + predict_set: a549_mantis_tomm20_denv + model_name: fnet3d_paper + experiment_id: mito__a549_mantis__fnet3d_paper__a549_mantis_tomm20_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/tomm20/fnet3d_paper/checkpoints/epoch=248-step=126990.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fnet3d_paper_a549trained_denv.zarr + +launcher: + job_name: FNet3DPaper_PRED_TOMM20_A549TR_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..cc0c8b96c --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,46 @@ +# FNet3D paper-baseline predict: mito trained on a549_mantis (tomm20), +# predicting against a549-mantis-tomm20-mock test. +# Best val-loss checkpoint from job 31965119 (epoch 248, val 0.8291). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# Both iPSC and a549 manifests use `tomm20`; targets/mito_tomm20.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_mock.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: a549_mantis + predict_set: a549_mantis_tomm20_mock + model_name: fnet3d_paper + experiment_id: mito__a549_mantis__fnet3d_paper__a549_mantis_tomm20_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/tomm20/fnet3d_paper/checkpoints/epoch=248-step=126990.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fnet3d_paper_a549trained_mock.zarr + +launcher: + job_name: FNet3DPaper_PRED_TOMM20_A549TR_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..6313f1385 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,46 @@ +# FNet3D paper-baseline predict: mito trained on a549_mantis (tomm20), +# predicting against a549-mantis-tomm20-zikv test. +# Best val-loss checkpoint from job 31965119 (epoch 248, val 0.8291). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# Both iPSC and a549 manifests use `tomm20`; targets/mito_tomm20.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_zikv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: a549_mantis + predict_set: a549_mantis_tomm20_zikv + model_name: fnet3d_paper + experiment_id: mito__a549_mantis__fnet3d_paper__a549_mantis_tomm20_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/tomm20/fnet3d_paper/checkpoints/epoch=248-step=126990.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fnet3d_paper_a549trained_zikv.zarr + +launcher: + job_name: FNet3DPaper_PRED_TOMM20_A549TR_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..727da6266 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,47 @@ +# FNet3D paper-baseline predict: mito trained on a549_mantis (tomm20), +# predicting against ipsc_confocal test_cropped. +# Best val-loss checkpoint from job 31965119 (epoch 248, val 0.8291). +# Wandb run 20260503-193857_FNet3D_A549_TOMM20_paper (state=finished, +# 392 ep / 199,999 steps; final val 0.9493 — drifted up from ep248 best). +# Both iPSC and a549 manifests use `tomm20`; targets/mito_tomm20.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: a549_mantis + predict_set: ipsc_confocal + model_name: fnet3d_paper + experiment_id: mito__a549_mantis__fnet3d_paper__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/tomm20/fnet3d_paper/checkpoints/epoch=248-step=126990.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_fnet3d_paper_a549trained.zarr + +launcher: + job_name: FNet3DPaper_PRED_TOMM20_A549TR_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/a549_mantis/train.yml new file mode 100644 index 000000000..9750e6613 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/a549_mantis/train.yml @@ -0,0 +1,50 @@ +# FNet3D paper-baseline fit on mitochondria (TOMM20 marker) — A549 mantis-lightsheet pooled (mock + DENV + ZIKV). +# target_channel=Structure, so the overlay's default norms/augs apply unchanged. +base: + - ../../../_internal/shared/model/train_sets/a549_mantis.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/data_overlays/fnet3d_paper_fit.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_gpu_any_long.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + train_set: a549_mantis + model_name: fnet3d_paper + experiment_id: mito__a549_mantis__fnet3d_paper + +trainer: + logger: + init_args: + name: FNet3D_A549_TOMM20_paper + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/tomm20/fnet3d_paper + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/tomm20/fnet3d_paper/checkpoints + +data: + init_args: + # A549 pooled store + target_channel — no resolver in this train_set. + target_channel: Structure + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/TOMM20_all.zarr + +launcher: + job_name: FNet3DPaper_A549_TOMM20 + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/tomm20/fnet3d_paper + # 512G to match the shared headroom convention across the fnet3d + # leaves on a549/joint workloads. mmap_preload after the BasicIndexer + # fix peaks at ~75 GB for TOMM20_all alone (single-set); 512G gives + # generous headroom. + sbatch: + mem: "512G" diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 100644 index 000000000..08dc34786 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: Mitochondria (TOMM20) predicted by FNet3DPaper on a549-mantis-tomm20-denv. +defaults: + - override /target: mito_tomm20 + - override /predict_set: a549_mantis_tomm20_denv + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fnet3d_paper__tomm20_denv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_tomm20_fnet3d_paper__tomm20_denv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 100644 index 000000000..8ba1ca031 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: Mitochondria (TOMM20) predicted by FNet3DPaper on a549-mantis-tomm20-mock. +defaults: + - override /target: mito_tomm20 + - override /predict_set: a549_mantis_tomm20_mock + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fnet3d_paper__tomm20_mock.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_tomm20_fnet3d_paper__tomm20_mock diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 100644 index 000000000..5ef0baaf4 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: Mitochondria (TOMM20) predicted by FNet3DPaper on a549-mantis-tomm20-zikv. +defaults: + - override /target: mito_tomm20 + - override /predict_set: a549_mantis_tomm20_zikv + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fnet3d_paper__tomm20_zikv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_tomm20_fnet3d_paper__tomm20_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/ipsc_confocal/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/ipsc_confocal/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..4218a6e63 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/ipsc_confocal/predict__a549_mantis_denv.yml @@ -0,0 +1,42 @@ +# FNet3D paper-baseline predict: mito (TOMM20) trained on iPSC, predicting against a549_mantis_tomm20_denv test. +# Same iPSC best val-loss checkpoint as predict__ipsc_confocal.yml (epoch 215, loss/validate=0.7571). +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_denv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: ipsc_confocal + predict_set: a549_mantis_tomm20_denv + model_name: fnet3d_paper + experiment_id: mito__ipsc_confocal__fnet3d_paper__a549_mantis_tomm20_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/tomm20/fnet3d_paper/checkpoints/epoch=215-step=187056.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fnet3d_paper__tomm20_denv.zarr + +launcher: + job_name: FNet3DPaper_PRED_TOMM20_ON_A549_tomm20_denv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/ipsc_confocal/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/ipsc_confocal/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..bb75c65b0 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/ipsc_confocal/predict__a549_mantis_mock.yml @@ -0,0 +1,42 @@ +# FNet3D paper-baseline predict: mito (TOMM20) trained on iPSC, predicting against a549_mantis_tomm20_mock test. +# Same iPSC best val-loss checkpoint as predict__ipsc_confocal.yml (epoch 215, loss/validate=0.7571). +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_mock.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: ipsc_confocal + predict_set: a549_mantis_tomm20_mock + model_name: fnet3d_paper + experiment_id: mito__ipsc_confocal__fnet3d_paper__a549_mantis_tomm20_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/tomm20/fnet3d_paper/checkpoints/epoch=215-step=187056.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fnet3d_paper__tomm20_mock.zarr + +launcher: + job_name: FNet3DPaper_PRED_TOMM20_ON_A549_tomm20_mock + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/ipsc_confocal/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/ipsc_confocal/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..461dec08f --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/ipsc_confocal/predict__a549_mantis_zikv.yml @@ -0,0 +1,42 @@ +# FNet3D paper-baseline predict: mito (TOMM20) trained on iPSC, predicting against a549_mantis_tomm20_zikv test. +# Same iPSC best val-loss checkpoint as predict__ipsc_confocal.yml (epoch 215, loss/validate=0.7571). +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_zikv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: ipsc_confocal + predict_set: a549_mantis_tomm20_zikv + model_name: fnet3d_paper + experiment_id: mito__ipsc_confocal__fnet3d_paper__a549_mantis_tomm20_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/tomm20/fnet3d_paper/checkpoints/epoch=215-step=187056.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fnet3d_paper__tomm20_zikv.zarr + +launcher: + job_name: FNet3DPaper_PRED_TOMM20_ON_A549_tomm20_zikv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/ipsc_confocal/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/ipsc_confocal/predict__ipsc_confocal.yml new file mode 100644 index 000000000..4db0561c6 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/ipsc_confocal/predict__ipsc_confocal.yml @@ -0,0 +1,42 @@ +# FNet3D paper-baseline predict: mito (TOMM20) against ipsc_confocal test_cropped. +# Uses best val-loss checkpoint (epoch 215, loss/validate=0.7571). +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: fnet3d_paper + experiment_id: mito__ipsc_confocal__fnet3d_paper__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/tomm20/fnet3d_paper/checkpoints/epoch=215-step=187056.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_fnet3d_paper.zarr + +launcher: + job_name: FNet3DPaper_PRED_TOMM20 + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/ipsc_confocal/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/ipsc_confocal/train.yml new file mode 100644 index 000000000..db67cfe4c --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/ipsc_confocal/train.yml @@ -0,0 +1,38 @@ +# FNet3D paper-baseline fit on mitochondria (TOMM20 marker) — AICS iPSC confocal. +# target_channel=Structure, so the overlay's default norms/augs apply unchanged. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/data_overlays/fnet3d_paper_fit.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_gpu_any_long.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + train_set: ipsc_confocal + model_name: fnet3d_paper + experiment_id: mito__ipsc_confocal__fnet3d_paper + +trainer: + logger: + init_args: + name: FNet3D_iPSC_TOMM20_paper + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/tomm20/fnet3d_paper + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/tomm20/fnet3d_paper/checkpoints + +launcher: + job_name: FNet3DPaper_TOMM20 + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/tomm20/fnet3d_paper diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..88bf835b9 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,46 @@ +# FNet3D paper-baseline predict: mito trained on joint iPSC+A549, +# predicting against a549-mantis-tomm20-denv test. +# Best val-loss checkpoint from job 31962517 (epoch 132, val 0.6786). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# Both iPSC and a549 manifests use `tomm20`; targets/mito_tomm20.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_denv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_tomm20_denv + model_name: fnet3d_paper + experiment_id: mito__joint_ipsc_confocal_a549_mantis__fnet3d_paper__a549_mantis_tomm20_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/tomm20/fnet3d_paper/checkpoints/epoch=132-step=183008.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fnet3d_paper_jointtrained_denv.zarr + +launcher: + job_name: FNet3DPaper_PRED_TOMM20_JOINTTR_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..043456898 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,46 @@ +# FNet3D paper-baseline predict: mito trained on joint iPSC+A549, +# predicting against a549-mantis-tomm20-mock test. +# Best val-loss checkpoint from job 31962517 (epoch 132, val 0.6786). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# Both iPSC and a549 manifests use `tomm20`; targets/mito_tomm20.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_mock.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_tomm20_mock + model_name: fnet3d_paper + experiment_id: mito__joint_ipsc_confocal_a549_mantis__fnet3d_paper__a549_mantis_tomm20_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/tomm20/fnet3d_paper/checkpoints/epoch=132-step=183008.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fnet3d_paper_jointtrained_mock.zarr + +launcher: + job_name: FNet3DPaper_PRED_TOMM20_JOINTTR_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..6bd1ed5b1 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,46 @@ +# FNet3D paper-baseline predict: mito trained on joint iPSC+A549, +# predicting against a549-mantis-tomm20-zikv test. +# Best val-loss checkpoint from job 31962517 (epoch 132, val 0.6786). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# Both iPSC and a549 manifests use `tomm20`; targets/mito_tomm20.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_zikv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_tomm20_zikv + model_name: fnet3d_paper + experiment_id: mito__joint_ipsc_confocal_a549_mantis__fnet3d_paper__a549_mantis_tomm20_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/tomm20/fnet3d_paper/checkpoints/epoch=132-step=183008.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_fnet3d_paper_jointtrained_zikv.zarr + +launcher: + job_name: FNet3DPaper_PRED_TOMM20_JOINTTR_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..8a7c911ff --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,47 @@ +# FNet3D paper-baseline predict: mito trained on joint iPSC+A549, +# predicting against ipsc_confocal test_cropped. +# Best val-loss checkpoint from job 31962517 (epoch 132, val 0.6786). +# Wandb run 20260503-181128_FNet3D_JOINT_TOMM20_paper (state=finished, +# 145 ep / 199,999 steps; final val 0.7129 — drifted up from ep132 best). +# Both iPSC and a549 manifests use `tomm20`; targets/mito_tomm20.yml handles +# both natively, no dataset_ref override needed. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: ipsc_confocal + model_name: fnet3d_paper + experiment_id: mito__joint_ipsc_confocal_a549_mantis__fnet3d_paper__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/tomm20/fnet3d_paper/checkpoints/epoch=132-step=183008.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_fnet3d_paper_jointtrained.zarr + +launcher: + job_name: FNet3DPaper_PRED_TOMM20_JOINTTR_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/joint_ipsc_confocal_a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/joint_ipsc_confocal_a549_mantis/train.yml new file mode 100644 index 000000000..a4fa68f83 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/fnet3d_paper/joint_ipsc_confocal_a549_mantis/train.yml @@ -0,0 +1,123 @@ +# FNet3D paper-baseline fit on mito (TOMM20) — joint +# ipsc_confocal + a549_mantis pooled. Mirrors +# mito/fnet3d_paper/ipsc_confocal/train.yml on the joint +# train_set. +# +# Joint leaf per Stage 7 of A549_EXPANSION_ROADMAP.md. +# BatchedConcatDataModule + two explicit HCSDataModule children; +# only model_overlays/fnet3d_paper_fit.yml is composed; data block +# inline. Norms + 8-crops-per-FOV diverge from the CellDiff/UNetViT +# conventions: target channel uses mean/std (not median/iqr) and +# val augmentations are CPU CenterSpatialCropd on the raw keys (the +# baseline's training pipeline doesn't go through GPU val transforms). +# +# Topology: single GPU, any model, long wall — same as +# fnet3d_paper/ipsc_confocal/train.yml. The paper baseline is single-GPU +# and we keep that here so iPSC-only and joint runs are apples-to-apples. +base: + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_gpu_any_long.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + gene: TOMM20 + target: mito + target_id: mito_tomm20 + train_set: joint_ipsc_confocal_a549_mantis + model_name: fnet3d_paper + experiment_id: mito__joint_ipsc_confocal_a549_mantis__fnet3d_paper + +trainer: + logger: + init_args: + name: FNet3D_JOINT_TOMM20_paper + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/tomm20/fnet3d_paper + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/tomm20/fnet3d_paper/checkpoints + +_hcs_init_args: &hcs_init_args + source_channel: Phase3D + target_channel: Structure + z_window_size: 32 + # See nucleus/fnet3d_paper/joint_*/train.yml for the rationale: joint + # mode does not divide batch_size by num_samples (unlike single-set), + # so 6 * num_samples=8 = 48 GPU samples matches single-set effective. + batch_size: 6 + num_workers: 8 + yx_patch_size: [64, 64] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Structure] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Structure] + w_key: Structure + spatial_size: [32, 64, 64] + num_samples: 8 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandFlipd + init_args: + keys: [source, target] + spatial_axes: [1] + prob: 0.5 + - class_path: viscy_transforms.BatchedRandFlipd + init_args: + keys: [source, target] + spatial_axes: [2] + prob: 0.5 + val_augmentations: + - class_path: viscy_transforms.CenterSpatialCropd + init_args: + keys: [Phase3D, Structure] + roi_size: [32, 64, 64] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + # ipsc_confocal — aics-hipsc TOMM20 train store + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/TOMM20.zarr + # a549_mantis — pooled TOMM20 all-conditions train store + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/TOMM20_all.zarr + +launcher: + job_name: FNet3DPaper_JOINT_TOMM20 + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/tomm20/fnet3d_paper + # Joint preloads two stores (iPSC + A549 pool) into /dev/shm; the default + # 256G cap is too tight (256G iPSC mem + ~50G A549 + worker peak OOMs). + # 512G is the smallest tier that fits joint preload + worker overhead. + sbatch: + mem: "512G" diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/a549_mantis/train.yml new file mode 100644 index 000000000..661cdf2f9 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/a549_mantis/train.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit fit on mitochondria (TOMM20 marker) — A549 mantis-lightsheet pooled (mock + DENV + ZIKV). +base: + - ../../../_internal/shared/model/train_sets/a549_mantis.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/data_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + train_set: a549_mantis + model_name: pix2pix3d_unetvit + experiment_id: mito__a549_mantis__pix2pix3d_unetvit + +trainer: + logger: + init_args: + name: pix2pix3d_unetvit_A549_TOMM20 + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/tomm20/pix2pix3d_unetvit + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/tomm20/pix2pix3d_unetvit/checkpoints + +data: + init_args: + # A549 pooled store + target_channel — no resolver in this train_set. + target_channel: Structure + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/TOMM20_all.zarr + +launcher: + job_name: pix2pix3d_unetvit_A549_TOMM20 + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/tomm20/pix2pix3d_unetvit diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 100644 index 000000000..4621fe2d7 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: mito (TOMM20) predicted by pix2pix3d_unetvit on a549-mantis-tomm20-denv. +defaults: + - override /target: mito_tomm20 + - override /predict_set: a549_mantis_tomm20_denv + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_pix2pix3d_unetvit__tomm20_denv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_tomm20_pix2pix3d_unetvit__tomm20_denv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 100644 index 000000000..fb0964aef --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: mito (TOMM20) predicted by pix2pix3d_unetvit on a549-mantis-tomm20-mock. +defaults: + - override /target: mito_tomm20 + - override /predict_set: a549_mantis_tomm20_mock + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_pix2pix3d_unetvit__tomm20_mock.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_tomm20_pix2pix3d_unetvit__tomm20_mock diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 100644 index 000000000..860f303e8 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: mito (TOMM20) predicted by pix2pix3d_unetvit on a549-mantis-tomm20-zikv. +defaults: + - override /target: mito_tomm20 + - override /predict_set: a549_mantis_tomm20_zikv + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_pix2pix3d_unetvit__tomm20_zikv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_tomm20_pix2pix3d_unetvit__tomm20_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/ipsc_confocal/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/ipsc_confocal/eval__ipsc_confocal.yaml new file mode 100644 index 000000000..75b74a339 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/ipsc_confocal/eval__ipsc_confocal.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: mito (TOMM20) predicted by pix2pix3d_unetvit on iPSC confocal. +defaults: + - override /target: mito_tomm20 + - override /predict_set: ipsc_confocal + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_pix2pix3d_unetvit.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/eval_tomm20_pix2pix3d_unetvit diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..0fa6d45bb --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_denv.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: mito (TOMM20 marker) trained on iPSC, predicting against a549_mantis_tomm20_denv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_denv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: ipsc_confocal + predict_set: a549_mantis_tomm20_denv + model_name: pix2pix3d_unetvit + experiment_id: mito__ipsc_confocal__pix2pix3d_unetvit__a549_mantis_tomm20_denv + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_pix2pix3d_unetvit__tomm20_denv.zarr + +launcher: + job_name: pix2pix3d_unetvit_PRED_TOMM20_ON_A549_tomm20_denv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..98760a979 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_mock.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: mito (TOMM20 marker) trained on iPSC, predicting against a549_mantis_tomm20_mock test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_mock.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: ipsc_confocal + predict_set: a549_mantis_tomm20_mock + model_name: pix2pix3d_unetvit + experiment_id: mito__ipsc_confocal__pix2pix3d_unetvit__a549_mantis_tomm20_mock + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_pix2pix3d_unetvit__tomm20_mock.zarr + +launcher: + job_name: pix2pix3d_unetvit_PRED_TOMM20_ON_A549_tomm20_mock + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..de91e33c0 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_zikv.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: mito (TOMM20 marker) trained on iPSC, predicting against a549_mantis_tomm20_zikv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_zikv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: ipsc_confocal + predict_set: a549_mantis_tomm20_zikv + model_name: pix2pix3d_unetvit + experiment_id: mito__ipsc_confocal__pix2pix3d_unetvit__a549_mantis_tomm20_zikv + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_pix2pix3d_unetvit__tomm20_zikv.zarr + +launcher: + job_name: pix2pix3d_unetvit_PRED_TOMM20_ON_A549_tomm20_zikv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/ipsc_confocal/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/ipsc_confocal/predict__ipsc_confocal.yml new file mode 100644 index 000000000..752d97aa6 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/ipsc_confocal/predict__ipsc_confocal.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: mito (TOMM20 marker) against ipsc_confocal test_cropped. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: pix2pix3d_unetvit + experiment_id: mito__ipsc_confocal__pix2pix3d_unetvit__ipsc_confocal + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_pix2pix3d_unetvit.zarr + +launcher: + job_name: pix2pix3d_unetvit_PRED_TOMM20 + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/ipsc_confocal/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/ipsc_confocal/train.yml new file mode 100644 index 000000000..c44405d4f --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/ipsc_confocal/train.yml @@ -0,0 +1,37 @@ +# pix2pix3d_unetvit fit on mitochondria (TOMM20 marker) — AICS iPSC confocal. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/data_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + train_set: ipsc_confocal + model_name: pix2pix3d_unetvit + experiment_id: mito__ipsc_confocal__pix2pix3d_unetvit + +trainer: + logger: + init_args: + name: pix2pix3d_unetvit_iPSC_TOMM20 + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/tomm20/pix2pix3d_unetvit + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/tomm20/pix2pix3d_unetvit/checkpoints + +launcher: + job_name: pix2pix3d_unetvit_TOMM20 + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/tomm20/pix2pix3d_unetvit diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..b1521ad49 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: mito (TOMM20 marker) trained on joint iPSC+A549, predicting against a549_mantis_tomm20_denv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_denv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_tomm20_denv + model_name: pix2pix3d_unetvit + experiment_id: mito__joint_ipsc_confocal_a549_mantis__pix2pix3d_unetvit__a549_mantis_tomm20_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/tomm20/pix2pix3d_unetvit/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/tomm20_pix2pix3d_unetvit__tomm20_denv.zarr + +launcher: + job_name: pix2pix3d_unetvit_JOINT_PRED_TOMM20_ON_A549_tomm20_denv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..3c2e5a501 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: mito (TOMM20 marker) trained on joint iPSC+A549, predicting against a549_mantis_tomm20_mock test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_mock.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_tomm20_mock + model_name: pix2pix3d_unetvit + experiment_id: mito__joint_ipsc_confocal_a549_mantis__pix2pix3d_unetvit__a549_mantis_tomm20_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/tomm20/pix2pix3d_unetvit/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/tomm20_pix2pix3d_unetvit__tomm20_mock.zarr + +launcher: + job_name: pix2pix3d_unetvit_JOINT_PRED_TOMM20_ON_A549_tomm20_mock + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..588766d46 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: mito (TOMM20 marker) trained on joint iPSC+A549, predicting against a549_mantis_tomm20_zikv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_zikv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_tomm20_zikv + model_name: pix2pix3d_unetvit + experiment_id: mito__joint_ipsc_confocal_a549_mantis__pix2pix3d_unetvit__a549_mantis_tomm20_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/tomm20/pix2pix3d_unetvit/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/tomm20_pix2pix3d_unetvit__tomm20_zikv.zarr + +launcher: + job_name: pix2pix3d_unetvit_JOINT_PRED_TOMM20_ON_A549_tomm20_zikv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..8a6899e0e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: mito (TOMM20 marker) trained on joint iPSC+A549, predicting against ipsc_confocal test. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: ipsc_confocal + model_name: pix2pix3d_unetvit + experiment_id: mito__joint_ipsc_confocal_a549_mantis__pix2pix3d_unetvit__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/tomm20/pix2pix3d_unetvit/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions/tomm20_pix2pix3d_unetvit.zarr + +launcher: + job_name: pix2pix3d_unetvit_JOINT_PRED_TOMM20_ON_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/train.yml new file mode 100644 index 000000000..45ce21b8c --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/train.yml @@ -0,0 +1,147 @@ +# pix2pix3d_unetvit fit on mito (TOMM20 marker) — joint ipsc_confocal + a549_mantis pooled. +# +# Joint leaf. Uses BatchedConcatDataModule with two explicit HCSDataModule +# children (no benchmark.dataset_ref — joint leaves bypass the single-dataset +# resolver). Only model_overlays/pix2pix3d_unetvit_fit.yml is composed; the +# data block is authored inline because joint hparams live on the children. +# +# Normalization is NormalizeSampled (fov_statistics) to match the single-set +# pix2pix3d_unetvit leaves — divergent from the celldiff joint which uses +# MinMaxSampled. Per-organelle prior is to keep joint and single-set +# normalizations identical so ablations are apples-to-apples. +# +# Topology: single H200, single GPU — same as pix2pix3d_unetvit/ipsc_confocal/train.yml. +base: + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + gene: TOMM20 + target: mito + target_id: mito_tomm20 + train_set: joint_ipsc_confocal_a549_mantis + model_name: pix2pix3d_unetvit + experiment_id: mito__joint_ipsc_confocal_a549_mantis__pix2pix3d_unetvit + +trainer: + logger: + init_args: + name: pix2pix3d_unetvit_JOINT_TOMM20 + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/tomm20/pix2pix3d_unetvit + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/tomm20/pix2pix3d_unetvit/checkpoints + +# Child HCSDataModule init_args shared across both datasets (only data_path +# differs). `_`-prefixed top-level keys are stripped by load_composed_config +# before reaching LightningCLI; the merge expansion under `data:` survives. +_hcs_init_args: &hcs_init_args + source_channel: Phase3D + target_channel: Structure + z_window_size: 13 + # batch_size=2 + num_samples=2 → 4 GPU samples/step, matching the single-set + # pix2pix3d_unetvit (batch=4, num_samples=2). BatchedConcatDataModule does + # NOT divide by num_samples (see CLAUDE.md), so joint.batch_size = + # single_set.batch_size / num_samples. + batch_size: 2 + num_workers: 4 + yx_patch_size: [512, 512] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Structure] + level: fov_statistics + subtrahend: median + divisor: iqr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Structure] + w_key: Structure + spatial_size: [13, 624, 624] + num_samples: 2 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + safe_crop_size: [8, 512, 512] + safe_crop_coverage: 0.9 + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/TOMM20.zarr + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/TOMM20_all.zarr + +launcher: + job_name: pix2pix3d_unetvit_JOINT_TOMM20 + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/tomm20/pix2pix3d_unetvit + # Joint preloads two stores (iPSC + A549 pool) into /dev/shm; default 256G + # is too tight for the iPSC marker store + A549 pool + worker overhead. + sbatch: + mem: "512G" diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/a549_mantis/train.yml new file mode 100644 index 000000000..4b0dfed93 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/a549_mantis/train.yml @@ -0,0 +1,43 @@ +# UNetViT3D fit on mitochondria (TOMM20 marker) — A549 mantis-lightsheet pooled (mock + DENV + ZIKV). +base: + - ../../../_internal/shared/model/train_sets/a549_mantis.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/data_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/model_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + train_set: a549_mantis + model_name: unetvit3d + experiment_id: mito__a549_mantis__unetvit3d + +trainer: + logger: + init_args: + name: UNetViT3D_A549_TOMM20 + save_dir: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/tomm20/unetvit3d + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/tomm20/unetvit3d/checkpoints + +data: + init_args: + # A549 pooled store + target_channel — no resolver in this train_set. + target_channel: Structure + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/TOMM20_all.zarr + +launcher: + job_name: UNetViT3D_A549_TOMM20 + run_root: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/tomm20/unetvit3d diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 100644 index 000000000..5ed7260c0 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: Mitochondria (TOMM20) predicted by UNetViT3D on a549-mantis-tomm20-denv. +defaults: + - override /target: mito_tomm20 + - override /predict_set: a549_mantis_tomm20_denv + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_unetvit3d__tomm20_denv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_tomm20_unetvit3d__tomm20_denv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 100644 index 000000000..21d60504c --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: Mitochondria (TOMM20) predicted by UNetViT3D on a549-mantis-tomm20-mock. +defaults: + - override /target: mito_tomm20 + - override /predict_set: a549_mantis_tomm20_mock + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_unetvit3d__tomm20_mock.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_tomm20_unetvit3d__tomm20_mock diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 100644 index 000000000..bf8c596a1 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: Mitochondria (TOMM20) predicted by UNetViT3D on a549-mantis-tomm20-zikv. +defaults: + - override /target: mito_tomm20 + - override /predict_set: a549_mantis_tomm20_zikv + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_unetvit3d__tomm20_zikv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_tomm20_unetvit3d__tomm20_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/ipsc_confocal/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/ipsc_confocal/eval__ipsc_confocal.yaml new file mode 100644 index 000000000..e85266660 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/ipsc_confocal/eval__ipsc_confocal.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: Mitochondria (TOMM20) predicted by UNetViT3D on iPSC confocal. +defaults: + - override /target: mito_tomm20 + - override /predict_set: ipsc_confocal + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_unetvit3d.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/eval_tomm20_unetvit3d diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/ipsc_confocal/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/ipsc_confocal/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..b80257994 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/ipsc_confocal/predict__a549_mantis_denv.yml @@ -0,0 +1,43 @@ +# UNetViT3D predict: mito (TOMM20) trained on iPSC, predicting against a549_mantis_tomm20_denv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_denv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/unetvit3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: ipsc_confocal + predict_set: a549_mantis_tomm20_denv + model_name: unetvit3d + experiment_id: mito__ipsc_confocal__unetvit3d__a549_mantis_tomm20_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/tomm20/unetvit3d/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_unetvit3d__tomm20_denv.zarr + +launcher: + job_name: UNetViT3D_PRED_TOMM20_ON_A549_tomm20_denv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/ipsc_confocal/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/ipsc_confocal/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..9e403ac5c --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/ipsc_confocal/predict__a549_mantis_mock.yml @@ -0,0 +1,43 @@ +# UNetViT3D predict: mito (TOMM20) trained on iPSC, predicting against a549_mantis_tomm20_mock test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_mock.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/unetvit3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: ipsc_confocal + predict_set: a549_mantis_tomm20_mock + model_name: unetvit3d + experiment_id: mito__ipsc_confocal__unetvit3d__a549_mantis_tomm20_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/tomm20/unetvit3d/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_unetvit3d__tomm20_mock.zarr + +launcher: + job_name: UNetViT3D_PRED_TOMM20_ON_A549_tomm20_mock + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/ipsc_confocal/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/ipsc_confocal/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..08f7cfeb6 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/ipsc_confocal/predict__a549_mantis_zikv.yml @@ -0,0 +1,43 @@ +# UNetViT3D predict: mito (TOMM20) trained on iPSC, predicting against a549_mantis_tomm20_zikv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_tomm20_zikv.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/unetvit3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: ipsc_confocal + predict_set: a549_mantis_tomm20_zikv + model_name: unetvit3d + experiment_id: mito__ipsc_confocal__unetvit3d__a549_mantis_tomm20_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/tomm20/unetvit3d/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/tomm20_unetvit3d__tomm20_zikv.zarr + +launcher: + job_name: UNetViT3D_PRED_TOMM20_ON_A549_tomm20_zikv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/ipsc_confocal/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/ipsc_confocal/predict__ipsc_confocal.yml new file mode 100644 index 000000000..c42fa98a2 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/ipsc_confocal/predict__ipsc_confocal.yml @@ -0,0 +1,43 @@ +# UNetViT3D predict: mito (TOMM20) against ipsc_confocal test_cropped. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/model_overlays/unetvit3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: unetvit3d + experiment_id: mito__ipsc_confocal__unetvit3d__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/tomm20/unetvit3d/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_unetvit3d.zarr + +launcher: + job_name: UNetViT3D_PRED_TOMM20 + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/ipsc_confocal/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/ipsc_confocal/train.yml new file mode 100644 index 000000000..47941d508 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/ipsc_confocal/train.yml @@ -0,0 +1,37 @@ +# UNetViT3D fit on mitochondria (TOMM20 marker) — AICS iPSC confocal. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/mito_tomm20.yml + - ../../../_internal/shared/model/data_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/model_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + train_set: ipsc_confocal + model_name: unetvit3d + experiment_id: mito__ipsc_confocal__unetvit3d + +trainer: + logger: + init_args: + name: UNetViT3D_iPSC_TOMM20 + save_dir: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/tomm20/unetvit3d + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/tomm20/unetvit3d/checkpoints + +launcher: + job_name: UNetViT3D_TOMM20 + run_root: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/tomm20/unetvit3d diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/joint_ipsc_confocal_a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/joint_ipsc_confocal_a549_mantis/train.yml new file mode 100644 index 000000000..49f9a631d --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/mito/unetvit3d/joint_ipsc_confocal_a549_mantis/train.yml @@ -0,0 +1,139 @@ +# UNetViT3D fit on mitochondria (TOMM20) — joint ipsc_confocal + a549_mantis pooled. +# +# Joint leaf per Stage 7 of A549_EXPANSION_ROADMAP.md. Uses +# BatchedConcatDataModule with two explicit HCSDataModule children +# (no benchmark.dataset_ref — joint leaves bypass the single-dataset +# resolver). Only model_overlays/unetvit3d_fit.yml is composed; the data +# block is authored inline because joint hparams live on the children. +# +# Topology: single H200, single GPU — same as unetvit3d/ipsc_confocal/train.yml. +# The paper baseline pattern is single-GPU and we keep that here so +# iPSC-only and joint runs are apples-to-apples. +base: + - ../../../_internal/shared/model/model_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: mito + gene: TOMM20 + target: mito + target_id: mito_tomm20 + train_set: joint_ipsc_confocal_a549_mantis + model_name: unetvit3d + experiment_id: mito__joint_ipsc_confocal_a549_mantis__unetvit3d + +trainer: + logger: + init_args: + name: UNetViT3D_JOINT_TOMM20 + save_dir: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/tomm20/unetvit3d + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/tomm20/unetvit3d/checkpoints + +_hcs_init_args: &hcs_init_args + source_channel: Phase3D + target_channel: Structure + z_window_size: 13 + batch_size: 4 + num_workers: 4 + yx_patch_size: [512, 512] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Structure] + level: fov_statistics + subtrahend: median + divisor: iqr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Structure] + w_key: Structure + spatial_size: [13, 624, 624] + num_samples: 2 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + safe_crop_size: [8, 512, 512] + safe_crop_coverage: 0.9 + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/TOMM20.zarr + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/TOMM20_all.zarr + +launcher: + job_name: UNetViT3D_JOINT_TOMM20 + run_root: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/tomm20/unetvit3d + # Joint preloads two stores (iPSC + A549 pool) into /dev/shm; the default + # 256G cap is too tight (256G iPSC mem + ~50G A549 + worker peak OOMs). + # 512G is the smallest tier that fits joint preload + worker overhead. + sbatch: + mem: "512G" diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..1ae97abd8 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,45 @@ +# CellDiff r2 predict: nucleus trained on A549 mantis, predicting against a549_mantis_h2b_denv test. +# A549 manifest keys nucleus by gene (`h2b`); override the target_id so the resolver finds h2b. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_denv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: a549_mantis + predict_set: a549_mantis_h2b_denv + model_name: celldiff + experiment_id: nucleus__a549_mantis__celldiff__a549_mantis_h2b_denv + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/nucl/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_celldiff_r2_a549trained_denv.zarr + +launcher: + job_name: CELLDiff_A549_PRED_NUCL_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..34e90c068 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,45 @@ +# CellDiff r2 predict: nucleus trained on A549 mantis, predicting against a549_mantis_h2b_mock test. +# A549 manifest keys nucleus by gene (`h2b`); override the target_id so the resolver finds h2b. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_mock.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: a549_mantis + predict_set: a549_mantis_h2b_mock + model_name: celldiff + experiment_id: nucleus__a549_mantis__celldiff__a549_mantis_h2b_mock + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/nucl/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_celldiff_r2_a549trained_mock.zarr + +launcher: + job_name: CELLDiff_A549_PRED_NUCL_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..8c2f3adc9 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,45 @@ +# CellDiff r2 predict: nucleus trained on A549 mantis, predicting against a549_mantis_h2b_zikv test. +# A549 manifest keys nucleus by gene (`h2b`); override the target_id so the resolver finds h2b. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_zikv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: a549_mantis + predict_set: a549_mantis_h2b_zikv + model_name: celldiff + experiment_id: nucleus__a549_mantis__celldiff__a549_mantis_h2b_zikv + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/nucl/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_celldiff_r2_a549trained_zikv.zarr + +launcher: + job_name: CELLDiff_A549_PRED_NUCL_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..0b03fb4cf --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: nucleus trained on A549 mantis, predicting against ipsc_confocal test (OOD). +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: a549_mantis + predict_set: ipsc_confocal + model_name: celldiff + experiment_id: nucleus__a549_mantis__celldiff__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/nucl/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 40 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_celldiff_r2_a549trained.zarr + +launcher: + job_name: CELLDiff_A549_PRED_NUCL_ON_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/a549_mantis/train.yml new file mode 100644 index 000000000..a47726292 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/a549_mantis/train.yml @@ -0,0 +1,42 @@ +# CellDiff fit on nucleus (Nuclei channel of cell.zarr) — A549 mantis-lightsheet pooled (mock + DENV + ZIKV). +base: + - ../../../_internal/shared/model/train_sets/a549_mantis.yml + - ../../../_internal/shared/model/targets/nucleus_celldiff.yml + - ../../../_internal/shared/model/data_overlays/celldiff_fit.yml + - ../../../_internal/shared/model/model_overlays/celldiff_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + train_set: a549_mantis + model_name: celldiff + experiment_id: nucleus__a549_mantis__celldiff + +trainer: + logger: + init_args: + name: CELLDiff_A549_NUCL + save_dir: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/nucl/celldiff_r2 + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + every_n_epochs: 1 + save_top_k: -1 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/nucl/celldiff_r2/checkpoints + +data: + init_args: + # A549 pooled store + target_channel — no resolver in this train_set. + target_channel: Nuclei + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/H2B_all.zarr + +launcher: + job_name: CELLDiff_A549_NUCL + run_root: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/nucl/celldiff_r2 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 100644 index 000000000..ab2c3d60e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Nucleus (H2B) predicted by CellDiff on a549-mantis-h2b-denv. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from the target group so the resolver finds h2b on a549-mantis-h2b-denv. +defaults: + - override /target: nucleus + - override /predict_set: a549_mantis_h2b_denv + +benchmark: + dataset_ref: + target: h2b + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_celldiff_denoise_denv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_nucl_celldiff_denoise_denv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 100644 index 000000000..aa56bb8fb --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Nucleus (H2B) predicted by CellDiff on a549-mantis-h2b-mock. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from the target group so the resolver finds h2b on a549-mantis-h2b-mock. +defaults: + - override /target: nucleus + - override /predict_set: a549_mantis_h2b_mock + +benchmark: + dataset_ref: + target: h2b + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_celldiff_denoise_mock.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_nucl_celldiff_denoise_mock diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 100644 index 000000000..5c716eadf --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Nucleus (H2B) predicted by CellDiff on a549-mantis-h2b-zikv. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from the target group so the resolver finds h2b on a549-mantis-h2b-zikv. +defaults: + - override /target: nucleus + - override /predict_set: a549_mantis_h2b_zikv + +benchmark: + dataset_ref: + target: h2b + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_celldiff_denoise_zikv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_nucl_celldiff_denoise_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/eval__ipsc_confocal.yaml new file mode 100644 index 000000000..a013096af --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/eval__ipsc_confocal.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: Nucleus predicted by CellDiff on iPSC confocal. +defaults: + - override /target: nucleus + - override /predict_set: ipsc_confocal + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_celldiff_denoise.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/eval_nucl_celldiff_denoise diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..d3db6f04c --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/predict__a549_mantis_denv.yml @@ -0,0 +1,48 @@ +# CellDiff predict: nucleus trained on iPSC, predicting against a549-mantis-h2b-denv test. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from targets/nucleus.yml so the resolver finds the h2b target on +# a549-mantis-h2b-denv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_denv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: ipsc_confocal + predict_set: a549_mantis_h2b_denv + model_name: celldiff + experiment_id: nucleus__ipsc_confocal__celldiff__a549_mantis_h2b_denv + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/nucl/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative # denoise, generate, sliding_window, or iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 # 8 for denoise and generate, 40 for iterative and sliding_window. + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_celldiff_r2_iterative_denv.zarr + +launcher: + job_name: CELLDiff_PRED_NUCL_ON_A549_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..657990b21 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/predict__a549_mantis_mock.yml @@ -0,0 +1,48 @@ +# CellDiff predict: nucleus trained on iPSC, predicting against a549-mantis-h2b-mock test. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from targets/nucleus.yml so the resolver finds the h2b target on +# a549-mantis-h2b-mock. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_mock.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: ipsc_confocal + predict_set: a549_mantis_h2b_mock + model_name: celldiff + experiment_id: nucleus__ipsc_confocal__celldiff__a549_mantis_h2b_mock + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/nucl/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative # denoise, generate, sliding_window, or iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 # 8 for denoise and generate, 40 for iterative and sliding_window. + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_celldiff_r2_iterative_mock.zarr + +launcher: + job_name: CELLDiff_PRED_NUCL_ON_A549_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..e7a2411c8 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/predict__a549_mantis_zikv.yml @@ -0,0 +1,48 @@ +# CellDiff predict: nucleus trained on iPSC, predicting against a549-mantis-h2b-zikv test. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from targets/nucleus.yml so the resolver finds the h2b target on +# a549-mantis-h2b-zikv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_zikv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: ipsc_confocal + predict_set: a549_mantis_h2b_zikv + model_name: celldiff + experiment_id: nucleus__ipsc_confocal__celldiff__a549_mantis_h2b_zikv + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/nucl/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative # denoise, generate, sliding_window, or iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 # 8 for denoise and generate, 40 for iterative and sliding_window. + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_celldiff_r2_iterative_zikv.zarr + +launcher: + job_name: CELLDiff_PRED_NUCL_ON_A549_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/predict__ipsc_confocal__denoise.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/predict__ipsc_confocal__denoise.yml new file mode 100644 index 000000000..ea5a1dbd7 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/predict__ipsc_confocal__denoise.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: nucleus on ipsc_confocal — denoise method. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: celldiff + experiment_id: nucleus__ipsc_confocal__celldiff__ipsc_confocal__denoise + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/nucl/celldiff_r2/checkpoints/last.ckpt + predict_method: denoise + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 8 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_celldiff_r2_denoise.zarr + +launcher: + job_name: CELLDiff_PRED_NUCL_DN + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/predict__ipsc_confocal__iterative.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/predict__ipsc_confocal__iterative.yml new file mode 100644 index 000000000..271a2de90 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/predict__ipsc_confocal__iterative.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: nucleus on ipsc_confocal — iterative method. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: celldiff + experiment_id: nucleus__ipsc_confocal__celldiff__ipsc_confocal__iterative + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/nucl/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 40 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_celldiff_r2_iterative.zarr + +launcher: + job_name: CELLDiff_PRED_NUCL_ITER + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/predict__ipsc_confocal__sliding_window.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/predict__ipsc_confocal__sliding_window.yml new file mode 100644 index 000000000..157c62433 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/predict__ipsc_confocal__sliding_window.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: nucleus on ipsc_confocal — sliding_window method. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: celldiff + experiment_id: nucleus__ipsc_confocal__celldiff__ipsc_confocal__sliding_window + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/nucl/celldiff_r2/checkpoints/last.ckpt + predict_method: sliding_window + predict_overlap: [0, 0, 0] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 40 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_celldiff_r2_sliding_window.zarr + +launcher: + job_name: CELLDiff_PRED_NUCL_SW + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/train.yml new file mode 100644 index 000000000..7bb175b56 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/ipsc_confocal/train.yml @@ -0,0 +1,36 @@ +# CellDiff fit on nucleus (Nuclei channel of cell.zarr) — AICS iPSC confocal. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/nucleus_celldiff.yml + - ../../../_internal/shared/model/data_overlays/celldiff_fit.yml + - ../../../_internal/shared/model/model_overlays/celldiff_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + train_set: ipsc_confocal + model_name: celldiff + experiment_id: nucleus__ipsc_confocal__celldiff + +trainer: + logger: + init_args: + name: CELLDiff_iPSC_NUCL + save_dir: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/nucl/celldiff_r2 + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + every_n_epochs: 1 + save_top_k: -1 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/nucl/celldiff_r2/checkpoints + +launcher: + job_name: CELLDiff_NUCL + run_root: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/nucl/celldiff_r2 diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..a65627875 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,48 @@ +# CellDiff predict: nucleus trained on joint iPSC+A549, predicting against a549-mantis-h2b-denv test. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from targets/nucleus.yml so the resolver finds the h2b target on +# a549-mantis-h2b-denv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_denv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_h2b_denv + model_name: celldiff + experiment_id: nucleus__joint_ipsc_confocal_a549_mantis__celldiff__a549_mantis_h2b_denv + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/nucl/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative # denoise, generate, sliding_window, or iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 # 8 for denoise and generate, 40 for iterative and sliding_window. + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/nucl_celldiff_r2_denv.zarr + +launcher: + job_name: CELLDiff_JOINT_PRED_NUCL_ON_A549_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..ebba8dd10 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,48 @@ +# CellDiff r2 predict: nucleus trained on joint iPSC+A549, predicting against a549-mantis-h2b-mock test. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from targets/nucleus.yml so the resolver finds the h2b target on +# a549-mantis-h2b-mock. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_mock.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_h2b_mock + model_name: celldiff + experiment_id: nucleus__joint_ipsc_confocal_a549_mantis__celldiff__a549_mantis_h2b_mock + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/nucl/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/nucl_celldiff_r2_mock.zarr + +launcher: + job_name: CELLDiff_JOINT_PRED_NUCL_ON_A549_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..30b4542f8 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,48 @@ +# CellDiff r2 predict: nucleus trained on joint iPSC+A549, predicting against a549-mantis-h2b-zikv test. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from targets/nucleus.yml so the resolver finds the h2b target on +# a549-mantis-h2b-zikv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_zikv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_h2b_zikv + model_name: celldiff + experiment_id: nucleus__joint_ipsc_confocal_a549_mantis__celldiff__a549_mantis_h2b_zikv + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/nucl/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 48 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/nucl_celldiff_r2_zikv.zarr + +launcher: + job_name: CELLDiff_JOINT_PRED_NUCL_ON_A549_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..1d3c7d874 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,42 @@ +# CellDiff r2 predict: nucleus trained on joint iPSC+A549, predicting against ipsc_confocal test. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/celldiff_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: ipsc_confocal + model_name: celldiff + experiment_id: nucleus__joint_ipsc_confocal_a549_mantis__celldiff__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/nucl/celldiff_r2/checkpoints/last.ckpt + predict_method: iterative + predict_overlap: [4, 256, 256] + +data: + init_args: + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + augmentations: [] + z_window_size: 40 + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions/nucl_celldiff_r2.zarr + +launcher: + job_name: CELLDiff_JOINT_PRED_NUCL_ON_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/joint_ipsc_confocal_a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/joint_ipsc_confocal_a549_mantis/train.yml new file mode 100644 index 000000000..cfae02837 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/celldiff/joint_ipsc_confocal_a549_mantis/train.yml @@ -0,0 +1,141 @@ +# CellDiff fit on nucleus (Nuclei) — joint ipsc_confocal + a549_mantis pooled. +# +# Joint leaf per Stage 7 of A549_EXPANSION_ROADMAP.md. Uses +# BatchedConcatDataModule with two explicit HCSDataModule children +# (no benchmark.dataset_ref — joint leaves bypass the single-dataset +# resolver). Only model_overlays/celldiff_fit.yml is composed; the data +# block is authored inline because joint hparams live on the children. +# +# iPSC source is the multi-marker cell.zarr (Brightfield, Nuclei, +# Membrane, Phase3D); A549 source is the H2B-marker pooled store +# H2B_all.zarr. The shared target_channel name is `Nuclei` in both. +# +# Topology: single H200, single GPU — same as celldiff/ipsc_confocal/train.yml. +# The paper baseline pattern is single-GPU and we keep that here so +# iPSC-only and joint runs are apples-to-apples. +base: + - ../../../_internal/shared/model/model_overlays/celldiff_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + gene: Nuclei + target: nucleus + target_id: nucleus + train_set: joint_ipsc_confocal_a549_mantis + model_name: celldiff + experiment_id: nucleus__joint_ipsc_confocal_a549_mantis__celldiff + +trainer: + logger: + init_args: + name: CELLDiff_JOINT_NUCL + save_dir: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/nucl/celldiff_r2 + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + every_n_epochs: 1 + save_top_k: -1 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/nucl/celldiff_r2/checkpoints + +_hcs_init_args: &hcs_init_args + source_channel: Phase3D + target_channel: Nuclei + z_window_size: 13 + batch_size: 2 + num_workers: 4 + yx_patch_size: [512, 512] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true + normalizations: + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Phase3D] + level: timepoint_statistics + - class_path: viscy_transforms.MinMaxSampled + init_args: + keys: [Nuclei] + level: timepoint_statistics + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Nuclei] + w_key: Nuclei + spatial_size: [13, 624, 624] + num_samples: 2 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + safe_crop_size: [8, 512, 512] + safe_crop_coverage: 0.9 + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + # ipsc_confocal — aics-hipsc multi-marker cell.zarr; HCSDataModule + # picks up only the requested target_channel (Nuclei). + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/cell.zarr + # a549_mantis — pooled H2B all-conditions train store + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/H2B_all.zarr + +launcher: + job_name: CELLDiff_JOINT_NUCL + run_root: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/nucl/celldiff_r2 + # Joint preloads two stores (iPSC + A549 pool) into /dev/shm; the default + # 256G cap is too tight (256G iPSC mem + ~50G A549 + worker peak OOMs). + # 512G is the smallest tier that fits joint preload + worker overhead. + sbatch: + mem: "512G" diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..f5f5388e6 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_denv.yml @@ -0,0 +1,46 @@ +# VSCyto3D random-init predict: nucleus (frozen randinit ckpt), A549 denv plate. +# Control ablation. A549 manifest keys nucleus by gene (`h2b`); override the +# iPSC-side `nucleus` target_id from targets/nucleus.yml so the resolver finds the +# h2b target on a549-mantis-h2b-denv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_denv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: randinit + predict_set: a549_mantis_h2b_denv + model_name: fcmae_vscyto3d_pretrained_randinit + experiment_id: nucleus__randinit__fcmae_vscyto3d_pretrained__a549_mantis_h2b_denv + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/randinit/nucl/fcmae_vscyto3d_pretrained/checkpoints/randinit.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_pretrained_randinit_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_RandInit_PRED_NUCL_ON_A549_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..e75d9babf --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_mock.yml @@ -0,0 +1,46 @@ +# VSCyto3D random-init predict: nucleus (frozen randinit ckpt), A549 mock plate. +# Control ablation. A549 manifest keys nucleus by gene (`h2b`); override the +# iPSC-side `nucleus` target_id from targets/nucleus.yml so the resolver finds the +# h2b target on a549-mantis-h2b-mock. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_mock.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: randinit + predict_set: a549_mantis_h2b_mock + model_name: fcmae_vscyto3d_pretrained_randinit + experiment_id: nucleus__randinit__fcmae_vscyto3d_pretrained__a549_mantis_h2b_mock + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/randinit/nucl/fcmae_vscyto3d_pretrained/checkpoints/randinit.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_pretrained_randinit_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_RandInit_PRED_NUCL_ON_A549_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..454351568 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__a549_mantis_zikv.yml @@ -0,0 +1,46 @@ +# VSCyto3D random-init predict: nucleus (frozen randinit ckpt), A549 zikv plate. +# Control ablation. A549 manifest keys nucleus by gene (`h2b`); override the +# iPSC-side `nucleus` target_id from targets/nucleus.yml so the resolver finds the +# h2b target on a549-mantis-h2b-zikv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_zikv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: randinit + predict_set: a549_mantis_h2b_zikv + model_name: fcmae_vscyto3d_pretrained_randinit + experiment_id: nucleus__randinit__fcmae_vscyto3d_pretrained__a549_mantis_h2b_zikv + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/randinit/nucl/fcmae_vscyto3d_pretrained/checkpoints/randinit.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_pretrained_randinit_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_RandInit_PRED_NUCL_ON_A549_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__ipsc_confocal.yml new file mode 100644 index 000000000..91da8959e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/_no_train_randinit/predict__ipsc_confocal.yml @@ -0,0 +1,44 @@ +# VSCyto3D random-init predict: nucleus (frozen randinit ckpt), iPSC test set. +# Control ablation — measures untrained model output for paper. +# References the frozen randinit.ckpt persisted by save_random_init_vscyto3d_ckpts.py +# so all 4 datasets (iPSC + 3 A549 plates) for this organelle reuse the same weights. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: randinit + predict_set: ipsc_confocal + model_name: fcmae_vscyto3d_pretrained_randinit + experiment_id: nucleus__randinit__fcmae_vscyto3d_pretrained__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/randinit/nucl/fcmae_vscyto3d_pretrained/checkpoints/randinit.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_fcmae_vscyto3d_pretrained_randinit.zarr + +launcher: + job_name: FCMAE_VSCyto3D_RandInit_PRED_NUCL + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..696fc353b --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,49 @@ +# FCMAE_VSCyto3D_Pretrained (VSCyto3D) predict: nucleus trained on a549_mantis (h2b), +# predicting against a549-mantis-h2b-denv test. +# Best val-loss checkpoint from job 31822558 (epoch 134, loss/validate=0.8142). +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from targets/nucleus.yml so the resolver finds the h2b target on +# a549-mantis-h2b-denv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_denv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: a549_mantis + predict_set: a549_mantis_h2b_denv + model_name: fcmae_vscyto3d_pretrained + experiment_id: nucleus__a549_mantis__fcmae_vscyto3d_pretrained__a549_mantis_h2b_denv + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/nucl/fcmae_vscyto3d_pretrained/checkpoints/epoch=134-step=29295.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_pretrained_a549trained_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_NUCL_A549TR_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..6f2be08e3 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,49 @@ +# FCMAE_VSCyto3D_Pretrained (VSCyto3D) predict: nucleus trained on a549_mantis (h2b), +# predicting against a549-mantis-h2b-mock test. +# Best val-loss checkpoint from job 31822558 (epoch 134, loss/validate=0.8142). +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from targets/nucleus.yml so the resolver finds the h2b target on +# a549-mantis-h2b-mock. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_mock.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: a549_mantis + predict_set: a549_mantis_h2b_mock + model_name: fcmae_vscyto3d_pretrained + experiment_id: nucleus__a549_mantis__fcmae_vscyto3d_pretrained__a549_mantis_h2b_mock + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/nucl/fcmae_vscyto3d_pretrained/checkpoints/epoch=134-step=29295.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_pretrained_a549trained_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_NUCL_A549TR_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..2f6e12b44 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,49 @@ +# FCMAE_VSCyto3D_Pretrained (VSCyto3D) predict: nucleus trained on a549_mantis (h2b), +# predicting against a549-mantis-h2b-zikv test. +# Best val-loss checkpoint from job 31822558 (epoch 134, loss/validate=0.8142). +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from targets/nucleus.yml so the resolver finds the h2b target on +# a549-mantis-h2b-zikv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_zikv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: a549_mantis + predict_set: a549_mantis_h2b_zikv + model_name: fcmae_vscyto3d_pretrained + experiment_id: nucleus__a549_mantis__fcmae_vscyto3d_pretrained__a549_mantis_h2b_zikv + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/nucl/fcmae_vscyto3d_pretrained/checkpoints/epoch=134-step=29295.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_pretrained_a549trained_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_NUCL_A549TR_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..feeb24517 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,43 @@ +# FCMAE_VSCyto3D_Pretrained (VSCyto3D) predict: nucleus trained on a549_mantis (h2b), +# predicting against ipsc_confocal test_cropped. +# Best val-loss checkpoint from job 31822558 (epoch 134, loss/validate=0.8142). +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: a549_mantis + predict_set: ipsc_confocal + model_name: fcmae_vscyto3d_pretrained + experiment_id: nucleus__a549_mantis__fcmae_vscyto3d_pretrained__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/nucl/fcmae_vscyto3d_pretrained/checkpoints/epoch=134-step=29295.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_fcmae_vscyto3d_pretrained_a549trained.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_NUCL_A549TR_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/a549_mantis/train.yml new file mode 100644 index 000000000..9d32fbce2 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/a549_mantis/train.yml @@ -0,0 +1,67 @@ +# FCMAE-class (FullyConvolutionalMAE, pretraining=False) with FCMAE- +# pretrained encoder init on nucleus (Nuclei marker). Companion to +# fcmae_vscyto3d_scratch.yml — the two leaves are identical except this +# one loads encoder weights from the published VSCyto3D FCMAE ckpt +# (400 ep on HEK + A549 + iPSC phase data). See vs_test/finetune_3d.py +# for the canonical recipe. +base: + - ../../../_internal/shared/model/train_sets/a549_mantis.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/data_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + train_set: a549_mantis + model_name: fcmae_vscyto3d_pretrained + experiment_id: nucleus__a549_mantis__fcmae_vscyto3d_pretrained + +# Override the FCMAE data overlay's hardcoded `Structure` augmentation +# keys (the overlay was authored for ER/Mito where target_channel == +# "Structure"). RandWeightedCropd needs the actual nucleus channel name +# in keys/w_key. spatial_size + num_samples kept identical to the FCMAE +# overlay so the augmentation policy matches ER/Mito. +data: + init_args: + # A549 pooled store + target_channel — no resolver in this train_set. + target_channel: Nuclei + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/H2B_all.zarr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Nuclei] + w_key: Nuclei + spatial_size: [20, 600, 600] + num_samples: 4 + +model: + init_args: + # Load only the encoder from the canonical VSCyto3D FCMAE ckpt — + # decoder/head stay at fresh init. Matches vs_test/finetune_3d.py:247. + encoder_only: true + ckpt_path: /hpc/projects/virtual_staining/models/mehta-lab/VSCyto3D/fcmae.ckpt + +trainer: + logger: + init_args: + name: FCMAE_VSCyto3D_Pretrained_A549_Nucleus + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/nucl/fcmae_vscyto3d_pretrained + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/nucl/fcmae_vscyto3d_pretrained/checkpoints + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_A549_Nucleus + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/nucl/fcmae_vscyto3d_pretrained diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 100644 index 000000000..3b2336ac4 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Nucleus (H2B) predicted by FCMAE_VSCyto3D_Pretrained on a549-mantis-h2b-denv. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from the target group so the resolver finds h2b on a549-mantis-h2b-denv. +defaults: + - override /target: nucleus + - override /predict_set: a549_mantis_h2b_denv + +benchmark: + dataset_ref: + target: h2b + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_pretrained_denv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_nucl_fcmae_vscyto3d_pretrained_denv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 100644 index 000000000..98538c332 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Nucleus (H2B) predicted by FCMAE_VSCyto3D_Pretrained on a549-mantis-h2b-mock. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from the target group so the resolver finds h2b on a549-mantis-h2b-mock. +defaults: + - override /target: nucleus + - override /predict_set: a549_mantis_h2b_mock + +benchmark: + dataset_ref: + target: h2b + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_pretrained_mock.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_nucl_fcmae_vscyto3d_pretrained_mock diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 100644 index 000000000..01ceaec5f --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Nucleus (H2B) predicted by FCMAE_VSCyto3D_Pretrained on a549-mantis-h2b-zikv. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from the target group so the resolver finds h2b on a549-mantis-h2b-zikv. +defaults: + - override /target: nucleus + - override /predict_set: a549_mantis_h2b_zikv + +benchmark: + dataset_ref: + target: h2b + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_pretrained_zikv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_nucl_fcmae_vscyto3d_pretrained_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..16358821b --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_denv.yml @@ -0,0 +1,52 @@ +# FCMAE_VSCyto3D_Pretrained predict: nucleus trained on iPSC, +# predicting against a549-mantis-h2b-denv test. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side +# `nucleus` target_id from targets/nucleus.yml so the resolver finds +# the h2b target on a549-mantis-h2b-denv. +# +# Pinned to best-val checkpoint from training run J31475094 +# (val 0.3921, epoch 89). Run cancelled at epoch 172 — val plateaued +# at epoch 89 and never recovered (~83 epochs without improvement). +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_denv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: ipsc_confocal + predict_set: a549_mantis_h2b_denv + model_name: fcmae_vscyto3d_pretrained + experiment_id: nucleus__ipsc_confocal__fcmae_vscyto3d_pretrained__a549_mantis_h2b_denv + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/nucl/fcmae_vscyto3d_pretrained/checkpoints/epoch=89-step=28080.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_pretrained_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_NUCL_ON_A549_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..5bd224569 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_mock.yml @@ -0,0 +1,52 @@ +# FCMAE_VSCyto3D_Pretrained predict: nucleus trained on iPSC, +# predicting against a549-mantis-h2b-mock test. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side +# `nucleus` target_id from targets/nucleus.yml so the resolver finds +# the h2b target on a549-mantis-h2b-mock. +# +# Pinned to best-val checkpoint from training run J31475094 +# (val 0.3921, epoch 89). Run cancelled at epoch 172 — val plateaued +# at epoch 89 and never recovered (~83 epochs without improvement). +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_mock.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: ipsc_confocal + predict_set: a549_mantis_h2b_mock + model_name: fcmae_vscyto3d_pretrained + experiment_id: nucleus__ipsc_confocal__fcmae_vscyto3d_pretrained__a549_mantis_h2b_mock + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/nucl/fcmae_vscyto3d_pretrained/checkpoints/epoch=89-step=28080.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_pretrained_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_NUCL_ON_A549_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..5b560e3eb --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__a549_mantis_zikv.yml @@ -0,0 +1,52 @@ +# FCMAE_VSCyto3D_Pretrained predict: nucleus trained on iPSC, +# predicting against a549-mantis-h2b-zikv test. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side +# `nucleus` target_id from targets/nucleus.yml so the resolver finds +# the h2b target on a549-mantis-h2b-zikv. +# +# Pinned to best-val checkpoint from training run J31475094 +# (val 0.3921, epoch 89). Run cancelled at epoch 172 — val plateaued +# at epoch 89 and never recovered (~83 epochs without improvement). +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_zikv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: ipsc_confocal + predict_set: a549_mantis_h2b_zikv + model_name: fcmae_vscyto3d_pretrained + experiment_id: nucleus__ipsc_confocal__fcmae_vscyto3d_pretrained__a549_mantis_h2b_zikv + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/nucl/fcmae_vscyto3d_pretrained/checkpoints/epoch=89-step=28080.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_pretrained_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_NUCL_ON_A549_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__ipsc_confocal.yml new file mode 100644 index 000000000..c5193febf --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/predict__ipsc_confocal.yml @@ -0,0 +1,45 @@ +# FCMAE_VSCyto3D_Pretrained predict: nucleus (H2B) against ipsc_confocal test_cropped. +# +# Pinned to best-val checkpoint from training run J31475094 +# (val 0.3921, epoch 89). Run cancelled at epoch 172 — val plateaued +# at epoch 89 and never recovered (~83 epochs without improvement). +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: fcmae_vscyto3d_pretrained + experiment_id: nucleus__ipsc_confocal__fcmae_vscyto3d_pretrained__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/nucl/fcmae_vscyto3d_pretrained/checkpoints/epoch=89-step=28080.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_fcmae_vscyto3d_pretrained.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_PRED_NUCL + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/train.yml new file mode 100644 index 000000000..fb8990970 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/train.yml @@ -0,0 +1,64 @@ +# FCMAE-class (FullyConvolutionalMAE, pretraining=False) with FCMAE- +# pretrained encoder init on nucleus (Nuclei marker). Companion to +# fcmae_vscyto3d_scratch.yml — the two leaves are identical except this +# one loads encoder weights from the published VSCyto3D FCMAE ckpt +# (400 ep on HEK + A549 + iPSC phase data). See vs_test/finetune_3d.py +# for the canonical recipe. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/data_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + train_set: ipsc_confocal + model_name: fcmae_vscyto3d_pretrained + experiment_id: nucleus__ipsc_confocal__fcmae_vscyto3d_pretrained + +# Override the FCMAE data overlay's hardcoded `Structure` augmentation +# keys (the overlay was authored for ER/Mito where target_channel == +# "Structure"). RandWeightedCropd needs the actual nucleus channel name +# in keys/w_key. spatial_size + num_samples kept identical to the FCMAE +# overlay so the augmentation policy matches ER/Mito. +data: + init_args: + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Nuclei] + w_key: Nuclei + spatial_size: [20, 600, 600] + num_samples: 4 + +model: + init_args: + # Load only the encoder from the canonical VSCyto3D FCMAE ckpt — + # decoder/head stay at fresh init. Matches vs_test/finetune_3d.py:247. + encoder_only: true + ckpt_path: /hpc/projects/virtual_staining/models/mehta-lab/VSCyto3D/fcmae.ckpt + +trainer: + logger: + init_args: + name: FCMAE_VSCyto3D_Pretrained_iPSC_Nucleus + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/nucl/fcmae_vscyto3d_pretrained + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/nucl/fcmae_vscyto3d_pretrained/checkpoints + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_Nucleus + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/nucl/fcmae_vscyto3d_pretrained diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/train.yml new file mode 100644 index 000000000..32a7756ef --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_pretrained/joint_ipsc_confocal_a549_mantis/train.yml @@ -0,0 +1,155 @@ +# FCMAE-class (FullyConvolutionalMAE, pretraining=False) with FCMAE- +# pretrained encoder init on nucleus (NUCL) — joint +# ipsc_confocal + a549_mantis pooled. Companion to +# fcmae_vscyto3d_scratch joint leaf — the two are identical except +# this one loads encoder weights from the published VSCyto3D FCMAE +# ckpt (400 ep on HEK + A549 + iPSC phase data). Mirrors +# nucleus/fcmae_vscyto3d_pretrained/ipsc_confocal/train.yml on +# the joint train_set. +# +# Joint leaf per Stage 7 of A549_EXPANSION_ROADMAP.md. Uses +# BatchedConcatDataModule with two explicit HCSDataModule children +# (no benchmark.dataset_ref — joint leaves bypass the single-dataset +# resolver). Only model_overlays/fcmae_vscyto3d_fit.yml is composed; +# the data block is authored inline because joint hparams live on +# the children. +# +# Topology: 4-GPU DDP (inherited from +# model_overlays/fcmae_vscyto3d_fit.yml's ddp_4gpu base; the overlay +# also pins strategy=ddp_find_unused_parameters_true because +# FullyConvolutionalMAE has decoder/head params that only receive +# gradients on some forward paths). +base: + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + gene: Nuclei + target: nucleus + target_id: nucleus + train_set: joint_ipsc_confocal_a549_mantis + model_name: fcmae_vscyto3d_pretrained + experiment_id: nucleus__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_pretrained + +model: + init_args: + # Load only the encoder from the canonical VSCyto3D FCMAE ckpt — + # decoder/head stay at fresh init. Matches vs_test/finetune_3d.py:247. + encoder_only: true + ckpt_path: /hpc/projects/virtual_staining/models/mehta-lab/VSCyto3D/fcmae.ckpt + +trainer: + logger: + init_args: + name: FCMAE_VSCyto3D_Pretrained_JOINT_NUCL + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/nucl/fcmae_vscyto3d_pretrained + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/nucl/fcmae_vscyto3d_pretrained/checkpoints + +_hcs_init_args: &hcs_init_args + source_channel: Phase3D + target_channel: Nuclei + z_window_size: 20 + # batch_size is NOT divided by num_samples in joint mode (see + # nucleus/fnet3d_paper/joint_*/train.yml for the rationale): 8 + # indices * num_samples=4 = 32 GPU samples per DDP rank, matching + # single-set's effective batch. + batch_size: 8 + num_workers: 4 + yx_patch_size: [384, 384] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Nuclei] + level: fov_statistics + subtrahend: median + divisor: iqr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Nuclei] + w_key: Nuclei + spatial_size: [20, 600, 600] + num_samples: 4 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + # ipsc_confocal — aics-hipsc multi-marker cell.zarr (Nuclei channel) + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/cell.zarr + # a549_mantis — pooled H2B all-conditions train store (Nuclei channel) + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/H2B_all.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Pretrained_JOINT_NUCL + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/nucl/fcmae_vscyto3d_pretrained diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..ac0f1a961 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,49 @@ +# FCMAE_VSCyto3D_Scratch (UNeXt2) predict: nucleus trained on a549_mantis (h2b), +# predicting against a549-mantis-h2b-denv test. +# Best val-loss checkpoint from job 31822562 (epoch 110, loss/validate=0.8345). +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from targets/nucleus.yml so the resolver finds the h2b target on +# a549-mantis-h2b-denv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_denv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: a549_mantis + predict_set: a549_mantis_h2b_denv + model_name: fcmae_vscyto3d_scratch + experiment_id: nucleus__a549_mantis__fcmae_vscyto3d_scratch__a549_mantis_h2b_denv + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/nucl/fcmae_vscyto3d_scratch/checkpoints/epoch=110-step=24087.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_scratch_a549trained_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_NUCL_A549TR_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..35a05f078 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,49 @@ +# FCMAE_VSCyto3D_Scratch (UNeXt2) predict: nucleus trained on a549_mantis (h2b), +# predicting against a549-mantis-h2b-mock test. +# Best val-loss checkpoint from job 31822562 (epoch 110, loss/validate=0.8345). +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from targets/nucleus.yml so the resolver finds the h2b target on +# a549-mantis-h2b-mock. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_mock.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: a549_mantis + predict_set: a549_mantis_h2b_mock + model_name: fcmae_vscyto3d_scratch + experiment_id: nucleus__a549_mantis__fcmae_vscyto3d_scratch__a549_mantis_h2b_mock + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/nucl/fcmae_vscyto3d_scratch/checkpoints/epoch=110-step=24087.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_scratch_a549trained_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_NUCL_A549TR_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..6542d8b3a --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,49 @@ +# FCMAE_VSCyto3D_Scratch (UNeXt2) predict: nucleus trained on a549_mantis (h2b), +# predicting against a549-mantis-h2b-zikv test. +# Best val-loss checkpoint from job 31822562 (epoch 110, loss/validate=0.8345). +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from targets/nucleus.yml so the resolver finds the h2b target on +# a549-mantis-h2b-zikv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_zikv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: a549_mantis + predict_set: a549_mantis_h2b_zikv + model_name: fcmae_vscyto3d_scratch + experiment_id: nucleus__a549_mantis__fcmae_vscyto3d_scratch__a549_mantis_h2b_zikv + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/nucl/fcmae_vscyto3d_scratch/checkpoints/epoch=110-step=24087.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_scratch_a549trained_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_NUCL_A549TR_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..e8b29d777 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,43 @@ +# FCMAE_VSCyto3D_Scratch (UNeXt2) predict: nucleus trained on a549_mantis (h2b), +# predicting against ipsc_confocal test_cropped. +# Best val-loss checkpoint from job 31822562 (epoch 110, loss/validate=0.8345). +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: a549_mantis + predict_set: ipsc_confocal + model_name: fcmae_vscyto3d_scratch + experiment_id: nucleus__a549_mantis__fcmae_vscyto3d_scratch__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/nucl/fcmae_vscyto3d_scratch/checkpoints/epoch=110-step=24087.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_fcmae_vscyto3d_scratch_a549trained.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_NUCL_A549TR_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/a549_mantis/train.yml new file mode 100644 index 000000000..75cc6f2e3 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/a549_mantis/train.yml @@ -0,0 +1,59 @@ +# FCMAE-class (FullyConvolutionalMAE, pretraining=False) random-init +# baseline on nucleus (Nuclei marker). Scratch control for the pretrained +# counterpart — the two leaves are identical except this one does NOT +# load pretrained encoder weights. See UNEXT2_VS_FCMAE_CLASSES.md for +# why this is the paper-adjacent scratch baseline (and not unext2.yml). +base: + - ../../../_internal/shared/model/train_sets/a549_mantis.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/data_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + train_set: a549_mantis + model_name: fcmae_vscyto3d_scratch + experiment_id: nucleus__a549_mantis__fcmae_vscyto3d_scratch + +# Override the FCMAE data overlay's hardcoded `Structure` augmentation +# keys (the overlay was authored for ER/Mito where target_channel == +# "Structure"). RandWeightedCropd needs the actual nucleus channel name +# in keys/w_key. spatial_size + num_samples kept identical to the FCMAE +# overlay so the augmentation policy matches ER/Mito. +data: + init_args: + # A549 pooled store + target_channel — no resolver in this train_set. + target_channel: Nuclei + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/H2B_all.zarr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Nuclei] + w_key: Nuclei + spatial_size: [20, 600, 600] + num_samples: 4 + +trainer: + logger: + init_args: + name: FCMAE_VSCyto3D_Scratch_A549_Nucleus + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/nucl/fcmae_vscyto3d_scratch + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/nucl/fcmae_vscyto3d_scratch/checkpoints + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_A549_Nucleus + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/nucl/fcmae_vscyto3d_scratch diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 100644 index 000000000..377bcd03b --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Nucleus (H2B) predicted by FCMAE_VSCyto3D_Scratch on a549-mantis-h2b-denv. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from the target group so the resolver finds h2b on a549-mantis-h2b-denv. +defaults: + - override /target: nucleus + - override /predict_set: a549_mantis_h2b_denv + +benchmark: + dataset_ref: + target: h2b + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_scratch_denv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_nucl_fcmae_vscyto3d_scratch_denv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 100644 index 000000000..29ddab61e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Nucleus (H2B) predicted by FCMAE_VSCyto3D_Scratch on a549-mantis-h2b-mock. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from the target group so the resolver finds h2b on a549-mantis-h2b-mock. +defaults: + - override /target: nucleus + - override /predict_set: a549_mantis_h2b_mock + +benchmark: + dataset_ref: + target: h2b + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_scratch_mock.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_nucl_fcmae_vscyto3d_scratch_mock diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 100644 index 000000000..757a9cbd1 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Nucleus (H2B) predicted by FCMAE_VSCyto3D_Scratch on a549-mantis-h2b-zikv. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from the target group so the resolver finds h2b on a549-mantis-h2b-zikv. +defaults: + - override /target: nucleus + - override /predict_set: a549_mantis_h2b_zikv + +benchmark: + dataset_ref: + target: h2b + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_scratch_zikv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_nucl_fcmae_vscyto3d_scratch_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..0ab3747e1 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_denv.yml @@ -0,0 +1,59 @@ +# FCMAE_VSCyto3D_Scratch predict: nucleus trained on iPSC, +# predicting against a549-mantis-h2b-denv test. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side +# `nucleus` target_id from targets/nucleus.yml so the resolver finds +# the h2b target on a549-mantis-h2b-denv. +# +# TODO: replace ckpt_path once iPSC FCMAE scratch nucleus training +# completes. Expected output (per fit leaf): +# /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/nucl/fcmae_vscyto3d_scratch/checkpoints/last.ckpt +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_denv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: ipsc_confocal + predict_set: a549_mantis_h2b_denv + model_name: fcmae_vscyto3d_scratch + experiment_id: nucleus__ipsc_confocal__fcmae_vscyto3d_scratch__a549_mantis_h2b_denv + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + # Best checkpoint from J31710710 (FCMAE_VSCyto3D_Scratch_iPSC_Nucleus): + # ep 80 / val_loss 0.39342 (49-epoch plateau, scancelled at 1d 8h elapsed). + # Note: pretrained variant (J31475094, ep 89 = 0.39215) edges scratch on the + # same data; downstream eval should prefer the pretrained predict configs + # unless explicitly ablating against the scratch baseline. + # Hardlink alias at run_root; underlying epoch=80-step=25272.ckpt also + # preserved in checkpoints_frozen_ep80_/. + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/nucl/fcmae_vscyto3d_scratch/best_ep80_val0.39342.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_scratch_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_NUCL_ON_A549_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..0c69a88e3 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_mock.yml @@ -0,0 +1,59 @@ +# FCMAE_VSCyto3D_Scratch predict: nucleus trained on iPSC, +# predicting against a549-mantis-h2b-mock test. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side +# `nucleus` target_id from targets/nucleus.yml so the resolver finds +# the h2b target on a549-mantis-h2b-mock. +# +# TODO: replace ckpt_path once iPSC FCMAE scratch nucleus training +# completes. Expected output (per fit leaf): +# /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/nucl/fcmae_vscyto3d_scratch/checkpoints/last.ckpt +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_mock.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: ipsc_confocal + predict_set: a549_mantis_h2b_mock + model_name: fcmae_vscyto3d_scratch + experiment_id: nucleus__ipsc_confocal__fcmae_vscyto3d_scratch__a549_mantis_h2b_mock + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + # Best checkpoint from J31710710 (FCMAE_VSCyto3D_Scratch_iPSC_Nucleus): + # ep 80 / val_loss 0.39342 (49-epoch plateau, scancelled at 1d 8h elapsed). + # Note: pretrained variant (J31475094, ep 89 = 0.39215) edges scratch on the + # same data; downstream eval should prefer the pretrained predict configs + # unless explicitly ablating against the scratch baseline. + # Hardlink alias at run_root; underlying epoch=80-step=25272.ckpt also + # preserved in checkpoints_frozen_ep80_/. + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/nucl/fcmae_vscyto3d_scratch/best_ep80_val0.39342.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_scratch_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_NUCL_ON_A549_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..3d506036c --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/predict__a549_mantis_zikv.yml @@ -0,0 +1,59 @@ +# FCMAE_VSCyto3D_Scratch predict: nucleus trained on iPSC, +# predicting against a549-mantis-h2b-zikv test. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side +# `nucleus` target_id from targets/nucleus.yml so the resolver finds +# the h2b target on a549-mantis-h2b-zikv. +# +# TODO: replace ckpt_path once iPSC FCMAE scratch nucleus training +# completes. Expected output (per fit leaf): +# /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/nucl/fcmae_vscyto3d_scratch/checkpoints/last.ckpt +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_zikv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: ipsc_confocal + predict_set: a549_mantis_h2b_zikv + model_name: fcmae_vscyto3d_scratch + experiment_id: nucleus__ipsc_confocal__fcmae_vscyto3d_scratch__a549_mantis_h2b_zikv + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + # Best checkpoint from J31710710 (FCMAE_VSCyto3D_Scratch_iPSC_Nucleus): + # ep 80 / val_loss 0.39342 (49-epoch plateau, scancelled at 1d 8h elapsed). + # Note: pretrained variant (J31475094, ep 89 = 0.39215) edges scratch on the + # same data; downstream eval should prefer the pretrained predict configs + # unless explicitly ablating against the scratch baseline. + # Hardlink alias at run_root; underlying epoch=80-step=25272.ckpt also + # preserved in checkpoints_frozen_ep80_/. + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/nucl/fcmae_vscyto3d_scratch/best_ep80_val0.39342.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_scratch_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_NUCL_ON_A549_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/predict__ipsc_confocal.yml new file mode 100644 index 000000000..5f0034900 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/predict__ipsc_confocal.yml @@ -0,0 +1,52 @@ +# FCMAE_VSCyto3D_Scratch predict: nucleus (H2B) against ipsc_confocal test_cropped. +# +# TODO: replace ckpt_path with best-val ckpt once iPSC FCMAE scratch +# nucleus training (J31710710, resumed from J31475096) completes. Expected dir: +# /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/nucl/fcmae_vscyto3d_scratch/checkpoints/ +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: fcmae_vscyto3d_scratch + experiment_id: nucleus__ipsc_confocal__fcmae_vscyto3d_scratch__ipsc_confocal + +model: + init_args: + # Best checkpoint from J31710710 (FCMAE_VSCyto3D_Scratch_iPSC_Nucleus): + # ep 80 / val_loss 0.39342 (49-epoch plateau, scancelled at 1d 8h elapsed). + # Note: pretrained variant (J31475094, ep 89 = 0.39215) edges scratch on the + # same data; downstream eval should prefer the pretrained predict configs + # unless explicitly ablating against the scratch baseline. + # Hardlink alias at run_root; underlying epoch=80-step=25272.ckpt also + # preserved in checkpoints_frozen_ep80_/. + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/nucl/fcmae_vscyto3d_scratch/best_ep80_val0.39342.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_fcmae_vscyto3d_scratch.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_NUCL + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/train.yml new file mode 100644 index 000000000..37687d096 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/train.yml @@ -0,0 +1,56 @@ +# FCMAE-class (FullyConvolutionalMAE, pretraining=False) random-init +# baseline on nucleus (Nuclei marker). Scratch control for the pretrained +# counterpart — the two leaves are identical except this one does NOT +# load pretrained encoder weights. See UNEXT2_VS_FCMAE_CLASSES.md for +# why this is the paper-adjacent scratch baseline (and not unext2.yml). +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/data_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + train_set: ipsc_confocal + model_name: fcmae_vscyto3d_scratch + experiment_id: nucleus__ipsc_confocal__fcmae_vscyto3d_scratch + +# Override the FCMAE data overlay's hardcoded `Structure` augmentation +# keys (the overlay was authored for ER/Mito where target_channel == +# "Structure"). RandWeightedCropd needs the actual nucleus channel name +# in keys/w_key. spatial_size + num_samples kept identical to the FCMAE +# overlay so the augmentation policy matches ER/Mito. +data: + init_args: + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Nuclei] + w_key: Nuclei + spatial_size: [20, 600, 600] + num_samples: 4 + +trainer: + logger: + init_args: + name: FCMAE_VSCyto3D_Scratch_iPSC_Nucleus + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/nucl/fcmae_vscyto3d_scratch + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/nucl/fcmae_vscyto3d_scratch/checkpoints + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_Nucleus + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/nucl/fcmae_vscyto3d_scratch diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..af719c355 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,49 @@ +# FCMAE_VSCyto3D_Scratch (UNeXt2) predict: nucleus trained on joint iPSC+A549, +# predicting against a549-mantis-h2b-denv test. +# Best val-loss checkpoint from job 31822521 (epoch 92, loss/validate=0.6448). +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from targets/nucleus.yml so the resolver finds the h2b target on +# a549-mantis-h2b-denv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_denv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_h2b_denv + model_name: fcmae_vscyto3d_scratch + experiment_id: nucleus__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_scratch__a549_mantis_h2b_denv + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/nucl/fcmae_vscyto3d_scratch/checkpoints/epoch=92-step=49290.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_scratch_jointtrained_denv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_NUCL_JOINTTR_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..f98b9b3f4 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,49 @@ +# FCMAE_VSCyto3D_Scratch (UNeXt2) predict: nucleus trained on joint iPSC+A549, +# predicting against a549-mantis-h2b-mock test. +# Best val-loss checkpoint from job 31822521 (epoch 92, loss/validate=0.6448). +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from targets/nucleus.yml so the resolver finds the h2b target on +# a549-mantis-h2b-mock. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_mock.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_h2b_mock + model_name: fcmae_vscyto3d_scratch + experiment_id: nucleus__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_scratch__a549_mantis_h2b_mock + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/nucl/fcmae_vscyto3d_scratch/checkpoints/epoch=92-step=49290.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_scratch_jointtrained_mock.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_NUCL_JOINTTR_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..a4951adde --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,49 @@ +# FCMAE_VSCyto3D_Scratch (UNeXt2) predict: nucleus trained on joint iPSC+A549, +# predicting against a549-mantis-h2b-zikv test. +# Best val-loss checkpoint from job 31822521 (epoch 92, loss/validate=0.6448). +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from targets/nucleus.yml so the resolver finds the h2b target on +# a549-mantis-h2b-zikv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_zikv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_h2b_zikv + model_name: fcmae_vscyto3d_scratch + experiment_id: nucleus__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_scratch__a549_mantis_h2b_zikv + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/nucl/fcmae_vscyto3d_scratch/checkpoints/epoch=92-step=49290.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fcmae_vscyto3d_scratch_jointtrained_zikv.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_NUCL_JOINTTR_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..d0e1d2766 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,45 @@ +# FCMAE_VSCyto3D_Scratch (UNeXt2) predict: nucleus trained on joint iPSC+A549, +# predicting against ipsc_confocal test_cropped. +# Best val-loss checkpoint from job 31822521 (epoch 92, loss/validate=0.6448). +# Job hit the 4-day SLURM wall at 2026-05-05 00:43 (TIMEOUT); 5 best-val ckpts +# saved by top-K — ep92 is the best of the 5. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: ipsc_confocal + model_name: fcmae_vscyto3d_scratch + experiment_id: nucleus__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_scratch__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/nucl/fcmae_vscyto3d_scratch/checkpoints/epoch=92-step=49290.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_fcmae_vscyto3d_scratch_jointtrained.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_PRED_NUCL_JOINTTR_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/train.yml new file mode 100644 index 000000000..5c78c67fa --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fcmae_vscyto3d_scratch/joint_ipsc_confocal_a549_mantis/train.yml @@ -0,0 +1,142 @@ +# FCMAE-class (FullyConvolutionalMAE, pretraining=False) random-init +# baseline on nucleus (NUCL) — joint ipsc_confocal + +# a549_mantis pooled. Scratch control for the pretrained counterpart +# — the two leaves are identical except this one does NOT load +# pretrained encoder weights. Mirrors +# nucleus/fcmae_vscyto3d_scratch/ipsc_confocal/train.yml on the +# joint train_set. +# +# Joint leaf per Stage 7 of A549_EXPANSION_ROADMAP.md. +# BatchedConcatDataModule + two explicit HCSDataModule children; +# only model_overlays/fcmae_vscyto3d_fit.yml is composed; data +# block inline. +# +# Topology: 4-GPU DDP +# (strategy=ddp_find_unused_parameters_true inherited from +# model_overlays/fcmae_vscyto3d_fit.yml). +base: + - ../../../_internal/shared/model/model_overlays/fcmae_vscyto3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_4gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + gene: Nuclei + target: nucleus + target_id: nucleus + train_set: joint_ipsc_confocal_a549_mantis + model_name: fcmae_vscyto3d_scratch + experiment_id: nucleus__joint_ipsc_confocal_a549_mantis__fcmae_vscyto3d_scratch + +trainer: + logger: + init_args: + name: FCMAE_VSCyto3D_Scratch_JOINT_NUCL + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/nucl/fcmae_vscyto3d_scratch + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/nucl/fcmae_vscyto3d_scratch/checkpoints + +_hcs_init_args: &hcs_init_args + source_channel: Phase3D + target_channel: Nuclei + z_window_size: 20 + # See nucleus/fnet3d_paper/joint_*/train.yml for the rationale: joint + # mode does not divide batch_size by num_samples, so 8 * 4 = 32 GPU + # samples per DDP rank matches single-set effective batch. + batch_size: 8 + num_workers: 4 + yx_patch_size: [384, 384] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Nuclei] + level: fov_statistics + subtrahend: median + divisor: iqr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Nuclei] + w_key: Nuclei + spatial_size: [20, 600, 600] + num_samples: 4 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [15, 384, 384] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + # ipsc_confocal — aics-hipsc multi-marker cell.zarr (Nuclei channel) + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/cell.zarr + # a549_mantis — pooled H2B all-conditions train store (Nuclei channel) + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/H2B_all.zarr + +launcher: + job_name: FCMAE_VSCyto3D_Scratch_JOINT_NUCL + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/nucl/fcmae_vscyto3d_scratch diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..fce42f22a --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,49 @@ +# FNet3D paper-baseline predict: nucleus trained on a549_mantis (h2b), +# predicting against a549-mantis-h2b-denv test. +# Best val-loss checkpoint from job 31858491 (epoch 293, loss/validate=0.2088). +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from targets/nucleus.yml so the resolver finds the h2b target on +# a549-mantis-h2b-denv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_denv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: a549_mantis + predict_set: a549_mantis_h2b_denv + model_name: fnet3d_paper + experiment_id: nucleus__a549_mantis__fnet3d_paper__a549_mantis_h2b_denv + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/nucl/fnet3d_paper/checkpoints/epoch=293-step=199920.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fnet3d_paper_a549trained_denv.zarr + +launcher: + job_name: FNet3DPaper_PRED_NUCL_A549TR_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..7ef9a9d87 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,49 @@ +# FNet3D paper-baseline predict: nucleus trained on a549_mantis (h2b), +# predicting against a549-mantis-h2b-mock test. +# Best val-loss checkpoint from job 31858491 (epoch 293, loss/validate=0.2088). +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from targets/nucleus.yml so the resolver finds the h2b target on +# a549-mantis-h2b-mock. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_mock.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: a549_mantis + predict_set: a549_mantis_h2b_mock + model_name: fnet3d_paper + experiment_id: nucleus__a549_mantis__fnet3d_paper__a549_mantis_h2b_mock + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/nucl/fnet3d_paper/checkpoints/epoch=293-step=199920.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fnet3d_paper_a549trained_mock.zarr + +launcher: + job_name: FNet3DPaper_PRED_NUCL_A549TR_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..6c90d6d4c --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,49 @@ +# FNet3D paper-baseline predict: nucleus trained on a549_mantis (h2b), +# predicting against a549-mantis-h2b-zikv test. +# Best val-loss checkpoint from job 31858491 (epoch 293, loss/validate=0.2088). +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from targets/nucleus.yml so the resolver finds the h2b target on +# a549-mantis-h2b-zikv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_zikv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: a549_mantis + predict_set: a549_mantis_h2b_zikv + model_name: fnet3d_paper + experiment_id: nucleus__a549_mantis__fnet3d_paper__a549_mantis_h2b_zikv + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/nucl/fnet3d_paper/checkpoints/epoch=293-step=199920.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fnet3d_paper_a549trained_zikv.zarr + +launcher: + job_name: FNet3DPaper_PRED_NUCL_A549TR_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..74899f44e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,43 @@ +# FNet3D paper-baseline predict: nucleus trained on a549_mantis (h2b), +# predicting against ipsc_confocal test_cropped. +# Best val-loss checkpoint from job 31858491 (epoch 293, loss/validate=0.2088). +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: a549_mantis + predict_set: ipsc_confocal + model_name: fnet3d_paper + experiment_id: nucleus__a549_mantis__fnet3d_paper__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/nucl/fnet3d_paper/checkpoints/epoch=293-step=199920.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_fnet3d_paper_a549trained.zarr + +launcher: + job_name: FNet3DPaper_PRED_NUCL_A549TR_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/a549_mantis/train.yml new file mode 100644 index 000000000..42097f045 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/a549_mantis/train.yml @@ -0,0 +1,77 @@ +# FNet3D paper-baseline fit on nucleus (Nuclei channel of cell.zarr) — A549 mantis-lightsheet pooled (mock + DENV + ZIKV). +# The overlay's norm/aug/val_aug are keyed on Structure (the SEC61B/TOMM20 target +# channel). Nucleus target_channel is Nuclei, so we list-replace those three lists +# here to re-key them. +base: + - ../../../_internal/shared/model/train_sets/a549_mantis.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/data_overlays/fnet3d_paper_fit.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_gpu_any_long.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + train_set: a549_mantis + model_name: fnet3d_paper + experiment_id: nucleus__a549_mantis__fnet3d_paper + +data: + init_args: + # A549 pooled store + target_channel — no resolver in this train_set. + target_channel: Nuclei + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/H2B_all.zarr + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Nuclei] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Nuclei] + w_key: Nuclei + spatial_size: [32, 64, 64] + num_samples: 8 + val_augmentations: + - class_path: viscy_transforms.CenterSpatialCropd + init_args: + keys: [Phase3D, Nuclei] + roi_size: [32, 64, 64] + +trainer: + logger: + init_args: + name: FNet3D_A549_NUCL_paper + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/nucl/fnet3d_paper + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/nucl/fnet3d_paper/checkpoints + +launcher: + job_name: FNet3DPaper_A549_NUCL + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/nucl/fnet3d_paper + # 512G to match the shared headroom convention across the fnet3d + # leaves on a549/joint workloads. mmap_preload after the BasicIndexer + # fix peaks at ~75 GB for H2B_all alone (single-set) or ~185 GB for + # joint cell.zarr + H2B_all; 512G gives generous headroom. + sbatch: + mem: "512G" diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 100644 index 000000000..aac52b2f2 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Nucleus (H2B) predicted by FNet3DPaper on a549-mantis-h2b-denv. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from the target group so the resolver finds h2b on a549-mantis-h2b-denv. +defaults: + - override /target: nucleus + - override /predict_set: a549_mantis_h2b_denv + +benchmark: + dataset_ref: + target: h2b + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fnet3d_paper_denv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_nucl_fnet3d_paper_denv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 100644 index 000000000..a989ac16e --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Nucleus (H2B) predicted by FNet3DPaper on a549-mantis-h2b-mock. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from the target group so the resolver finds h2b on a549-mantis-h2b-mock. +defaults: + - override /target: nucleus + - override /predict_set: a549_mantis_h2b_mock + +benchmark: + dataset_ref: + target: h2b + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fnet3d_paper_mock.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_nucl_fnet3d_paper_mock diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 100644 index 000000000..050db00da --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Nucleus (H2B) predicted by FNet3DPaper on a549-mantis-h2b-zikv. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from the target group so the resolver finds h2b on a549-mantis-h2b-zikv. +defaults: + - override /target: nucleus + - override /predict_set: a549_mantis_h2b_zikv + +benchmark: + dataset_ref: + target: h2b + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fnet3d_paper_zikv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_nucl_fnet3d_paper_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/ipsc_confocal/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/ipsc_confocal/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..d8bb88fa2 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/ipsc_confocal/predict__a549_mantis_denv.yml @@ -0,0 +1,48 @@ +# FNet3D paper-baseline predict: nucleus trained on iPSC, predicting against a549-mantis-h2b-denv test. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from targets/nucleus.yml so the resolver finds the h2b target on +# a549-mantis-h2b-denv. +# Same iPSC best val-loss checkpoint as predict__ipsc_confocal.yml (epoch 226, loss/validate=0.7932). +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_denv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: ipsc_confocal + predict_set: a549_mantis_h2b_denv + model_name: fnet3d_paper + experiment_id: nucleus__ipsc_confocal__fnet3d_paper__a549_mantis_h2b_denv + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/nucl/fnet3d_paper/checkpoints/epoch=226-step=196582.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fnet3d_paper_denv.zarr + +launcher: + job_name: FNet3DPaper_PRED_NUCL_ON_A549_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/ipsc_confocal/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/ipsc_confocal/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..f8f61d38c --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/ipsc_confocal/predict__a549_mantis_mock.yml @@ -0,0 +1,48 @@ +# FNet3D paper-baseline predict: nucleus trained on iPSC, predicting against a549-mantis-h2b-mock test. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from targets/nucleus.yml so the resolver finds the h2b target on +# a549-mantis-h2b-mock. +# Same iPSC best val-loss checkpoint as predict__ipsc_confocal.yml (epoch 226, loss/validate=0.7932). +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_mock.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: ipsc_confocal + predict_set: a549_mantis_h2b_mock + model_name: fnet3d_paper + experiment_id: nucleus__ipsc_confocal__fnet3d_paper__a549_mantis_h2b_mock + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/nucl/fnet3d_paper/checkpoints/epoch=226-step=196582.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fnet3d_paper_mock.zarr + +launcher: + job_name: FNet3DPaper_PRED_NUCL_ON_A549_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/ipsc_confocal/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/ipsc_confocal/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..56b773fae --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/ipsc_confocal/predict__a549_mantis_zikv.yml @@ -0,0 +1,48 @@ +# FNet3D paper-baseline predict: nucleus trained on iPSC, predicting against a549-mantis-h2b-zikv test. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from targets/nucleus.yml so the resolver finds the h2b target on +# a549-mantis-h2b-zikv. +# Same iPSC best val-loss checkpoint as predict__ipsc_confocal.yml (epoch 226, loss/validate=0.7932). +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_zikv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: ipsc_confocal + predict_set: a549_mantis_h2b_zikv + model_name: fnet3d_paper + experiment_id: nucleus__ipsc_confocal__fnet3d_paper__a549_mantis_h2b_zikv + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/nucl/fnet3d_paper/checkpoints/epoch=226-step=196582.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fnet3d_paper_zikv.zarr + +launcher: + job_name: FNet3DPaper_PRED_NUCL_ON_A549_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/ipsc_confocal/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/ipsc_confocal/predict__ipsc_confocal.yml new file mode 100644 index 000000000..cc5a3b93b --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/ipsc_confocal/predict__ipsc_confocal.yml @@ -0,0 +1,42 @@ +# FNet3D paper-baseline predict: nucleus against ipsc_confocal test_cropped. +# Uses best val-loss checkpoint (epoch 226, loss/validate=0.7932). +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: fnet3d_paper + experiment_id: nucleus__ipsc_confocal__fnet3d_paper__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/nucl/fnet3d_paper/checkpoints/epoch=226-step=196582.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_fnet3d_paper.zarr + +launcher: + job_name: FNet3DPaper_PRED_NUCL + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/ipsc_confocal/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/ipsc_confocal/train.yml new file mode 100644 index 000000000..6978bc815 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/ipsc_confocal/train.yml @@ -0,0 +1,72 @@ +# FNet3D paper-baseline fit on nucleus (Nuclei channel of cell.zarr) — AICS iPSC confocal. +# The overlay's norm/aug/val_aug are keyed on Structure (the SEC61B/TOMM20 target +# channel). Nucleus target_channel is Nuclei, so we list-replace those three lists +# here to re-key them. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/data_overlays/fnet3d_paper_fit.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_gpu_any_long.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + train_set: ipsc_confocal + model_name: fnet3d_paper + experiment_id: nucleus__ipsc_confocal__fnet3d_paper + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Nuclei] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Nuclei] + w_key: Nuclei + spatial_size: [32, 64, 64] + num_samples: 8 + val_augmentations: + - class_path: viscy_transforms.CenterSpatialCropd + init_args: + keys: [Phase3D, Nuclei] + roi_size: [32, 64, 64] + +trainer: + logger: + init_args: + name: FNet3D_iPSC_NUCL_paper + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/nucl/fnet3d_paper + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/nucl/fnet3d_paper/checkpoints + +launcher: + job_name: FNet3DPaper_NUCL + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/nucl/fnet3d_paper + # cell.zarr-backed preload pushes MaxVMSize past the shared 256G cap + # (observed 264G on the first launch; worker OOM-killed in validation). + sbatch: + mem: "512G" diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..0868b75ee --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,50 @@ +# FNet3D paper-baseline predict: nucleus trained on joint iPSC+A549, +# predicting against a549-mantis-h2b-denv test. +# Best val-loss checkpoint from job 31962520 (epoch 126, val 0.9709). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from targets/nucleus.yml so the resolver finds the h2b target on +# a549-mantis-h2b-denv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_denv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_h2b_denv + model_name: fnet3d_paper + experiment_id: nucleus__joint_ipsc_confocal_a549_mantis__fnet3d_paper__a549_mantis_h2b_denv + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/nucl/fnet3d_paper/checkpoints/epoch=126-step=196342.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fnet3d_paper_jointtrained_denv.zarr + +launcher: + job_name: FNet3DPaper_PRED_NUCL_JOINTTR_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..a6eda96b6 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,50 @@ +# FNet3D paper-baseline predict: nucleus trained on joint iPSC+A549, +# predicting against a549-mantis-h2b-mock test. +# Best val-loss checkpoint from job 31962520 (epoch 126, val 0.9709). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from targets/nucleus.yml so the resolver finds the h2b target on +# a549-mantis-h2b-mock. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_mock.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_h2b_mock + model_name: fnet3d_paper + experiment_id: nucleus__joint_ipsc_confocal_a549_mantis__fnet3d_paper__a549_mantis_h2b_mock + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/nucl/fnet3d_paper/checkpoints/epoch=126-step=196342.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fnet3d_paper_jointtrained_mock.zarr + +launcher: + job_name: FNet3DPaper_PRED_NUCL_JOINTTR_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..f7d4c170f --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,50 @@ +# FNet3D paper-baseline predict: nucleus trained on joint iPSC+A549, +# predicting against a549-mantis-h2b-zikv test. +# Best val-loss checkpoint from job 31962520 (epoch 126, val 0.9709). See +# predict__ipsc_confocal.yml in this dir for full provenance. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from targets/nucleus.yml so the resolver finds the h2b target on +# a549-mantis-h2b-zikv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_zikv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_h2b_zikv + model_name: fnet3d_paper + experiment_id: nucleus__joint_ipsc_confocal_a549_mantis__fnet3d_paper__a549_mantis_h2b_zikv + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/nucl/fnet3d_paper/checkpoints/epoch=126-step=196342.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fnet3d_paper_jointtrained_zikv.zarr + +launcher: + job_name: FNet3DPaper_PRED_NUCL_JOINTTR_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..79361b568 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,44 @@ +# FNet3D paper-baseline predict: nucleus trained on joint iPSC+A549, +# predicting against ipsc_confocal test_cropped. +# Best val-loss checkpoint from job 31962520 (epoch 126, val 0.9709). +# Job completed at 2026-05-05T15:23:37 (elapsed 1d 21h 12m). +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: ipsc_confocal + model_name: fnet3d_paper + experiment_id: nucleus__joint_ipsc_confocal_a549_mantis__fnet3d_paper__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/nucl/fnet3d_paper/checkpoints/epoch=126-step=196342.ckpt + +data: + init_args: + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_fnet3d_paper_jointtrained.zarr + +launcher: + job_name: FNet3DPaper_PRED_NUCL_JOINTTR_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/joint_ipsc_confocal_a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/joint_ipsc_confocal_a549_mantis/train.yml new file mode 100644 index 000000000..2ba6acbec --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/fnet3d_paper/joint_ipsc_confocal_a549_mantis/train.yml @@ -0,0 +1,127 @@ +# FNet3D paper-baseline fit on nucleus (NUCL) — joint +# ipsc_confocal + a549_mantis pooled. Mirrors +# nucleus/fnet3d_paper/ipsc_confocal/train.yml on the joint +# train_set. +# +# Joint leaf per Stage 7 of A549_EXPANSION_ROADMAP.md. +# BatchedConcatDataModule + two explicit HCSDataModule children; +# only model_overlays/fnet3d_paper_fit.yml is composed; data block +# inline. Norms + 8-crops-per-FOV diverge from the CellDiff/UNetViT +# conventions: target channel uses mean/std (not median/iqr) and +# val augmentations are CPU CenterSpatialCropd on the raw keys (the +# baseline's training pipeline doesn't go through GPU val transforms). +# +# Topology: single GPU, any model, long wall — same as +# fnet3d_paper/ipsc_confocal/train.yml. The paper baseline is single-GPU +# and we keep that here so iPSC-only and joint runs are apples-to-apples. +base: + - ../../../_internal/shared/model/model_overlays/fnet3d_paper_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_gpu_any_long.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + gene: Nuclei + target: nucleus + target_id: nucleus + train_set: joint_ipsc_confocal_a549_mantis + model_name: fnet3d_paper + experiment_id: nucleus__joint_ipsc_confocal_a549_mantis__fnet3d_paper + +trainer: + logger: + init_args: + name: FNet3D_JOINT_NUCL_paper + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/nucl/fnet3d_paper + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/nucl/fnet3d_paper/checkpoints + +_hcs_init_args: &hcs_init_args + source_channel: Phase3D + target_channel: Nuclei + z_window_size: 32 + # batch_size in joint mode is NOT divided by RandWeightedCropd + # num_samples (BatchedConcatDataModule.train_dataloader uses + # batch_size as-is, unlike HCSDataModule.train_dataloader which + # divides by train_patches_per_stack). To match single-set's + # effective on-GPU batch of 48 (single-set's batch_size 48 / 8), + # use 6 here so 6 indices * 8 num_samples = 48 GPU samples. + batch_size: 6 + num_workers: 8 + yx_patch_size: [64, 64] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Nuclei] + level: fov_statistics + subtrahend: mean + divisor: std + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Nuclei] + w_key: Nuclei + spatial_size: [32, 64, 64] + num_samples: 8 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandFlipd + init_args: + keys: [source, target] + spatial_axes: [1] + prob: 0.5 + - class_path: viscy_transforms.BatchedRandFlipd + init_args: + keys: [source, target] + spatial_axes: [2] + prob: 0.5 + val_augmentations: + - class_path: viscy_transforms.CenterSpatialCropd + init_args: + keys: [Phase3D, Nuclei] + roi_size: [32, 64, 64] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + # ipsc_confocal — aics-hipsc multi-marker cell.zarr (Nuclei channel) + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/cell.zarr + # a549_mantis — pooled H2B all-conditions train store (Nuclei channel) + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/H2B_all.zarr + +launcher: + job_name: FNet3DPaper_JOINT_NUCL + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/nucl/fnet3d_paper + # 512G to match the shared headroom convention across the fnet3d + # leaves on a549/joint workloads. mmap_preload after the BasicIndexer + # fix peaks at ~185 GB for joint cell.zarr + H2B_all; 512G gives + # generous headroom for worker buffers and validation transients. + sbatch: + mem: "512G" diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/a549_mantis/train.yml new file mode 100644 index 000000000..63aa2b781 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/a549_mantis/train.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit fit on nucleus (Nuclei channel of cell.zarr) — A549 mantis-lightsheet pooled (mock + DENV + ZIKV). +base: + - ../../../_internal/shared/model/train_sets/a549_mantis.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/data_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + train_set: a549_mantis + model_name: pix2pix3d_unetvit + experiment_id: nucleus__a549_mantis__pix2pix3d_unetvit + +trainer: + logger: + init_args: + name: pix2pix3d_unetvit_A549_NUCL + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/nucl/pix2pix3d_unetvit + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/nucl/pix2pix3d_unetvit/checkpoints + +data: + init_args: + # A549 pooled store + target_channel — no resolver in this train_set. + target_channel: Nuclei + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/H2B_all.zarr + +launcher: + job_name: pix2pix3d_unetvit_A549_NUCL + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/a549_mantis/nucl/pix2pix3d_unetvit diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 100644 index 000000000..03bdb1fce --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: nucleus (Nuclei) predicted by pix2pix3d_unetvit on a549-mantis-h2b-denv. +defaults: + - override /target: nucleus + - override /predict_set: a549_mantis_h2b_denv + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_pix2pix3d_unetvit__h2b_denv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_nucl_pix2pix3d_unetvit__h2b_denv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 100644 index 000000000..0ea3c544b --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: nucleus (Nuclei) predicted by pix2pix3d_unetvit on a549-mantis-h2b-mock. +defaults: + - override /target: nucleus + - override /predict_set: a549_mantis_h2b_mock + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_pix2pix3d_unetvit__h2b_mock.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_nucl_pix2pix3d_unetvit__h2b_mock diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 100644 index 000000000..bbe85c533 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: nucleus (Nuclei) predicted by pix2pix3d_unetvit on a549-mantis-h2b-zikv. +defaults: + - override /target: nucleus + - override /predict_set: a549_mantis_h2b_zikv + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_pix2pix3d_unetvit__h2b_zikv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_nucl_pix2pix3d_unetvit__h2b_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/ipsc_confocal/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/ipsc_confocal/eval__ipsc_confocal.yaml new file mode 100644 index 000000000..9f548eeb9 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/ipsc_confocal/eval__ipsc_confocal.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: nucleus (Nuclei) predicted by pix2pix3d_unetvit on iPSC confocal. +defaults: + - override /target: nucleus + - override /predict_set: ipsc_confocal + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_pix2pix3d_unetvit.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/eval_nucl_pix2pix3d_unetvit diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..cf13564a8 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_denv.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: nucleus (Nuclei marker) trained on iPSC, predicting against a549_mantis_h2b_denv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_denv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: ipsc_confocal + predict_set: a549_mantis_h2b_denv + model_name: pix2pix3d_unetvit + experiment_id: nucleus__ipsc_confocal__pix2pix3d_unetvit__a549_mantis_h2b_denv + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_pix2pix3d_unetvit__h2b_denv.zarr + +launcher: + job_name: pix2pix3d_unetvit_PRED_NUCL_ON_A549_h2b_denv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..51a959b47 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_mock.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: nucleus (Nuclei marker) trained on iPSC, predicting against a549_mantis_h2b_mock test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_mock.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: ipsc_confocal + predict_set: a549_mantis_h2b_mock + model_name: pix2pix3d_unetvit + experiment_id: nucleus__ipsc_confocal__pix2pix3d_unetvit__a549_mantis_h2b_mock + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_pix2pix3d_unetvit__h2b_mock.zarr + +launcher: + job_name: pix2pix3d_unetvit_PRED_NUCL_ON_A549_h2b_mock + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..f17bb2a3b --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/ipsc_confocal/predict__a549_mantis_zikv.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: nucleus (Nuclei marker) trained on iPSC, predicting against a549_mantis_h2b_zikv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_zikv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: ipsc_confocal + predict_set: a549_mantis_h2b_zikv + model_name: pix2pix3d_unetvit + experiment_id: nucleus__ipsc_confocal__pix2pix3d_unetvit__a549_mantis_h2b_zikv + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_pix2pix3d_unetvit__h2b_zikv.zarr + +launcher: + job_name: pix2pix3d_unetvit_PRED_NUCL_ON_A549_h2b_zikv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/ipsc_confocal/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/ipsc_confocal/predict__ipsc_confocal.yml new file mode 100644 index 000000000..1d469cdee --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/ipsc_confocal/predict__ipsc_confocal.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: nucleus (Nuclei marker) against ipsc_confocal test_cropped. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: pix2pix3d_unetvit + experiment_id: nucleus__ipsc_confocal__pix2pix3d_unetvit__ipsc_confocal + +model: + init_args: + ckpt_path: REPLACE_ME_WITH_PRODUCTION_CHECKPOINT_PATH + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_pix2pix3d_unetvit.zarr + +launcher: + job_name: pix2pix3d_unetvit_PRED_NUCL + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/ipsc_confocal/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/ipsc_confocal/train.yml new file mode 100644 index 000000000..7ccba9fcd --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/ipsc_confocal/train.yml @@ -0,0 +1,37 @@ +# pix2pix3d_unetvit fit on nucleus (Nuclei channel of cell.zarr) — AICS iPSC confocal. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/data_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + train_set: ipsc_confocal + model_name: pix2pix3d_unetvit + experiment_id: nucleus__ipsc_confocal__pix2pix3d_unetvit + +trainer: + logger: + init_args: + name: pix2pix3d_unetvit_iPSC_NUCL + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/nucl/pix2pix3d_unetvit + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/nucl/pix2pix3d_unetvit/checkpoints + +launcher: + job_name: pix2pix3d_unetvit_NUCL + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/ipsc/nucl/pix2pix3d_unetvit diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..212bd86f7 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_denv.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: nucleus (Nuclei marker) trained on joint iPSC+A549, predicting against a549_mantis_h2b_denv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_denv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_h2b_denv + model_name: pix2pix3d_unetvit + experiment_id: nucleus__joint_ipsc_confocal_a549_mantis__pix2pix3d_unetvit__a549_mantis_h2b_denv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/nucl/pix2pix3d_unetvit/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/nucl_pix2pix3d_unetvit__h2b_denv.zarr + +launcher: + job_name: pix2pix3d_unetvit_JOINT_PRED_NUCL_ON_A549_h2b_denv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..fc26cf2c0 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_mock.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: nucleus (Nuclei marker) trained on joint iPSC+A549, predicting against a549_mantis_h2b_mock test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_mock.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_h2b_mock + model_name: pix2pix3d_unetvit + experiment_id: nucleus__joint_ipsc_confocal_a549_mantis__pix2pix3d_unetvit__a549_mantis_h2b_mock + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/nucl/pix2pix3d_unetvit/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/nucl_pix2pix3d_unetvit__h2b_mock.zarr + +launcher: + job_name: pix2pix3d_unetvit_JOINT_PRED_NUCL_ON_A549_h2b_mock + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..eed4382cc --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__a549_mantis_zikv.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: nucleus (Nuclei marker) trained on joint iPSC+A549, predicting against a549_mantis_h2b_zikv test. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_zikv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: a549_mantis_h2b_zikv + model_name: pix2pix3d_unetvit + experiment_id: nucleus__joint_ipsc_confocal_a549_mantis__pix2pix3d_unetvit__a549_mantis_h2b_zikv + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/nucl/pix2pix3d_unetvit/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/nucl_pix2pix3d_unetvit__h2b_zikv.zarr + +launcher: + job_name: pix2pix3d_unetvit_JOINT_PRED_NUCL_ON_A549_h2b_zikv + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml new file mode 100644 index 000000000..4779410e6 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/predict__ipsc_confocal.yml @@ -0,0 +1,43 @@ +# pix2pix3d_unetvit predict: nucleus (Nuclei marker) trained on joint iPSC+A549, predicting against ipsc_confocal test. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: joint_ipsc_confocal_a549_mantis + predict_set: ipsc_confocal + model_name: pix2pix3d_unetvit + experiment_id: nucleus__joint_ipsc_confocal_a549_mantis__pix2pix3d_unetvit__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/nucl/pix2pix3d_unetvit/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions/nucl_pix2pix3d_unetvit.zarr + +launcher: + job_name: pix2pix3d_unetvit_JOINT_PRED_NUCL_ON_IPSC + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/train.yml new file mode 100644 index 000000000..f8d5457a6 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/pix2pix3d_unetvit/joint_ipsc_confocal_a549_mantis/train.yml @@ -0,0 +1,147 @@ +# pix2pix3d_unetvit fit on nucleus (Nuclei marker) — joint ipsc_confocal + a549_mantis pooled. +# +# Joint leaf. Uses BatchedConcatDataModule with two explicit HCSDataModule +# children (no benchmark.dataset_ref — joint leaves bypass the single-dataset +# resolver). Only model_overlays/pix2pix3d_unetvit_fit.yml is composed; the +# data block is authored inline because joint hparams live on the children. +# +# Normalization is NormalizeSampled (fov_statistics) to match the single-set +# pix2pix3d_unetvit leaves — divergent from the celldiff joint which uses +# MinMaxSampled. Per-organelle prior is to keep joint and single-set +# normalizations identical so ablations are apples-to-apples. +# +# Topology: single H200, single GPU — same as pix2pix3d_unetvit/ipsc_confocal/train.yml. +base: + - ../../../_internal/shared/model/model_overlays/pix2pix3d_unetvit_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + gene: Nuclei + target: nucleus + target_id: nucleus + train_set: joint_ipsc_confocal_a549_mantis + model_name: pix2pix3d_unetvit + experiment_id: nucleus__joint_ipsc_confocal_a549_mantis__pix2pix3d_unetvit + +trainer: + logger: + init_args: + name: pix2pix3d_unetvit_JOINT_NUCL + save_dir: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/nucl/pix2pix3d_unetvit + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/nucl/pix2pix3d_unetvit/checkpoints + +# Child HCSDataModule init_args shared across both datasets (only data_path +# differs). `_`-prefixed top-level keys are stripped by load_composed_config +# before reaching LightningCLI; the merge expansion under `data:` survives. +_hcs_init_args: &hcs_init_args + source_channel: Phase3D + target_channel: Nuclei + z_window_size: 13 + # batch_size=2 + num_samples=2 → 4 GPU samples/step, matching the single-set + # pix2pix3d_unetvit (batch=4, num_samples=2). BatchedConcatDataModule does + # NOT divide by num_samples (see CLAUDE.md), so joint.batch_size = + # single_set.batch_size / num_samples. + batch_size: 2 + num_workers: 4 + yx_patch_size: [512, 512] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Nuclei] + level: fov_statistics + subtrahend: median + divisor: iqr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Nuclei] + w_key: Nuclei + spatial_size: [13, 624, 624] + num_samples: 2 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + safe_crop_size: [8, 512, 512] + safe_crop_coverage: 0.9 + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/cell.zarr + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/H2B_all.zarr + +launcher: + job_name: pix2pix3d_unetvit_JOINT_NUCL + run_root: /hpc/projects/comp.micro/virtual_staining/models/dynacell/joint_ipsc_confocal_a549_mantis/nucl/pix2pix3d_unetvit + # Joint preloads two stores (iPSC + A549 pool) into /dev/shm; default 256G + # is too tight for the iPSC marker store + A549 pool + worker overhead. + sbatch: + mem: "512G" diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/a549_mantis/train.yml new file mode 100644 index 000000000..759e49978 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/a549_mantis/train.yml @@ -0,0 +1,43 @@ +# UNetViT3D fit on nucleus (Nuclei channel of cell.zarr) — A549 mantis-lightsheet pooled (mock + DENV + ZIKV). +base: + - ../../../_internal/shared/model/train_sets/a549_mantis.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/data_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/model_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + train_set: a549_mantis + model_name: unetvit3d + experiment_id: nucleus__a549_mantis__unetvit3d + +trainer: + logger: + init_args: + name: UNetViT3D_A549_NUCL + save_dir: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/nucl/unetvit3d + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/nucl/unetvit3d/checkpoints + +data: + init_args: + # A549 pooled store + target_channel — no resolver in this train_set. + target_channel: Nuclei + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/H2B_all.zarr + +launcher: + job_name: UNetViT3D_A549_NUCL + run_root: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/a549_mantis/nucl/unetvit3d diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/ipsc_confocal/eval__a549_mantis_denv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/ipsc_confocal/eval__a549_mantis_denv.yaml new file mode 100644 index 000000000..fa77376fd --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/ipsc_confocal/eval__a549_mantis_denv.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Nucleus (H2B) predicted by UNetViT3D on a549-mantis-h2b-denv. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from the target group so the resolver finds h2b on a549-mantis-h2b-denv. +defaults: + - override /target: nucleus + - override /predict_set: a549_mantis_h2b_denv + +benchmark: + dataset_ref: + target: h2b + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucleus_unetvit3d_denv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_nucleus_unetvit3d_denv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/ipsc_confocal/eval__a549_mantis_mock.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/ipsc_confocal/eval__a549_mantis_mock.yaml new file mode 100644 index 000000000..f16267809 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/ipsc_confocal/eval__a549_mantis_mock.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Nucleus (H2B) predicted by UNetViT3D on a549-mantis-h2b-mock. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from the target group so the resolver finds h2b on a549-mantis-h2b-mock. +defaults: + - override /target: nucleus + - override /predict_set: a549_mantis_h2b_mock + +benchmark: + dataset_ref: + target: h2b + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucleus_unetvit3d_mock.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_nucleus_unetvit3d_mock diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/ipsc_confocal/eval__a549_mantis_zikv.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/ipsc_confocal/eval__a549_mantis_zikv.yaml new file mode 100644 index 000000000..c4a555eea --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/ipsc_confocal/eval__a549_mantis_zikv.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Benchmark eval leaf: Nucleus (H2B) predicted by UNetViT3D on a549-mantis-h2b-zikv. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from the target group so the resolver finds h2b on a549-mantis-h2b-zikv. +defaults: + - override /target: nucleus + - override /predict_set: a549_mantis_h2b_zikv + +benchmark: + dataset_ref: + target: h2b + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucleus_unetvit3d_zikv.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/eval_nucleus_unetvit3d_zikv diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/ipsc_confocal/eval__ipsc_confocal.yaml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/ipsc_confocal/eval__ipsc_confocal.yaml new file mode 100644 index 000000000..e22f22915 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/ipsc_confocal/eval__ipsc_confocal.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Benchmark eval leaf: Nucleus predicted by UNetViT3D on iPSC confocal. +defaults: + - override /target: nucleus + - override /predict_set: ipsc_confocal + +io: + pred_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucleus_unetvit3d.zarr + +compute_feature_metrics: true + +save: + save_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/eval_nucleus_unetvit3d diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/ipsc_confocal/predict__a549_mantis_denv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/ipsc_confocal/predict__a549_mantis_denv.yml new file mode 100644 index 000000000..052f53080 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/ipsc_confocal/predict__a549_mantis_denv.yml @@ -0,0 +1,49 @@ +# UNetViT3D predict: nucleus trained on iPSC, predicting against a549-mantis-h2b-denv test. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from targets/nucleus.yml so the resolver finds the h2b target on +# a549-mantis-h2b-denv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_denv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/unetvit3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: ipsc_confocal + predict_set: a549_mantis_h2b_denv + model_name: unetvit3d + experiment_id: nucleus__ipsc_confocal__unetvit3d__a549_mantis_h2b_denv + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/nucl/unetvit3d/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucleus_unetvit3d_denv.zarr + +launcher: + job_name: UNetViT3D_PRED_NUCLEUS_ON_A549_DENV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/ipsc_confocal/predict__a549_mantis_mock.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/ipsc_confocal/predict__a549_mantis_mock.yml new file mode 100644 index 000000000..b2f8f8095 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/ipsc_confocal/predict__a549_mantis_mock.yml @@ -0,0 +1,49 @@ +# UNetViT3D predict: nucleus trained on iPSC, predicting against a549-mantis-h2b-mock test. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from targets/nucleus.yml so the resolver finds the h2b target on +# a549-mantis-h2b-mock. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_mock.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/unetvit3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: ipsc_confocal + predict_set: a549_mantis_h2b_mock + model_name: unetvit3d + experiment_id: nucleus__ipsc_confocal__unetvit3d__a549_mantis_h2b_mock + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/nucl/unetvit3d/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucleus_unetvit3d_mock.zarr + +launcher: + job_name: UNetViT3D_PRED_NUCLEUS_ON_A549_MOCK + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/ipsc_confocal/predict__a549_mantis_zikv.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/ipsc_confocal/predict__a549_mantis_zikv.yml new file mode 100644 index 000000000..32597b74a --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/ipsc_confocal/predict__a549_mantis_zikv.yml @@ -0,0 +1,49 @@ +# UNetViT3D predict: nucleus trained on iPSC, predicting against a549-mantis-h2b-zikv test. +# A549 manifest keys nucleus by gene (`h2b`); override the iPSC-side `nucleus` +# target_id from targets/nucleus.yml so the resolver finds the h2b target on +# a549-mantis-h2b-zikv. +base: + - ../../../_internal/shared/model/predict_sets/a549_mantis_h2b_zikv.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/unetvit3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: ipsc_confocal + predict_set: a549_mantis_h2b_zikv + model_name: unetvit3d + experiment_id: nucleus__ipsc_confocal__unetvit3d__a549_mantis_h2b_zikv + # Override the iPSC-side `nucleus` target to a549's gene-keyed `h2b`. + dataset_ref: + target: h2b + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/nucl/unetvit3d/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucleus_unetvit3d_zikv.zarr + +launcher: + job_name: UNetViT3D_PRED_NUCLEUS_ON_A549_ZIKV + run_root: /hpc/projects/virtual_staining/training/dynacell/a549/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/ipsc_confocal/predict__ipsc_confocal.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/ipsc_confocal/predict__ipsc_confocal.yml new file mode 100644 index 000000000..5d8b4ca9a --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/ipsc_confocal/predict__ipsc_confocal.yml @@ -0,0 +1,43 @@ +# UNetViT3D predict: Nucleus against ipsc_confocal test_cropped. +base: + - ../../../_internal/shared/model/predict_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/model_overlays/unetvit3d_predict.yml + - ../../../_internal/shared/model/launcher_profiles/mode_predict.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_predict_any_gpu.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + trained_on: ipsc_confocal + predict_set: ipsc_confocal + model_name: unetvit3d + experiment_id: nucleus__ipsc_confocal__unetvit3d__ipsc_confocal + +model: + init_args: + ckpt_path: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/nucl/unetvit3d/checkpoints/last.ckpt + +data: + init_args: + # override target-inherited normalizations: predict only reads source + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + # clear target-inherited RandWeightedCropd; predict has no CPU augs + augmentations: [] + +trainer: + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucleus_unetvit3d.zarr + +launcher: + job_name: UNetViT3D_PRED_NUCLEUS + run_root: /hpc/projects/virtual_staining/training/dynacell/ipsc/predictions diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/ipsc_confocal/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/ipsc_confocal/train.yml new file mode 100644 index 000000000..ef6f7334a --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/ipsc_confocal/train.yml @@ -0,0 +1,37 @@ +# UNetViT3D fit on nucleus (Nuclei channel of cell.zarr) — AICS iPSC confocal. +base: + - ../../../_internal/shared/model/train_sets/ipsc_confocal.yml + - ../../../_internal/shared/model/targets/nucleus.yml + - ../../../_internal/shared/model/data_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/model_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + train_set: ipsc_confocal + model_name: unetvit3d + experiment_id: nucleus__ipsc_confocal__unetvit3d + +trainer: + logger: + init_args: + name: UNetViT3D_iPSC_NUCL + save_dir: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/nucl/unetvit3d + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/nucl/unetvit3d/checkpoints + +launcher: + job_name: UNetViT3D_NUCL + run_root: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/ipsc/nucl/unetvit3d diff --git a/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/joint_ipsc_confocal_a549_mantis/train.yml b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/joint_ipsc_confocal_a549_mantis/train.yml new file mode 100644 index 000000000..875860261 --- /dev/null +++ b/applications/dynacell/configs/benchmarks/virtual_staining/nucleus/unetvit3d/joint_ipsc_confocal_a549_mantis/train.yml @@ -0,0 +1,143 @@ +# UNetViT3D fit on nucleus (Nuclei) — joint ipsc_confocal + a549_mantis pooled. +# +# Joint leaf per Stage 7 of A549_EXPANSION_ROADMAP.md. Uses +# BatchedConcatDataModule with two explicit HCSDataModule children +# (no benchmark.dataset_ref — joint leaves bypass the single-dataset +# resolver). Only model_overlays/unetvit3d_fit.yml is composed; the data +# block is authored inline because joint hparams live on the children. +# +# iPSC source is the multi-marker cell.zarr (Brightfield, Nuclei, +# Membrane, Phase3D); A549 source is the H2B-marker pooled store +# H2B_all.zarr. The shared target_channel name is `Nuclei` in both. +# +# Topology: single H200, single GPU — same as unetvit3d/ipsc_confocal/train.yml. +# The paper baseline pattern is single-GPU and we keep that here so +# iPSC-only and joint runs are apples-to-apples. +base: + - ../../../_internal/shared/model/model_overlays/unetvit3d_fit.yml + - ../../../_internal/shared/model/launcher_profiles/mode_fit.yml + - ../../../_internal/shared/model/launcher_profiles/hardware_h200_single.yml + - ../../../_internal/shared/model/launcher_profiles/runtime_shared.yml + +benchmark: + task: virtual_staining + organelle: nucleus + gene: Nuclei + target: nucleus + target_id: nucleus + train_set: joint_ipsc_confocal_a549_mantis + model_name: unetvit3d + experiment_id: nucleus__joint_ipsc_confocal_a549_mantis__unetvit3d + +trainer: + logger: + init_args: + name: UNetViT3D_JOINT_NUCL + save_dir: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/nucl/unetvit3d + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 4 + save_last: true + dirpath: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/nucl/unetvit3d/checkpoints + +_hcs_init_args: &hcs_init_args + source_channel: Phase3D + target_channel: Nuclei + z_window_size: 13 + batch_size: 4 + num_workers: 4 + yx_patch_size: [512, 512] + split_ratio: 0.8 + mmap_preload: true + scratch_dir: /dev/shm + persistent_workers: true + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D] + level: fov_statistics + subtrahend: mean + divisor: std + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Nuclei] + level: fov_statistics + subtrahend: median + divisor: iqr + augmentations: + - class_path: viscy_transforms.RandWeightedCropd + init_args: + keys: [Phase3D, Nuclei] + w_key: Nuclei + spatial_size: [13, 624, 624] + num_samples: 2 + gpu_augmentations: + - class_path: viscy_transforms.BatchedRandAffined + init_args: + keys: [source, target] + prob: 0.8 + rotate_range: [3.14, 0, 0] + shear_range: [0.0, 0.05, 0.05] + scale_range: [[0.7, 1.3], [0.5, 1.5], [0.5, 1.5]] + safe_crop_size: [8, 512, 512] + safe_crop_coverage: 0.9 + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + - class_path: viscy_transforms.BatchedRandAdjustContrastd + init_args: + keys: [source] + prob: 0.5 + gamma: [0.8, 1.2] + - class_path: viscy_transforms.BatchedRandScaleIntensityd + init_args: + keys: [source] + prob: 0.5 + factors: 0.5 + - class_path: viscy_transforms.BatchedRandGaussianNoised + init_args: + keys: [source] + prob: 0.5 + mean: 0.0 + std: 0.3 + - class_path: viscy_transforms.BatchedRandGaussianSmoothd + init_args: + keys: [source] + prob: 0.5 + sigma_x: [0.25, 0.75] + sigma_y: [0.25, 0.75] + sigma_z: [0.25, 0.75] + val_gpu_augmentations: + - class_path: viscy_transforms.BatchedCenterSpatialCropd + init_args: + keys: [source, target] + roi_size: [8, 512, 512] + +data: + class_path: viscy_data.BatchedConcatDataModule + init_args: + data_modules: + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/cell.zarr + - class_path: viscy_data.hcs.HCSDataModule + init_args: + <<: *hcs_init_args + data_path: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/H2B_all.zarr + +launcher: + job_name: UNetViT3D_JOINT_NUCL + run_root: /hpc/projects/comp.micro/virtual_staining/models/cell_diff_vs_viscy/joint_ipsc_confocal_a549_mantis/nucl/unetvit3d + # Joint preloads two stores (iPSC + A549 pool) into /dev/shm; the default + # 256G cap is too tight (256G iPSC mem + ~50G A549 + worker peak OOMs). + # 512G is the smallest tier that fits joint preload + worker overhead. + sbatch: + mem: "512G" diff --git a/applications/dynacell/configs/evaluations/celldiff/run_eval_celldiff_a549.sh b/applications/dynacell/configs/evaluations/celldiff/run_eval_celldiff_a549.sh new file mode 100644 index 000000000..781945e1a --- /dev/null +++ b/applications/dynacell/configs/evaluations/celldiff/run_eval_celldiff_a549.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# A549 CellDiff (iterative) evaluation — 4 organelles × 3 infection conditions. + +set -euo pipefail +ml uv +source ".envrc" + +PRED_ROOT=/hpc/projects/virtual_staining/training/dynacell/a549/predictions +GT_ROOT=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1 +OUT_ROOT=/hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings + +V1_SPACING="[0.174,0.1494,0.1494]" +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +run_eval () { + local target=$1 infection=$2 gt_basename=$3 \ + pred_zarr=$4 pred_chan=$5 gt_chan=$6 spacing=$7 + local save_dir="${OUT_ROOT}/eval_celldiff_iterative_${target}_${infection}" + echo ">>> celldiff_iterative ${target} ${infection}" + uv run dynacell evaluate \ + target_name="${target}" \ + io.pred_path="${PRED_ROOT}/${pred_zarr}" \ + io.pred_channel_name="${pred_chan}" \ + io.gt_path="${GT_ROOT}/test/${gt_basename}.ozx" \ + io.gt_channel_name="${gt_chan}" \ + io.cell_segmentation_path="${GT_ROOT}/test/${gt_basename}_seg_cleaned.zarr" \ + pixel_metrics.spacing="${spacing}" \ + save.save_dir="${save_dir}" \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true +} + +# SEC61B (ER) +run_eval er mock SEC61B_mock sec61b_celldiff_iterative__sec61b_mock.zarr Structure_prediction Structure "${V1_SPACING}" +run_eval er denv SEC61B_DENV sec61b_celldiff_iterative__sec61b_denv.zarr Structure_prediction Structure "${V1_SPACING}" +run_eval er zikv SEC61B_ZIKV sec61b_celldiff_iterative__sec61b_zikv.zarr Structure_prediction Structure "${V1_SPACING}" + +# # CAAX (membrane) +# run_eval membrane mock CAAX_mock memb_celldiff_iterative_mock.zarr Membrane_prediction Membrane "${V1_SPACING}" +# run_eval membrane denv CAAX_DENV memb_celldiff_iterative_denv.zarr Membrane_prediction Membrane "${V1_SPACING}" +# run_eval membrane zikv CAAX_ZIKV memb_celldiff_iterative_zikv.zarr Membrane_prediction Membrane "${V1_SPACING}" + +# # H2B (nucleus) +# run_eval nucleus mock H2B_mock nucl_celldiff_iterative_mock.zarr Nuclei_prediction Nuclei "${V1_SPACING}" +# run_eval nucleus denv H2B_DENV nucl_celldiff_iterative_denv.zarr Nuclei_prediction Nuclei "${V1_SPACING}" +# run_eval nucleus zikv H2B_ZIKV nucl_celldiff_iterative_zikv.zarr Nuclei_prediction Nuclei "${V1_SPACING}" + +# TOMM20 (mitochondria) +run_eval mitochondria mock TOMM20_mock tomm20_celldiff_iterative__tomm20_mock.zarr Structure_prediction Structure "${V1_SPACING}" +run_eval mitochondria denv TOMM20_DENV tomm20_celldiff_iterative__tomm20_denv.zarr Structure_prediction Structure "${V1_SPACING}" +run_eval mitochondria zikv TOMM20_ZIKV tomm20_celldiff_iterative__tomm20_zikv.zarr Structure_prediction Structure "${V1_SPACING}" diff --git a/applications/dynacell/configs/evaluations/celldiff/run_eval_denoise.sh b/applications/dynacell/configs/evaluations/celldiff/run_eval_denoise.sh new file mode 100644 index 000000000..17839df8d --- /dev/null +++ b/applications/dynacell/configs/evaluations/celldiff/run_eval_denoise.sh @@ -0,0 +1,59 @@ +ml uv + +source ".envrc" + +# CELL-Diff denoise — ER (SEC61B) +uv run dynacell evaluate \ + target_name=er \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_celldiff_denoise.zarr \ + io.pred_channel_name=Structure_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/SEC61B.zarr \ + io.gt_channel_name=Structure \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/SEC61B_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations/eval_celldiff_denoise_sec61b \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt'" \ + force_recompute.all=true + +# CELL-Diff denoise — Membrane +uv run dynacell evaluate \ + target_name=membrane \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_celldiff_denoise.zarr \ + io.pred_channel_name=Membrane_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell.zarr \ + io.gt_channel_name=Membrane \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations/eval_celldiff_denoise_membrane \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt'" \ + force_recompute.all=true + +# CELL-Diff denoise — Mitochondria (TOMM20) +uv run dynacell evaluate \ + target_name=mitochondria \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_celldiff_denoise.zarr \ + io.pred_channel_name=Structure_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/TOMM20.zarr \ + io.gt_channel_name=Structure \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/TOMM20_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations/eval_celldiff_denoise_tomm20 \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt'" \ + force_recompute.all=true + +# CELL-Diff denoise — Nucleus +uv run dynacell evaluate \ + target_name=nucleus \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_celldiff_denoise.zarr \ + io.pred_channel_name=Nuclei_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell.zarr \ + io.gt_channel_name=Nuclei \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations/eval_celldiff_denoise_nucleus \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt'" \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/celldiff/run_eval_iterative.sh b/applications/dynacell/configs/evaluations/celldiff/run_eval_iterative.sh new file mode 100644 index 000000000..ce5c7645f --- /dev/null +++ b/applications/dynacell/configs/evaluations/celldiff/run_eval_iterative.sh @@ -0,0 +1,59 @@ +ml uv + +source ".envrc" + +# CELL-Diff iterative — ER (SEC61B) +uv run dynacell evaluate \ + target_name=er \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_celldiff_iterative.zarr \ + io.pred_channel_name=Structure_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/SEC61B.zarr \ + io.gt_channel_name=Structure \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/SEC61B_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations/eval_celldiff_iterative_sec61b \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt'" \ + force_recompute.all=true + +# CELL-Diff iterative — Membrane +uv run dynacell evaluate \ + target_name=membrane \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_celldiff_iterative.zarr \ + io.pred_channel_name=Membrane_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell.zarr \ + io.gt_channel_name=Membrane \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations/eval_celldiff_iterative_membrane \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt'" \ + force_recompute.all=true + +# CELL-Diff iterative — Mitochondria (TOMM20) +uv run dynacell evaluate \ + target_name=mitochondria \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_celldiff_iterative.zarr \ + io.pred_channel_name=Structure_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/TOMM20.zarr \ + io.gt_channel_name=Structure \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/TOMM20_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations/eval_celldiff_iterative_tomm20 \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt'" \ + force_recompute.all=true + +# CELL-Diff iterative — Nucleus +uv run dynacell evaluate \ + target_name=nucleus \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_celldiff_iterative.zarr \ + io.pred_channel_name=Nuclei_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell.zarr \ + io.gt_channel_name=Nuclei \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations/eval_celldiff_iterative_nucleus \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt'" \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/celldiff/run_eval_mix_trained_a549_pred_denv.sh b/applications/dynacell/configs/evaluations/celldiff/run_eval_mix_trained_a549_pred_denv.sh new file mode 100644 index 000000000..c7cfcc9de --- /dev/null +++ b/applications/dynacell/configs/evaluations/celldiff/run_eval_mix_trained_a549_pred_denv.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# CellDiff joint (iPSC+A549) model — membrane prediction on A549 DENV test set. + +set -euo pipefail +ml uv +source ".envrc" + +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +uv run dynacell evaluate \ + target_name=membrane \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/memb_celldiff_denv.zarr \ + io.pred_channel_name=Membrane_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_DENV.ozx \ + io.gt_channel_name=Membrane \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_DENV_seg_cleaned.zarr \ + pixel_metrics.spacing=[0.174,0.1494,0.1494] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/a549/joint_evaluations/eval_celldiff_joint_membrane_denv \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/celldiff/run_eval_mix_trained_a549_pred_mock.sh b/applications/dynacell/configs/evaluations/celldiff/run_eval_mix_trained_a549_pred_mock.sh new file mode 100644 index 000000000..6eb6ded19 --- /dev/null +++ b/applications/dynacell/configs/evaluations/celldiff/run_eval_mix_trained_a549_pred_mock.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# CellDiff joint (iPSC+A549) model — membrane prediction on A549 mock test set. + +set -euo pipefail +ml uv +source ".envrc" + +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +uv run dynacell evaluate \ + target_name=membrane \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/memb_celldiff_mock.zarr \ + io.pred_channel_name=Membrane_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_mock.ozx \ + io.gt_channel_name=Membrane \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_mock_seg_cleaned.zarr \ + pixel_metrics.spacing=[0.174,0.1494,0.1494] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/a549/joint_evaluations/eval_celldiff_joint_membrane_mock \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/celldiff/run_eval_mix_trained_a549_pred_zikv.sh b/applications/dynacell/configs/evaluations/celldiff/run_eval_mix_trained_a549_pred_zikv.sh new file mode 100644 index 000000000..5f9376c36 --- /dev/null +++ b/applications/dynacell/configs/evaluations/celldiff/run_eval_mix_trained_a549_pred_zikv.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# CellDiff joint (iPSC+A549) model — membrane prediction on A549 ZIKV test set. + +set -euo pipefail +ml uv +source ".envrc" + +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +uv run dynacell evaluate \ + target_name=membrane \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/memb_celldiff_zikv.zarr \ + io.pred_channel_name=Membrane_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_ZIKV.ozx \ + io.gt_channel_name=Membrane \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_ZIKV_seg_cleaned.zarr \ + pixel_metrics.spacing=[0.174,0.1494,0.1494] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/a549/joint_evaluations/eval_celldiff_joint_membrane_zikv \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/celldiff/run_eval_mix_trained_ipsc_pred.sh b/applications/dynacell/configs/evaluations/celldiff/run_eval_mix_trained_ipsc_pred.sh new file mode 100644 index 000000000..73da2af79 --- /dev/null +++ b/applications/dynacell/configs/evaluations/celldiff/run_eval_mix_trained_ipsc_pred.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# CellDiff joint (iPSC+A549) model — membrane prediction on iPSC test set. + +set -euo pipefail +ml uv +source ".envrc" + +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +# Membrane +uv run dynacell evaluate \ + target_name=membrane \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions/memb_celldiff.zarr \ + io.pred_channel_name=Membrane_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell.zarr \ + io.gt_channel_name=Membrane \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/joint_evaluations/eval_celldiff_joint_membrane \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/celldiff/run_eval_sliding_window.sh b/applications/dynacell/configs/evaluations/celldiff/run_eval_sliding_window.sh new file mode 100644 index 000000000..f5fcf8141 --- /dev/null +++ b/applications/dynacell/configs/evaluations/celldiff/run_eval_sliding_window.sh @@ -0,0 +1,59 @@ +ml uv + +source ".envrc" + +# CELL-Diff sliding window — ER (SEC61B) +uv run dynacell evaluate \ + target_name=er \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_celldiff_sliding_window.zarr \ + io.pred_channel_name=Structure_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/SEC61B.zarr \ + io.gt_channel_name=Structure \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/SEC61B_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations/eval_celldiff_sliding_window_sec61b \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt'" \ + force_recompute.all=true + +# CELL-Diff sliding window — Membrane +uv run dynacell evaluate \ + target_name=membrane \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_celldiff_sliding_window.zarr \ + io.pred_channel_name=Membrane_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell.zarr \ + io.gt_channel_name=Membrane \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations/eval_celldiff_sliding_window_membrane \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt'" \ + force_recompute.all=true + +# CELL-Diff sliding window — Mitochondria (TOMM20) +uv run dynacell evaluate \ + target_name=mitochondria \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_celldiff_sliding_window.zarr \ + io.pred_channel_name=Structure_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/TOMM20.zarr \ + io.gt_channel_name=Structure \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/TOMM20_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations/eval_celldiff_sliding_window_tomm20 \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt'" \ + force_recompute.all=true + +# CELL-Diff sliding window — Nucleus +uv run dynacell evaluate \ + target_name=nucleus \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_celldiff_sliding_window.zarr \ + io.pred_channel_name=Nuclei_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell.zarr \ + io.gt_channel_name=Nuclei \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations/eval_celldiff_sliding_window_nucleus \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt'" \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/fnet3d/run_a549_trained_a549.sh b/applications/dynacell/configs/evaluations/fnet3d/run_a549_trained_a549.sh new file mode 100644 index 000000000..de6181aeb --- /dev/null +++ b/applications/dynacell/configs/evaluations/fnet3d/run_a549_trained_a549.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# FNet3D A549-trained — evaluate on A549 test set (nucleus + membrane × 3 infections). + +set -euo pipefail +ml uv +source ".envrc" + +PRED_ROOT=/hpc/projects/virtual_staining/training/dynacell/a549/predictions +GT_ROOT=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1 +OUT_ROOT=/hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained + +V1_SPACING="[0.174,0.1494,0.1494]" +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +run_eval () { + local target=$1 infection=$2 gt_basename=$3 \ + pred_zarr=$4 pred_chan=$5 gt_chan=$6 spacing=$7 + local save_dir="${OUT_ROOT}/eval_fnet3d_a549trained_${target}_${infection}" + echo ">>> fnet3d a549trained ${target} ${infection}" + uv run dynacell evaluate \ + target_name="${target}" \ + io.pred_path="${PRED_ROOT}/${pred_zarr}" \ + io.pred_channel_name="${pred_chan}" \ + io.gt_path="${GT_ROOT}/test/${gt_basename}.ozx" \ + io.gt_channel_name="${gt_chan}" \ + io.cell_segmentation_path="${GT_ROOT}/test/${gt_basename}_seg_cleaned.zarr" \ + pixel_metrics.spacing="${spacing}" \ + save.save_dir="${save_dir}" \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true +} + +# H2B (nucleus) +run_eval nucleus mock H2B_mock nucl_fnet3d_paper_a549trained_mock.zarr Nuclei_prediction Nuclei "${V1_SPACING}" +run_eval nucleus denv H2B_DENV nucl_fnet3d_paper_a549trained_denv.zarr Nuclei_prediction Nuclei "${V1_SPACING}" +run_eval nucleus zikv H2B_ZIKV nucl_fnet3d_paper_a549trained_zikv.zarr Nuclei_prediction Nuclei "${V1_SPACING}" + +# CAAX (membrane) +run_eval membrane mock CAAX_mock memb_fnet3d_paper_a549trained_mock.zarr Membrane_prediction Membrane "${V1_SPACING}" +run_eval membrane denv CAAX_DENV memb_fnet3d_paper_a549trained_denv.zarr Membrane_prediction Membrane "${V1_SPACING}" +run_eval membrane zikv CAAX_ZIKV memb_fnet3d_paper_a549trained_zikv.zarr Membrane_prediction Membrane "${V1_SPACING}" diff --git a/applications/dynacell/configs/evaluations/fnet3d/run_a549_trained_ipsc.sh b/applications/dynacell/configs/evaluations/fnet3d/run_a549_trained_ipsc.sh new file mode 100644 index 000000000..03996ecac --- /dev/null +++ b/applications/dynacell/configs/evaluations/fnet3d/run_a549_trained_ipsc.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# FNet3D A549-trained — evaluate on iPSC test set (nucleus + membrane). + +set -euo pipefail +ml uv +source ".envrc" + +PRED_ROOT=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions +GT_ROOT=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped +OUT_ROOT=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_a549trained + +IPSC_SPACING="[0.29,0.108,0.108]" +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +# Nucleus (H2B) +echo ">>> fnet3d a549trained nucleus (iPSC)" +uv run dynacell evaluate \ + target_name=nucleus \ + io.pred_path="${PRED_ROOT}/nucl_fnet3d_paper_a549trained.zarr" \ + io.pred_channel_name=Nuclei_prediction \ + io.gt_path="${GT_ROOT}/cell.zarr" \ + io.gt_channel_name=Nuclei \ + io.cell_segmentation_path="${GT_ROOT}/cell_segmented_cleaned.zarr" \ + pixel_metrics.spacing="${IPSC_SPACING}" \ + save.save_dir="${OUT_ROOT}/eval_fnet3d_a549trained_nucleus" \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true + +# Membrane (CAAX) +echo ">>> fnet3d a549trained membrane (iPSC)" +uv run dynacell evaluate \ + target_name=membrane \ + io.pred_path="${PRED_ROOT}/memb_fnet3d_paper_a549trained.zarr" \ + io.pred_channel_name=Membrane_prediction \ + io.gt_path="${GT_ROOT}/cell.zarr" \ + io.gt_channel_name=Membrane \ + io.cell_segmentation_path="${GT_ROOT}/cell_segmented_cleaned.zarr" \ + pixel_metrics.spacing="${IPSC_SPACING}" \ + save.save_dir="${OUT_ROOT}/eval_fnet3d_a549trained_membrane" \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/fnet3d/run_eval_fnet3d.sh b/applications/dynacell/configs/evaluations/fnet3d/run_eval_fnet3d.sh new file mode 100644 index 000000000..fa59cab8c --- /dev/null +++ b/applications/dynacell/configs/evaluations/fnet3d/run_eval_fnet3d.sh @@ -0,0 +1,59 @@ +ml uv + +source ".envrc" + +# FNet3D — ER (SEC61B) +uv run dynacell evaluate \ + target_name=er \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_fnet3d_paper.zarr \ + io.pred_channel_name=Structure_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/SEC61B.zarr \ + io.gt_channel_name=Structure \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/SEC61B_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations/eval_fnet3d_sec61b \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt'" \ + force_recompute.all=true + +# FNet3D — Membrane +uv run dynacell evaluate \ + target_name=membrane \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_fnet3d_paper.zarr \ + io.pred_channel_name=Membrane_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell.zarr \ + io.gt_channel_name=Membrane \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations/eval_fnet3d_membrane \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt'" \ + force_recompute.all=true + +# FNet3D — Mitochondria (TOMM20) +uv run dynacell evaluate \ + target_name=mitochondria \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_fnet3d_paper.zarr \ + io.pred_channel_name=Structure_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/TOMM20.zarr \ + io.gt_channel_name=Structure \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/TOMM20_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations/eval_fnet3d_tomm20 \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt'" \ + force_recompute.all=true + +# FNet3D — Nucleus +uv run dynacell evaluate \ + target_name=nucleus \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_fnet3d_paper.zarr \ + io.pred_channel_name=Nuclei_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell.zarr \ + io.gt_channel_name=Nuclei \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations/eval_fnet3d_nucleus \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt'" \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/fnet3d/run_eval_fnet3d_a549.sh b/applications/dynacell/configs/evaluations/fnet3d/run_eval_fnet3d_a549.sh new file mode 100755 index 000000000..38bb128bc --- /dev/null +++ b/applications/dynacell/configs/evaluations/fnet3d/run_eval_fnet3d_a549.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# A549 FNet3D evaluation — 4 organelles × 3 infection conditions. + +set -euo pipefail +ml uv +source ".envrc" + +PRED_ROOT=/hpc/projects/virtual_staining/training/dynacell/a549/predictions +GT_ROOT=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1 +OUT_ROOT=/hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings + +V1_SPACING="[0.174,0.1494,0.1494]" +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +run_eval () { + local target=$1 infection=$2 gt_basename=$3 \ + pred_zarr=$4 pred_chan=$5 gt_chan=$6 spacing=$7 + local save_dir="${OUT_ROOT}/eval_fnet3d_${target}_${infection}" + echo ">>> fnet3d ${target} ${infection}" + uv run dynacell evaluate \ + target_name="${target}" \ + io.pred_path="${PRED_ROOT}/${pred_zarr}" \ + io.pred_channel_name="${pred_chan}" \ + io.gt_path="${GT_ROOT}/test/${gt_basename}.ozx" \ + io.gt_channel_name="${gt_chan}" \ + io.cell_segmentation_path="${GT_ROOT}/test/${gt_basename}_seg_cleaned.zarr" \ + pixel_metrics.spacing="${spacing}" \ + save.save_dir="${save_dir}" \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true +} + +# SEC61B (ER) +run_eval er mock SEC61B_mock sec61b_fnet3d_paper__sec61b_mock.zarr Structure_prediction Structure "${V1_SPACING}" +run_eval er denv SEC61B_DENV sec61b_fnet3d_paper__sec61b_denv.zarr Structure_prediction Structure "${V1_SPACING}" +run_eval er zikv SEC61B_ZIKV sec61b_fnet3d_paper__sec61b_zikv.zarr Structure_prediction Structure "${V1_SPACING}" + +# CAAX (membrane) +# run_eval membrane mock CAAX_mock memb_fnet3d_paper_mock.zarr Membrane_prediction Membrane "${V1_SPACING}" +# run_eval membrane denv CAAX_DENV memb_fnet3d_paper_denv.zarr Membrane_prediction Membrane "${V1_SPACING}" +# run_eval membrane zikv CAAX_ZIKV memb_fnet3d_paper_zikv.zarr Membrane_prediction Membrane "${V1_SPACING}" + +# H2B (nucleus) +# run_eval nucleus mock H2B_mock nucl_fnet3d_paper_mock.zarr Nuclei_prediction Nuclei "${V1_SPACING}" +# run_eval nucleus denv H2B_DENV nucl_fnet3d_paper_denv.zarr Nuclei_prediction Nuclei "${V1_SPACING}" +# run_eval nucleus zikv H2B_ZIKV nucl_fnet3d_paper_zikv.zarr Nuclei_prediction Nuclei "${V1_SPACING}" + +# TOMM20 (mitochondria) +run_eval mitochondria mock TOMM20_mock tomm20_fnet3d_paper__tomm20_mock.zarr Structure_prediction Structure "${V1_SPACING}" +run_eval mitochondria denv TOMM20_DENV tomm20_fnet3d_paper__tomm20_denv.zarr Structure_prediction Structure "${V1_SPACING}" +run_eval mitochondria zikv TOMM20_ZIKV tomm20_fnet3d_paper__tomm20_zikv.zarr Structure_prediction Structure "${V1_SPACING}" diff --git a/applications/dynacell/configs/evaluations/fnet3d/run_eval_fnet3d_jointtrained_a549.sh b/applications/dynacell/configs/evaluations/fnet3d/run_eval_fnet3d_jointtrained_a549.sh new file mode 100755 index 000000000..6e357c9d3 --- /dev/null +++ b/applications/dynacell/configs/evaluations/fnet3d/run_eval_fnet3d_jointtrained_a549.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +# FNet3D joint-trained (iPSC + A549 mantis) — evaluate on A549 test set (nucleus × 3 infections). +# Membrane already evaluated under joint_evaluations/eval_fnet3d_joint_membrane_. + +set -euo pipefail +ml uv +source ".envrc" + +PRED_ROOT=/hpc/projects/virtual_staining/training/dynacell/a549/predictions +GT_ROOT=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1 +OUT_ROOT=/hpc/projects/virtual_staining/training/dynacell/a549/joint_evaluations + +V1_SPACING="[0.174,0.1494,0.1494]" +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +mkdir -p "${OUT_ROOT}" + +run_eval () { + local infection=$1 gt_basename=$2 pred_zarr=$3 + local save_dir="${OUT_ROOT}/eval_fnet3d_joint_nucleus_${infection}" + echo ">>> fnet3d joint nucleus ${infection}" + uv run dynacell evaluate \ + target_name=nucleus \ + io.pred_path="${PRED_ROOT}/${pred_zarr}" \ + io.pred_channel_name=Nuclei_prediction \ + io.gt_path="${GT_ROOT}/test/${gt_basename}.ozx" \ + io.gt_channel_name=Nuclei \ + io.cell_segmentation_path="${GT_ROOT}/test/${gt_basename}_seg_cleaned.zarr" \ + pixel_metrics.spacing="${V1_SPACING}" \ + save.save_dir="${save_dir}" \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true +} + +run_eval mock H2B_mock nucl_fnet3d_paper_jointtrained_mock.zarr +run_eval denv H2B_DENV nucl_fnet3d_paper_jointtrained_denv.zarr +run_eval zikv H2B_ZIKV nucl_fnet3d_paper_jointtrained_zikv.zarr diff --git a/applications/dynacell/configs/evaluations/fnet3d/run_eval_fnet3d_jointtrained_ipsc.sh b/applications/dynacell/configs/evaluations/fnet3d/run_eval_fnet3d_jointtrained_ipsc.sh new file mode 100755 index 000000000..d48e232e3 --- /dev/null +++ b/applications/dynacell/configs/evaluations/fnet3d/run_eval_fnet3d_jointtrained_ipsc.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# FNet3D joint-trained (iPSC + A549 mantis) — evaluate on iPSC test set (nucleus). +# Membrane already evaluated under joint_evaluations/eval_fnet3d_joint_membrane. + +set -euo pipefail +ml uv +source ".envrc" + +PRED_ROOT=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions +GT_ROOT=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped +OUT_ROOT=/hpc/projects/virtual_staining/training/dynacell/ipsc/joint_evaluations + +IPSC_SPACING="[0.29,0.108,0.108]" +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +mkdir -p "${OUT_ROOT}" + +echo ">>> fnet3d joint nucleus (iPSC)" +uv run dynacell evaluate \ + target_name=nucleus \ + io.pred_path="${PRED_ROOT}/nucl_fnet3d_paper_jointtrained.zarr" \ + io.pred_channel_name=Nuclei_prediction \ + io.gt_path="${GT_ROOT}/cell.zarr" \ + io.gt_channel_name=Nuclei \ + io.cell_segmentation_path="${GT_ROOT}/cell_segmented_cleaned.zarr" \ + pixel_metrics.spacing="${IPSC_SPACING}" \ + save.save_dir="${OUT_ROOT}/eval_fnet3d_joint_nucleus" \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/fnet3d/run_eval_mix_trained_a549_pred_membrane_denv.sh b/applications/dynacell/configs/evaluations/fnet3d/run_eval_mix_trained_a549_pred_membrane_denv.sh new file mode 100644 index 000000000..7b614d282 --- /dev/null +++ b/applications/dynacell/configs/evaluations/fnet3d/run_eval_mix_trained_a549_pred_membrane_denv.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# FNet3D joint (iPSC+A549) model — membrane prediction on A549 DENV test set. + +set -euo pipefail +ml uv +source ".envrc" + +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +uv run dynacell evaluate \ + target_name=membrane \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fnet3d_paper_jointtrained_denv.zarr \ + io.pred_channel_name=Membrane_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_DENV.ozx \ + io.gt_channel_name=Membrane \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_DENV_seg_cleaned.zarr \ + pixel_metrics.spacing=[0.174,0.1494,0.1494] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/a549/joint_evaluations/eval_fnet3d_joint_membrane_denv \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/fnet3d/run_eval_mix_trained_a549_pred_membrane_mock.sh b/applications/dynacell/configs/evaluations/fnet3d/run_eval_mix_trained_a549_pred_membrane_mock.sh new file mode 100644 index 000000000..ee3ab8fd8 --- /dev/null +++ b/applications/dynacell/configs/evaluations/fnet3d/run_eval_mix_trained_a549_pred_membrane_mock.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# FNet3D joint (iPSC+A549) model — membrane prediction on A549 mock test set. + +set -euo pipefail +ml uv +source ".envrc" + +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +uv run dynacell evaluate \ + target_name=membrane \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fnet3d_paper_jointtrained_mock.zarr \ + io.pred_channel_name=Membrane_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_mock.ozx \ + io.gt_channel_name=Membrane \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_mock_seg_cleaned.zarr \ + pixel_metrics.spacing=[0.174,0.1494,0.1494] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/a549/joint_evaluations/eval_fnet3d_joint_membrane_mock \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/fnet3d/run_eval_mix_trained_a549_pred_membrane_zikv.sh b/applications/dynacell/configs/evaluations/fnet3d/run_eval_mix_trained_a549_pred_membrane_zikv.sh new file mode 100644 index 000000000..d98172220 --- /dev/null +++ b/applications/dynacell/configs/evaluations/fnet3d/run_eval_mix_trained_a549_pred_membrane_zikv.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# FNet3D joint (iPSC+A549) model — membrane prediction on A549 ZIKV test set. + +set -euo pipefail +ml uv +source ".envrc" + +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +uv run dynacell evaluate \ + target_name=membrane \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/a549/predictions/memb_fnet3d_paper_jointtrained_zikv.zarr \ + io.pred_channel_name=Membrane_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_ZIKV.ozx \ + io.gt_channel_name=Membrane \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_ZIKV_seg_cleaned.zarr \ + pixel_metrics.spacing=[0.174,0.1494,0.1494] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/a549/joint_evaluations/eval_fnet3d_joint_membrane_zikv \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/fnet3d/run_eval_mix_trained_a549_pred_nucleus_denv.sh b/applications/dynacell/configs/evaluations/fnet3d/run_eval_mix_trained_a549_pred_nucleus_denv.sh new file mode 100644 index 000000000..de10ee080 --- /dev/null +++ b/applications/dynacell/configs/evaluations/fnet3d/run_eval_mix_trained_a549_pred_nucleus_denv.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# FNet3D joint (iPSC+A549) model — nucleus prediction on A549 DENV test set. + +set -euo pipefail +ml uv +source ".envrc" + +uv run dynacell evaluate \ + target_name=nucleus \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fnet3d_paper_jointtrained_denv.zarr \ + io.pred_channel_name=Nuclei_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/H2B_DENV.ozx \ + io.gt_channel_name=Nuclei \ + pixel_metrics.spacing=[0.174,0.1494,0.1494] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/a549/joint_evaluations/eval_fnet3d_joint_nucleus_denv \ + compute_feature_metrics=true \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/fnet3d/run_eval_mix_trained_a549_pred_nucleus_mock.sh b/applications/dynacell/configs/evaluations/fnet3d/run_eval_mix_trained_a549_pred_nucleus_mock.sh new file mode 100644 index 000000000..0afeded55 --- /dev/null +++ b/applications/dynacell/configs/evaluations/fnet3d/run_eval_mix_trained_a549_pred_nucleus_mock.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# FNet3D joint (iPSC+A549) model — nucleus prediction on A549 mock test set. + +set -euo pipefail +ml uv +source ".envrc" + +uv run dynacell evaluate \ + target_name=nucleus \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fnet3d_paper_jointtrained_mock.zarr \ + io.pred_channel_name=Nuclei_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/H2B_mock.ozx \ + io.gt_channel_name=Nuclei \ + pixel_metrics.spacing=[0.174,0.1494,0.1494] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/a549/joint_evaluations/eval_fnet3d_joint_nucleus_mock \ + compute_feature_metrics=true \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/fnet3d/run_eval_mix_trained_a549_pred_nucleus_zikv.sh b/applications/dynacell/configs/evaluations/fnet3d/run_eval_mix_trained_a549_pred_nucleus_zikv.sh new file mode 100644 index 000000000..e968485f2 --- /dev/null +++ b/applications/dynacell/configs/evaluations/fnet3d/run_eval_mix_trained_a549_pred_nucleus_zikv.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# FNet3D joint (iPSC+A549) model — nucleus prediction on A549 ZIKV test set. + +set -euo pipefail +ml uv +source ".envrc" + +uv run dynacell evaluate \ + target_name=nucleus \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/a549/predictions/nucl_fnet3d_paper_jointtrained_zikv.zarr \ + io.pred_channel_name=Nuclei_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/H2B_ZIKV.ozx \ + io.gt_channel_name=Nuclei \ + pixel_metrics.spacing=[0.174,0.1494,0.1494] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/a549/joint_evaluations/eval_fnet3d_joint_nucleus_zikv \ + compute_feature_metrics=true \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/fnet3d/run_eval_mix_trained_ipsc_pred.sh b/applications/dynacell/configs/evaluations/fnet3d/run_eval_mix_trained_ipsc_pred.sh new file mode 100644 index 000000000..539cfa239 --- /dev/null +++ b/applications/dynacell/configs/evaluations/fnet3d/run_eval_mix_trained_ipsc_pred.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# FNet3D joint (iPSC+A549) model — membrane prediction on iPSC test set. + +set -euo pipefail +ml uv +source ".envrc" + +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +uv run dynacell evaluate \ + target_name=membrane \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions/memb_fnet3d_paper_jointtrained.zarr \ + io.pred_channel_name=Membrane_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell.zarr \ + io.gt_channel_name=Membrane \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/joint_evaluations/eval_fnet3d_joint_membrane \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/unetvit3d/run_eval_unetvit3d.sh b/applications/dynacell/configs/evaluations/unetvit3d/run_eval_unetvit3d.sh new file mode 100644 index 000000000..b06680bd7 --- /dev/null +++ b/applications/dynacell/configs/evaluations/unetvit3d/run_eval_unetvit3d.sh @@ -0,0 +1,59 @@ +ml uv + +source ".envrc" + +# UNetViT3D — ER (SEC61B) +uv run dynacell evaluate \ + target_name=er \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_unetvit3d.zarr \ + io.pred_channel_name=Structure_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/SEC61B.zarr \ + io.gt_channel_name=Structure \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/SEC61B_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations/eval_unetvit3d_sec61b \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt'" \ + force_recompute.all=true + +# UNetViT3D — Membrane +uv run dynacell evaluate \ + target_name=membrane \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_unetvit3d.zarr \ + io.pred_channel_name=Membrane_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell.zarr \ + io.gt_channel_name=Membrane \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations/eval_unetvit3d_membrane \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt'" \ + force_recompute.all=true + +# UNetViT3D — Mitochondria (TOMM20) +uv run dynacell evaluate \ + target_name=mitochondria \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_unetvit3d.zarr \ + io.pred_channel_name=Structure_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/TOMM20.zarr \ + io.gt_channel_name=Structure \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/TOMM20_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations/eval_unetvit3d_tomm20 \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt'" \ + force_recompute.all=true + +# UNetViT3D — Nucleus +uv run dynacell evaluate \ + target_name=nucleus \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_unetvit3d.zarr \ + io.pred_channel_name=Nuclei_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell.zarr \ + io.gt_channel_name=Nuclei \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations/eval_unetvit3d_nucleus \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt'" \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/unetvit3d/run_eval_unetvit3d_a549.sh b/applications/dynacell/configs/evaluations/unetvit3d/run_eval_unetvit3d_a549.sh new file mode 100755 index 000000000..847f82ab5 --- /dev/null +++ b/applications/dynacell/configs/evaluations/unetvit3d/run_eval_unetvit3d_a549.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# A549 UNetViT3D evaluation — 4 organelles × 3 infection conditions. + +set -euo pipefail +ml uv +source ".envrc" + +PRED_ROOT=/hpc/projects/virtual_staining/training/dynacell/a549/predictions +GT_ROOT=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1 +OUT_ROOT=/hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings + +V1_SPACING="[0.174,0.1494,0.1494]" +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +run_eval () { + local target=$1 infection=$2 gt_basename=$3 \ + pred_zarr=$4 pred_chan=$5 gt_chan=$6 spacing=$7 + local save_dir="${OUT_ROOT}/eval_unetvit3d_${target}_${infection}" + echo ">>> unetvit3d ${target} ${infection}" + uv run dynacell evaluate \ + target_name="${target}" \ + io.pred_path="${PRED_ROOT}/${pred_zarr}" \ + io.pred_channel_name="${pred_chan}" \ + io.gt_path="${GT_ROOT}/test/${gt_basename}.ozx" \ + io.gt_channel_name="${gt_chan}" \ + io.cell_segmentation_path="${GT_ROOT}/test/${gt_basename}_seg_cleaned.zarr" \ + pixel_metrics.spacing="${spacing}" \ + save.save_dir="${save_dir}" \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true +} + +# SEC61B (ER) +run_eval er mock SEC61B_mock sec61b_unetvit3d__sec61b_mock.zarr Structure_prediction Structure "${V1_SPACING}" +run_eval er denv SEC61B_DENV sec61b_unetvit3d__sec61b_denv.zarr Structure_prediction Structure "${V1_SPACING}" +run_eval er zikv SEC61B_ZIKV sec61b_unetvit3d__sec61b_zikv.zarr Structure_prediction Structure "${V1_SPACING}" + +# CAAX (membrane) +# run_eval membrane mock CAAX_mock memb_unetvit3d_mock.zarr Membrane_prediction Membrane "${V1_SPACING}" +# run_eval membrane denv CAAX_DENV memb_unetvit3d_denv.zarr Membrane_prediction Membrane "${V1_SPACING}" +# run_eval membrane zikv CAAX_ZIKV memb_unetvit3d_zikv.zarr Membrane_prediction Membrane "${V1_SPACING}" + +# H2B (nucleus) +# run_eval nucleus mock H2B_mock nucleus_unetvit3d_mock.zarr Nuclei_prediction Nuclei "${V1_SPACING}" +# run_eval nucleus denv H2B_DENV nucleus_unetvit3d_denv.zarr Nuclei_prediction Nuclei "${V1_SPACING}" +# run_eval nucleus zikv H2B_ZIKV nucleus_unetvit3d_zikv.zarr Nuclei_prediction Nuclei "${V1_SPACING}" + +# TOMM20 (mitochondria) +run_eval mitochondria mock TOMM20_mock tomm20_unetvit3d__tomm20_mock.zarr Structure_prediction Structure "${V1_SPACING}" +run_eval mitochondria denv TOMM20_DENV tomm20_unetvit3d__tomm20_denv.zarr Structure_prediction Structure "${V1_SPACING}" +run_eval mitochondria zikv TOMM20_ZIKV tomm20_unetvit3d__tomm20_zikv.zarr Structure_prediction Structure "${V1_SPACING}" diff --git a/applications/dynacell/configs/evaluations/unext2/run_a549_trained_a549.sh b/applications/dynacell/configs/evaluations/unext2/run_a549_trained_a549.sh new file mode 100644 index 000000000..35e68123d --- /dev/null +++ b/applications/dynacell/configs/evaluations/unext2/run_a549_trained_a549.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# UNeXt2 (fcmae_vscyto3d_scratch) A549-trained — evaluate on A549 test set (nucleus + membrane × 3 infections). + +set -euo pipefail +ml uv +source ".envrc" + +PRED_ROOT=/hpc/projects/virtual_staining/training/dynacell/a549/predictions +GT_ROOT=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1 +OUT_ROOT=/hpc/projects/virtual_staining/training/dynacell/a549/evaluations_a549trained + +V1_SPACING="[0.174,0.1494,0.1494]" +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +run_eval () { + local target=$1 infection=$2 gt_basename=$3 \ + pred_zarr=$4 pred_chan=$5 gt_chan=$6 spacing=$7 + local save_dir="${OUT_ROOT}/eval_unext2_a549trained_${target}_${infection}" + echo ">>> unext2 a549trained ${target} ${infection}" + uv run dynacell evaluate \ + target_name="${target}" \ + io.pred_path="${PRED_ROOT}/${pred_zarr}" \ + io.pred_channel_name="${pred_chan}" \ + io.gt_path="${GT_ROOT}/test/${gt_basename}.ozx" \ + io.gt_channel_name="${gt_chan}" \ + io.cell_segmentation_path="${GT_ROOT}/test/${gt_basename}_seg_cleaned.zarr" \ + pixel_metrics.spacing="${spacing}" \ + save.save_dir="${save_dir}" \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true +} + +# H2B (nucleus) +run_eval nucleus mock H2B_mock nucl_fcmae_vscyto3d_scratch_a549trained_mock.zarr Nuclei_prediction Nuclei "${V1_SPACING}" +run_eval nucleus denv H2B_DENV nucl_fcmae_vscyto3d_scratch_a549trained_denv.zarr Nuclei_prediction Nuclei "${V1_SPACING}" +run_eval nucleus zikv H2B_ZIKV nucl_fcmae_vscyto3d_scratch_a549trained_zikv.zarr Nuclei_prediction Nuclei "${V1_SPACING}" + +# CAAX (membrane) +run_eval membrane mock CAAX_mock memb_fcmae_vscyto3d_scratch_a549trained_mock.zarr Membrane_prediction Membrane "${V1_SPACING}" +run_eval membrane denv CAAX_DENV memb_fcmae_vscyto3d_scratch_a549trained_denv.zarr Membrane_prediction Membrane "${V1_SPACING}" +run_eval membrane zikv CAAX_ZIKV memb_fcmae_vscyto3d_scratch_a549trained_zikv.zarr Membrane_prediction Membrane "${V1_SPACING}" diff --git a/applications/dynacell/configs/evaluations/unext2/run_a549_trained_ipsc.sh b/applications/dynacell/configs/evaluations/unext2/run_a549_trained_ipsc.sh new file mode 100644 index 000000000..e97280bc8 --- /dev/null +++ b/applications/dynacell/configs/evaluations/unext2/run_a549_trained_ipsc.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# UNeXt2 (fcmae_vscyto3d_scratch) A549-trained — evaluate on iPSC test set (nucleus + membrane). + +set -euo pipefail +ml uv +source ".envrc" + +PRED_ROOT=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions +GT_ROOT=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped +OUT_ROOT=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations_a549trained + +IPSC_SPACING="[0.29,0.108,0.108]" +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +# Nucleus (H2B) +echo ">>> unext2 a549trained nucleus (iPSC)" +uv run dynacell evaluate \ + target_name=nucleus \ + io.pred_path="${PRED_ROOT}/nucl_fcmae_vscyto3d_scratch_a549trained.zarr" \ + io.pred_channel_name=Nuclei_prediction \ + io.gt_path="${GT_ROOT}/cell.zarr" \ + io.gt_channel_name=Nuclei \ + io.cell_segmentation_path="${GT_ROOT}/cell_segmented_cleaned.zarr" \ + pixel_metrics.spacing="${IPSC_SPACING}" \ + save.save_dir="${OUT_ROOT}/eval_unext2_a549trained_nucleus" \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true + +# Membrane (CAAX) +echo ">>> unext2 a549trained membrane (iPSC)" +uv run dynacell evaluate \ + target_name=membrane \ + io.pred_path="${PRED_ROOT}/memb_fcmae_vscyto3d_scratch_a549trained.zarr" \ + io.pred_channel_name=Membrane_prediction \ + io.gt_path="${GT_ROOT}/cell.zarr" \ + io.gt_channel_name=Membrane \ + io.cell_segmentation_path="${GT_ROOT}/cell_segmented_cleaned.zarr" \ + pixel_metrics.spacing="${IPSC_SPACING}" \ + save.save_dir="${OUT_ROOT}/eval_unext2_a549trained_membrane" \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/unext2/run_eval_mix_trained_a549_pred_denv.sh b/applications/dynacell/configs/evaluations/unext2/run_eval_mix_trained_a549_pred_denv.sh new file mode 100644 index 000000000..892fa6be7 --- /dev/null +++ b/applications/dynacell/configs/evaluations/unext2/run_eval_mix_trained_a549_pred_denv.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# UNeXt2 joint (iPSC+A549) model — membrane prediction on A549 DENV test set. + +set -euo pipefail +ml uv +source ".envrc" + +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +uv run dynacell evaluate \ + target_name=membrane \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/memb_fcmae_vscyto3d_scratch_jointtrained_denv.zarr \ + io.pred_channel_name=Membrane_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_DENV.ozx \ + io.gt_channel_name=Membrane \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_DENV_seg_cleaned.zarr \ + pixel_metrics.spacing=[0.174,0.1494,0.1494] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/a549/joint_evaluations/eval_unext2_joint_membrane_denv \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/unext2/run_eval_mix_trained_a549_pred_mock.sh b/applications/dynacell/configs/evaluations/unext2/run_eval_mix_trained_a549_pred_mock.sh new file mode 100644 index 000000000..af4d918fc --- /dev/null +++ b/applications/dynacell/configs/evaluations/unext2/run_eval_mix_trained_a549_pred_mock.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# UNeXt2 joint (iPSC+A549) model — membrane prediction on A549 mock test set. + +set -euo pipefail +ml uv +source ".envrc" + +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +uv run dynacell evaluate \ + target_name=membrane \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/memb_fcmae_vscyto3d_scratch_jointtrained_mock.zarr \ + io.pred_channel_name=Membrane_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_mock.ozx \ + io.gt_channel_name=Membrane \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_mock_seg_cleaned.zarr \ + pixel_metrics.spacing=[0.174,0.1494,0.1494] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/a549/joint_evaluations/eval_unext2_joint_membrane_mock \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/unext2/run_eval_mix_trained_a549_pred_zikv.sh b/applications/dynacell/configs/evaluations/unext2/run_eval_mix_trained_a549_pred_zikv.sh new file mode 100644 index 000000000..fecd31cef --- /dev/null +++ b/applications/dynacell/configs/evaluations/unext2/run_eval_mix_trained_a549_pred_zikv.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# UNeXt2 joint (iPSC+A549) model — membrane prediction on A549 ZIKV test set. + +set -euo pipefail +ml uv +source ".envrc" + +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +uv run dynacell evaluate \ + target_name=membrane \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/memb_fcmae_vscyto3d_scratch_jointtrained_zikv.zarr \ + io.pred_channel_name=Membrane_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_ZIKV.ozx \ + io.gt_channel_name=Membrane \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_ZIKV_seg_cleaned.zarr \ + pixel_metrics.spacing=[0.174,0.1494,0.1494] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/a549/joint_evaluations/eval_unext2_joint_membrane_zikv \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/unext2/run_eval_mix_trained_ipsc_pred.sh b/applications/dynacell/configs/evaluations/unext2/run_eval_mix_trained_ipsc_pred.sh new file mode 100644 index 000000000..8a4d35dc6 --- /dev/null +++ b/applications/dynacell/configs/evaluations/unext2/run_eval_mix_trained_ipsc_pred.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# UNeXt2 joint (iPSC+A549) model — membrane prediction on iPSC test set. + +set -euo pipefail +ml uv +source ".envrc" + +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +uv run dynacell evaluate \ + target_name=membrane \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions/memb_fcmae_vscyto3d_scratch_jointtrained.zarr \ + io.pred_channel_name=Membrane_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell.zarr \ + io.gt_channel_name=Membrane \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/joint_evaluations/eval_unext2_joint_membrane \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/unext2/run_eval_unext2.sh b/applications/dynacell/configs/evaluations/unext2/run_eval_unext2.sh new file mode 100644 index 000000000..75f353462 --- /dev/null +++ b/applications/dynacell/configs/evaluations/unext2/run_eval_unext2.sh @@ -0,0 +1,59 @@ +ml uv + +source ".envrc" + +# UNext2 — ER (SEC61B) +uv run dynacell evaluate \ + target_name=er \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_fcmae_vscyto3d_scratch.zarr \ + io.pred_channel_name=Structure_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/SEC61B.zarr \ + io.gt_channel_name=Structure \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/SEC61B_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations/eval_unext2_sec61b \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt'" \ + force_recompute.all=true + +# UNext2 — Membrane +uv run dynacell evaluate \ + target_name=membrane \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_fcmae_vscyto3d_scratch.zarr \ + io.pred_channel_name=Membrane_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell.zarr \ + io.gt_channel_name=Membrane \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations/eval_unext2_membrane \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt'" \ + force_recompute.all=true + +# UNext2 — Mitochondria (TOMM20) +uv run dynacell evaluate \ + target_name=mitochondria \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_fcmae_vscyto3d_scratch.zarr \ + io.pred_channel_name=Structure_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/TOMM20.zarr \ + io.gt_channel_name=Structure \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/TOMM20_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations/eval_unext2_tomm20 \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt'" \ + force_recompute.all=true + +# UNext2 — Nucleus +uv run dynacell evaluate \ + target_name=nucleus \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_fcmae_vscyto3d_scratch.zarr \ + io.pred_channel_name=Nuclei_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell.zarr \ + io.gt_channel_name=Nuclei \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations/eval_unext2_nucleus \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt'" \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/unext2/run_eval_unext2_a549.sh b/applications/dynacell/configs/evaluations/unext2/run_eval_unext2_a549.sh new file mode 100644 index 000000000..e3f20b11b --- /dev/null +++ b/applications/dynacell/configs/evaluations/unext2/run_eval_unext2_a549.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# A549 UNext2 (fcmae_vscyto3d_scratch) evaluation — 4 organelles × 3 infection conditions. + +set -euo pipefail +ml uv +source ".envrc" + +PRED_ROOT=/hpc/projects/virtual_staining/training/dynacell/a549/predictions +GT_ROOT=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1 +OUT_ROOT=/hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings + +V1_SPACING="[0.174,0.1494,0.1494]" +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +run_eval () { + local target=$1 infection=$2 gt_basename=$3 \ + pred_zarr=$4 pred_chan=$5 gt_chan=$6 spacing=$7 + local save_dir="${OUT_ROOT}/eval_unext2_${target}_${infection}" + echo ">>> unext2 ${target} ${infection}" + uv run dynacell evaluate \ + target_name="${target}" \ + io.pred_path="${PRED_ROOT}/${pred_zarr}" \ + io.pred_channel_name="${pred_chan}" \ + io.gt_path="${GT_ROOT}/test/${gt_basename}.ozx" \ + io.gt_channel_name="${gt_chan}" \ + io.cell_segmentation_path="${GT_ROOT}/test/${gt_basename}_seg_cleaned.zarr" \ + pixel_metrics.spacing="${spacing}" \ + save.save_dir="${save_dir}" \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true +} + +# SEC61B (ER) +run_eval er mock SEC61B_mock sec61b_fcmae_vscyto3d_scratch__sec61b_mock.zarr Structure_prediction Structure "${V1_SPACING}" +run_eval er denv SEC61B_DENV sec61b_fcmae_vscyto3d_scratch__sec61b_denv.zarr Structure_prediction Structure "${V1_SPACING}" +run_eval er zikv SEC61B_ZIKV sec61b_fcmae_vscyto3d_scratch__sec61b_zikv.zarr Structure_prediction Structure "${V1_SPACING}" + +# CAAX (membrane) +# run_eval membrane mock CAAX_mock memb_fcmae_vscyto3d_scratch_mock.zarr Membrane_prediction Membrane "${V1_SPACING}" +# run_eval membrane denv CAAX_DENV memb_fcmae_vscyto3d_scratch_denv.zarr Membrane_prediction Membrane "${V1_SPACING}" +# run_eval membrane zikv CAAX_ZIKV memb_fcmae_vscyto3d_scratch_zikv.zarr Membrane_prediction Membrane "${V1_SPACING}" + +# H2B (nucleus) +# run_eval nucleus mock H2B_mock nucl_fcmae_vscyto3d_scratch_mock.zarr Nuclei_prediction Nuclei "${V1_SPACING}" +run_eval nucleus denv H2B_DENV nucl_fcmae_vscyto3d_scratch_denv.zarr Nuclei_prediction Nuclei "${V1_SPACING}" +run_eval nucleus zikv H2B_ZIKV nucl_fcmae_vscyto3d_scratch_zikv.zarr Nuclei_prediction Nuclei "${V1_SPACING}" + +# TOMM20 (mitochondria) +run_eval mitochondria mock TOMM20_mock tomm20_fcmae_vscyto3d_scratch__tomm20_mock.zarr Structure_prediction Structure "${V1_SPACING}" +run_eval mitochondria denv TOMM20_DENV tomm20_fcmae_vscyto3d_scratch__tomm20_denv.zarr Structure_prediction Structure "${V1_SPACING}" +run_eval mitochondria zikv TOMM20_ZIKV tomm20_fcmae_vscyto3d_scratch__tomm20_zikv.zarr Structure_prediction Structure "${V1_SPACING}" diff --git a/applications/dynacell/configs/evaluations/unext2/run_eval_unext2_jointtrained_a549.sh b/applications/dynacell/configs/evaluations/unext2/run_eval_unext2_jointtrained_a549.sh new file mode 100755 index 000000000..d1269f6fc --- /dev/null +++ b/applications/dynacell/configs/evaluations/unext2/run_eval_unext2_jointtrained_a549.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# UNeXt2 (fcmae_vscyto3d_scratch) joint-trained (iPSC + A549 mantis) — +# evaluate on A549 test set (membrane × 3 infections). Companion to the +# existing joint membrane evals (eval_{fnet3d,vscyto3d,celldiff}_joint_membrane_). + +set -euo pipefail +ml uv +source ".envrc" + +PRED_ROOT=/hpc/projects/virtual_staining/training/dynacell/a549/predictions +GT_ROOT=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1 +OUT_ROOT=/hpc/projects/virtual_staining/training/dynacell/a549/joint_evaluations + +V1_SPACING="[0.174,0.1494,0.1494]" +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +mkdir -p "${OUT_ROOT}" + +run_eval () { + local infection=$1 gt_basename=$2 pred_zarr=$3 + local save_dir="${OUT_ROOT}/eval_unext2_joint_membrane_${infection}" + echo ">>> unext2 joint membrane ${infection}" + uv run dynacell evaluate \ + target_name=membrane \ + io.pred_path="${PRED_ROOT}/${pred_zarr}" \ + io.pred_channel_name=Membrane_prediction \ + io.gt_path="${GT_ROOT}/test/${gt_basename}.ozx" \ + io.gt_channel_name=Membrane \ + io.cell_segmentation_path="${GT_ROOT}/test/${gt_basename}_seg_cleaned.zarr" \ + pixel_metrics.spacing="${V1_SPACING}" \ + save.save_dir="${save_dir}" \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true +} + +run_eval mock CAAX_mock memb_fcmae_vscyto3d_scratch_jointtrained_mock.zarr +run_eval denv CAAX_DENV memb_fcmae_vscyto3d_scratch_jointtrained_denv.zarr +run_eval zikv CAAX_ZIKV memb_fcmae_vscyto3d_scratch_jointtrained_zikv.zarr diff --git a/applications/dynacell/configs/evaluations/unext2/run_eval_unext2_jointtrained_ipsc.sh b/applications/dynacell/configs/evaluations/unext2/run_eval_unext2_jointtrained_ipsc.sh new file mode 100755 index 000000000..4bed1e2b1 --- /dev/null +++ b/applications/dynacell/configs/evaluations/unext2/run_eval_unext2_jointtrained_ipsc.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# UNeXt2 (fcmae_vscyto3d_scratch) joint-trained (iPSC + A549 mantis) — +# evaluate on iPSC test set (membrane). Companion to the existing +# joint membrane evals (eval_{fnet3d,vscyto3d,celldiff}_joint_membrane). + +set -euo pipefail +ml uv +source ".envrc" + +PRED_ROOT=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions +GT_ROOT=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped +OUT_ROOT=/hpc/projects/virtual_staining/training/dynacell/ipsc/joint_evaluations + +IPSC_SPACING="[0.29,0.108,0.108]" +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +mkdir -p "${OUT_ROOT}" + +echo ">>> unext2 joint membrane (iPSC)" +uv run dynacell evaluate \ + target_name=membrane \ + io.pred_path="${PRED_ROOT}/memb_fcmae_vscyto3d_scratch_jointtrained.zarr" \ + io.pred_channel_name=Membrane_prediction \ + io.gt_path="${GT_ROOT}/cell.zarr" \ + io.gt_channel_name=Membrane \ + io.cell_segmentation_path="${GT_ROOT}/cell_segmented_cleaned.zarr" \ + pixel_metrics.spacing="${IPSC_SPACING}" \ + save.save_dir="${OUT_ROOT}/eval_unext2_joint_membrane" \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/vscyto3d/run_eval_mix_trained_a549_pred_denv.sh b/applications/dynacell/configs/evaluations/vscyto3d/run_eval_mix_trained_a549_pred_denv.sh new file mode 100644 index 000000000..97b849ec5 --- /dev/null +++ b/applications/dynacell/configs/evaluations/vscyto3d/run_eval_mix_trained_a549_pred_denv.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# VSCyto3D joint (iPSC+A549) model — membrane prediction on A549 DENV test set. + +set -euo pipefail +ml uv +source ".envrc" + +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +uv run dynacell evaluate \ + target_name=membrane \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/memb_fcmae_vscyto3d_pretrained_jointtrained_denv.zarr \ + io.pred_channel_name=Membrane_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_DENV.ozx \ + io.gt_channel_name=Membrane \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_DENV_seg_cleaned.zarr \ + pixel_metrics.spacing=[0.174,0.1494,0.1494] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/a549/joint_evaluations/eval_vscyto3d_joint_membrane_denv \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/vscyto3d/run_eval_mix_trained_a549_pred_mock.sh b/applications/dynacell/configs/evaluations/vscyto3d/run_eval_mix_trained_a549_pred_mock.sh new file mode 100644 index 000000000..440f6389f --- /dev/null +++ b/applications/dynacell/configs/evaluations/vscyto3d/run_eval_mix_trained_a549_pred_mock.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# VSCyto3D joint (iPSC+A549) model — membrane prediction on A549 mock test set. + +set -euo pipefail +ml uv +source ".envrc" + +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +uv run dynacell evaluate \ + target_name=membrane \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/memb_fcmae_vscyto3d_pretrained_jointtrained_mock.zarr \ + io.pred_channel_name=Membrane_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_mock.ozx \ + io.gt_channel_name=Membrane \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_mock_seg_cleaned.zarr \ + pixel_metrics.spacing=[0.174,0.1494,0.1494] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/a549/joint_evaluations/eval_vscyto3d_joint_membrane_mock \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/vscyto3d/run_eval_mix_trained_a549_pred_zikv.sh b/applications/dynacell/configs/evaluations/vscyto3d/run_eval_mix_trained_a549_pred_zikv.sh new file mode 100644 index 000000000..974159ef8 --- /dev/null +++ b/applications/dynacell/configs/evaluations/vscyto3d/run_eval_mix_trained_a549_pred_zikv.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# VSCyto3D joint (iPSC+A549) model — membrane prediction on A549 ZIKV test set. + +set -euo pipefail +ml uv +source ".envrc" + +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +uv run dynacell evaluate \ + target_name=membrane \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/a549/joint_predictions/memb_fcmae_vscyto3d_pretrained_jointtrained_zikv.zarr \ + io.pred_channel_name=Membrane_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_ZIKV.ozx \ + io.gt_channel_name=Membrane \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_ZIKV_seg_cleaned.zarr \ + pixel_metrics.spacing=[0.174,0.1494,0.1494] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/a549/joint_evaluations/eval_vscyto3d_joint_membrane_zikv \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/vscyto3d/run_eval_mix_trained_ipsc_pred.sh b/applications/dynacell/configs/evaluations/vscyto3d/run_eval_mix_trained_ipsc_pred.sh new file mode 100644 index 000000000..02adfee78 --- /dev/null +++ b/applications/dynacell/configs/evaluations/vscyto3d/run_eval_mix_trained_ipsc_pred.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# VSCyto3D joint (iPSC+A549) model — membrane prediction on iPSC test set. + +set -euo pipefail +ml uv +source ".envrc" + +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +uv run dynacell evaluate \ + target_name=membrane \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/joint_predictions/memb_fcmae_vscyto3d_pretrained_jointtrained.zarr \ + io.pred_channel_name=Membrane_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell.zarr \ + io.gt_channel_name=Membrane \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/joint_evaluations/eval_vscyto3d_joint_membrane \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/vscyto3d/run_eval_vscyto3d.sh b/applications/dynacell/configs/evaluations/vscyto3d/run_eval_vscyto3d.sh new file mode 100644 index 000000000..afd40f3b9 --- /dev/null +++ b/applications/dynacell/configs/evaluations/vscyto3d/run_eval_vscyto3d.sh @@ -0,0 +1,59 @@ +ml uv + +source ".envrc" + +# VSCyto3D — ER (SEC61B) +uv run dynacell evaluate \ + target_name=er \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/sec61b_fcmae_vscyto3d_pretrained.zarr \ + io.pred_channel_name=Structure_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/SEC61B.zarr \ + io.gt_channel_name=Structure \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/SEC61B_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations/eval_vscyto3d_sec61b \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt'" \ + force_recompute.all=true + +# VSCyto3D — Membrane +uv run dynacell evaluate \ + target_name=membrane \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/memb_fcmae_vscyto3d_pretrained.zarr \ + io.pred_channel_name=Membrane_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell.zarr \ + io.gt_channel_name=Membrane \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations/eval_vscyto3d_membrane \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt'" \ + force_recompute.all=true + +# VSCyto3D — Mitochondria (TOMM20) +uv run dynacell evaluate \ + target_name=mitochondria \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/tomm20_fcmae_vscyto3d_pretrained.zarr \ + io.pred_channel_name=Structure_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/TOMM20.zarr \ + io.gt_channel_name=Structure \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/TOMM20_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations/eval_vscyto3d_tomm20 \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt'" \ + force_recompute.all=true + +# VSCyto3D — Nucleus +uv run dynacell evaluate \ + target_name=nucleus \ + io.pred_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/predictions/nucl_fcmae_vscyto3d_pretrained.zarr \ + io.pred_channel_name=Nuclei_prediction \ + io.gt_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell.zarr \ + io.gt_channel_name=Nuclei \ + io.cell_segmentation_path=/hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell_segmented_cleaned.zarr \ + pixel_metrics.spacing=[0.29,0.108,0.108] \ + save.save_dir=/hpc/projects/virtual_staining/training/dynacell/ipsc/evaluations/eval_vscyto3d_nucleus \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt'" \ + force_recompute.all=true diff --git a/applications/dynacell/configs/evaluations/vscyto3d/run_eval_vscyto3d_a549.sh b/applications/dynacell/configs/evaluations/vscyto3d/run_eval_vscyto3d_a549.sh new file mode 100644 index 000000000..7edbdcce2 --- /dev/null +++ b/applications/dynacell/configs/evaluations/vscyto3d/run_eval_vscyto3d_a549.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# A549 VSCyto3D (fcmae_vscyto3d_pretrained) evaluation — 4 organelles × 3 infection conditions. + +set -euo pipefail +ml uv +source ".envrc" + +PRED_ROOT=/hpc/projects/virtual_staining/training/dynacell/a549/predictions +GT_ROOT=/hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1 +OUT_ROOT=/hpc/projects/virtual_staining/training/dynacell/a549/evaluations_with_embeddings + +V1_SPACING="[0.174,0.1494,0.1494]" +DYNACLR_CKPT='/hpc/projects/organelle_phenotyping/models/SEC61_TOMM20_G3BP1_Sensor/time_interval/dynaclr_gfp_rfp_Ph/organelle_sensor_phase_maxproj_ver3_150epochs/saved_checkpoints/epoch=104-step=53760.ckpt' + +run_eval () { + local target=$1 infection=$2 gt_basename=$3 \ + pred_zarr=$4 pred_chan=$5 gt_chan=$6 spacing=$7 + local save_dir="${OUT_ROOT}/eval_vscyto3d_${target}_${infection}" + echo ">>> vscyto3d ${target} ${infection}" + uv run dynacell evaluate \ + target_name="${target}" \ + io.pred_path="${PRED_ROOT}/${pred_zarr}" \ + io.pred_channel_name="${pred_chan}" \ + io.gt_path="${GT_ROOT}/test/${gt_basename}.ozx" \ + io.gt_channel_name="${gt_chan}" \ + io.cell_segmentation_path="${GT_ROOT}/test/${gt_basename}_seg_cleaned.zarr" \ + pixel_metrics.spacing="${spacing}" \ + save.save_dir="${save_dir}" \ + compute_feature_metrics=true \ + "feature_extractor.dynaclr.checkpoint='${DYNACLR_CKPT}'" \ + force_recompute.all=true +} + +# SEC61B (ER) +run_eval er mock SEC61B_mock sec61b_fcmae_vscyto3d_pretrained__sec61b_mock.zarr Structure_prediction Structure "${V1_SPACING}" +run_eval er denv SEC61B_DENV sec61b_fcmae_vscyto3d_pretrained__sec61b_denv.zarr Structure_prediction Structure "${V1_SPACING}" +run_eval er zikv SEC61B_ZIKV sec61b_fcmae_vscyto3d_pretrained__sec61b_zikv.zarr Structure_prediction Structure "${V1_SPACING}" + +# CAAX (membrane) +# run_eval membrane mock CAAX_mock memb_fcmae_vscyto3d_pretrained_mock.zarr Membrane_prediction Membrane "${V1_SPACING}" +# run_eval membrane denv CAAX_DENV memb_fcmae_vscyto3d_pretrained_denv.zarr Membrane_prediction Membrane "${V1_SPACING}" +# run_eval membrane zikv CAAX_ZIKV memb_fcmae_vscyto3d_pretrained_zikv.zarr Membrane_prediction Membrane "${V1_SPACING}" + +# H2B (nucleus) +# run_eval nucleus mock H2B_mock nucl_fcmae_vscyto3d_pretrained_mock.zarr Nuclei_prediction Nuclei "${V1_SPACING}" +# run_eval nucleus denv H2B_DENV nucl_fcmae_vscyto3d_pretrained_denv.zarr Nuclei_prediction Nuclei "${V1_SPACING}" +# run_eval nucleus zikv H2B_ZIKV nucl_fcmae_vscyto3d_pretrained_zikv.zarr Nuclei_prediction Nuclei "${V1_SPACING}" + +# TOMM20 (mitochondria) +run_eval mitochondria mock TOMM20_mock tomm20_fcmae_vscyto3d_pretrained__tomm20_mock.zarr Structure_prediction Structure "${V1_SPACING}" +run_eval mitochondria denv TOMM20_DENV tomm20_fcmae_vscyto3d_pretrained__tomm20_denv.zarr Structure_prediction Structure "${V1_SPACING}" +run_eval mitochondria zikv TOMM20_ZIKV tomm20_fcmae_vscyto3d_pretrained__tomm20_zikv.zarr Structure_prediction Structure "${V1_SPACING}" diff --git a/applications/dynacell/configs/examples/celldiff/fit.yml b/applications/dynacell/configs/examples/celldiff/fit.yml new file mode 100644 index 000000000..a4ce46588 --- /dev/null +++ b/applications/dynacell/configs/examples/celldiff/fit.yml @@ -0,0 +1,34 @@ +# CellDiff flow-matching: fit from scratch. +# Usage: cd applications/dynacell/configs/examples && uv run dynacell fit -c celldiff/fit.yml +base: + - ../../recipes/trainer/fit.yml + - ../../recipes/topology/ddp_4gpu.yml + - ../../recipes/data/hcs_phase_fluor_3d.yml + - ../../recipes/models/celldiff_fm.yml + +model: + init_args: + lr: 0.0002 + schedule: WarmupCosine + num_log_steps: 10 + +trainer: + precision: bf16-mixed + max_epochs: 200 + # Flow-matching training checkpoints by epoch count, not validation loss. + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + every_n_epochs: 10 + save_top_k: -1 + save_last: true + +data: + init_args: + data_path: #TODO + z_window_size: 8 + batch_size: 4 + yx_patch_size: [512, 512] diff --git a/applications/dynacell/configs/examples/celldiff/predict.yml b/applications/dynacell/configs/examples/celldiff/predict.yml new file mode 100644 index 000000000..53c16a583 --- /dev/null +++ b/applications/dynacell/configs/examples/celldiff/predict.yml @@ -0,0 +1,22 @@ +# CellDiff flow-matching: predict from checkpoint. +# Usage: cd applications/dynacell/configs/examples && uv run dynacell predict -c celldiff/predict.yml +base: + - ../../recipes/trainer/predict.yml + - ../../recipes/topology/single_gpu.yml + - ../../recipes/data/hcs_phase_fluor_3d.yml + - ../../recipes/models/celldiff_fm.yml + +model: + init_args: + num_generate_steps: 100 +# predict_method: generate + predict_method: iterative # denoise, generate, sliding_window (non-overlapping), or iterative (overlapping) + predict_overlap: [4, 256, 256] + ckpt_path: #TODO checkpoint path + +data: + init_args: + data_path: #TODO HCS OME-Zarr test data + z_window_size: 40 + batch_size: 1 + yx_patch_size: [512, 512] diff --git a/applications/dynacell/configs/examples/fnet3d/fit.yml b/applications/dynacell/configs/examples/fnet3d/fit.yml new file mode 100644 index 000000000..74e536750 --- /dev/null +++ b/applications/dynacell/configs/examples/fnet3d/fit.yml @@ -0,0 +1,24 @@ +# FNet3D: supervised training (benchmark baseline). +# Usage: cd applications/dynacell/configs/examples && uv run dynacell fit -c fnet3d/fit.yml +base: + - ../../recipes/trainer/fit.yml + - ../../recipes/topology/ddp_4gpu.yml + - ../../recipes/data/hcs_phase_fluor_3d.yml + - ../../recipes/models/fnet3d.yml + +model: + init_args: + lr: 0.001 + schedule: Constant + +trainer: + precision: 16-mixed + max_epochs: 200 + max_steps: 50000 + +data: + init_args: + data_path: #TODO HCS OME-Zarr data + z_window_size: 32 + batch_size: 24 + yx_patch_size: [64, 64] diff --git a/applications/dynacell/configs/examples/fnet3d/predict.yml b/applications/dynacell/configs/examples/fnet3d/predict.yml new file mode 100644 index 000000000..7b90b1f1c --- /dev/null +++ b/applications/dynacell/configs/examples/fnet3d/predict.yml @@ -0,0 +1,18 @@ +# FNet3D: predict from checkpoint. +# Usage: cd applications/dynacell/configs/examples && uv run dynacell predict -c fnet3d/predict.yml +base: + - ../../recipes/trainer/predict.yml + - ../../recipes/topology/single_gpu.yml + - ../../recipes/data/hcs_phase_fluor_3d.yml + - ../../recipes/models/fnet3d.yml + +model: + init_args: + ckpt_path: #TODO checkpoint path + +data: + init_args: + data_path: #TODO HCS OME-Zarr data + z_window_size: 32 + batch_size: 4 + yx_patch_size: [64, 64] diff --git a/applications/dynacell/configs/examples/unetvit3d/fit.yml b/applications/dynacell/configs/examples/unetvit3d/fit.yml new file mode 100644 index 000000000..742606466 --- /dev/null +++ b/applications/dynacell/configs/examples/unetvit3d/fit.yml @@ -0,0 +1,23 @@ +# UNetViT3D: supervised training. +# Usage: cd applications/dynacell/configs/examples && uv run dynacell fit -c unetvit3d/fit.yml +base: + - ../../recipes/trainer/fit.yml + - ../../recipes/topology/ddp_4gpu.yml + - ../../recipes/data/hcs_phase_fluor_3d.yml + - ../../recipes/models/unetvit3d.yml + +model: + init_args: + lr: 0.0002 + schedule: WarmupCosine + +trainer: + precision: 16-mixed + max_epochs: 200 + +data: + init_args: + data_path: #TODO HCS OME-Zarr data + z_window_size: 8 + batch_size: 8 + yx_patch_size: [512, 512] diff --git a/applications/dynacell/configs/examples/unetvit3d/predict.yml b/applications/dynacell/configs/examples/unetvit3d/predict.yml new file mode 100644 index 000000000..9e0c179f9 --- /dev/null +++ b/applications/dynacell/configs/examples/unetvit3d/predict.yml @@ -0,0 +1,19 @@ +# UNetViT3D: predict from checkpoint. +# yx_patch_size and z_window_size must match the model's input_spatial_size. +# Usage: cd applications/dynacell/configs/examples && uv run dynacell predict -c unetvit3d/predict.yml +base: + - ../../recipes/trainer/predict.yml + - ../../recipes/topology/single_gpu.yml + - ../../recipes/data/hcs_phase_fluor_3d.yml + - ../../recipes/models/unetvit3d.yml + +model: + init_args: + ckpt_path: #TODO checkpoint path + +data: + init_args: + data_path: #TODO HCS OME-Zarr data + z_window_size: 8 + batch_size: 4 + yx_patch_size: [512, 512] diff --git a/applications/dynacell/configs/examples/unext2/fit.yml b/applications/dynacell/configs/examples/unext2/fit.yml new file mode 100644 index 000000000..d066abd6c --- /dev/null +++ b/applications/dynacell/configs/examples/unext2/fit.yml @@ -0,0 +1,23 @@ +# UNeXt2 (VSCyto3D): supervised training. +# Usage: cd applications/dynacell/configs/examples && uv run dynacell fit -c unext2/fit.yml +base: + - ../../recipes/trainer/fit.yml + - ../../recipes/topology/ddp_4gpu.yml + - ../../recipes/data/hcs_phase_fluor_3d.yml + - ../../recipes/models/unext2_3d.yml + +model: + init_args: + lr: 0.0002 + schedule: WarmupCosine + +trainer: + precision: 16-mixed + max_epochs: 200 + +data: + init_args: + data_path: #TODO HCS OME-Zarr data + z_window_size: 15 + batch_size: 8 + yx_patch_size: [256, 256] diff --git a/applications/dynacell/configs/examples/unext2/predict.yml b/applications/dynacell/configs/examples/unext2/predict.yml new file mode 100644 index 000000000..c2a7d38c1 --- /dev/null +++ b/applications/dynacell/configs/examples/unext2/predict.yml @@ -0,0 +1,18 @@ +# UNeXt2 (VSCyto3D): predict from checkpoint. +# Usage: cd applications/dynacell/configs/examples && uv run dynacell predict -c unext2/predict.yml +base: + - ../../recipes/trainer/predict.yml + - ../../recipes/topology/single_gpu.yml + - ../../recipes/data/hcs_phase_fluor_3d.yml + - ../../recipes/models/unext2_3d.yml + +model: + init_args: + ckpt_path: #TODO checkpoint path + +data: + init_args: + data_path: #TODO HCS OME-Zarr test data + z_window_size: 15 + batch_size: 1 + yx_patch_size: [256, 256] diff --git a/applications/dynacell/configs/recipes/data/hcs_phase_fluor_3d.yml b/applications/dynacell/configs/recipes/data/hcs_phase_fluor_3d.yml new file mode 100644 index 000000000..45f16c829 --- /dev/null +++ b/applications/dynacell/configs/recipes/data/hcs_phase_fluor_3d.yml @@ -0,0 +1,20 @@ +# Data recipe: HCSDataModule for Phase3D → Fluorescence, 3D. +data: + class_path: viscy_data.hcs.HCSDataModule + init_args: + data_path: #TODO HCS OME-Zarr data + source_channel: Phase3D + target_channel: Fluorescence + z_window_size: 8 + split_ratio: 0.8 + batch_size: 16 + num_workers: 8 + yx_patch_size: [512, 512] + mmap_preload: false + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D, Fluorescence] + level: fov_statistics + subtrahend: mean + divisor: std diff --git a/applications/dynacell/configs/recipes/models/celldiff_fm.yml b/applications/dynacell/configs/recipes/models/celldiff_fm.yml new file mode 100644 index 000000000..2fbb1536d --- /dev/null +++ b/applications/dynacell/configs/recipes/models/celldiff_fm.yml @@ -0,0 +1,16 @@ +model: + class_path: dynacell.engine.DynacellFlowMatching + init_args: + net_config: + input_spatial_size: [8, 512, 512] + in_channels: 1 + dims: [64, 128, 256, 256] + num_res_block: [2, 2, 2] + hidden_size: 512 + num_heads: 8 + dim_head: 64 + num_hidden_layers: 8 + patch_size: 4 + transport_config: + path_type: Linear + prediction: velocity diff --git a/applications/dynacell/configs/recipes/models/fnet3d.yml b/applications/dynacell/configs/recipes/models/fnet3d.yml new file mode 100644 index 000000000..f896f9f74 --- /dev/null +++ b/applications/dynacell/configs/recipes/models/fnet3d.yml @@ -0,0 +1,11 @@ +# Model recipe: FNet3D — recursive encoder-decoder (Ounkomol et al. 2018). +model: + class_path: dynacell.engine.DynacellUNet + init_args: + architecture: FNet3D + model_config: + in_channels: 1 + out_channels: 1 + depth: 4 + mult_chan: 32 + in_stack_depth: 32 diff --git a/applications/dynacell/configs/recipes/models/fnet3d_z8.yml b/applications/dynacell/configs/recipes/models/fnet3d_z8.yml new file mode 100644 index 000000000..4cf7a5009 --- /dev/null +++ b/applications/dynacell/configs/recipes/models/fnet3d_z8.yml @@ -0,0 +1,11 @@ +# Model recipe: FNet3D for z=8 input (depth=3, divisor=8). +model: + class_path: dynacell.engine.DynacellUNet + init_args: + architecture: FNet3D + model_config: + in_channels: 1 + out_channels: 1 + depth: 3 + mult_chan: 32 + in_stack_depth: 8 diff --git a/applications/dynacell/configs/recipes/models/pix2pix3d_unetvit.yml b/applications/dynacell/configs/recipes/models/pix2pix3d_unetvit.yml new file mode 100644 index 000000000..6aa9fbdbe --- /dev/null +++ b/applications/dynacell/configs/recipes/models/pix2pix3d_unetvit.yml @@ -0,0 +1,23 @@ +# Model recipe: pix2pix3d_unetvit — UNetViT3D generator + 3D PatchGAN +# discriminator trained with LSGAN + L1. +model: + class_path: dynacell.engine.DynacellGAN + init_args: + architecture: UNetViT3D + generator_config: + input_spatial_size: [8, 512, 512] + in_channels: 1 + out_channels: 1 + dims: [64, 128, 256, 256] + num_res_block: [2, 2, 2] + hidden_size: 512 + num_heads: 8 + dim_head: 64 + num_hidden_layers: 8 + patch_size: 4 + discriminator_config: + in_channels: 2 + base_channels: 64 + num_scales: 2 # multi-scale D (pix2pixHD-style); set 1 to ablate + use_spectral_norm: true + lambda_l1: 100.0 diff --git a/applications/dynacell/configs/recipes/models/unetvit3d.yml b/applications/dynacell/configs/recipes/models/unetvit3d.yml new file mode 100644 index 000000000..bf0242c21 --- /dev/null +++ b/applications/dynacell/configs/recipes/models/unetvit3d.yml @@ -0,0 +1,16 @@ +# Model recipe: UNetViT3D — 3D U-Net with ViT bottleneck. +model: + class_path: dynacell.engine.DynacellUNet + init_args: + architecture: UNetViT3D + model_config: + input_spatial_size: [8, 512, 512] + in_channels: 1 + out_channels: 1 + dims: [64, 128, 256, 256] + num_res_block: [2, 2, 2] + hidden_size: 512 + num_heads: 8 + dim_head: 64 + num_hidden_layers: 8 + patch_size: 4 diff --git a/applications/dynacell/configs/recipes/models/unext2_3d.yml b/applications/dynacell/configs/recipes/models/unext2_3d.yml new file mode 100644 index 000000000..ccebd75ca --- /dev/null +++ b/applications/dynacell/configs/recipes/models/unext2_3d.yml @@ -0,0 +1,15 @@ +# Model recipe: UNeXt2 (VSCyto3D) for z=15 input (stem=[5,4,4]). +# Matches the published VSCyto3D architecture (compmicro-czb/VSCyto3D). +model: + class_path: dynacell.engine.DynacellUNet + init_args: + architecture: UNeXt2 + model_config: + in_channels: 1 + out_channels: 1 + in_stack_depth: 15 + backbone: convnextv2_tiny + stem_kernel_size: [5, 4, 4] + decoder_mode: pixelshuffle + head_expansion_ratio: 4 + head_pool: true diff --git a/applications/dynacell/configs/recipes/models/unext2_3d_z8.yml b/applications/dynacell/configs/recipes/models/unext2_3d_z8.yml new file mode 100644 index 000000000..291811491 --- /dev/null +++ b/applications/dynacell/configs/recipes/models/unext2_3d_z8.yml @@ -0,0 +1,14 @@ +# Model recipe: UNeXt2 (VSCyto3D) for z=8 input (stem=[8,4,4]). +model: + class_path: dynacell.engine.DynacellUNet + init_args: + architecture: UNeXt2 + model_config: + in_channels: 1 + out_channels: 1 + in_stack_depth: 8 + backbone: convnextv2_tiny + stem_kernel_size: [8, 4, 4] + decoder_mode: pixelshuffle + head_expansion_ratio: 4 + head_pool: true diff --git a/applications/dynacell/configs/recipes/modes/spotlight.yml b/applications/dynacell/configs/recipes/modes/spotlight.yml new file mode 100644 index 000000000..5d686b260 --- /dev/null +++ b/applications/dynacell/configs/recipes/modes/spotlight.yml @@ -0,0 +1,21 @@ +# Mode recipe: Spotlight foreground-aware loss (Kalinin et al. 2025). +model: + init_args: + loss_function: + class_path: viscy_utils.losses.SpotlightLoss + init_args: + lambda_mse: 0.5 + sigmoid_k: -0.95 + fg_threshold: 0.0 +data: + init_args: + fg_mask_key: fg_mask + min_nonzero_fraction: 0.001 + nonzero_threshold: 0.0 + normalizations: + - class_path: viscy_transforms.NormalizeSampled + init_args: + keys: [Phase3D, Fluorescence] + level: fov_statistics + subtrahend: otsu_threshold + divisor: std diff --git a/applications/dynacell/configs/recipes/topology/ddp_4gpu.yml b/applications/dynacell/configs/recipes/topology/ddp_4gpu.yml new file mode 100644 index 000000000..6ecdb4ad8 --- /dev/null +++ b/applications/dynacell/configs/recipes/topology/ddp_4gpu.yml @@ -0,0 +1,6 @@ +# Topology recipe: 4-GPU DDP training on a single node. +trainer: + accelerator: gpu + strategy: ddp + devices: 4 + num_nodes: 1 diff --git a/applications/dynacell/configs/recipes/topology/ddp_4gpu_gan.yml b/applications/dynacell/configs/recipes/topology/ddp_4gpu_gan.yml new file mode 100644 index 000000000..513f415b5 --- /dev/null +++ b/applications/dynacell/configs/recipes/topology/ddp_4gpu_gan.yml @@ -0,0 +1,25 @@ +# Topology recipe: 4-GPU DDP for GAN training (manual optimization, two opts). +# +# GAN engines (e.g. DynacellGAN / pix2pix3d_unetvit) use manual optimization +# with two optimizers (G and D) trained on alternating sub-steps. Each +# `training_step` toggles `requires_grad` on the inactive side, so the +# parameters of whichever side is *not* updating in that sub-step look +# "unused" from DDP's reducer perspective on each backward pass. +# +# DDP's reducer is set up at module-wrap time over the FULL parameter set; +# with the default `find_unused_parameters=False`, hitting an unused param +# raises: +# "Expected to mark a variable ready only once. ... Parameter ... did not +# receive gradient ..." +# at first backward and kills the job. The Lightning string shortcut +# `ddp_find_unused_parameters_true` instantiates a `DDPStrategy` with +# `find_unused_parameters=True`, which makes the reducer tolerate the +# alternating-grad pattern (small per-step overhead, acceptable here). +# +# Do not use this topology for non-GAN models — plain `ddp_4gpu.yml` skips +# the unused-param scan and is strictly faster for fully-active graphs. +trainer: + accelerator: gpu + strategy: ddp_find_unused_parameters_true + devices: 4 + num_nodes: 1 diff --git a/applications/dynacell/configs/recipes/topology/single_gpu.yml b/applications/dynacell/configs/recipes/topology/single_gpu.yml new file mode 100644 index 000000000..a05fa451a --- /dev/null +++ b/applications/dynacell/configs/recipes/topology/single_gpu.yml @@ -0,0 +1,7 @@ +# Single-GPU training. strategy=auto lets Lightning pick single_device; +# plain ddp at devices=1 would add pointless process-group overhead. +trainer: + accelerator: gpu + strategy: auto + devices: 1 + num_nodes: 1 diff --git a/applications/dynacell/configs/recipes/trainer/fit.yml b/applications/dynacell/configs/recipes/trainer/fit.yml new file mode 100644 index 000000000..25c4fa085 --- /dev/null +++ b/applications/dynacell/configs/recipes/trainer/fit.yml @@ -0,0 +1,22 @@ +# Topology (accelerator / devices / strategy / num_nodes) lives in +# recipes/topology/*.yml. Precision lives in model overlays. +# max_epochs and max_steps also live in model overlays or leaves. +seed_everything: 42 +trainer: + log_every_n_steps: 10 + enable_checkpointing: true + inference_mode: true + logger: + class_path: lightning.pytorch.loggers.WandbLogger + init_args: + project: dynacell + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: step + - class_path: lightning.pytorch.callbacks.ModelCheckpoint + init_args: + monitor: loss/validate + every_n_epochs: 1 + save_top_k: 5 + save_last: true diff --git a/applications/dynacell/configs/recipes/trainer/predict.yml b/applications/dynacell/configs/recipes/trainer/predict.yml new file mode 100644 index 000000000..d6a6bd349 --- /dev/null +++ b/applications/dynacell/configs/recipes/trainer/predict.yml @@ -0,0 +1,10 @@ +# Unified predict trainer recipe. +# Topology lives in recipes/topology/single_gpu.yml; prediction is always +# single-GPU here. +trainer: + precision: 32-true + callbacks: + - class_path: viscy_utils.callbacks.prediction_writer.HCSPredictionWriter + init_args: + output_store: #TODO output zarr path +return_predictions: false diff --git a/applications/dynacell/pyproject.toml b/applications/dynacell/pyproject.toml new file mode 100644 index 000000000..6ec61c954 --- /dev/null +++ b/applications/dynacell/pyproject.toml @@ -0,0 +1,124 @@ +[build-system] +build-backend = "hatchling.build" +requires = [ "hatchling", "uv-dynamic-versioning" ] + +[project] +name = "dynacell" +description = "Benchmark virtual staining with UNetViT3D and FNet3D architectures" +readme = "README.md" +keywords = [ + "benchmarking", + "deep learning", + "microscopy", + "virtual staining", +] +license = "BSD-3-Clause" +authors = [ { name = "Biohub", email = "compmicro@czbiohub.org" } ] +requires-python = ">=3.12" +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Topic :: Scientific/Engineering :: Image Processing", +] +dynamic = [ "version" ] +dependencies = [ + "hydra-core>=1.2", + "lightning>=2.3", + "monai", + "omegaconf", + "pydantic>=2", + "viscy-data[mmap]", + "viscy-models[celldiff]", + "viscy-transforms", + "viscy-utils", +] +optional-dependencies.eval = [ + "accelerate>=1.13", + "aicsmlsegment", + "aicssegmentation", + "cellpose", + "cubic==0.7.0a9", + "dynaclr", + "hydra-core>=1.2", + "iohub>=0.3.6", + "itk", + "matplotlib", + "pandas", + "scikit-image", + "scikit-learn>=1.4", + "scipy", + "segmenter-model-zoo", + # Pinned to a commit on toshas/torch-fidelity master that includes MIND + # (commit a51aa64 "add Monge Inception Distance") on top of v0.4.0. + # Upgrade path: drop the `@5e211a9` suffix and pin a tagged release once + # upstream cuts one that includes MIND (target: `torch-fidelity>=0.5.0`). + "torch-fidelity @ git+https://github.com/toshas/torch-fidelity.git@5e211a9", + "tqdm", + "transformers", +] +optional-dependencies.eval_gpu = [ + # CUDA-13 builds so cupy/cucim match torch's CUDA-13 stack (PyPI torch 2.12 is + # cu13, and the HPC GPU driver is CUDA 13). cupy/cucim find their CUDA libs via + # cuda-pathfinder, which resolves to the cu13 nvidia wheels torch already pulls + # -- a consistent single-CUDA-major venv, so cupy works with no LD_LIBRARY_PATH. + # (cupy-cuda12x on a cu13 torch breaks: pathfinder hands it libcublas.so.13.) + "cucim-cu13", + "cupy-cuda13x", +] +optional-dependencies.preprocess = [ + "iohub>=0.3.6", + "tqdm", +] +optional-dependencies.report = [ + "hydra-core>=1.2", + "matplotlib", + "pandas", +] +# Default fit/predict trainer logger in configs/recipes/trainer/fit.yml is +# WandbLogger. Install with `pip install dynacell[wandb]` to satisfy that +# default, or override `trainer.logger=null` (or supply your own logger +# block) in the leaf / via `--override` to opt out of W&B entirely. +optional-dependencies.wandb = [ + "wandb", +] +urls.Homepage = "https://github.com/mehta-lab/VisCy" +urls.Issues = "https://github.com/mehta-lab/VisCy/issues" +urls.Repository = "https://github.com/mehta-lab/VisCy" + +scripts.dynacell = "dynacell.__main__:main_cli" + +# Default manifest registry. Auto-discovered by +# ``dynacell.data.resolver.discover_manifest_roots`` so the resolver +# works without ``DYNACELL_MANIFEST_ROOTS`` on a fresh clone. Override +# the env var (or pass cli_roots) to point at a different registry. +entry-points."dynacell.manifest_roots".dynacell_default = "dynacell._manifests" + +[dependency-groups] +dev = [ { include-group = "test" } ] +test = [ + "pytest>=9.0.2", + "pytest-cov>=7", + "tensorboard", +] + +[tool.hatch.metadata] +allow-direct-references = true + +[tool.hatch.version] +source = "uv-dynamic-versioning" + +[tool.hatch.build.targets.wheel] +packages = [ "src/dynacell" ] + +[tool.uv-dynamic-versioning] +vcs = "git" +style = "pep440" +pattern-prefix = "dynacell-" +fallback-version = "0.0.0" diff --git a/applications/dynacell/src/dynacell/__init__.py b/applications/dynacell/src/dynacell/__init__.py new file mode 100644 index 000000000..e0cee86cb --- /dev/null +++ b/applications/dynacell/src/dynacell/__init__.py @@ -0,0 +1,20 @@ +"""Dynacell: benchmark virtual staining application.""" + +__all__ = ["DynacellFlowMatching", "DynacellGAN", "DynacellUNet"] + + +def __getattr__(name: str): + # Lazy imports to avoid pulling in heavy training deps on every import. + if name == "DynacellFlowMatching": + from dynacell.engine import DynacellFlowMatching + + return DynacellFlowMatching + if name == "DynacellGAN": + from dynacell.engine import DynacellGAN + + return DynacellGAN + if name == "DynacellUNet": + from dynacell.engine import DynacellUNet + + return DynacellUNet + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/applications/dynacell/src/dynacell/__main__.py b/applications/dynacell/src/dynacell/__main__.py new file mode 100644 index 000000000..a357c4dbe --- /dev/null +++ b/applications/dynacell/src/dynacell/__main__.py @@ -0,0 +1,163 @@ +"""CLI entry point for the Dynacell application. + +Routes Lightning subcommands (fit, predict, test, validate) to +``viscy_utils.cli.main()`` and Hydra subcommands (evaluate, report) +to their respective entry points. + +Usage +----- +cd applications/dynacell/configs/examples +uv run dynacell fit -c unetvit3d/fit.yml +uv run dynacell evaluate io.pred_path=... target_name=sec61b +uv run dynacell report results_dirs.ModelA=/path/to/results +""" + +import importlib +import os +import sys +from pathlib import Path + +_HYDRA_COMMANDS: dict[str, tuple[str, str, str]] = { + "evaluate": ("dynacell.evaluation.pipeline", "evaluate_model", "eval"), + "evaluate-grouped": ("dynacell.evaluation.pipeline", "evaluate_model_grouped", "eval"), + "precompute-gt": ("dynacell.evaluation.precompute_cli", "precompute_gt", "eval"), + "report": ("dynacell.reporting.cli", "generate_report", "report"), +} + +# HPC-specific config groups (target, feature_extractor/dynaclr, benchmark eval +# leaves) live outside the Python package so the wheel ships only schema + path- +# free references. Editable installs / repo checkouts expose these through +# hydra.searchpath; wheel installs without the repo simply don't see them, and +# external users provide their own groups via --config-dir. +_EXTERNAL_SEARCHPATHS: tuple[str, ...] = ( + "configs/benchmarks/virtual_staining/_internal", + "configs/benchmarks/virtual_staining/_internal/shared/eval", +) + +# Team-shared Hugging Face hub cache on project storage. CZ Biohub-specific +# default path; other sites override via the ``DYNACELL_SHARED_HF_CACHE`` +# environment variable. Repo-checkout invocations of the Hydra subcommands +# default ``HF_HUB_CACHE`` here so gated models (e.g. DINOv3) download once +# per team instead of once per user. +# +# We set ``HF_HUB_CACHE`` rather than ``HF_HOME``: ``HF_HOME`` relocates +# the entire HF directory including the auth token file, so a shared +# ``HF_HOME`` blocks HF from finding each user's personal ``~/.cache/ +# huggingface/token``. That breaks per-user gated-repo ACLs (HF returns +# 401 because the request goes out unauthenticated). ``HF_HUB_CACHE`` +# only relocates weights/datasets; tokens stay at the per-user default. +_DEFAULT_SHARED_HF_CACHE = "/hpc/projects/comp.micro/virtual_staining/models/dynacell/evaluation/hf_cache" +_SHARED_HF_CACHE = Path(os.environ.get("DYNACELL_SHARED_HF_CACHE", _DEFAULT_SHARED_HF_CACHE)) + + +def _external_configs_dirs() -> list[Path]: + """Return existing repo-checkout searchpath roots for Hydra eval groups. + + Walks up from this module until it finds the nearest ``pyproject.toml`` + (the application root in editable installs), then returns every + configured subpath that exists on disk. Missing paths are silently + skipped so wheel installs behave the same as repo checkouts where the + dirs were removed. + """ + for parent in Path(__file__).resolve().parents: + if (parent / "pyproject.toml").exists(): + return [p for sub in _EXTERNAL_SEARCHPATHS if (p := parent / sub).is_dir()] + return [] + + +def _maybe_set_shared_hf_cache() -> None: + """Point HF_HUB_CACHE at the team-shared cache on a repo checkout. + + Only fires when (a) ``HF_HUB_CACHE`` is not already set by the + caller, (b) we're running from a repo checkout (external Hydra + searchpaths resolve), and (c) the shared cache dir exists on this + machine. Wheel installs and non-HPC environments fall through to + the normal per-user ``~/.cache/huggingface/hub`` default. + """ + if "HF_HUB_CACHE" in os.environ: + return + if not _external_configs_dirs(): + return + if not _SHARED_HF_CACHE.is_dir(): + return + os.environ["HF_HUB_CACHE"] = str(_SHARED_HF_CACHE) + + +def _inject_external_configs(argv: list[str]) -> list[str]: + """Inject a hydra.searchpath override so external configs are discoverable. + + Hydra's argparse uses a single ``overrides`` positional with + ``nargs="*"``, which means the first contiguous run of positional args + is greedily consumed and any later positional (after a flag like + ``-c job``) is reported as an unrecognized argument. To keep both + ``dynacell evaluate -c job leaf=x`` and + ``dynacell evaluate leaf=x -c job`` working, insert the token + adjacent to an existing positional override when one is present; + otherwise append. + """ + dirs = _external_configs_dirs() + if not dirs: + return argv + token = f"hydra.searchpath=[{','.join(f'file://{d}' for d in dirs)}]" + for i, arg in enumerate(argv[1:], start=1): + if not arg.startswith("-") and "=" in arg: + return argv[:i] + [token] + argv[i:] + return argv + [token] + + +def main_cli(): + """Console script entry point for ``dynacell`` command.""" + # Apply BLAS/OMP env caps BEFORE any torch/numpy import. The import below + # is stdlib + threadpoolctl only — no torch — so the env vars set here + # bite at first BLAS C-extension load, which happens transitively when + # we import the Hydra command module or viscy_utils.cli below. + from dynacell.evaluation.runtime import early_apply_env_caps + + early_apply_env_caps() + + if len(sys.argv) >= 2 and sys.argv[1] in _HYDRA_COMMANDS: + command = sys.argv[1] + module_path, func_name, extra = _HYDRA_COMMANDS[command] + sys.argv = [sys.argv[0]] + sys.argv[2:] # strip subcommand for Hydra + sys.argv = _inject_external_configs(sys.argv) + _maybe_set_shared_hf_cache() + try: + module = importlib.import_module(module_path) + except ModuleNotFoundError as e: + print(f"Missing dependencies for 'dynacell {command}': {e}\nInstall with: pip install 'dynacell[{extra}]'") + raise SystemExit(1) from e + from dynacell.data.resolver import ( + ManifestNotFoundError, + NoManifestRootsError, + TargetNotFoundError, + ) + + # Hydra's @hydra.main decorator wraps exceptions in a generic + # "Error executing job" banner and calls sys.exit(1) unless + # HYDRA_FULL_ERROR=1 is set. Force the full-error path so our + # dataset-resolver errors propagate here and we can print a + # clean message + SystemExit(2) instead of a cryptic banner. + os.environ.setdefault("HYDRA_FULL_ERROR", "1") + try: + getattr(module, func_name)() + except (NoManifestRootsError, ManifestNotFoundError, TargetNotFoundError) as e: + print(str(e), file=sys.stderr) + raise SystemExit(2) from e + else: + from dynacell._compose_hook import _dynacell_ref_resolver + from dynacell.data.resolver import ( + ManifestNotFoundError, + NoManifestRootsError, + TargetNotFoundError, + ) + from viscy_utils.cli import main + + try: + main(resolver=_dynacell_ref_resolver) + except (NoManifestRootsError, ManifestNotFoundError, TargetNotFoundError) as e: + print(str(e), file=sys.stderr) + raise SystemExit(2) from e + + +if __name__ == "__main__": + main_cli() diff --git a/applications/dynacell/src/dynacell/_compose_hook.py b/applications/dynacell/src/dynacell/_compose_hook.py new file mode 100644 index 000000000..53a6cc402 --- /dev/null +++ b/applications/dynacell/src/dynacell/_compose_hook.py @@ -0,0 +1,83 @@ +"""Composition-time resolver hook for DynaCell benchmark leaves. + +Threaded into :func:`viscy_utils.compose.load_composed_config` via the +``resolver`` keyword argument; run once after the final deep-merge. +Reads ``benchmark.dataset_ref: {dataset, target}`` from the composed dict +and splices concrete ``data_path``, ``source_channel``, ``target_channel`` +into ``data.init_args`` from the resolved :class:`DatasetManifest`. + +Partial references (only ``dataset`` or only ``target``) are a strict +no-op, so shared train/predict-set fragments can declare one half of +``dataset_ref`` without breaking leaves whose target fragment has not +yet been migrated. +""" + +from __future__ import annotations + +import copy +import sys + +from dynacell.data import ( + DatasetRef, + ResolvedDataset, + dataset_ref_from_dict, + resolve_dataset_ref, +) + + +def _infer_mode(composed: dict) -> str: + """Return the Lightning subcommand ("fit", "predict", or "validate").""" + launcher_mode = composed.get("launcher", {}).get("mode") + if launcher_mode in {"fit", "predict", "validate"}: + return launcher_mode + for arg in sys.argv[1:]: + if arg in {"fit", "predict", "validate"}: + return arg + raise ValueError("Cannot infer Lightning mode for dataset_ref resolution; set launcher.mode in the leaf config.") + + +def _splice_resolved(composed: dict, resolved: ResolvedDataset, mode: str, ref: DatasetRef) -> dict: + """Return a deep-copied composed dict with resolved fields spliced in. + + Raises ``ValueError`` if the composed dict already declares any of + the resolved data fields. A full ``dataset_ref`` is the single + source of truth — composed fragments must not co-declare + ``data_path``, ``source_channel``, or ``target_channel``. + """ + out = copy.deepcopy(composed) + data = out.setdefault("data", {}) + init_args = data.setdefault("init_args", {}) + resolved_values = { + "data_path": str(resolved.data_path_test if mode == "predict" else resolved.data_path_train), + "source_channel": resolved.source_channel, + "target_channel": resolved.target_channel, + } + conflicts = {field: (init_args[field], value) for field, value in resolved_values.items() if field in init_args} + if conflicts: + details = "; ".join( + f"{k}: composed={composed_value!r} vs manifest={manifest_value!r}" + for k, (composed_value, manifest_value) in conflicts.items() + ) + raise ValueError( + f"benchmark.dataset_ref={{dataset: {ref.dataset}, target: {ref.target}}} " + f"conflicts with explicit data.init_args fields: {details}. " + "Remove one side — either drop the conflicting explicit fields " + "or remove dataset_ref." + ) + init_args.update(resolved_values) + out.setdefault("benchmark", {})["spacing"] = resolved.spacing.as_list() + return out + + +def _dynacell_ref_resolver(composed: dict) -> dict: + """Resolve ``benchmark.dataset_ref`` against the manifest registry. + + Strict partial-ref no-op: returns the input dict unchanged unless + both ``dataset`` and ``target`` keys are present under + ``benchmark.dataset_ref``. + """ + ref = dataset_ref_from_dict(composed.get("benchmark", {}).get("dataset_ref")) + if ref is None: + return composed + resolved = resolve_dataset_ref(ref) + return _splice_resolved(composed, resolved, _infer_mode(composed), ref) diff --git a/applications/dynacell/src/dynacell/_manifests/__init__.py b/applications/dynacell/src/dynacell/_manifests/__init__.py new file mode 100644 index 000000000..dc4a24739 --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/__init__.py @@ -0,0 +1,15 @@ +"""Bundled dataset manifests — the default registry for the DynaCell resolver. + +This package ships canonical manifest YAMLs (mirrored from +``dynacell-paper/_configs/datasets/``) so the resolver works out-of-the-box +on any clone. Auto-discovered via the ``dynacell.manifest_roots`` entry +point declared in ``applications/dynacell/pyproject.toml``. + +VisCy is the source of truth for manifest *content* (this directory). +``dynacell-paper`` is the source of truth for manifest *authoring* — when +a new dataset is preprocessed there, the change is mirrored back here and +``tests/test_manifest_sync.py`` enforces the parity. + +Override at runtime with ``DYNACELL_MANIFEST_ROOTS=/path/to/other/registry`` +(env var) or by passing ``cli_roots=`` to ``discover_manifest_roots``. +""" diff --git a/applications/dynacell/src/dynacell/_manifests/a549-mantis-caax-denv/manifest.yaml b/applications/dynacell/src/dynacell/_manifests/a549-mantis-caax-denv/manifest.yaml new file mode 100644 index 000000000..13e751ba2 --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/a549-mantis-caax-denv/manifest.yaml @@ -0,0 +1,27 @@ +name: a549-mantis-caax-denv +version: '1' +description: "A549 mantis condition-pooled \u2014 caax on DENV (pool-internal 0/0/fov\ + \ naming, plate provenance in per-position zattrs and the colocated provenance.json\ + \ sidecar)." +cell_type: A549 +imaging_modality: mantis-lightsheet +spacing: + z: 0.174 + y: 0.1494 + x: 0.1494 +channels: + source: Phase3D + auxiliary: + - Brightfield +targets: + caax: + gene: CAAX + organelle: membrane + display_name: Membrane (CAAX) + target_channel: Membrane + stores: + train: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/CAAX_DENV.ozx + test: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_DENV.ozx + cell_segmentation: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_DENV_seg_cleaned.zarr + gt_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache/caax_denv + splits: splits/caax_train_test.yaml diff --git a/applications/dynacell/src/dynacell/_manifests/a549-mantis-caax-denv/splits/caax_train_test.yaml b/applications/dynacell/src/dynacell/_manifests/a549-mantis-caax-denv/splits/caax_train_test.yaml new file mode 100644 index 000000000..7eef68a46 --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/a549-mantis-caax-denv/splits/caax_train_test.yaml @@ -0,0 +1,31 @@ +split_version: '1.0' +random_seed: 0 +selection_criteria: + source: a549-mantis condition-pooled assembly + target: caax + condition: DENV + pool_naming: 0/0/fov sequential across contributing plates +train: + count: 6 + fovs: + - 0/0/fov0000 + - 0/0/fov0001 + - 0/0/fov0002 + - 0/0/fov0003 + - 0/0/fov0004 + - 0/0/fov0005 +test: + count: 12 + fovs: + - 0/0/fov0000 + - 0/0/fov0001 + - 0/0/fov0002 + - 0/0/fov0003 + - 0/0/fov0004 + - 0/0/fov0005 + - 0/0/fov0006 + - 0/0/fov0007 + - 0/0/fov0008 + - 0/0/fov0009 + - 0/0/fov0010 + - 0/0/fov0011 diff --git a/applications/dynacell/src/dynacell/_manifests/a549-mantis-caax-mock/manifest.yaml b/applications/dynacell/src/dynacell/_manifests/a549-mantis-caax-mock/manifest.yaml new file mode 100644 index 000000000..1c2d99d28 --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/a549-mantis-caax-mock/manifest.yaml @@ -0,0 +1,27 @@ +name: a549-mantis-caax-mock +version: '1' +description: "A549 mantis condition-pooled \u2014 caax on mock (pool-internal 0/0/fov\ + \ naming, plate provenance in per-position zattrs and the colocated provenance.json\ + \ sidecar)." +cell_type: A549 +imaging_modality: mantis-lightsheet +spacing: + z: 0.174 + y: 0.1494 + x: 0.1494 +channels: + source: Phase3D + auxiliary: + - Brightfield +targets: + caax: + gene: CAAX + organelle: membrane + display_name: Membrane (CAAX) + target_channel: Membrane + stores: + train: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/CAAX_mock.ozx + test: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_mock.ozx + cell_segmentation: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_mock_seg_cleaned.zarr + gt_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache/caax_mock + splits: splits/caax_train_test.yaml diff --git a/applications/dynacell/src/dynacell/_manifests/a549-mantis-caax-mock/splits/caax_train_test.yaml b/applications/dynacell/src/dynacell/_manifests/a549-mantis-caax-mock/splits/caax_train_test.yaml new file mode 100644 index 000000000..f5e315358 --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/a549-mantis-caax-mock/splits/caax_train_test.yaml @@ -0,0 +1,37 @@ +split_version: '1.0' +random_seed: 0 +selection_criteria: + source: a549-mantis condition-pooled assembly + target: caax + condition: mock + pool_naming: 0/0/fov sequential across contributing plates +train: + count: 12 + fovs: + - 0/0/fov0000 + - 0/0/fov0001 + - 0/0/fov0002 + - 0/0/fov0003 + - 0/0/fov0004 + - 0/0/fov0005 + - 0/0/fov0006 + - 0/0/fov0007 + - 0/0/fov0008 + - 0/0/fov0009 + - 0/0/fov0010 + - 0/0/fov0011 +test: + count: 12 + fovs: + - 0/0/fov0000 + - 0/0/fov0001 + - 0/0/fov0002 + - 0/0/fov0003 + - 0/0/fov0004 + - 0/0/fov0005 + - 0/0/fov0006 + - 0/0/fov0007 + - 0/0/fov0008 + - 0/0/fov0009 + - 0/0/fov0010 + - 0/0/fov0011 diff --git a/applications/dynacell/src/dynacell/_manifests/a549-mantis-caax-zikv/manifest.yaml b/applications/dynacell/src/dynacell/_manifests/a549-mantis-caax-zikv/manifest.yaml new file mode 100644 index 000000000..aee5f9968 --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/a549-mantis-caax-zikv/manifest.yaml @@ -0,0 +1,27 @@ +name: a549-mantis-caax-zikv +version: '1' +description: "A549 mantis condition-pooled \u2014 caax on ZIKV (pool-internal 0/0/fov\ + \ naming, plate provenance in per-position zattrs and the colocated provenance.json\ + \ sidecar)." +cell_type: A549 +imaging_modality: mantis-lightsheet +spacing: + z: 0.174 + y: 0.1494 + x: 0.1494 +channels: + source: Phase3D + auxiliary: + - Brightfield +targets: + caax: + gene: CAAX + organelle: membrane + display_name: Membrane (CAAX) + target_channel: Membrane + stores: + train: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/CAAX_ZIKV.ozx + test: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_ZIKV.ozx + cell_segmentation: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/CAAX_ZIKV_seg_cleaned.zarr + gt_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache/caax_zikv + splits: splits/caax_train_test.yaml diff --git a/applications/dynacell/src/dynacell/_manifests/a549-mantis-caax-zikv/splits/caax_train_test.yaml b/applications/dynacell/src/dynacell/_manifests/a549-mantis-caax-zikv/splits/caax_train_test.yaml new file mode 100644 index 000000000..68c02e1cc --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/a549-mantis-caax-zikv/splits/caax_train_test.yaml @@ -0,0 +1,37 @@ +split_version: '1.0' +random_seed: 0 +selection_criteria: + source: a549-mantis condition-pooled assembly + target: caax + condition: ZIKV + pool_naming: 0/0/fov sequential across contributing plates +train: + count: 12 + fovs: + - 0/0/fov0000 + - 0/0/fov0001 + - 0/0/fov0002 + - 0/0/fov0003 + - 0/0/fov0004 + - 0/0/fov0005 + - 0/0/fov0006 + - 0/0/fov0007 + - 0/0/fov0008 + - 0/0/fov0009 + - 0/0/fov0010 + - 0/0/fov0011 +test: + count: 12 + fovs: + - 0/0/fov0000 + - 0/0/fov0001 + - 0/0/fov0002 + - 0/0/fov0003 + - 0/0/fov0004 + - 0/0/fov0005 + - 0/0/fov0006 + - 0/0/fov0007 + - 0/0/fov0008 + - 0/0/fov0009 + - 0/0/fov0010 + - 0/0/fov0011 diff --git a/applications/dynacell/src/dynacell/_manifests/a549-mantis-h2b-denv/manifest.yaml b/applications/dynacell/src/dynacell/_manifests/a549-mantis-h2b-denv/manifest.yaml new file mode 100644 index 000000000..3a9798989 --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/a549-mantis-h2b-denv/manifest.yaml @@ -0,0 +1,27 @@ +name: a549-mantis-h2b-denv +version: '1' +description: "A549 mantis condition-pooled \u2014 h2b on DENV (pool-internal 0/0/fov\ + \ naming, plate provenance in per-position zattrs and the colocated provenance.json\ + \ sidecar)." +cell_type: A549 +imaging_modality: mantis-lightsheet +spacing: + z: 0.174 + y: 0.1494 + x: 0.1494 +channels: + source: Phase3D + auxiliary: + - Brightfield +targets: + h2b: + gene: H2B + organelle: nuclei + display_name: Nuclei (H2B) + target_channel: Nuclei + stores: + train: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/H2B_DENV.ozx + test: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/H2B_DENV.ozx + cell_segmentation: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/H2B_DENV_seg_cleaned.zarr + gt_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache/h2b_denv + splits: splits/h2b_train_test.yaml diff --git a/applications/dynacell/src/dynacell/_manifests/a549-mantis-h2b-denv/splits/h2b_train_test.yaml b/applications/dynacell/src/dynacell/_manifests/a549-mantis-h2b-denv/splits/h2b_train_test.yaml new file mode 100644 index 000000000..6ecba4b06 --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/a549-mantis-h2b-denv/splits/h2b_train_test.yaml @@ -0,0 +1,31 @@ +split_version: '1.0' +random_seed: 0 +selection_criteria: + source: a549-mantis condition-pooled assembly + target: h2b + condition: DENV + pool_naming: 0/0/fov sequential across contributing plates +train: + count: 6 + fovs: + - 0/0/fov0000 + - 0/0/fov0001 + - 0/0/fov0002 + - 0/0/fov0003 + - 0/0/fov0004 + - 0/0/fov0005 +test: + count: 12 + fovs: + - 0/0/fov0000 + - 0/0/fov0001 + - 0/0/fov0002 + - 0/0/fov0003 + - 0/0/fov0004 + - 0/0/fov0005 + - 0/0/fov0006 + - 0/0/fov0007 + - 0/0/fov0008 + - 0/0/fov0009 + - 0/0/fov0010 + - 0/0/fov0011 diff --git a/applications/dynacell/src/dynacell/_manifests/a549-mantis-h2b-mock/manifest.yaml b/applications/dynacell/src/dynacell/_manifests/a549-mantis-h2b-mock/manifest.yaml new file mode 100644 index 000000000..a501d4234 --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/a549-mantis-h2b-mock/manifest.yaml @@ -0,0 +1,27 @@ +name: a549-mantis-h2b-mock +version: '1' +description: "A549 mantis condition-pooled \u2014 h2b on mock (pool-internal 0/0/fov\ + \ naming, plate provenance in per-position zattrs and the colocated provenance.json\ + \ sidecar)." +cell_type: A549 +imaging_modality: mantis-lightsheet +spacing: + z: 0.174 + y: 0.1494 + x: 0.1494 +channels: + source: Phase3D + auxiliary: + - Brightfield +targets: + h2b: + gene: H2B + organelle: nuclei + display_name: Nuclei (H2B) + target_channel: Nuclei + stores: + train: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/H2B_mock.ozx + test: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/H2B_mock.ozx + cell_segmentation: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/H2B_mock_seg_cleaned.zarr + gt_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache/h2b_mock + splits: splits/h2b_train_test.yaml diff --git a/applications/dynacell/src/dynacell/_manifests/a549-mantis-h2b-mock/splits/h2b_train_test.yaml b/applications/dynacell/src/dynacell/_manifests/a549-mantis-h2b-mock/splits/h2b_train_test.yaml new file mode 100644 index 000000000..6fa7400e6 --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/a549-mantis-h2b-mock/splits/h2b_train_test.yaml @@ -0,0 +1,37 @@ +split_version: '1.0' +random_seed: 0 +selection_criteria: + source: a549-mantis condition-pooled assembly + target: h2b + condition: mock + pool_naming: 0/0/fov sequential across contributing plates +train: + count: 12 + fovs: + - 0/0/fov0000 + - 0/0/fov0001 + - 0/0/fov0002 + - 0/0/fov0003 + - 0/0/fov0004 + - 0/0/fov0005 + - 0/0/fov0006 + - 0/0/fov0007 + - 0/0/fov0008 + - 0/0/fov0009 + - 0/0/fov0010 + - 0/0/fov0011 +test: + count: 12 + fovs: + - 0/0/fov0000 + - 0/0/fov0001 + - 0/0/fov0002 + - 0/0/fov0003 + - 0/0/fov0004 + - 0/0/fov0005 + - 0/0/fov0006 + - 0/0/fov0007 + - 0/0/fov0008 + - 0/0/fov0009 + - 0/0/fov0010 + - 0/0/fov0011 diff --git a/applications/dynacell/src/dynacell/_manifests/a549-mantis-h2b-zikv/manifest.yaml b/applications/dynacell/src/dynacell/_manifests/a549-mantis-h2b-zikv/manifest.yaml new file mode 100644 index 000000000..a6d764aec --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/a549-mantis-h2b-zikv/manifest.yaml @@ -0,0 +1,27 @@ +name: a549-mantis-h2b-zikv +version: '1' +description: "A549 mantis condition-pooled \u2014 h2b on ZIKV (pool-internal 0/0/fov\ + \ naming, plate provenance in per-position zattrs and the colocated provenance.json\ + \ sidecar)." +cell_type: A549 +imaging_modality: mantis-lightsheet +spacing: + z: 0.174 + y: 0.1494 + x: 0.1494 +channels: + source: Phase3D + auxiliary: + - Brightfield +targets: + h2b: + gene: H2B + organelle: nuclei + display_name: Nuclei (H2B) + target_channel: Nuclei + stores: + train: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/H2B_ZIKV.ozx + test: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/H2B_ZIKV.ozx + cell_segmentation: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/H2B_ZIKV_seg_cleaned.zarr + gt_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache/h2b_zikv + splits: splits/h2b_train_test.yaml diff --git a/applications/dynacell/src/dynacell/_manifests/a549-mantis-h2b-zikv/splits/h2b_train_test.yaml b/applications/dynacell/src/dynacell/_manifests/a549-mantis-h2b-zikv/splits/h2b_train_test.yaml new file mode 100644 index 000000000..99292f5dd --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/a549-mantis-h2b-zikv/splits/h2b_train_test.yaml @@ -0,0 +1,37 @@ +split_version: '1.0' +random_seed: 0 +selection_criteria: + source: a549-mantis condition-pooled assembly + target: h2b + condition: ZIKV + pool_naming: 0/0/fov sequential across contributing plates +train: + count: 12 + fovs: + - 0/0/fov0000 + - 0/0/fov0001 + - 0/0/fov0002 + - 0/0/fov0003 + - 0/0/fov0004 + - 0/0/fov0005 + - 0/0/fov0006 + - 0/0/fov0007 + - 0/0/fov0008 + - 0/0/fov0009 + - 0/0/fov0010 + - 0/0/fov0011 +test: + count: 12 + fovs: + - 0/0/fov0000 + - 0/0/fov0001 + - 0/0/fov0002 + - 0/0/fov0003 + - 0/0/fov0004 + - 0/0/fov0005 + - 0/0/fov0006 + - 0/0/fov0007 + - 0/0/fov0008 + - 0/0/fov0009 + - 0/0/fov0010 + - 0/0/fov0011 diff --git a/applications/dynacell/src/dynacell/_manifests/a549-mantis-sec61b-denv/manifest.yaml b/applications/dynacell/src/dynacell/_manifests/a549-mantis-sec61b-denv/manifest.yaml new file mode 100644 index 000000000..3cd86826b --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/a549-mantis-sec61b-denv/manifest.yaml @@ -0,0 +1,27 @@ +name: a549-mantis-sec61b-denv +version: '1' +description: "A549 mantis condition-pooled \u2014 sec61b on DENV (pool-internal 0/0/fov\ + \ naming, plate provenance in per-position zattrs and the colocated provenance.json\ + \ sidecar)." +cell_type: A549 +imaging_modality: mantis-lightsheet +spacing: + z: 0.174 + y: 0.1494 + x: 0.1494 +channels: + source: Phase3D + auxiliary: + - Brightfield +targets: + sec61b: + gene: SEC61B + organelle: er + display_name: ER (Sec61b) + target_channel: Structure + stores: + train: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/SEC61B_DENV.ozx + test: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/SEC61B_DENV.ozx + cell_segmentation: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/SEC61B_DENV_seg_cleaned.zarr + gt_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache/sec61b_denv + splits: splits/sec61b_train_test.yaml diff --git a/applications/dynacell/src/dynacell/_manifests/a549-mantis-sec61b-denv/splits/sec61b_train_test.yaml b/applications/dynacell/src/dynacell/_manifests/a549-mantis-sec61b-denv/splits/sec61b_train_test.yaml new file mode 100644 index 000000000..e001c1ff6 --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/a549-mantis-sec61b-denv/splits/sec61b_train_test.yaml @@ -0,0 +1,27 @@ +split_version: '1.0' +random_seed: 0 +selection_criteria: + source: a549-mantis condition-pooled assembly + target: sec61b + condition: DENV + pool_naming: 0/0/fov sequential across contributing plates +train: + count: 2 + fovs: + - 0/0/fov0000 + - 0/0/fov0001 +test: + count: 12 + fovs: + - 0/0/fov0000 + - 0/0/fov0001 + - 0/0/fov0002 + - 0/0/fov0003 + - 0/0/fov0004 + - 0/0/fov0005 + - 0/0/fov0006 + - 0/0/fov0007 + - 0/0/fov0008 + - 0/0/fov0009 + - 0/0/fov0010 + - 0/0/fov0011 diff --git a/applications/dynacell/src/dynacell/_manifests/a549-mantis-sec61b-mock/manifest.yaml b/applications/dynacell/src/dynacell/_manifests/a549-mantis-sec61b-mock/manifest.yaml new file mode 100644 index 000000000..3eeb1f45c --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/a549-mantis-sec61b-mock/manifest.yaml @@ -0,0 +1,27 @@ +name: a549-mantis-sec61b-mock +version: '1' +description: "A549 mantis condition-pooled \u2014 sec61b on mock (pool-internal 0/0/fov\ + \ naming, plate provenance in per-position zattrs and the colocated provenance.json\ + \ sidecar)." +cell_type: A549 +imaging_modality: mantis-lightsheet +spacing: + z: 0.174 + y: 0.1494 + x: 0.1494 +channels: + source: Phase3D + auxiliary: + - Brightfield +targets: + sec61b: + gene: SEC61B + organelle: er + display_name: ER (Sec61b) + target_channel: Structure + stores: + train: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/SEC61B_mock.ozx + test: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/SEC61B_mock.ozx + cell_segmentation: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/SEC61B_mock_seg_cleaned.zarr + gt_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache/sec61b_mock + splits: splits/sec61b_train_test.yaml diff --git a/applications/dynacell/src/dynacell/_manifests/a549-mantis-sec61b-mock/splits/sec61b_train_test.yaml b/applications/dynacell/src/dynacell/_manifests/a549-mantis-sec61b-mock/splits/sec61b_train_test.yaml new file mode 100644 index 000000000..18141424a --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/a549-mantis-sec61b-mock/splits/sec61b_train_test.yaml @@ -0,0 +1,36 @@ +split_version: '1.0' +random_seed: 0 +selection_criteria: + source: a549-mantis condition-pooled assembly + target: sec61b + condition: mock + pool_naming: 0/0/fov sequential across contributing plates +train: + count: 11 + fovs: + - 0/0/fov0000 + - 0/0/fov0001 + - 0/0/fov0002 + - 0/0/fov0003 + - 0/0/fov0004 + - 0/0/fov0005 + - 0/0/fov0006 + - 0/0/fov0007 + - 0/0/fov0008 + - 0/0/fov0009 + - 0/0/fov0010 +test: + count: 12 + fovs: + - 0/0/fov0000 + - 0/0/fov0001 + - 0/0/fov0002 + - 0/0/fov0003 + - 0/0/fov0004 + - 0/0/fov0005 + - 0/0/fov0006 + - 0/0/fov0007 + - 0/0/fov0008 + - 0/0/fov0009 + - 0/0/fov0010 + - 0/0/fov0011 diff --git a/applications/dynacell/src/dynacell/_manifests/a549-mantis-sec61b-zikv/manifest.yaml b/applications/dynacell/src/dynacell/_manifests/a549-mantis-sec61b-zikv/manifest.yaml new file mode 100644 index 000000000..70affa040 --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/a549-mantis-sec61b-zikv/manifest.yaml @@ -0,0 +1,27 @@ +name: a549-mantis-sec61b-zikv +version: '1' +description: "A549 mantis condition-pooled \u2014 sec61b on ZIKV (pool-internal 0/0/fov\ + \ naming, plate provenance in per-position zattrs and the colocated provenance.json\ + \ sidecar)." +cell_type: A549 +imaging_modality: mantis-lightsheet +spacing: + z: 0.174 + y: 0.1494 + x: 0.1494 +channels: + source: Phase3D + auxiliary: + - Brightfield +targets: + sec61b: + gene: SEC61B + organelle: er + display_name: ER (Sec61b) + target_channel: Structure + stores: + train: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/SEC61B_ZIKV.ozx + test: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/SEC61B_ZIKV.ozx + cell_segmentation: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/SEC61B_ZIKV_seg_cleaned.zarr + gt_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache/sec61b_zikv + splits: splits/sec61b_train_test.yaml diff --git a/applications/dynacell/src/dynacell/_manifests/a549-mantis-sec61b-zikv/splits/sec61b_train_test.yaml b/applications/dynacell/src/dynacell/_manifests/a549-mantis-sec61b-zikv/splits/sec61b_train_test.yaml new file mode 100644 index 000000000..fb2693ada --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/a549-mantis-sec61b-zikv/splits/sec61b_train_test.yaml @@ -0,0 +1,40 @@ +split_version: '1.0' +random_seed: 0 +selection_criteria: + source: a549-mantis condition-pooled assembly + target: sec61b + condition: ZIKV + pool_naming: 0/0/fov sequential across contributing plates +train: + count: 15 + fovs: + - 0/0/fov0000 + - 0/0/fov0001 + - 0/0/fov0002 + - 0/0/fov0003 + - 0/0/fov0004 + - 0/0/fov0005 + - 0/0/fov0006 + - 0/0/fov0007 + - 0/0/fov0008 + - 0/0/fov0009 + - 0/0/fov0010 + - 0/0/fov0011 + - 0/0/fov0012 + - 0/0/fov0013 + - 0/0/fov0014 +test: + count: 12 + fovs: + - 0/0/fov0000 + - 0/0/fov0001 + - 0/0/fov0002 + - 0/0/fov0003 + - 0/0/fov0004 + - 0/0/fov0005 + - 0/0/fov0006 + - 0/0/fov0007 + - 0/0/fov0008 + - 0/0/fov0009 + - 0/0/fov0010 + - 0/0/fov0011 diff --git a/applications/dynacell/src/dynacell/_manifests/a549-mantis-tomm20-denv/manifest.yaml b/applications/dynacell/src/dynacell/_manifests/a549-mantis-tomm20-denv/manifest.yaml new file mode 100644 index 000000000..c25922437 --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/a549-mantis-tomm20-denv/manifest.yaml @@ -0,0 +1,27 @@ +name: a549-mantis-tomm20-denv +version: '1' +description: "A549 mantis condition-pooled \u2014 tomm20 on DENV (pool-internal 0/0/fov\ + \ naming, plate provenance in per-position zattrs and the colocated provenance.json\ + \ sidecar)." +cell_type: A549 +imaging_modality: mantis-lightsheet +spacing: + z: 0.174 + y: 0.1494 + x: 0.1494 +channels: + source: Phase3D + auxiliary: + - Brightfield +targets: + tomm20: + gene: TOMM20 + organelle: mitochondria + display_name: Mitochondria (TOMM20) + target_channel: Structure + stores: + train: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/TOMM20_DENV.ozx + test: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/TOMM20_DENV.ozx + cell_segmentation: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/TOMM20_DENV_seg_cleaned.zarr + gt_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache/tomm20_denv + splits: splits/tomm20_train_test.yaml diff --git a/applications/dynacell/src/dynacell/_manifests/a549-mantis-tomm20-denv/splits/tomm20_train_test.yaml b/applications/dynacell/src/dynacell/_manifests/a549-mantis-tomm20-denv/splits/tomm20_train_test.yaml new file mode 100644 index 000000000..76678fa21 --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/a549-mantis-tomm20-denv/splits/tomm20_train_test.yaml @@ -0,0 +1,30 @@ +split_version: '1.0' +random_seed: 0 +selection_criteria: + source: a549-mantis condition-pooled assembly + target: tomm20 + condition: DENV + pool_naming: 0/0/fov sequential across contributing plates +train: + count: 5 + fovs: + - 0/0/fov0000 + - 0/0/fov0001 + - 0/0/fov0002 + - 0/0/fov0003 + - 0/0/fov0004 +test: + count: 12 + fovs: + - 0/0/fov0000 + - 0/0/fov0001 + - 0/0/fov0002 + - 0/0/fov0003 + - 0/0/fov0004 + - 0/0/fov0005 + - 0/0/fov0006 + - 0/0/fov0007 + - 0/0/fov0008 + - 0/0/fov0009 + - 0/0/fov0010 + - 0/0/fov0011 diff --git a/applications/dynacell/src/dynacell/_manifests/a549-mantis-tomm20-mock/manifest.yaml b/applications/dynacell/src/dynacell/_manifests/a549-mantis-tomm20-mock/manifest.yaml new file mode 100644 index 000000000..e9940a947 --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/a549-mantis-tomm20-mock/manifest.yaml @@ -0,0 +1,27 @@ +name: a549-mantis-tomm20-mock +version: '1' +description: "A549 mantis condition-pooled \u2014 tomm20 on mock (pool-internal 0/0/fov\ + \ naming, plate provenance in per-position zattrs and the colocated provenance.json\ + \ sidecar)." +cell_type: A549 +imaging_modality: mantis-lightsheet +spacing: + z: 0.174 + y: 0.1494 + x: 0.1494 +channels: + source: Phase3D + auxiliary: + - Brightfield +targets: + tomm20: + gene: TOMM20 + organelle: mitochondria + display_name: Mitochondria (TOMM20) + target_channel: Structure + stores: + train: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/TOMM20_mock.ozx + test: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/TOMM20_mock.ozx + cell_segmentation: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/TOMM20_mock_seg_cleaned.zarr + gt_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache/tomm20_mock + splits: splits/tomm20_train_test.yaml diff --git a/applications/dynacell/src/dynacell/_manifests/a549-mantis-tomm20-mock/splits/tomm20_train_test.yaml b/applications/dynacell/src/dynacell/_manifests/a549-mantis-tomm20-mock/splits/tomm20_train_test.yaml new file mode 100644 index 000000000..9009cf38c --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/a549-mantis-tomm20-mock/splits/tomm20_train_test.yaml @@ -0,0 +1,38 @@ +split_version: '1.0' +random_seed: 0 +selection_criteria: + source: a549-mantis condition-pooled assembly + target: tomm20 + condition: mock + pool_naming: 0/0/fov sequential across contributing plates +train: + count: 13 + fovs: + - 0/0/fov0000 + - 0/0/fov0001 + - 0/0/fov0002 + - 0/0/fov0003 + - 0/0/fov0004 + - 0/0/fov0005 + - 0/0/fov0006 + - 0/0/fov0007 + - 0/0/fov0008 + - 0/0/fov0009 + - 0/0/fov0010 + - 0/0/fov0011 + - 0/0/fov0012 +test: + count: 12 + fovs: + - 0/0/fov0000 + - 0/0/fov0001 + - 0/0/fov0002 + - 0/0/fov0003 + - 0/0/fov0004 + - 0/0/fov0005 + - 0/0/fov0006 + - 0/0/fov0007 + - 0/0/fov0008 + - 0/0/fov0009 + - 0/0/fov0010 + - 0/0/fov0011 diff --git a/applications/dynacell/src/dynacell/_manifests/a549-mantis-tomm20-zikv/manifest.yaml b/applications/dynacell/src/dynacell/_manifests/a549-mantis-tomm20-zikv/manifest.yaml new file mode 100644 index 000000000..df63e66d0 --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/a549-mantis-tomm20-zikv/manifest.yaml @@ -0,0 +1,27 @@ +name: a549-mantis-tomm20-zikv +version: '1' +description: "A549 mantis condition-pooled \u2014 tomm20 on ZIKV (pool-internal 0/0/fov\ + \ naming, plate provenance in per-position zattrs and the colocated provenance.json\ + \ sidecar)." +cell_type: A549 +imaging_modality: mantis-lightsheet +spacing: + z: 0.174 + y: 0.1494 + x: 0.1494 +channels: + source: Phase3D + auxiliary: + - Brightfield +targets: + tomm20: + gene: TOMM20 + organelle: mitochondria + display_name: Mitochondria (TOMM20) + target_channel: Structure + stores: + train: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/train/TOMM20_ZIKV.ozx + test: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/TOMM20_ZIKV.ozx + cell_segmentation: /hpc/projects/virtual_staining/training/dynacell/a549/mantis_v1/test/TOMM20_ZIKV_seg_cleaned.zarr + gt_cache_dir: /hpc/projects/virtual_staining/training/dynacell/a549/eval_cache/tomm20_zikv + splits: splits/tomm20_train_test.yaml diff --git a/applications/dynacell/src/dynacell/_manifests/a549-mantis-tomm20-zikv/splits/tomm20_train_test.yaml b/applications/dynacell/src/dynacell/_manifests/a549-mantis-tomm20-zikv/splits/tomm20_train_test.yaml new file mode 100644 index 000000000..909097382 --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/a549-mantis-tomm20-zikv/splits/tomm20_train_test.yaml @@ -0,0 +1,37 @@ +split_version: '1.0' +random_seed: 0 +selection_criteria: + source: a549-mantis condition-pooled assembly + target: tomm20 + condition: ZIKV + pool_naming: 0/0/fov sequential across contributing plates +train: + count: 12 + fovs: + - 0/0/fov0000 + - 0/0/fov0001 + - 0/0/fov0002 + - 0/0/fov0003 + - 0/0/fov0004 + - 0/0/fov0005 + - 0/0/fov0006 + - 0/0/fov0007 + - 0/0/fov0008 + - 0/0/fov0009 + - 0/0/fov0010 + - 0/0/fov0011 +test: + count: 12 + fovs: + - 0/0/fov0000 + - 0/0/fov0001 + - 0/0/fov0002 + - 0/0/fov0003 + - 0/0/fov0004 + - 0/0/fov0005 + - 0/0/fov0006 + - 0/0/fov0007 + - 0/0/fov0008 + - 0/0/fov0009 + - 0/0/fov0010 + - 0/0/fov0011 diff --git a/applications/dynacell/src/dynacell/_manifests/aics-hipsc/manifest.yaml b/applications/dynacell/src/dynacell/_manifests/aics-hipsc/manifest.yaml new file mode 100644 index 000000000..7043e8761 --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/aics-hipsc/manifest.yaml @@ -0,0 +1,66 @@ +name: aics-hipsc +version: "4" +description: "WTC-11 hiPSC confocal dataset from Allen Institute for Cell Science" +cell_type: WTC-11 hiPSC +imaging_modality: confocal + +spacing: + z: 0.290 + y: 0.108 + x: 0.108 + +channels: + source: Phase3D + auxiliary: + - Brightfield + - Nuclei + - Membrane + +targets: + sec61b: + gene: SEC61B + organelle: er + display_name: "ER (Sec61b)" + target_channel: Structure + stores: + train: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/SEC61B.zarr + test: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/SEC61B.zarr + cell_segmentation: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/SEC61B_segmented_cleaned.zarr + gt_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache/SEC61B + splits: splits/sec61b_train_val_test.yaml + + tomm20: + gene: TOMM20 + organelle: mitochondria + display_name: "Mitochondria (TOMM20)" + target_channel: Structure + stores: + train: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/TOMM20.zarr + test: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/TOMM20.zarr + cell_segmentation: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/TOMM20_segmented_cleaned.zarr + gt_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache/TOMM20 + splits: splits/tomm20_train_val_test.yaml + + nucleus: + gene: Nuclei + organelle: nucleus + display_name: "Nucleus" + target_channel: Nuclei + stores: + train: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/cell.zarr + test: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell.zarr + cell_segmentation: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell_segmented_cleaned.zarr + gt_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache/nucleus + splits: splits/nucleus_train_val_test.yaml + + membrane: + gene: Membrane + organelle: membrane + display_name: "Membrane" + target_channel: Membrane + stores: + train: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/train/cell.zarr + test: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell.zarr + cell_segmentation: /hpc/projects/virtual_staining/training/dynacell/ipsc/dataset_v4/test_cropped/cell_segmented_cleaned.zarr + gt_cache_dir: /hpc/projects/virtual_staining/training/dynacell/ipsc/eval_cache/membrane + splits: splits/membrane_train_val_test.yaml diff --git a/applications/dynacell/src/dynacell/_manifests/aics-hipsc/splits/membrane_train_val_test.yaml b/applications/dynacell/src/dynacell/_manifests/aics-hipsc/splits/membrane_train_val_test.yaml new file mode 100644 index 000000000..575c3929d --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/aics-hipsc/splits/membrane_train_val_test.yaml @@ -0,0 +1,12 @@ +split_version: "1.0" +random_seed: 42 +selection_criteria: + organelle: Membrane + source_store: cell.zarr + notes: "Membrane channel selected from shared cell.zarr (also serves nucleus target)." +train: + count: 500 + fovs: [] +test: + count: 100 + fovs: [] diff --git a/applications/dynacell/src/dynacell/_manifests/aics-hipsc/splits/nucleus_train_val_test.yaml b/applications/dynacell/src/dynacell/_manifests/aics-hipsc/splits/nucleus_train_val_test.yaml new file mode 100644 index 000000000..34606e731 --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/aics-hipsc/splits/nucleus_train_val_test.yaml @@ -0,0 +1,12 @@ +split_version: "1.0" +random_seed: 42 +selection_criteria: + organelle: Nuclei + source_store: cell.zarr + notes: "Nucleus channel selected from shared cell.zarr (also serves membrane target)." +train: + count: 500 + fovs: [] +test: + count: 100 + fovs: [] diff --git a/applications/dynacell/src/dynacell/_manifests/aics-hipsc/splits/sec61b_train_val_test.yaml b/applications/dynacell/src/dynacell/_manifests/aics-hipsc/splits/sec61b_train_val_test.yaml new file mode 100644 index 000000000..d27c7b7c5 --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/aics-hipsc/splits/sec61b_train_val_test.yaml @@ -0,0 +1,11 @@ +split_version: "1.0" +random_seed: 42 +selection_criteria: + organelle: SEC61B + min_depth: 44 +train: + count: 500 + fovs: [] +test: + count: 100 + fovs: [] diff --git a/applications/dynacell/src/dynacell/_manifests/aics-hipsc/splits/tomm20_train_val_test.yaml b/applications/dynacell/src/dynacell/_manifests/aics-hipsc/splits/tomm20_train_val_test.yaml new file mode 100644 index 000000000..f3aecdaba --- /dev/null +++ b/applications/dynacell/src/dynacell/_manifests/aics-hipsc/splits/tomm20_train_val_test.yaml @@ -0,0 +1,11 @@ +split_version: "1.0" +random_seed: 42 +selection_criteria: + organelle: TOMM20 + min_depth: 44 +train: + count: 500 + fovs: [] +test: + count: 100 + fovs: [] diff --git a/applications/dynacell/src/dynacell/celldiff_wrapper.py b/applications/dynacell/src/dynacell/celldiff_wrapper.py new file mode 100644 index 000000000..a22baa730 --- /dev/null +++ b/applications/dynacell/src/dynacell/celldiff_wrapper.py @@ -0,0 +1,384 @@ +"""Flow-matching virtual staining wrapper for CELLDiffNet. + +Wraps the :class:`~viscy_models.celldiff.CELLDiffNet` backbone with +flow-matching transport to provide training loss computation and +ODE-based generation (single-patch, non-overlapping tiles, sliding window). + +This module belongs in the application layer because it owns training +semantics (transport sampling, path planning, loss aggregation). +The reusable backbone and transport numerics live in ``viscy-models``. +""" + +import itertools + +import torch +from torch import Tensor, nn + +from viscy_models.celldiff import CELLDiffNet +from viscy_models.celldiff.modules.transport import Sampler, create_transport + + +class CELLDiff3DVS(nn.Module): + """Flow-matching virtual staining model. + + Wraps a :class:`CELLDiffNet` backbone with a flow-matching transport to + provide training loss computation and inference (generation) methods. + + Parameters + ---------- + net : CELLDiffNet + Backbone network for velocity prediction. + path_type : str + Flow path type, e.g. ``"Linear"``. + prediction : str + Prediction target, e.g. ``"velocity"``. + loss_weight : str or None + Optional loss weighting scheme (``"velocity"`` or ``"likelihood"``). + train_eps : float or None + Training epsilon for transport stability. + sample_eps : float or None + Sampling epsilon for transport stability. + """ + + def __init__( + self, + net: CELLDiffNet, + path_type: str = "Linear", + prediction: str = "velocity", + loss_weight: str | None = None, + train_eps: float | None = None, + sample_eps: float | None = None, + ) -> None: + super().__init__() + self.net = net + self.path_type = path_type + self.prediction = prediction + self.transport = create_transport(path_type, prediction, loss_weight, train_eps, sample_eps) + self.transport_sampler = Sampler(self.transport) + + def forward(self, phase: Tensor, target: Tensor) -> Tensor: + """Compute flow-matching training loss. + + Parameters + ---------- + phase : Tensor + Phase contrast input of shape ``(B, 1, D, H, W)``. + target : Tensor + Fluorescence target of shape ``(B, C, D, H, W)``. + + Returns + ------- + Tensor + Scalar training loss. + """ + t, x0, x1 = self.transport.sample(target) + t, xt, ut = self.transport.path_sampler.plan(t, x0, x1) + pred = self.net(xt, phase, t) + loss_dict = self.transport.training_losses(pred, x0, x1, xt, ut, t) + return loss_dict["loss"].mean() + + def _noise_like_target(self, phase: Tensor) -> Tensor: + """Create Gaussian noise with the network's output channel count. + + Parameters + ---------- + phase : Tensor + Phase conditioning tensor whose batch and spatial dims are reused. + + Returns + ------- + Tensor + Noise of shape ``(B, in_channels, D, H, W)``. + """ + b, _c, *spatial = phase.shape + in_ch = self.net.inconv.in_channels + return torch.randn(b, in_ch, *spatial, device=phase.device, dtype=phase.dtype) + + def generate(self, phase: Tensor, num_steps: int = 100) -> Tensor: + """Generate virtual staining via ODE sampling. + + Parameters + ---------- + phase : Tensor + Phase contrast input of shape ``(B, 1, D, H, W)``. + num_steps : int + Number of ODE integration steps. + + Returns + ------- + Tensor + Predicted fluorescence of shape ``(B, in_channels, D, H, W)``. + """ + target = self._noise_like_target(phase) + sample_fn = self.transport_sampler.sample_ode(num_steps=num_steps) + + def fn(xt: Tensor, t: Tensor) -> Tensor: + return self.net(xt, phase, t) + + with torch.no_grad(): + target = sample_fn(target, fn)[-1] + + return target + + def generate_sliding_window(self, phase: Tensor, num_steps: int = 100) -> Tensor: + """Generate virtual staining via tiled sliding window (stride == patch size). + + Partitions the input into non-overlapping patches of size + ``net.input_spatial_size``. Each patch is generated independently + with fresh Gaussian noise and the results are written back into the + corresponding region of the output tensor. The last tile along each + axis is snapped to the image edge, so it may overlap its predecessor + when the image size is not an exact multiple of the patch size. + + Parameters + ---------- + phase : Tensor + Phase contrast input of shape ``(..., D, H, W)``. + num_steps : int + Number of ODE integration steps per patch. + + Returns + ------- + Tensor + Predicted fluorescence of shape ``(..., D, H, W)``. + """ + spatial = tuple(phase.shape[-3:]) + patch_spatial = tuple(self.net.input_spatial_size) + n_spatial = 3 + + for i in range(n_spatial): + if spatial[i] < patch_spatial[i]: + raise ValueError(f"spatial dim {i} ({spatial[i]}) must be >= patch dim ({patch_spatial[i]})") + + in_ch = self.net.inconv.in_channels + out_shape = (*phase.shape[:-4], in_ch, *phase.shape[-3:]) + out = torch.empty(out_shape, device=phase.device, dtype=phase.dtype) + sample_fn = self.transport_sampler.sample_ode(num_steps=num_steps) + + start_lists: list[list[int]] = [] + for i in range(n_spatial): + S, P = spatial[i], patch_spatial[i] + starts = list(range(0, S - P + 1, P)) + if starts[-1] != S - P: + starts.append(S - P) + start_lists.append(starts) + + with torch.no_grad(): + for starts in itertools.product(*start_lists): + slicer = [slice(None)] * phase.dim() + for i, st in enumerate(starts): + slicer[-(n_spatial - i)] = slice(st, st + patch_spatial[i]) + phase_patch = phase[tuple(slicer)] + xt = self._noise_like_target(phase_patch) + + def fn( + xt_: Tensor, + t_: Tensor, + _p: Tensor = phase_patch, + ) -> Tensor: + return self.net(xt_, _p, t_) + + out[tuple(slicer)] = sample_fn(xt, fn)[-1] + + return out + + def generate_iterative( + self, + phase: Tensor, + num_steps: int = 100, + overlap_size: int | tuple[int, ...] = 256, + ) -> Tensor: + """Generate virtual staining via overlapping sliding window with velocity anchoring. + + Slides overlapping patches across the input. For each patch the + overlap region (already generated by an earlier patch) is used to + steer the ODE trajectory toward the previously computed output values + rather than letting the solver integrate freely. + + **Anchoring mechanism** (requires Linear path + velocity prediction): + At every ODE step the network predicts a velocity ``v``. Under the + Linear flow the starting point is ``x0 = xt - t * v``. For pixels in + the overlap region we override the velocity with + ``v_anchored = out_known - x0``, which is the exact velocity that + would integrate ``x0`` to the already-computed target ``out_known``. + Outside the overlap the free velocity ``v`` is used unchanged. + + Parameters + ---------- + phase : Tensor + Phase contrast input of shape ``(..., D, H, W)``. + num_steps : int + Number of ODE integration steps per patch. + overlap_size : int or tuple of int + Overlap in each spatial dimension ``(od, oh, ow)``. + A single int applies the same overlap to all three dimensions. + + Returns + ------- + Tensor + Predicted fluorescence of shape ``(..., D, H, W)``. + + Raises + ------ + NotImplementedError + If ``path_type`` is not ``"Linear"`` or ``prediction`` is not + ``"velocity"``, since the anchoring formula is path-specific. + """ + spatial = tuple(phase.shape[-3:]) + patch_spatial = tuple(self.net.input_spatial_size) + n_spatial = 3 + + if isinstance(overlap_size, int): + overlap = (overlap_size,) * n_spatial + else: + overlap = tuple(overlap_size) + if len(overlap) != n_spatial: + raise ValueError("overlap_size must be int or a 3-tuple") + + for i in range(n_spatial): + s_i, p_i, ov = spatial[i], patch_spatial[i], overlap[i] + if s_i < p_i: + raise ValueError(f"spatial dim {i} ({s_i}) must be >= patch dim ({p_i})") + if not (0 <= ov < p_i): + raise ValueError(f"overlap at dim {i} must satisfy 0 <= overlap < patch (got {ov} vs patch {p_i})") + + if self.path_type != "Linear" or self.prediction != "velocity": + raise NotImplementedError( + "generate_iterative only supports Linear path with velocity prediction, " + f"got path_type={self.path_type!r}, prediction={self.prediction!r}" + ) + + in_ch = self.net.inconv.in_channels + out_shape = (*phase.shape[:-4], in_ch, *phase.shape[-3:]) + out = torch.full(out_shape, float("nan"), device=phase.device, dtype=phase.dtype) + sample_fn = self.transport_sampler.sample_ode(num_steps=num_steps) + + start_lists: list[list[int]] = [] + for i in range(n_spatial): + s_i, p_i, ov = spatial[i], patch_spatial[i], overlap[i] + stride = p_i - ov + last = s_i - p_i + starts = [0] + while True: + nxt = starts[-1] + stride + if nxt >= last: + break + starts.append(nxt) + if starts[-1] != last: + starts.append(last) + start_lists.append(starts) + + with torch.no_grad(): + for starts in itertools.product(*start_lists): + slicer = [slice(None)] * phase.dim() + for i, st in enumerate(starts): + slicer[-(n_spatial - i)] = slice(st, st + patch_spatial[i]) + + phase_patch = phase[tuple(slicer)] + out_patch = out[tuple(slicer)].clone() + xt = self._noise_like_target(phase_patch) + known_mask = ~torch.isnan(out_patch) + + def fn( + xt_: Tensor, + t_: Tensor, + _p: Tensor = phase_patch, + _out: Tensor = out_patch, + _mask: Tensor = known_mask, + ) -> Tensor: + v = self.net(xt_, _p, t_) + # Infer x0 from the Linear-path formula: x0 = xt - t*v. + t_exp = t_.reshape(t_.shape[0], *([1] * (xt_.dim() - 1))) + x0_ = xt_ - t_exp * v + # Velocity that integrates x0 exactly to the known target: v = x1 - x0. + v_out = _out - x0_ + # Use the anchored velocity in the overlap region, free velocity elsewhere. + return torch.where(_mask, v_out, v) + + patch_out = sample_fn(xt, fn)[-1] + out[tuple(slicer)] = patch_out + + return out + + def denoise_sliding_window( + self, + phase: Tensor, + overlap_size: int | tuple[int, ...] = 0, + ) -> Tensor: + """Estimate the conditional mean via overlapping tiled single-step Euler updates. + + Slides overlapping patches across the input. Each patch is denoised + independently with fresh Gaussian noise and the results are accumulated + with a count tensor; overlapping regions are averaged, which reduces + variance and approximates the conditional mean. + + Parameters + ---------- + phase : Tensor + Phase contrast input of shape ``(..., D, H, W)``. + overlap_size : int or tuple of int + Overlap in each spatial dimension ``(od, oh, ow)``. + A single int applies the same overlap to all three dimensions. + + Returns + ------- + Tensor + Predicted fluorescence of shape ``(..., D, H, W)``. + """ + if self.path_type != "Linear" or self.prediction != "velocity": + raise NotImplementedError( + "denoise_sliding_window only supports Linear path with velocity prediction, " + f"got path_type={self.path_type!r}, prediction={self.prediction!r}" + ) + + spatial = tuple(phase.shape[-3:]) + patch_spatial = tuple(self.net.input_spatial_size) + n_spatial = 3 + + if isinstance(overlap_size, int): + overlap = (overlap_size,) * n_spatial + else: + overlap = tuple(overlap_size) + if len(overlap) != n_spatial: + raise ValueError("overlap_size must be int or a 3-tuple") + + for i in range(n_spatial): + S, P, Ov = spatial[i], patch_spatial[i], overlap[i] + if S < P: + raise ValueError(f"spatial dim {i} ({S}) must be >= patch dim ({P})") + if not (0 <= Ov < P): + raise ValueError(f"overlap at dim {i} must satisfy 0 <= overlap < patch (got {Ov} vs {P})") + + in_ch = self.net.inconv.in_channels + out_shape = (*phase.shape[:-4], in_ch, *phase.shape[-3:]) + prediction_sum = torch.zeros(out_shape, device=phase.device, dtype=phase.dtype) + prediction_count = torch.zeros(out_shape, device=phase.device, dtype=phase.dtype) + + start_lists: list[list[int]] = [] + for i in range(n_spatial): + S, P, Ov = spatial[i], patch_spatial[i], overlap[i] + stride = P - Ov + last = S - P + starts = [0] + while starts[-1] + stride < last: + starts.append(starts[-1] + stride) + if starts[-1] != last: + starts.append(last) + start_lists.append(starts) + + with torch.no_grad(): + for starts in itertools.product(*start_lists): + slicer = [slice(None)] * phase.dim() + for i, st in enumerate(starts): + slicer[-(n_spatial - i)] = slice(st, st + patch_spatial[i]) + phase_patch = phase[tuple(slicer)] + xt = self._noise_like_target(phase_patch) + t = torch.zeros(xt.shape[0], device=xt.device, dtype=xt.dtype) + pred = self.net(xt, phase_patch, t) + patch_out = pred + xt + prediction_sum[tuple(slicer)] += patch_out + prediction_count[tuple(slicer)] += 1 + + if not torch.all(prediction_count > 0): + raise RuntimeError("sliding window left uncovered voxels") + return prediction_sum / prediction_count diff --git a/applications/dynacell/src/dynacell/data/__init__.py b/applications/dynacell/src/dynacell/data/__init__.py new file mode 100644 index 000000000..9843e505a --- /dev/null +++ b/applications/dynacell/src/dynacell/data/__init__.py @@ -0,0 +1,56 @@ +"""Dataset schemas and path-based loaders for the DynaCell benchmark.""" + +from dynacell.data.collections import ( + BenchmarkCollection, + ChannelEntry, + CollectionExperiment, + Provenance, + load_collection, +) +from dynacell.data.manifests import ( + DatasetManifest, + DatasetRef, + SplitDefinition, + StoreLocations, + TargetConfig, + VoxelSpacing, + get_target, + load_manifest, + load_splits, +) +from dynacell.data.resolver import ( + ManifestNotFoundError, + NoManifestRootsError, + ResolvedDataset, + TargetNotFoundError, + dataset_ref_from_dict, + discover_manifest_roots, + resolve_dataset_ref, +) +from dynacell.data.specs import BenchmarkSpec, load_benchmark_spec + +__all__ = [ + "BenchmarkCollection", + "BenchmarkSpec", + "ChannelEntry", + "CollectionExperiment", + "DatasetManifest", + "DatasetRef", + "ManifestNotFoundError", + "NoManifestRootsError", + "Provenance", + "ResolvedDataset", + "SplitDefinition", + "StoreLocations", + "TargetConfig", + "TargetNotFoundError", + "VoxelSpacing", + "dataset_ref_from_dict", + "discover_manifest_roots", + "get_target", + "load_benchmark_spec", + "load_collection", + "load_manifest", + "load_splits", + "resolve_dataset_ref", +] diff --git a/applications/dynacell/src/dynacell/data/_yaml.py b/applications/dynacell/src/dynacell/data/_yaml.py new file mode 100644 index 000000000..0da122a48 --- /dev/null +++ b/applications/dynacell/src/dynacell/data/_yaml.py @@ -0,0 +1,30 @@ +"""Shared OmegaConf + Pydantic YAML loading.""" + +from __future__ import annotations + +from pathlib import Path +from typing import TypeVar + +from omegaconf import OmegaConf +from pydantic import BaseModel + +T = TypeVar("T", bound=BaseModel) + + +def load_yaml(path: Path, model_class: type[T]) -> T: + """Load a YAML file and validate it against a Pydantic model. + + Parameters + ---------- + path : Path + Path to a YAML file. + model_class : type[T] + Pydantic model class to validate against. + + Returns + ------- + T + Validated model instance. + """ + raw = OmegaConf.to_container(OmegaConf.load(path), resolve=True) + return model_class.model_validate(raw) diff --git a/applications/dynacell/src/dynacell/data/collections.py b/applications/dynacell/src/dynacell/data/collections.py new file mode 100644 index 000000000..9102fcd6c --- /dev/null +++ b/applications/dynacell/src/dynacell/data/collections.py @@ -0,0 +1,67 @@ +"""Frozen collection schemas for benchmark data curation.""" + +from __future__ import annotations + +from pathlib import Path + +from pydantic import BaseModel, Field + +from dynacell.data._yaml import load_yaml +from viscy_data.collection import ChannelEntry + + +class Provenance(BaseModel): + """Airtable-derived provenance for a frozen collection. + + Stricter than ``viscy_data.collection.Provenance`` — requires + ``created_at`` and ``created_by`` for benchmark traceability. + """ + + airtable_base_id: str | None = None + airtable_query: str | None = None + record_ids: list[str] = Field(default_factory=list) + created_at: str + created_by: str + + +class CollectionExperiment(BaseModel): + """One experiment within a benchmark collection.""" + + name: str + data_path: Path + channels: list[ChannelEntry] + perturbation_wells: dict[str, list[str]] | None = None + interval_minutes: float | None = None + start_hpi: float | None = None + marker: str | None = None + organelle: str | None = None + pixel_size_xy_um: float + pixel_size_z_um: float | None = None + exclude_fovs: list[str] = Field(default_factory=list) + + +class BenchmarkCollection(BaseModel): + """Frozen collection tying experiments to train/test FOV membership.""" + + name: str + description: str + provenance: Provenance + experiments: list[CollectionExperiment] + train_fovs: list[str] | None = None + test_fovs: list[str] | None = None + + +def load_collection(collection_path: Path) -> BenchmarkCollection: + """Load and validate a frozen benchmark collection. + + Parameters + ---------- + collection_path : Path + Path to a collection YAML file. + + Returns + ------- + BenchmarkCollection + Validated collection. + """ + return load_yaml(collection_path, BenchmarkCollection) diff --git a/applications/dynacell/src/dynacell/data/manifests.py b/applications/dynacell/src/dynacell/data/manifests.py new file mode 100644 index 000000000..5189d88ee --- /dev/null +++ b/applications/dynacell/src/dynacell/data/manifests.py @@ -0,0 +1,179 @@ +"""Dataset manifest schemas and loaders for the DynaCell benchmark. + +Pydantic models that parse and validate YAML manifests. Loaders accept +explicit file paths — no import-time registry or hardcoded config roots. +""" + +from __future__ import annotations + +from functools import lru_cache +from pathlib import Path + +from pydantic import BaseModel, field_validator, model_validator + +from dynacell.data._yaml import load_yaml + + +class DatasetRef(BaseModel): + """Reference to a dataset target, resolved against a manifest registry. + + Carried under ``benchmark.dataset_ref`` in benchmark leaf configs. + The composition-time resolver reads this reference and splices + ``data_path``, ``source_channel``, and ``target_channel`` into the + composed Lightning config. + """ + + dataset: str + target: str + + +class VoxelSpacing(BaseModel): + """Physical voxel spacing in micrometers.""" + + z: float + y: float + x: float + + def as_list(self) -> list[float]: + """Return spacing as ``[z, y, x]`` list for metric functions.""" + return [self.z, self.y, self.x] + + +class StoreLocations(BaseModel): + """Zarr store paths for a single organelle target.""" + + train: Path + test: Path + cell_segmentation: Path | None = None + gt_cache_dir: Path | None = None + + +class TargetConfig(BaseModel): + """Configuration for a single organelle prediction target.""" + + gene: str + organelle: str + display_name: str + target_channel: str + stores: StoreLocations + splits: str + + +class DatasetManifest(BaseModel): + """Top-level dataset manifest.""" + + name: str + version: str + description: str + cell_type: str + imaging_modality: str + spacing: VoxelSpacing + channels: dict[str, str | list[str]] + targets: dict[str, TargetConfig] + + @field_validator("targets") + @classmethod + def _targets_not_empty(cls, v: dict) -> dict: + """Validate that at least one target is defined.""" + if not v: + raise ValueError("Manifest must define at least one target.") + return v + + @property + def source_channel(self) -> str: + """Return the single source channel name for source-target datasets. + + ``channels["source"]`` may be a string or a single-element list; a + multi-element list is rejected since downstream ``HCSDataModule`` + takes one channel name. + """ + source = self.channels["source"] + if isinstance(source, str): + return source + if isinstance(source, list) and len(source) == 1: + return source[0] + raise ValueError(f"Manifest source channel must be a string or single-element list, got {source!r}.") + + +class SplitDefinition(BaseModel): + """Train/val/test FOV split for one organelle.""" + + split_version: str + random_seed: int + source_stores: list[Path] | None = None + selection_criteria: dict | None = None + train: dict + test: dict + val: dict | None = None + + @model_validator(mode="after") + def _check_counts(self) -> SplitDefinition: + """Validate count matches len(fovs) when fovs is non-empty.""" + for split_name in ("train", "val", "test"): + split = getattr(self, split_name) + if split is None: + continue + fovs = split.get("fovs", []) + if fovs and "count" in split: + if len(fovs) != split["count"]: + raise ValueError(f"{split_name} declares count={split['count']} but has {len(fovs)} FOVs.") + return self + + +@lru_cache(maxsize=64) +def load_manifest(manifest_path: Path) -> DatasetManifest: + """Load and validate a dataset manifest from a YAML file. + + Cached by resolved path; manifests are treated as immutable within a + process (same policy as :func:`viscy_utils.compose._load_yaml_cached`). + + Parameters + ---------- + manifest_path : Path + Path to a dataset manifest YAML file. + + Returns + ------- + DatasetManifest + Validated manifest. + """ + return load_yaml(manifest_path, DatasetManifest) + + +def load_splits(split_path: Path) -> SplitDefinition: + """Load and validate a split definition from a YAML file. + + Parameters + ---------- + split_path : Path + Path to a split definition YAML file. + + Returns + ------- + SplitDefinition + Validated split definition. + """ + return load_yaml(split_path, SplitDefinition) + + +def get_target(manifest: DatasetManifest, target_name: str) -> TargetConfig: + """Get a specific target from a loaded manifest. + + Parameters + ---------- + manifest : DatasetManifest + A loaded dataset manifest. + target_name : str + Name of the target (e.g., ``"sec61b"``). + + Returns + ------- + TargetConfig + Target configuration. + + Raises + ------ + KeyError + If ``target_name`` is not in the manifest. + """ + return manifest.targets[target_name] diff --git a/applications/dynacell/src/dynacell/data/resolver.py b/applications/dynacell/src/dynacell/data/resolver.py new file mode 100644 index 000000000..29a26ad7a --- /dev/null +++ b/applications/dynacell/src/dynacell/data/resolver.py @@ -0,0 +1,196 @@ +"""Manifest-driven dataset reference resolution for the DynaCell benchmark. + +Turns a :class:`DatasetRef` (``{dataset, target}``) into concrete paths and +channel names by reading a Pydantic :class:`DatasetManifest` YAML discovered +via manifest roots. Callers compose this with the config pipeline via +:mod:`dynacell._compose_hook`. + +Manifest root precedence (highest wins): + +1. ``cli_roots`` argument. +2. ``DYNACELL_MANIFEST_ROOTS`` env var (``os.pathsep``-separated paths). +3. Python entry points under group ``dynacell.manifest_roots``. + +For each root (in order), the resolver looks for +``//manifest.yaml``. First hit wins. No recursion, no +globbing. +""" + +from __future__ import annotations + +import os +from importlib import resources +from importlib.metadata import entry_points +from pathlib import Path + +from pydantic import BaseModel + +from dynacell.data.manifests import ( + DatasetRef, + VoxelSpacing, + load_manifest, +) + + +class NoManifestRootsError(RuntimeError): + """No manifest roots could be discovered from CLI, env, or entry points.""" + + +class ManifestNotFoundError(LookupError): + """Dataset slug not found under any configured manifest root.""" + + +class TargetNotFoundError(LookupError): + """Target slug not present in the located dataset manifest.""" + + +class ResolvedDataset(BaseModel): + """Flat view of the manifest fields a composed config needs.""" + + manifest_path: Path + data_path_train: Path + data_path_test: Path + source_channel: str + target_channel: str + spacing: VoxelSpacing + cell_segmentation_path: Path | None = None + gt_cache_dir: Path | None = None + + +_ENV_VAR = "DYNACELL_MANIFEST_ROOTS" +_ENTRY_POINT_GROUP = "dynacell.manifest_roots" + +REQUIRED_REF_KEYS: tuple[str, ...] = ("dataset", "target") + + +def dataset_ref_from_dict(ref_dict: object) -> DatasetRef | None: + """Validate a ``benchmark.dataset_ref`` dict, returning ``None`` for partial refs. + + Shared between the Lightning-side compose hook and the Hydra-side + eval hook so the "full ref vs partial ref vs no ref" policy stays + identical across surfaces. A missing dict, non-dict value, or + partial dict (either ``dataset`` or ``target`` missing) is treated + as a no-op signal (returns ``None``). A dict with both keys present + is validated via Pydantic — malformed values surface as the usual + :class:`pydantic.ValidationError`. + """ + if not isinstance(ref_dict, dict): + return None + if not all(k in ref_dict for k in REQUIRED_REF_KEYS): + return None + return DatasetRef.model_validate(ref_dict) + + +def _entry_point_roots() -> list[Path]: + """Resolve entry-point-registered manifest roots to package resource dirs.""" + roots: list[Path] = [] + for ep in entry_points(group=_ENTRY_POINT_GROUP): + module = ep.load() + resource_dir = resources.files(module) + roots.append(Path(str(resource_dir))) + return roots + + +def discover_manifest_roots(cli_roots: list[Path] | None = None) -> list[Path]: + """Return manifest roots in precedence order (CLI → env var → entry points). + + Parameters + ---------- + cli_roots : list[Path] or None + Explicit roots provided by the caller. If given, they take + precedence over environment and entry points but do not replace + them — lower-precedence roots still contribute. + + Returns + ------- + list[Path] + Non-empty list of roots to scan. + + Raises + ------ + NoManifestRootsError + If no roots are configured at any precedence level. + """ + roots: list[Path] = [] + if cli_roots: + roots.extend(Path(p) for p in cli_roots) + env_value = os.environ.get(_ENV_VAR) + if env_value: + roots.extend(Path(p) for p in env_value.split(os.pathsep) if p) + roots.extend(_entry_point_roots()) + if not roots: + raise NoManifestRootsError( + "No dynacell manifest roots configured.\n\n" + "VisCy ships its own bundled registry at " + "``dynacell._manifests``; this error means the entry-point " + "provider declared in applications/dynacell/pyproject.toml " + "didn't load.\n\n" + "Confirm dynacell was installed cleanly (``uv sync`` from the " + "VisCy worktree). To override with a different registry, set " + f"``{_ENV_VAR}=/path/to/datasets`` (env var) or pass " + "``cli_roots=`` to ``discover_manifest_roots``.\n" + ) + return roots + + +def _find_manifest(dataset: str, roots: list[Path]) -> Path: + """Return the first ``//manifest.yaml`` that exists.""" + searched: list[Path] = [] + for root in roots: + candidate = root / dataset / "manifest.yaml" + searched.append(candidate) + if candidate.is_file(): + return candidate + lines = "\n".join(f" - {p}" for p in searched) + raise ManifestNotFoundError(f"dataset {dataset!r} not found.\n\nSearched:\n{lines}\n") + + +def resolve_dataset_ref( + ref: DatasetRef, + roots: list[Path] | None = None, +) -> ResolvedDataset: + """Resolve a :class:`DatasetRef` against the manifest registry. + + Parameters + ---------- + ref : DatasetRef + The reference to resolve. + roots : list[Path] or None + Optional explicit roots (CLI-provided). Falls back to env var and + entry points per :func:`discover_manifest_roots`. + + Returns + ------- + ResolvedDataset + Flat view of the fields the composed config needs. + + Raises + ------ + NoManifestRootsError + If no manifest roots are configured. + ManifestNotFoundError + If the dataset slug is not found under any root. + TargetNotFoundError + If the target slug is not defined in the located manifest. + """ + all_roots = discover_manifest_roots(roots) + manifest_path = _find_manifest(ref.dataset, all_roots) + manifest = load_manifest(manifest_path) + if ref.target not in manifest.targets: + available = ", ".join(sorted(manifest.targets)) or "(none)" + raise TargetNotFoundError( + f"target {ref.target!r} not found in dataset {ref.dataset!r}.\n\n" + f"Manifest: {manifest_path}\n" + f"Available targets: {available}\n" + ) + target = manifest.targets[ref.target] + return ResolvedDataset( + manifest_path=manifest_path, + data_path_train=target.stores.train, + data_path_test=target.stores.test, + source_channel=manifest.source_channel, + target_channel=target.target_channel, + spacing=manifest.spacing, + cell_segmentation_path=target.stores.cell_segmentation, + gt_cache_dir=target.stores.gt_cache_dir, + ) diff --git a/applications/dynacell/src/dynacell/data/specs.py b/applications/dynacell/src/dynacell/data/specs.py new file mode 100644 index 000000000..f72b694c1 --- /dev/null +++ b/applications/dynacell/src/dynacell/data/specs.py @@ -0,0 +1,41 @@ +"""Benchmark spec schemas for reproducible benchmark runs.""" + +from __future__ import annotations + +from pathlib import Path + +from pydantic import BaseModel, Field + +from dynacell.data._yaml import load_yaml + + +class BenchmarkSpec(BaseModel): + """Executable benchmark recipe tying together pipeline stages.""" + + name: str + version: str + description: str + collection_path: Path + preprocess_configs: list[Path] = Field(default_factory=list) + train_preset: str | None = None + predict_preset: str | None = None + evaluate_config: Path | None = None + report_config: Path | None = None + output_root: Path + checkpoint_path: Path | None = None + + +def load_benchmark_spec(spec_path: Path) -> BenchmarkSpec: + """Load and validate a benchmark spec. + + Parameters + ---------- + spec_path : Path + Path to a benchmark spec YAML file. + + Returns + ------- + BenchmarkSpec + Validated benchmark spec. + """ + return load_yaml(spec_path, BenchmarkSpec) diff --git a/applications/dynacell/src/dynacell/engine.py b/applications/dynacell/src/dynacell/engine.py new file mode 100644 index 000000000..a9cfbd075 --- /dev/null +++ b/applications/dynacell/src/dynacell/engine.py @@ -0,0 +1,1363 @@ +"""Dynacell LightningModules for virtual staining benchmarks. + +Provides :class:`DynacellUNet` for supervised regression, +:class:`DynacellFlowMatching` for flow-matching generative staining, and +:class:`DynacellGAN` for adversarial (LSGAN + L1) virtual staining. +""" + +import copy +import inspect +import itertools +import logging +from typing import Literal, Sequence + +import numpy as np +import torch +import torch.nn.functional as F +from lightning.pytorch import LightningModule +from monai.transforms import DivisiblePad +from torch import Tensor, nn + +from dynacell.celldiff_wrapper import CELLDiff3DVS +from viscy_data import Sample +from viscy_models import Unet3d, UNeXt2 +from viscy_models.celldiff import CELLDiffNet, UNetViT3D +from viscy_models.gan import ( + MultiScalePatchGAN3D, + lsgan_d_loss, + lsgan_g_loss, + nonsat_d_loss, + nonsat_g_loss, + r1_penalty, + r2_penalty, + rpgan_d_loss, + rpgan_g_loss, +) +from viscy_models.unet.fcmae import FullyConvolutionalMAE +from viscy_utils.log_images import detach_sample, log_image_grid +from viscy_utils.optimizers import configure_adamw_scheduler + +_logger = logging.getLogger("lightning.pytorch") + +_ARCHITECTURE: dict[str, type[nn.Module]] = { + "UNetViT3D": UNetViT3D, + "FNet3D": Unet3d, + "UNeXt2": UNeXt2, + "fcmae": FullyConvolutionalMAE, +} + + +def _aggregate_validation_losses( + validation_losses: list[list[tuple[Tensor, int]]], +) -> Tensor: + """Compute sample-weighted mean loss across dataloaders. + + Parameters + ---------- + validation_losses : list of list of (Tensor, int) + Per-dataloader list of ``(scalar_loss, batch_size)`` tuples + accumulated during validation. + + Returns + ------- + Tensor + Scalar weighted mean loss. + """ + dl_means: list[Tensor] = [] + dl_totals: list[Tensor] = [] + for dl_batches in validation_losses: + losses, sizes = zip(*dl_batches) + sizes_t = torch.tensor(sizes, dtype=torch.float, device=losses[0].device) + dl_means.append((torch.stack(losses) * sizes_t).sum() / sizes_t.sum()) + dl_totals.append(sizes_t.sum()) + total_n = torch.stack(dl_totals).sum() + weighted = torch.stack([m * n for m, n in zip(dl_means, dl_totals)]).sum() + return weighted / total_n + + +def _log_samples(module: LightningModule, key: str, imgs: Sequence[Sequence[np.ndarray]]) -> None: + """Log a list of detached image samples to the experiment logger at rank 0.""" + if not imgs or not module.trainer.is_global_zero or module.logger is None: + return + log_image_grid(module.logger, key, imgs, module.current_epoch) + + +def _make_divisible_pad(model: nn.Module) -> DivisiblePad: + """Build a DivisiblePad matching the model's spatial downsampling axes. + + Parameters + ---------- + model : nn.Module + A model with ``num_blocks`` and optionally ``downsamples_z``. + + Returns + ------- + DivisiblePad + Pads YX (and Z if ``downsamples_z``) to the nearest multiple of + ``2**num_blocks``. + """ + down_factor = 2**model.num_blocks + if getattr(model, "downsamples_z", False): + return DivisiblePad((0, down_factor, down_factor, down_factor)) + return DivisiblePad((0, 0, down_factor, down_factor)) + + +def _center_crop_to_shape(tensor: Tensor, spatial_shape: tuple[int, ...]) -> Tensor: + """Center-crop trailing spatial dimensions to the requested shape.""" + slices = [slice(None)] * tensor.ndim + start_dim = tensor.ndim - len(spatial_shape) + for dim, size in enumerate(spatial_shape, start=start_dim): + current = tensor.shape[dim] + if current < size: + raise ValueError(f"Cannot crop dimension {dim} from {current} to {size}") + start = (current - size) // 2 + slices[dim] = slice(start, start + size) + return tensor[tuple(slices)] + + +class DynacellUNet(LightningModule): + """Supervised regression U-Net for benchmark virtual staining. + + Parameters + ---------- + architecture : {"UNetViT3D", "FNet3D", "UNeXt2", "fcmae"} + Architecture key selecting the backbone. + model_config : dict | None + Keyword arguments forwarded to the backbone constructor. + loss_function : nn.Module | None + Loss function. Defaults to ``nn.MSELoss()``. + lr : float + Learning rate. + schedule : {"WarmupCosine", "Constant"} + LR scheduler type. + log_batches_per_epoch : int + Batches to log images per epoch. + log_samples_per_batch : int + Samples per batch to log. + example_input_yx_shape : Sequence[int] + YX shape for example input (used by FNet3D for graph logging). + Ignored when the model provides ``input_spatial_size``. + ckpt_path : str | None + Path to a checkpoint to load **weights only** at construction time. + Intended for inference (predict/test), not training resumption — + optimizer state, epoch counters, and scheduler state are not + restored. + encoder_only : bool, default False + When True, ``ckpt_path`` must be set, and only the + ``model.encoder.*`` weights are loaded (decoder/head stay at fresh + init). Intended for finetuning from an FCMAE-pretrained encoder. + Only supported for ``architecture='fcmae'``. + + Note: on resumed runs (via trainer-level ``--ckpt_path``), this + pre-load still fires in ``__init__`` before Lightning restores + the resume checkpoint, and the resume state overwrites it. The + file at ``ckpt_path`` must therefore remain accessible for the + lifetime of any run based on a pretrained leaf. + """ + + def __init__( + self, + architecture: Literal["UNetViT3D", "FNet3D", "UNeXt2", "fcmae"] = "UNetViT3D", + model_config: dict | None = None, + loss_function: nn.Module | None = None, + lr: float = 1e-3, + schedule: Literal["WarmupCosine", "Constant"] = "Constant", + warmup_steps: int = 3, + warmup_multiplier: float = 1e-3, + log_batches_per_epoch: int = 8, + log_samples_per_batch: int = 1, + example_input_yx_shape: Sequence[int] = (256, 256), + predict_method: Literal["full_image", "sliding_window"] = "full_image", + predict_overlap: tuple[int, int, int] = (4, 256, 256), + ckpt_path: str | None = None, + encoder_only: bool = False, + ) -> None: + super().__init__() + self.save_hyperparameters(ignore=["loss_function", "ckpt_path", "encoder_only"]) + if model_config is None: + model_config = {} + net_class = _ARCHITECTURE.get(architecture) + if net_class is None: + raise ValueError(f"Architecture {architecture!r} not in {set(_ARCHITECTURE)}") + self.model = net_class(**model_config) + self.loss_function = loss_function if loss_function is not None else nn.MSELoss() + self.lr = lr + self.schedule = schedule + self.warmup_steps = warmup_steps + self.warmup_multiplier = warmup_multiplier + self.log_batches_per_epoch = log_batches_per_epoch + self.log_samples_per_batch = log_samples_per_batch + self.predict_method = predict_method + self.predict_overlap = predict_overlap + + self.training_step_outputs: list = [] + # Each entry is a list of (loss, batch_size) tuples for weighted aggregation. + self.validation_losses: list[list[tuple[Tensor, int]]] = [] + self.validation_step_outputs: list = [] + + # Cache fg_mask compatibility to avoid per-batch inspect.signature(). + sig = inspect.signature(self.loss_function.forward) + self._loss_accepts_fg_mask = "fg_mask" in sig.parameters or any( + p.kind == inspect.Parameter.VAR_KEYWORD for p in sig.parameters.values() + ) + + # Build example_input_array for graph logging (TensorBoard/W&B). + in_channels = model_config.get("in_channels") or 1 + if hasattr(self.model, "input_spatial_size"): + # UNetViT3D: must use exact spatial dims. + d, h, w = self.model.input_spatial_size + else: + # FNet3D: flexible spatial, use in_stack_depth + user YX. + d = model_config.get("in_stack_depth") or 5 + h, w = example_input_yx_shape + self.example_input_array = torch.rand(1, in_channels, d, h, w) + + if encoder_only: + if ckpt_path is None: + raise ValueError("DynacellUNet(encoder_only=True) requires ckpt_path to be set") + if not isinstance(self.model, FullyConvolutionalMAE): + raise ValueError(f"encoder_only is only supported for architecture='fcmae', got {architecture!r}") + state_dict = torch.load(ckpt_path, weights_only=True, map_location="cpu")["state_dict"] + prefix = "model.encoder." + encoder_weights = {k.removeprefix(prefix): v for k, v in state_dict.items() if k.startswith(prefix)} + self.model.encoder.load_state_dict(encoder_weights, strict=True) + _logger.info(f"Loaded {len(encoder_weights)} encoder parameters from {ckpt_path}") + elif ckpt_path is not None: + self.load_state_dict(torch.load(ckpt_path, weights_only=True, map_location="cpu")["state_dict"]) + + def forward(self, x: Tensor) -> Tensor: + """Run forward pass through the model. + + Parameters + ---------- + x : Tensor + Input tensor of shape ``(B, C, D, H, W)``. + + Returns + ------- + Tensor + Model output. + """ + return self.model(x) + + def _compute_loss(self, pred: Tensor, target: Tensor, batch: Sample) -> Tensor: + """Compute loss, optionally passing fg_mask to the loss function.""" + if "fg_mask" in batch: + if not self._loss_accepts_fg_mask: + raise TypeError( + f"{type(self.loss_function).__name__} does not accept 'fg_mask'. " + f"Use SpotlightLoss or remove fg_mask_key from the data config." + ) + return self.loss_function(pred, target, fg_mask=batch["fg_mask"]) + return self.loss_function(pred, target) + + def training_step(self, batch: Sample, batch_idx: int) -> Tensor: + """Execute a single training step. + + Parameters + ---------- + batch : Sample + Input batch. + batch_idx : int + Batch index. + + Returns + ------- + Tensor + Training loss. + """ + source = batch["source"] + target = batch["target"] + pred = self.forward(source) + loss = self._compute_loss(pred, target, batch) + if batch_idx < self.log_batches_per_epoch: + self.training_step_outputs.extend(detach_sample((source, target, pred), self.log_samples_per_batch)) + self.log( + "loss/train", + loss, + on_step=True, + on_epoch=True, + prog_bar=True, + logger=True, + sync_dist=True, + batch_size=source.shape[0], + ) + return loss + + def validation_step(self, batch: Sample, batch_idx: int, dataloader_idx: int = 0): + """Execute a single validation step. + + Parameters + ---------- + batch : Sample + Input batch. + batch_idx : int + Batch index. + dataloader_idx : int + Dataloader index. + """ + source: Tensor = batch["source"] + target: Tensor = batch["target"] + pred = self.forward(source) + loss = self._compute_loss(pred, target, batch) + if dataloader_idx + 1 > len(self.validation_losses): + self.validation_losses.append([]) + self.validation_losses[dataloader_idx].append((loss.detach(), source.shape[0])) + self.log( + f"loss/val/{dataloader_idx}", + loss, + sync_dist=True, + batch_size=source.shape[0], + ) + if batch_idx < self.log_batches_per_epoch: + self.validation_step_outputs.extend(detach_sample((source, target, pred), self.log_samples_per_batch)) + + def on_predict_start(self) -> None: + """Build the divisible-pad transform for tiled inference.""" + self._predict_pad = _make_divisible_pad(self.model) + + def predict_step(self, batch: Sample, batch_idx: int, dataloader_idx: int = 0) -> Tensor: + """Execute a single prediction step. + + Pads the input tile to the nearest multiple of the model's downsampling + factor, runs the forward pass, then crops back to the original shape. + + Parameters + ---------- + batch : Sample + Input batch. Only ``"source"`` is used. + batch_idx : int + Batch index. + dataloader_idx : int + Dataloader index, defaults to 0. + + Returns + ------- + Tensor + Model prediction, cropped to the input spatial shape. + """ + source = batch["source"] + original_shape = source.shape[2:] + source = self._predict_pad(source) + if self.predict_method == "full_image": + prediction = self.forward(source) + elif self.predict_method == "sliding_window": + prediction = self.predict_sliding_window(source, overlap_size=self.predict_overlap) + else: + raise ValueError( + f"Unknown predict_method: {self.predict_method!r}. Choose 'full_image' or 'sliding_window'." + ) + return _center_crop_to_shape(prediction, original_shape) + + def on_train_epoch_end(self): + """Log training image samples.""" + _log_samples(self, "train_samples", self.training_step_outputs) + self.training_step_outputs = [] + + def on_validation_epoch_end(self): + """Log validation samples and aggregate loss weighted by batch size.""" + super().on_validation_epoch_end() + _log_samples(self, "val_samples", self.validation_step_outputs) + if self.validation_losses: + self.log("loss/validate", _aggregate_validation_losses(self.validation_losses), sync_dist=True) + self.validation_step_outputs.clear() + self.validation_losses.clear() + + def configure_optimizers(self): + """Configure AdamW optimizer with LR scheduler.""" + return configure_adamw_scheduler( + self, + self.model, + self.lr, + self.schedule, + warmup_steps=self.warmup_steps, + warmup_multiplier=self.warmup_multiplier, + ) + + def predict_sliding_window(self, source: Tensor, overlap_size: tuple[int, int, int] = (4, 256, 256)) -> Tensor: + """Run sliding-window inference over a large input volume. + + Overlapping regions are averaged across all covering patches. + + Parameters + ---------- + source : Tensor + Input tensor of shape ``(B, C, D, H, W)``. + overlap_size : tuple of int + Overlap in ``(D, H, W)`` between adjacent patches. + + Returns + ------- + Tensor + Prediction with the same spatial shape as ``source``. + """ + spatial = source.shape[-3:] + patch_spatial = tuple(self.model.input_spatial_size) + n_spatial = 3 + overlap = tuple(overlap_size) + + for i in range(n_spatial): + S, P, ov = spatial[i], patch_spatial[i], overlap[i] + if S < P: + raise ValueError(f"spatial dim {i} size {S} must be >= patch size {P}") + if not (0 <= ov < P): + raise ValueError(f"overlap at dim {i} must satisfy 0 <= overlap < patch (got {ov} vs {P})") + + # Accumulators are allocated lazily from the first patch output so + # their channel dimension matches the model's out_channels (which can + # differ from source's in_channels, e.g. 1 phase in -> 2 target out). + prediction_sum: Tensor | None = None + prediction_count: Tensor | None = None + + start_lists = [] + for i in range(n_spatial): + S, P, ov = spatial[i], patch_spatial[i], overlap[i] + stride = P - ov + last = S - P + starts = [0] + while starts[-1] + stride < last: + starts.append(starts[-1] + stride) + if starts[-1] != last: + starts.append(last) + start_lists.append(starts) + + with torch.no_grad(): + for starts in itertools.product(*start_lists): + slicer: list = [slice(None)] * source.ndim + for i, st in enumerate(starts): + slicer[-(n_spatial - i)] = slice(st, st + patch_spatial[i]) + patch_out = self.forward(source[tuple(slicer)]) + if prediction_sum is None: + out_shape = list(source.shape) + out_shape[1] = patch_out.shape[1] + prediction_sum = torch.zeros(out_shape, device=source.device, dtype=patch_out.dtype) + prediction_count = torch.zeros(out_shape, device=source.device, dtype=patch_out.dtype) + prediction_sum[tuple(slicer)] += patch_out + prediction_count[tuple(slicer)] += 1 + + if prediction_sum is None: + raise RuntimeError("sliding window produced no patches") + if not torch.all(prediction_count > 0): + raise RuntimeError("sliding window left uncovered voxels") + return prediction_sum / prediction_count + + +class DynacellFlowMatching(LightningModule): + """Flow-matching LightningModule for generative virtual staining. + + Wraps :class:`~dynacell.celldiff_wrapper.CELLDiff3DVS` for training, + validation image logging, and ODE-based prediction. The flow-matching + loss is computed entirely inside ``CELLDiff3DVS.forward``; no external + loss function is needed. + + Parameters + ---------- + net_config : dict or None + Keyword arguments forwarded to ``CELLDiffNet``. + transport_config : dict or None + Keyword arguments forwarded to ``CELLDiff3DVS`` (excluding ``net``). + Supports ``path_type``, ``prediction``, ``loss_weight``, ``train_eps``, + ``sample_eps``. + lr : float + Learning rate for AdamW optimizer. + schedule : {"WarmupCosine", "Constant"} + Learning rate schedule. + log_batches_per_epoch : int + Number of batches per epoch to accumulate for image logging. + log_samples_per_batch : int + Number of samples per batch to log. + num_generate_steps : int + Number of ODE steps for prediction inference. + num_log_steps : int + Number of ODE steps for validation image generation (cheaper than + ``num_generate_steps``). + compute_validation_loss : bool + Whether to compute and log flow-matching validation loss on the + validation loader. Disabled by default to preserve the previous + cheaper validation behavior. + predict_method : {"denoise", "generate", "sliding_window", "iterative"} + Prediction generation method. ``"generate"`` runs single-patch ODE + (default, matches standard HCS tile workflow). ``"sliding_window"`` + partitions the volume into **non-overlapping** tiles (ignores + ``predict_overlap``; passing a non-zero overlap raises so users + aren't silently misled). ``"iterative"`` slides overlapping tiles + with velocity anchoring — use this when you want + ``predict_overlap`` to apply. ``"denoise"`` uses the noise-space + overlap tiler. + predict_overlap : int or tuple of int + Overlap for ``denoise`` and ``iterative``. Ignored by + ``sliding_window``; must be ``0`` or ``[0, 0, 0]`` when + ``predict_method='sliding_window'``. + ckpt_path : str | None + Path to a checkpoint to load **weights only** at construction time. + Intended for inference (predict/test), not training resumption — + optimizer state, epoch counters, and scheduler state are not + restored. Bypasses LightningCLI's checkpoint hparam merging, so + predict-time settings (``predict_method``, ``predict_overlap``, + etc.) are taken from the config rather than the checkpoint. + """ + + def __init__( + self, + net_config: dict | None = None, + transport_config: dict | None = None, + lr: float = 1e-4, + schedule: Literal["WarmupCosine", "Constant"] = "WarmupCosine", + warmup_steps: int = 3, + warmup_multiplier: float = 1e-3, + log_batches_per_epoch: int = 8, + log_samples_per_batch: int = 1, + num_generate_steps: int = 100, + num_log_steps: int = 10, + compute_validation_loss: bool = False, + predict_method: Literal["denoise", "generate", "sliding_window", "iterative"] = "generate", + predict_overlap: int | tuple[int, int, int] = 256, + ckpt_path: str | None = None, + ) -> None: + super().__init__() + self.save_hyperparameters( + ignore=["predict_method", "predict_overlap", "num_generate_steps", "num_log_steps", "ckpt_path"] + ) + net = CELLDiffNet(**(net_config or {})) + self.model = CELLDiff3DVS(net, **(transport_config or {})) + self.lr = lr + self.schedule = schedule + self.warmup_steps = warmup_steps + self.warmup_multiplier = warmup_multiplier + self.log_batches_per_epoch = log_batches_per_epoch + self.log_samples_per_batch = log_samples_per_batch + self.num_generate_steps = num_generate_steps + self.num_log_steps = num_log_steps + self.compute_validation_loss = compute_validation_loss + self.predict_method = predict_method + self.predict_overlap = predict_overlap + self._training_step_outputs: list = [] + self._validation_losses: list[list[tuple[Tensor, int]]] = [] + self._val_log_batch: tuple[Tensor, Tensor] | None = None + if ckpt_path is not None: + self.load_state_dict(torch.load(ckpt_path, weights_only=True, map_location="cpu")["state_dict"]) + + def training_step(self, batch: dict, batch_idx: int) -> Tensor: + """Compute flow-matching training loss for one batch. + + Parameters + ---------- + batch : dict + Must contain ``"source"`` and ``"target"`` tensors. + batch_idx : int + Batch index. + + Returns + ------- + Tensor + Scalar flow-matching loss. + """ + phase: Tensor = batch["source"] + target: Tensor = batch["target"] + loss = self.model(phase, target) + self.log( + "loss/train", + loss, + on_step=True, + on_epoch=True, + prog_bar=True, + logger=True, + sync_dist=True, + batch_size=phase.shape[0], + ) + if batch_idx < self.log_batches_per_epoch: + self._training_step_outputs.extend(detach_sample((phase, target), self.log_samples_per_batch)) + return loss + + def validation_step(self, batch: dict, batch_idx: int, dataloader_idx: int = 0) -> None: + """Capture validation samples and optionally compute loss.""" + if batch_idx == 0 and self._val_log_batch is None: + n = self.log_samples_per_batch + self._val_log_batch = ( + batch["source"][:n].clone(), + batch["target"][:n].clone(), + ) + if not self.compute_validation_loss: + return + phase: Tensor = batch["source"] + target: Tensor = batch["target"] + loss = self.model(phase, target) + if dataloader_idx + 1 > len(self._validation_losses): + self._validation_losses.append([]) + self._validation_losses[dataloader_idx].append((loss.detach(), phase.shape[0])) + self.log( + f"loss/val/{dataloader_idx}", + loss, + sync_dist=True, + batch_size=phase.shape[0], + ) + + def on_train_epoch_end(self) -> None: + """Log training image samples at end of epoch.""" + _log_samples(self, "train_samples", self._training_step_outputs) + self._training_step_outputs = [] + + def on_validation_epoch_end(self) -> None: + """Generate ODE samples from captured validation batch and log.""" + super().on_validation_epoch_end() + if self._val_log_batch is not None: + if self.logger is not None: + phase_log, target_log = self._val_log_batch + n = min(self.log_samples_per_batch, phase_log.shape[0]) + generated = self.model.generate(phase_log[:n], num_steps=self.num_log_steps) + gen_samples = detach_sample((phase_log[:n], target_log[:n], generated), n) + _log_samples(self, "val_generated_samples", gen_samples) + self._val_log_batch = None + if self._validation_losses: + self.log("loss/validate", _aggregate_validation_losses(self._validation_losses), sync_dist=True) + self._validation_losses.clear() + + def predict_step(self, batch: dict, batch_idx: int, dataloader_idx: int = 0) -> Tensor: + """Generate virtual staining for one batch via ODE sampling. + + Pads source if smaller than ``input_spatial_size``, dispatches to + the configured predict method, then crops back to the original shape. + + Parameters + ---------- + batch : dict + Must contain ``"source"`` tensor. + batch_idx : int + Batch index. + dataloader_idx : int + Dataloader index. + + Returns + ------- + Tensor + Generated fluorescence, cropped to original spatial shape. + """ + source: Tensor = batch["source"] + original_shape = source.shape[2:] + + # Pad source if any spatial dim is smaller than input_spatial_size. + patch_size = self.model.net.input_spatial_size + min_size = tuple(patch_size) + if any(s < p for s, p in zip(source.shape[2:], min_size)): + pad: list[int] = [] + for s, p in zip(reversed(source.shape[2:]), reversed(min_size)): + pad.extend([0, max(0, p - s)]) + source = F.pad(source, pad, mode="replicate") + + if self.predict_method == "denoise": + prediction = self.model.denoise_sliding_window(source, overlap_size=self.predict_overlap) + elif self.predict_method == "generate": + prediction = self.model.generate(source, num_steps=self.num_generate_steps) + elif self.predict_method == "sliding_window": + # generate_sliding_window partitions into non-overlapping tiles + # and does NOT consume predict_overlap. A non-zero overlap means + # the user wants overlapping tiled inference — route them to + # `iterative`, which anchors overlapping regions via velocity. + overlap = self.predict_overlap + overlap_values = (overlap,) * 3 if isinstance(overlap, int) else tuple(overlap) + if any(o > 0 for o in overlap_values): + raise ValueError( + "predict_method='sliding_window' uses non-overlapping tiles and " + f"ignores predict_overlap (got {overlap_values}). " + "Use predict_method='iterative' for overlap-anchored tiled inference, " + "or set predict_overlap=[0, 0, 0] to acknowledge the non-overlapping behavior." + ) + prediction = self.model.generate_sliding_window(source, num_steps=self.num_generate_steps) + elif self.predict_method == "iterative": + prediction = self.model.generate_iterative( + source, + num_steps=self.num_generate_steps, + overlap_size=self.predict_overlap, + ) + else: + raise ValueError( + f"Unknown predict_method: {self.predict_method!r}. " + "Choose 'denoise', 'generate', 'sliding_window', or 'iterative'." + ) + + return prediction[:, :, : original_shape[0], : original_shape[1], : original_shape[2]] + + def configure_optimizers(self): + """Configure AdamW optimizer with LR scheduler.""" + return configure_adamw_scheduler( + self, + self.model, + self.lr, + self.schedule, + warmup_steps=self.warmup_steps, + warmup_multiplier=self.warmup_multiplier, + ) + + +class DynacellGAN(LightningModule): + """Adversarial virtual-staining LightningModule. + + Pairs a regression-style generator (default ``UNetViT3D``) with a + multi-scale 3D PatchGAN discriminator. The training step alternates a + discriminator update and a generator update per batch using Lightning's + manual-optimization API. + + Supports three adversarial loss families via ``loss_type``: + + - ``"lsgan"`` (default, legacy): MSE-to-real / MSE-to-zero. + - ``"nonsat"``: non-saturating softplus loss (StyleGAN2 convention). + - ``"rpgan"``: relativistic pairing loss (R3GAN, NeurIPS 2024). + + Optional modernization knobs (all default OFF for legacy safety; the + 14 existing leaves that compose ``pix2pix3d_unetvit_fit.yml`` are + therefore unaffected by the wiring of these knobs): + + - R1 / R2 zero-centered gradient penalties on a lazy schedule + (``r1_every`` D-steps, with StyleGAN2-style unbiased ``* r1_every`` + rescaling on the loss contribution). + - Generator weight EMA with half-life parametrized via ``ema_kimg``. + - LeCam regularization with sync_dist'd batch-mean EMA buffers. + + Parameters + ---------- + architecture : {"UNetViT3D"} + Generator architecture key. Looked up in the shared + :data:`_ARCHITECTURE` registry. + generator_config : dict or None + Keyword arguments forwarded to the generator constructor. + discriminator_config : dict or None + Keyword arguments forwarded to :class:`MultiScalePatchGAN3D`. + lambda_l1 : float + Weight of the L1 reconstruction loss in the generator objective. + loss_type : {"lsgan", "nonsat", "rpgan"} + Adversarial loss family. Default ``"lsgan"`` matches the + pre-modernization recipe. ``"nonsat"`` is the StyleGAN2 default; + ``"rpgan"`` requires nonzero ``r1_gamma`` and (recommended) + nonzero ``r2_gamma`` for convergence. + lambda_adv : float + Weight on the adversarial term in the generator objective. + Defaults to ``1.0`` (legacy-equivalent). + r1_gamma : float + R1 gradient-penalty weight (Mescheder 2018). ``0.0`` disables R1. + r2_gamma : float + R2 gradient-penalty weight on fake samples (R3GAN). ``0.0`` + disables R2. + r1_every : int + Lazy schedule: apply R1 / R2 every ``r1_every`` D-steps with a + ``* r1_every`` unbiased rescaling factor. Default ``16`` matches + StyleGAN2-ADA's ``D_reg_interval``. Only consulted when + ``r1_gamma > 0 or r2_gamma > 0``. + ema_kimg : float or None + Generator EMA half-life in thousands of images. ``None`` (default) + disables EMA entirely; no shadow submodule is constructed. + ``10.0`` matches StyleGAN2's 256² default; with global batch ``B`` + the per-step decay is ``0.5 ** (B / (ema_kimg * 1000))``. + lecam_gamma : float + LeCam regularization weight (Tseng et al. 2021). ``0.0`` disables + LeCam entirely; no EMA buffers are registered. + lecam_decay : float + EMA decay for LeCam's running D output statistics. Default + ``0.9`` matches the ``google/lecam-gan`` reference. + use_ema_at_predict : bool + When True (default) AND ``generator_ema`` exists, ``predict_step`` + / ``forward`` use the EMA generator. Set False to force + raw-generator predictions from a modernized checkpoint without + editing the predict overlay. + lr_g : float + Learning rate for the generator optimizer. + lr_d : float + Learning rate for the discriminator optimizer. + schedule : {"WarmupCosine"} + Learning rate schedule. Only ``"WarmupCosine"`` is supported. + warmup_steps : int + Number of warmup steps for the WarmupCosine schedule. + warmup_multiplier : float + Initial LR multiplier at step 0. + log_batches_per_epoch : int + Maximum number of batches per epoch to accumulate for image logging. + log_samples_per_batch : int + Number of samples per batch to log. + example_input_yx_shape : Sequence of int + YX shape used to build ``example_input_array`` for graph logging + when the generator does not advertise an ``input_spatial_size``. + predict_method : {"full_image"} + Prediction method. Only ``"full_image"`` is supported. + predict_overlap : tuple of int + Reserved for future tiled inference; currently unused at predict. + ckpt_path : str or None + Optional path to a Lightning checkpoint to load weights from at + construction time. Loaded with ``strict=False`` so pre-modernization + checkpoints (which lack ``generator_ema.*`` / ``_lecam_ema_*``) load + cleanly. The lazy-reg counter ``_d_step_count`` lives outside + ``state_dict`` and is restored via ``on_load_checkpoint`` (defaults + to ``0`` when absent). When the checkpoint has no ``generator_ema.*`` + keys but EMA is enabled, the EMA submodule is seeded from the loaded + generator weights (so inference matches the non-EMA inference path + on legacy checkpoints). + """ + + def __init__( + self, + architecture: Literal["UNetViT3D"] = "UNetViT3D", + generator_config: dict | None = None, + discriminator_config: dict | None = None, + lambda_l1: float = 100.0, + loss_type: Literal["lsgan", "nonsat", "rpgan"] = "lsgan", + lambda_adv: float = 1.0, + r1_gamma: float = 0.0, + r2_gamma: float = 0.0, + r1_every: int = 16, + ema_kimg: float | None = None, + lecam_gamma: float = 0.0, + lecam_decay: float = 0.9, + use_ema_at_predict: bool = True, + lr_g: float = 3e-4, + lr_d: float = 3e-4, + schedule: Literal["WarmupCosine"] = "WarmupCosine", + warmup_steps: int = 8500, + warmup_multiplier: float = 1e-3, + log_batches_per_epoch: int = 8, + log_samples_per_batch: int = 1, + example_input_yx_shape: Sequence[int] = (512, 512), + predict_method: Literal["full_image"] = "full_image", + predict_overlap: tuple[int, int, int] = (4, 256, 256), + ckpt_path: str | None = None, + ) -> None: + super().__init__() + # Lightning's manual-optimization API: required because the GAN + # alternates two optimizers per training_step. + self.automatic_optimization = False + self.save_hyperparameters(ignore=["ckpt_path"]) + + net_class = _ARCHITECTURE.get(architecture) + if net_class is None: + raise ValueError(f"Architecture {architecture!r} not in {set(_ARCHITECTURE)}") + if loss_type not in ("lsgan", "nonsat", "rpgan"): + raise ValueError(f"Unknown loss_type {loss_type!r}; expected lsgan|nonsat|rpgan.") + if loss_type == "rpgan" and (r1_gamma <= 0.0 or r2_gamma <= 0.0): + raise ValueError( + "RpGAN requires nonzero r1_gamma AND r2_gamma for convergence on sharp " + "distributions (R3GAN Theorem 3.1). " + f"Got r1_gamma={r1_gamma}, r2_gamma={r2_gamma}." + ) + if ema_kimg is not None and ema_kimg <= 0.0: + raise ValueError( + f"ema_kimg must be > 0 (or None to disable EMA); got {ema_kimg}. " + "ema_kimg=0 would freeze EMA at init weights with no signal." + ) + if r1_every < 1: + raise ValueError( + f"r1_every must be >= 1 (it is the modulo period for the lazy R1/R2 schedule); got {r1_every}." + ) + self.generator = net_class(**(generator_config or {})) + self.discriminator = MultiScalePatchGAN3D(**(discriminator_config or {})) + + self.lambda_l1 = lambda_l1 + self.loss_type = loss_type + self.lambda_adv = lambda_adv + self.r1_gamma = r1_gamma + self.r2_gamma = r2_gamma + self.r1_every = r1_every + self.ema_kimg = ema_kimg + self.lecam_gamma = lecam_gamma + self.lecam_decay = lecam_decay + self.use_ema_at_predict = use_ema_at_predict + self.lr_g = lr_g + self.lr_d = lr_d + self.schedule = schedule + self.warmup_steps = warmup_steps + self.warmup_multiplier = warmup_multiplier + self.log_batches_per_epoch = log_batches_per_epoch + self.log_samples_per_batch = log_samples_per_batch + self.predict_method = predict_method + self.predict_overlap = predict_overlap + + # D-step counter for lazy R1 schedule. self.global_step would + # advance by 2 per training_step (D opt + G opt) so it can't be used + # directly. Stored as a Python int — not a buffer — so the lazy-reg + # modulo gate avoids a CUDA→CPU sync each step. Persistence is via + # on_save_checkpoint / on_load_checkpoint below; all DDP ranks start + # at 0 and advance deterministically in lockstep. + self._d_step_count: int = 0 + + # Generator EMA shadow. Custom deepcopy (not timm) so we control + # device/dtype precisely. requires_grad_(False) keeps EMA params out + # of opt_g and out of DDP's gradient reducer. + if ema_kimg is not None: + self.generator_ema = copy.deepcopy(self.generator) + self.generator_ema.requires_grad_(False) + else: + self.generator_ema = None + + # LeCam EMA buffers — only registered when LeCam is enabled. + if lecam_gamma > 0.0: + self.register_buffer("_lecam_ema_real", torch.tensor(0.0)) + self.register_buffer("_lecam_ema_fake", torch.tensor(0.0)) + + self.training_step_outputs: list = [] + # Two accumulators: raw-generator + EMA-generator validation losses. + # The second one is only populated when generator_ema exists. + self.validation_losses_raw: list[list[tuple[Tensor, int]]] = [] + self.validation_losses_ema: list[list[tuple[Tensor, int]]] = [] + self.validation_step_outputs: list = [] + + # Build example_input_array for graph logging (TensorBoard/W&B). + gen_cfg = generator_config or {} + in_channels = gen_cfg.get("in_channels") or 1 + if hasattr(self.generator, "input_spatial_size"): + d, h, w = self.generator.input_spatial_size + else: + d = gen_cfg.get("in_stack_depth") or 5 + h, w = example_input_yx_shape + self.example_input_array = torch.rand(1, in_channels, d, h, w) + + if ckpt_path is not None: + state = torch.load(ckpt_path, weights_only=True, map_location="cpu")["state_dict"] + # strict=False: pre-modernization checkpoints don't carry + # generator_ema.* / _lecam_ema_* keys. Filter missing-key warnings + # to expected-missing prefixes; anything else is a genuine + # state-dict mismatch (renamed layer, dropped module, wrong + # checkpoint kind). RAISE rather than warn — silent half-loaded + # models burn hours of training before users notice. (The lazy-reg + # counter `_d_step_count` lives outside state_dict and is restored + # via on_load_checkpoint, so it never appears in missing_keys.) + incompat = self.load_state_dict(state, strict=False) + expected_missing = ("generator_ema.", "_lecam_ema_") + unexpected_missing = [k for k in incompat.missing_keys if not k.startswith(expected_missing)] + if unexpected_missing: + raise RuntimeError( + f"Checkpoint {ckpt_path!r} is missing keys that are not part of the " + f"modernization additions: {unexpected_missing}. The model would load " + "with partially random weights. If this is intentional, drop those " + "submodules from the model config or use an explicit migration step." + ) + if incompat.unexpected_keys: + _logger.warning( + "Checkpoint %s has unexpected keys ignored by strict=False load: %s", + ckpt_path, + incompat.unexpected_keys, + ) + # Seed EMA from loaded generator when ckpt has no EMA section, + # so inference paths return the loaded weights (not the + # random-init deepcopy from __init__). RAISE on partial-EMA ckpt + # to catch a corrupted save (e.g., crash mid-checkpoint, or a + # future buffer added to generator_ema that an old ckpt lacks) — + # silently half-seeding would produce nonsense at the partial layers. + if self.generator_ema is not None: + ema_keys_in_ckpt = sum(1 for k in state if k.startswith("generator_ema.")) + ema_keys_expected = len(self.generator_ema.state_dict()) + if ema_keys_in_ckpt == 0: + self.generator_ema.load_state_dict(self.generator.state_dict()) + _logger.info( + "Checkpoint %s has no generator_ema.* keys; seeded EMA shadow from loaded generator weights.", + ckpt_path, + ) + elif ema_keys_in_ckpt != ema_keys_expected: + raise RuntimeError( + f"Checkpoint {ckpt_path!r} has {ema_keys_in_ckpt} generator_ema.* " + f"keys but the EMA submodule expects {ema_keys_expected}. " + "Partial EMA loads would leave random-init values in the missing " + "EMA layers and silently produce wrong inference outputs." + ) + + @staticmethod + def _set_requires_grad(module: nn.Module, value: bool) -> None: + """Toggle ``requires_grad`` on every parameter of ``module``. + + Parameters + ---------- + module : nn.Module + Module whose parameters should have ``requires_grad`` set. + value : bool + New value for ``requires_grad``. + """ + for p in module.parameters(): + p.requires_grad = value + + def _inference_generator(self) -> nn.Module: + """Return the generator used for inference paths. + + Returns the EMA shadow if it exists AND ``use_ema_at_predict`` is + True; otherwise the raw generator. Used by ``forward`` and + ``predict_step``. + """ + if self.generator_ema is not None and self.use_ema_at_predict: + return self.generator_ema + return self.generator + + def on_save_checkpoint(self, checkpoint: dict) -> None: + """Persist the lazy-reg D-step counter alongside ``state_dict``. + + ``_d_step_count`` is a plain Python int (not a buffer) to keep the + lazy-reg modulo gate sync-free, so Lightning's automatic state_dict + round-trip does not capture it. + """ + checkpoint["_d_step_count"] = self._d_step_count + + def on_load_checkpoint(self, checkpoint: dict) -> None: + """Restore the lazy-reg D-step counter from the checkpoint. + + Falls back to ``0`` on pre-modernization checkpoints that pre-date the + counter. + """ + self._d_step_count = int(checkpoint.get("_d_step_count", 0)) + + def forward(self, x: Tensor) -> Tensor: + """Run a generator-only forward pass (inference contract). + + The discriminator is not exposed at inference time; ``forward`` + returns the EMA generator's output when available, otherwise the + raw generator. + + Parameters + ---------- + x : Tensor + Input tensor of shape ``(B, C, D, H, W)``. + + Returns + ------- + Tensor + Generator output (EMA generator if available). + """ + return self._inference_generator()(x) + + def _adv_d_loss(self, d_real: list[Tensor], d_fake: list[Tensor]) -> Tensor: + """Dispatch the configured adversarial D loss across loss families.""" + if self.loss_type == "lsgan": + return lsgan_d_loss(d_real, d_fake) + if self.loss_type == "nonsat": + return nonsat_d_loss(d_real, d_fake) + # rpgan + return rpgan_d_loss(d_real, d_fake) + + def _adv_g_loss(self, d_real: list[Tensor] | None, d_fake: list[Tensor]) -> Tensor: + """Dispatch the configured adversarial G loss. + + For RpGAN, ``d_real`` must be freshly computed against the + post-D-update discriminator (not reused from the D step). + """ + if self.loss_type == "lsgan": + return lsgan_g_loss(d_fake) + if self.loss_type == "nonsat": + return nonsat_g_loss(d_fake) + # rpgan + if d_real is None: + raise ValueError("RpGAN G loss requires fresh d_real logits; got None.") + return rpgan_g_loss(d_real, d_fake) + + def training_step(self, batch: Sample, batch_idx: int) -> None: + """Run one alternating D/G optimization step. + + The discriminator is updated first using a detached generator + forward (so D's loss has no gradient path into G), then the + generator is updated. ``requires_grad`` is toggled per phase so + each backward populates ``.grad`` only on the side being trained. + + Lazy R1 / R2 gradient penalties fire every ``r1_every`` D-steps + (tracked via ``_d_step_count``, NOT ``self.global_step`` which + advances per opt.step() and would fire R1 every 8 batches with two + opt steps per batch). LeCam regularization is added inline using + sync_dist'd batch means so EMA buffers stay synchronized across + DDP ranks without explicit ``dist.all_reduce`` on the buffers. + + Parameters + ---------- + batch : Sample + Batch dict with ``"source"`` and ``"target"`` tensors. + batch_idx : int + Batch index within the epoch. + """ + source = batch["source"] + target = batch["target"] + opt_g, opt_d = self.optimizers() + sch_g, sch_d = self.lr_schedulers() + + # --- D step (D updates; G frozen, no-grad forward) --- + self._set_requires_grad(self.generator, False) + self._set_requires_grad(self.discriminator, True) + with torch.no_grad(): + pred = self.generator(source) + real_pair = torch.cat([source, target], dim=1) + fake_pair = torch.cat([source, pred], dim=1) + d_real = self.discriminator(real_pair) + d_fake = self.discriminator(fake_pair) + d_loss = self._adv_d_loss(d_real, d_fake) + + # Increment D-step counter BEFORE the lazy reg check, then check + # if R1 or R2 is enabled AND we're on a lazy-reg step. + self._d_step_count += 1 + r1_value: Tensor | None = None + r2_value: Tensor | None = None + do_lazy_reg = (self.r1_gamma > 0.0 or self.r2_gamma > 0.0) and self._d_step_count % self.r1_every == 0 + if do_lazy_reg: + # Mescheder R1 grad-of-grad needs fp32; under Lightning bf16-mixed, + # autocast() injects bf16 into D forwards which is numerically fragile + # for create_graph=True. Disable autocast around the penalty. + with torch.amp.autocast(device_type=source.device.type, enabled=False): + if self.r1_gamma > 0.0: + real_fp32 = real_pair.detach().float() + r1_value = r1_penalty(self.discriminator, real_fp32) + # (γ/2) is Mescheder's standard formula factor; + # `* r1_every` is the separate StyleGAN2 unbiased rescaling. + d_loss = d_loss + (self.r1_gamma / 2) * r1_value * self.r1_every + if self.r2_gamma > 0.0: + fake_fp32 = fake_pair.detach().float() + r2_value = r2_penalty(self.discriminator, fake_fp32) + d_loss = d_loss + (self.r2_gamma / 2) * r2_value * self.r1_every + + if self.lecam_gamma > 0.0: + # Use Lightning's all_gather for cross-rank mean so all ranks + # see identical scalar -> identical EMA buffer update. + real_mean = torch.stack([d.mean() for d in d_real]).mean().detach() + fake_mean = torch.stack([d.mean() for d in d_fake]).mean().detach() + if self.trainer is not None and self.trainer.world_size > 1: + real_mean = self.all_gather(real_mean).mean() + fake_mean = self.all_gather(fake_mean).mean() + self._lecam_ema_real.mul_(self.lecam_decay).add_(real_mean * (1.0 - self.lecam_decay)) + self._lecam_ema_fake.mul_(self.lecam_decay).add_(fake_mean * (1.0 - self.lecam_decay)) + # Multi-scale LeCam: relu hinge on each scale, averaged. + lecam_per_scale = [ + F.relu(real - self._lecam_ema_fake).pow(2).mean() + F.relu(self._lecam_ema_real - fake).pow(2).mean() + for real, fake in zip(d_real, d_fake, strict=True) + ] + d_loss = d_loss + self.lecam_gamma * torch.stack(lecam_per_scale).mean() + + opt_d.zero_grad(set_to_none=True) + self.manual_backward(d_loss) + opt_d.step() + # Clear D grads so the no-D-grads-after-G-step invariant is verifiable. + opt_d.zero_grad(set_to_none=True) + + # --- G step (G updates; D frozen, fwd-only) --- + self._set_requires_grad(self.generator, True) + self._set_requires_grad(self.discriminator, False) + pred = self.generator(source) + d_fake_for_g = self.discriminator(torch.cat([source, pred], dim=1)) + # RpGAN G loss is relativistic — needs fresh d_real against the + # POST-D-update discriminator (R3GAN Trainer.py convention). + if self.loss_type == "rpgan": + d_real_for_g = self.discriminator(torch.cat([source, target], dim=1)) + adv_loss = self._adv_g_loss(d_real_for_g, d_fake_for_g) + else: + adv_loss = self._adv_g_loss(None, d_fake_for_g) + l1_loss = F.l1_loss(pred, target) + g_loss = self.lambda_adv * adv_loss + self.lambda_l1 * l1_loss + opt_g.zero_grad(set_to_none=True) + self.manual_backward(g_loss) + opt_g.step() + + # Generator EMA update — uses the StyleGAN2 formula + # decay = 0.5 ** (global_batch_size / (ema_kimg * 1000)). World-size + # multiplier matters here because all ranks see identical post-step + # generator weights (DDP-synced via opt_g.step()) and apply the + # identical EMA update — so the shadow stays in lockstep. + if self.generator_ema is not None: + bs = source.shape[0] + if self.trainer is not None and self.trainer.world_size > 1: + bs = bs * self.trainer.world_size + decay = 0.5 ** (bs / max(self.ema_kimg * 1000.0, 1e-8)) + with torch.no_grad(): + for p_ema, p in zip( + self.generator_ema.parameters(), + self.generator.parameters(), + strict=True, + ): + p_ema.lerp_(p.detach(), 1.0 - decay) + for b_ema, b in zip( + self.generator_ema.buffers(), + self.generator.buffers(), + strict=True, + ): + b_ema.copy_(b) + + # WarmupCosine is step-based and ignored by Lightning's automatic + # scheduler machinery when ``automatic_optimization = False``. + sch_g.step() + sch_d.step() + + if batch_idx < self.log_batches_per_epoch: + self.training_step_outputs.extend(detach_sample((source, target, pred), self.log_samples_per_batch)) + + log_payload: dict[str, Tensor] = { + "loss/d_train": d_loss, + "loss/g_train": g_loss, + "loss/g_adv_train": adv_loss, + "loss/g_l1_train": l1_loss, + } + self.log_dict( + log_payload, + on_step=True, + on_epoch=True, + sync_dist=True, + batch_size=source.size(0), + ) + # Sparse R1 / R2 logging: only on the steps they fire. sync_dist=False + # because all DDP ranks fire on the same _d_step_count (deterministic) + # so there's no rank-mismatch risk; sync_dist=True would invite + # deadlock-on-skip if any rank ever stops firing. + if r1_value is not None: + self.log("reg/r1", r1_value.detach(), on_step=True, on_epoch=True, sync_dist=False) + if r2_value is not None: + self.log("reg/r2", r2_value.detach(), on_step=True, on_epoch=True, sync_dist=False) + + def validation_step(self, batch: Sample, batch_idx: int, dataloader_idx: int = 0) -> Tensor: + """Compute generator L1 validation loss(es) and capture samples. + + Always runs the raw generator forward and accumulates into + ``validation_losses_raw`` (drives the back-compat ``loss/validate`` + alias). When ``generator_ema`` exists, ALSO runs the EMA generator + forward and accumulates into ``validation_losses_ema`` (drives + ``loss/validate_ema``). Modernized leaves should + ``monitor: loss/validate_ema`` once EMA is enabled. + + Logged sample grids use the EMA generator's prediction when + available — matches what ``predict_step`` will produce. + + Parameters + ---------- + batch : Sample + Batch dict with ``"source"`` and ``"target"`` tensors. + batch_idx : int + Batch index. + dataloader_idx : int + Index of the validation dataloader. + + Returns + ------- + Tensor + Scalar L1 loss on this batch (raw generator). + """ + source: Tensor = batch["source"] + target: Tensor = batch["target"] + # Raw generator pass (always). + pred_raw = self.generator(source) + l1_raw = F.l1_loss(pred_raw, target) + if dataloader_idx + 1 > len(self.validation_losses_raw): + self.validation_losses_raw.append([]) + self.validation_losses_raw[dataloader_idx].append((l1_raw.detach(), source.shape[0])) + self.log( + f"loss/val/{dataloader_idx}", + l1_raw, + sync_dist=True, + batch_size=source.shape[0], + ) + # EMA generator pass (only when EMA submodule exists). + pred_for_samples = pred_raw + if self.generator_ema is not None: + with torch.no_grad(): + pred_ema = self.generator_ema(source) + l1_ema = F.l1_loss(pred_ema, target) + if dataloader_idx + 1 > len(self.validation_losses_ema): + self.validation_losses_ema.append([]) + self.validation_losses_ema[dataloader_idx].append((l1_ema.detach(), source.shape[0])) + self.log( + f"loss/val_ema/{dataloader_idx}", + l1_ema, + sync_dist=True, + batch_size=source.shape[0], + ) + # Sample logging follows the inference path so the dashboard + # tracks what `predict_step` (via `_inference_generator`) actually + # emits. If EMA exists but use_ema_at_predict=False, predict_step + # uses the raw generator, so logged samples must match. + if self.use_ema_at_predict: + pred_for_samples = pred_ema + if batch_idx < self.log_batches_per_epoch: + self.validation_step_outputs.extend( + detach_sample((source, target, pred_for_samples), self.log_samples_per_batch) + ) + return l1_raw + + def on_train_epoch_end(self) -> None: + """Log accumulated training image samples and reset the buffer.""" + _log_samples(self, "train_samples", self.training_step_outputs) + self.training_step_outputs = [] + + def on_validation_epoch_end(self) -> None: + """Log validation samples and ``loss/validate`` (raw) + ``loss/validate_ema``.""" + super().on_validation_epoch_end() + _log_samples(self, "val_samples", self.validation_step_outputs) + # Back-compat alias: legacy leaves' ModelCheckpoint(monitor="loss/validate") + # keys off the raw-generator val loss. Always emit it. + if self.validation_losses_raw: + self.log( + "loss/validate", + _aggregate_validation_losses(self.validation_losses_raw), + sync_dist=True, + ) + # Modernized alias: only emit when EMA generator exists. Modernized + # leaves switch ModelCheckpoint(monitor="loss/validate_ema"). + if self.generator_ema is not None and self.validation_losses_ema: + self.log( + "loss/validate_ema", + _aggregate_validation_losses(self.validation_losses_ema), + sync_dist=True, + ) + self.validation_step_outputs.clear() + self.validation_losses_raw.clear() + self.validation_losses_ema.clear() + + def configure_optimizers(self): + """Build two AdamW optimizers + WarmupCosine schedulers via the shared helper.""" + [opt_g], [sch_g] = configure_adamw_scheduler( + self, + self.generator, + self.lr_g, + self.schedule, + warmup_steps=self.warmup_steps, + warmup_multiplier=self.warmup_multiplier, + ) + [opt_d], [sch_d] = configure_adamw_scheduler( + self, + self.discriminator, + self.lr_d, + self.schedule, + warmup_steps=self.warmup_steps, + warmup_multiplier=self.warmup_multiplier, + ) + return [opt_g, opt_d], [sch_g, sch_d] + + def on_predict_start(self) -> None: + """Build the divisible-pad transform matching the generator. + + Also logs which generator (raw vs EMA) will be used at inference, so + users notice a silent fallback when EMA is unexpectedly disabled + (e.g., a predict overlay that forgot to set ``ema_kimg`` on a + modernized checkpoint, or ``use_ema_at_predict=False``). + """ + self._predict_pad = _make_divisible_pad(self.generator) + which = "EMA" if (self.generator_ema is not None and self.use_ema_at_predict) else "raw" + _logger.info( + "DynacellGAN predict: using %s generator (ema_kimg=%s, use_ema_at_predict=%s, generator_ema=%s)", + which, + self.ema_kimg, + self.use_ema_at_predict, + "present" if self.generator_ema is not None else "absent", + ) + + def predict_step(self, batch: Sample, batch_idx: int, dataloader_idx: int = 0) -> Tensor: + """Run a generator-only prediction step with divisible padding. + + Pads the input tile to the nearest multiple of the generator's + downsampling factor, runs the generator forward pass, then crops + back to the original spatial shape. The discriminator is not + exposed at inference. + + Parameters + ---------- + batch : Sample + Batch dict. Only ``"source"`` is used. + batch_idx : int + Batch index. + dataloader_idx : int + Dataloader index. + + Returns + ------- + Tensor + Generator prediction cropped to the input spatial shape. + """ + source = batch["source"] + original_shape = source.shape[2:] + source = self._predict_pad(source) + if self.predict_method == "full_image": + # Inference uses EMA generator when available (and use_ema_at_predict=True). + prediction = self._inference_generator()(source) + else: + raise ValueError(f"Unknown predict_method: {self.predict_method!r}. Choose 'full_image'.") + return _center_crop_to_shape(prediction, original_shape) diff --git a/applications/dynacell/src/dynacell/evaluation/CLAUDE.md b/applications/dynacell/src/dynacell/evaluation/CLAUDE.md new file mode 100644 index 000000000..08c14a0ed --- /dev/null +++ b/applications/dynacell/src/dynacell/evaluation/CLAUDE.md @@ -0,0 +1,160 @@ +# dynacell/evaluation — Claude Code reference + +Code in this directory uses **cubic** (CUDA-accelerated 3D bioimage +computing) for any GPU-accelerated numerical work — image preprocessing +before / after model inference, metric calculations, cropping/resizing, +percentile clips, Gaussian filters, etc. Cubic is a hard runtime +dependency of the eval extras (`applications/dynacell/pyproject.toml` +pins `cubic==0.7.0a9`). Do not gate cubic imports behind `try/except` +or fall back to scipy/skimage paths. + +The GPU-resident Cellpose-SAM entry point is +`cubic.segmentation.segment_cpsam` (single host→device upload, masks +returned to host; GPU-only by contract). The marker-controlled watershed +helper `segment_watershed` is **not** re-exported from +`cubic.segmentation` — import it from +`cubic.segmentation.segment_utils`. + +Below is the same guidance the upstream cubic repository ships in its +`AGENTS.md`, condensed and adapted for this module. **Read it before +adding GPU-aware code here.** It is also fine to read `cubic/AGENTS.md` +directly at `../cubic/` for the canonical version. + +## Device management (`cubic.cuda`) + +Core utilities for device-agnostic computation: + +- `CUDAManager` – Singleton managing CuPy/cuCIM resources +- `get_array_module(array)` – Returns `np` or `cp` based on array location (**use sparingly** - prefer `np.` directly) +- `asnumpy(array)` / `ascupy(array)` – Transfer arrays between CPU/GPU (**preferred** over direct CuPy calls) +- `to_device(array, device)` – Move array to specific device (`"CPU"` or `"GPU"`) +- `to_same_device(source, reference)` – Move source array to same device as reference +- `check_same_device(*arrays)` – Verify all arrays are on the same device +- `get_device(array)` – Returns `"CPU"` or `"GPU"` + +**Important**: + +- `get_array_module()` should only be used when creating new arrays that + must be on a specific device. For most operations, use `np.` directly + — NumPy functions work on both NumPy and CuPy arrays through duck + typing. +- **Always use `cubic.cuda` functions** for device operations (moving + arrays, checking devices) rather than directly calling CuPy functions. + This maintains the abstraction layer and ensures consistent behavior. + +## Device-agnostic wrappers + +- `cubic.scipy` – Proxy module for device-agnostic SciPy / cupyx.scipy access +- `cubic.skimage` – Proxy module for device-agnostic scikit-image / cuCIM access +- `cubic.cucim` – CuCIM integration for GPU-accelerated image I/O + +These modules automatically route function calls to CPU (NumPy / SciPy / +scikit-image) or GPU (CuPy / cuCIM) implementations based on the input +array's device. + +## ⚠️ CRITICAL: device-agnostic code pattern + +**All functions in `cubic` automatically support both CPU and GPU +without any code changes** — they work with NumPy arrays (CPU) or CuPy +arrays (GPU) based solely on the input array's device location. The same +function call works on both devices; just transfer the input array to +the desired device using `cubic.cuda` functions. + +**Avoid using `xp` (array module) interface as much as possible.** Prefer +`np.` or array methods (`.func()`) to maximize code portability between +NumPy and CuPy without modifications. + +**Preferred approach** (use `np.` directly): + +```python +import numpy as np + +# NumPy functions work on both NumPy and CuPy arrays +result = np.fft.fftn(image) # ✅ Works on both CPU/GPU arrays +result = np.abs(array) # ✅ Works on both CPU/GPU arrays +result = np.bincount(bin_id, weights) # ✅ Works on both CPU/GPU arrays +result = np.sqrt(k0 * k0 + k1 * k1) # ✅ Works on both CPU/GPU arrays +result = array.ravel() # ✅ Array methods work on both +result = array.astype(np.float32) # ✅ Array methods work on both +``` + +**Avoid when possible** (using `xp` interface): + +```python +from cubic.cuda import get_array_module + +xp = get_array_module(array) +result = xp.fft.fftn(image) # ⚠️ Only use when necessary +result = xp.asarray(data) # ⚠️ Only use when creating new arrays on specific device +``` + +**When `xp` is OK** (limited cases): + +- Creating new arrays that must be on the same device as existing arrays: `xp.asarray()`, `xp.zeros()`, `xp.ones()` +- Device-specific functions not available in NumPy: `xp.fft.fftfreq()` for device placement +- Functions that don't work with NumPy's duck-typing: rare, prefer `np.` when possible + +## Device operations (use `cubic.cuda` functions) + +When you need to move arrays between devices or check device placement, +**always use functions from `cubic.cuda`** rather than directly calling +CuPy functions: + +```python +from cubic.cuda import asnumpy, ascupy, to_device, to_same_device, check_same_device, get_device + +# ✅ Preferred: Use cubic.cuda functions +cpu_array = asnumpy(gpu_array) # Move to CPU +gpu_array = ascupy(cpu_array) # Move to GPU +target_array = to_device(source_array, "GPU") # Move to specific device +aligned_array = to_same_device(array1, array2) # Move to same device as reference +check_same_device(array1, array2) # Verify same device +device = get_device(array) # Check current device + +# ❌ Avoid: Direct CuPy calls +import cupy as cp +cpu_array = cp.asnumpy(gpu_array) # Don't do this — breaks abstraction +``` + +**Rationale**: Using `np.` directly allows code to work seamlessly with +both NumPy and CuPy arrays through duck typing. This maximizes +portability and allows users to port NumPy code in/out with minimal +modifications. The `xp` interface should only be used when absolutely +necessary for device placement or when NumPy functions don't support +CuPy arrays (rare). For device operations, always use `cubic.cuda` +functions to maintain the abstraction layer and ensure consistent +behavior. + +## Concrete example in this module + +`segmentation.py`'s `_smooth_nucleus_input` is the minimal canonical +shape: + +```python +from cubic.cuda import ascupy, asnumpy +from cubic.skimage import filters as _cubic_filters + +def _smooth_nucleus_input(img, sigma=NUCLEUS_GAUSSIAN_SIGMA): + img_dev = ascupy(img.astype(np.float32, copy=False)) # move to GPU + smoothed = _cubic_filters.gaussian(img_dev, sigma=sigma, preserve_range=True) # cubic proxy auto-dispatches + return asnumpy(smoothed) # caller wants numpy +``` + +No `try/except` around the cubic imports, no scipy fallback. If CUDA +isn't available the call route falls through cubic's own CPU path +(scikit-image), and that's the right outcome — but in practice the eval +pipeline already requires CUDA for the SuperModel inference downstream, +so the GPU path is what runs. + +## Don't + +- Don't add a scipy / skimage fallback alongside a cubic call. Pick one + via cubic — it already handles both backends. +- Don't add `if torch.cuda.is_available():` dispatches around cubic calls + for the same reason; cubic decides the backend from the input array + type. +- Don't `import cupy as cp` and call CuPy directly. Use `cubic.cuda.*` + for device transfers and `cubic.skimage` / `cubic.scipy` for array + operations. +- Don't gate cubic imports with `try/except ImportError: None`. Cubic is + a hard dep here. If it's missing, the pipeline is broken — fail loud. diff --git a/applications/dynacell/src/dynacell/evaluation/README.md b/applications/dynacell/src/dynacell/evaluation/README.md new file mode 100644 index 000000000..07523e223 --- /dev/null +++ b/applications/dynacell/src/dynacell/evaluation/README.md @@ -0,0 +1,307 @@ +# dynacell.evaluation + +End-to-end evaluation pipeline for virtual staining predictions against fluorescence ground truth. + +## Components + +| Module | Purpose | +|---|---| +| `pipeline.py` | Hydra orchestrator. CLIs: `dynacell evaluate` (single-condition) and `dynacell evaluate-grouped` (one model load, N I/O conditions). | +| `metrics.py` | Pixel, mask, and feature metrics (CP regionprops + DINOv3 + DynaCLR + CELL-DINO), computed symmetrically for GT/predictions and combined pairwise. | +| `segmentation.py` | `aicssegmentation` workflows + SuperModel for `nucleus`/`membrane`. | +| `cache.py`, `pipeline_cache.py` | Artifact cache: on-disk layout, manifest, identity check, per-FOV load-or-compute wrappers, batched `precompute_deep_features`. | +| `model_loader.py` | Shared `load_eval_models(config, flags=...)` returning an `EvalModels` bundle. Used by both `evaluate` and `precompute-gt`. | +| `runtime.py` | BLAS/OMP thread caps, `ProcessPoolExecutor` worker initializer, `gpu_serialization_lock`, region timers. | +| `precompute_cli.py` | `dynacell precompute-gt` — fills the GT cache without running the eval loop. | +| `utils.py` | `DinoV3FeatureExtractor`, `DynaCLRFeatureExtractor`, `CellDinoFeatureExtractor`, plot helpers. | +| `_configs/*.yaml` | Hydra schemas: `eval.yaml`, `precompute.yaml`, `eval_grouped.yaml`. | + +Other files (`io.py`, `formatting.py`, `spectral_pcc/`) house readers and bead/PSF diagnostics. Pixel metrics (PCC, SSIM, NRMSE, PSNR) are now backed by `cubic.metrics`. + +## Inputs + +- `io.pred_path` — model predictions, HCS OME-Zarr (channel: `io.pred_channel_name`) +- `io.gt_path` — fluorescence ground truth (channel: `io.gt_channel_name`) +- `io.cell_segmentation_path` — *optional* precomputed cell segmentation HCS OME-Zarr. Required when `compute_feature_metrics=true` or when building CP/DINOv3/DynaCLR/CELL-DINO cache entries. Position layout must match GT/pred 1:1. +- `io.gt_cache_dir`, `io.pred_cache_dir` — *optional* artifact cache directories; must be distinct. See [Caches](#caches). + +## Quick start + +```bash +uv run dynacell evaluate \ + target=er_sec61b \ + predict_set=ipsc_confocal \ + io.pred_path=/hpc/.../fnet3d_sec61b.zarr \ + save.save_dir=/hpc/.../eval_fnet3d_sec61b +``` + +Add `compute_feature_metrics=true` to enable feature metrics. Smoke test on a subset of FOVs with `limit_positions=N`. + +## Submission tooling + +| Tool | Use case | +|---|---| +| `dynacell evaluate ...` / `dynacell evaluate-grouped leaf=...` | Run a single eval (or grouped multi-condition eval) inline. Foreground, no sbatch. | +| `tools/submit_evaluation_job.py ` | Submit a single eval leaf as one sbatch. Mirror of `submit_benchmark_job.py` for eval. | +| `tools/submit_evaluation_batch.py ` | Submit N eval leaves as one sbatch with `--parallel N` (chunked waves of N concurrent processes on the shared GPU). Used by `tools/evaluate_batch.sh`. | +| `tools/run_eval_direct.slurm ` | Direct-launch slurm script for hand-authored eval leaves that bypass the submit helpers. | + +For multi-condition evals over `(model, organelle) × {mock, denv, zikv}`, prefer `evaluate-grouped` over N separate `evaluate` calls — it amortizes the ~30–90 s SuperModel + DINOv3 + DynaCLR + CELL-DINO load across conditions. See [`applications/dynacell/CLAUDE.md`](../../../CLAUDE.md#grouped-multi-condition-eval) "Grouped multi-condition eval" for the executor + cache-mode tradeoffs. + +For the predict-side submission tooling (`submit_benchmark_batch.py` + `predict_batch.sh`, with serial / `--array` / `--parallel P` modes), see [`applications/dynacell/CLAUDE.md`](../../../CLAUDE.md#predict-submission-modes) "Predict submission modes" and the [benchmarks README](../../../configs/benchmarks/virtual_staining/README.md#multi-leaf-submission-with-submit_benchmark_batchpy). + +## Configuration + +`dynacell evaluate` is a Hydra entrypoint — override any field with `key=value` (CLI overrides win over groups). Settings that travel with a (target, marker, dataset) combination live in named Hydra **config groups**: + +| Group | Options | What it sets | Source | +|---|---|---|---| +| `target` | `er_sec61b`, `mito_tomm20`, `membrane`, `nucleus` | `target_name`, `benchmark.dataset_ref.target` | repo-checkout `_internal/shared/eval/target/` | +| `predict_set` | `ipsc_confocal` | `benchmark.dataset_ref.dataset` | in-package | +| `feature_extractor/dinov3` | `lvd1689m` | `feature_extractor.dinov3.pretrained_model_name` | in-package | +| `feature_extractor/dynaclr` | `default` | `feature_extractor.dynaclr.checkpoint` + 8-field encoder dict | repo-checkout `_internal/shared/eval/feature_extractor/dynaclr/` | +| `leaf` | `///eval__` | Composes all of the above for one canonical run | repo-checkout `_internal/leaf/` (symlink tree) | + +Select a group: `=