moozy 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. moozy/__init__.py +0 -0
  2. moozy/__main__.py +4 -0
  3. moozy/cli/__init__.py +3 -0
  4. moozy/cli/_types.py +65 -0
  5. moozy/cli/encode.py +47 -0
  6. moozy/cli/main.py +16 -0
  7. moozy/cli/train/__init__.py +29 -0
  8. moozy/cli/train/_stage1.py +317 -0
  9. moozy/cli/train/_stage2.py +273 -0
  10. moozy/config/__init__.py +0 -0
  11. moozy/config/data.py +60 -0
  12. moozy/config/model.py +54 -0
  13. moozy/config/stage1.py +133 -0
  14. moozy/config/stage2.py +123 -0
  15. moozy/config/tasks.py +28 -0
  16. moozy/config/training.py +61 -0
  17. moozy/data/__init__.py +1 -0
  18. moozy/data/features/__init__.py +26 -0
  19. moozy/data/features/grid.py +50 -0
  20. moozy/data/features/index.py +39 -0
  21. moozy/data/features/io.py +187 -0
  22. moozy/data/features/token_cap.py +91 -0
  23. moozy/data/features/transforms.py +83 -0
  24. moozy/data/stage1/__init__.py +14 -0
  25. moozy/data/stage1/collate.py +61 -0
  26. moozy/data/stage1/crops.py +63 -0
  27. moozy/data/stage1/dataset.py +326 -0
  28. moozy/data/stage1/loader.py +56 -0
  29. moozy/data/stage1/masking.py +127 -0
  30. moozy/data/stage1/transforms.py +32 -0
  31. moozy/data/stage1/types.py +25 -0
  32. moozy/data/stage2/__init__.py +16 -0
  33. moozy/data/stage2/batching.py +31 -0
  34. moozy/data/stage2/dataset.py +206 -0
  35. moozy/data/stage2/loader.py +30 -0
  36. moozy/data/stage2/transforms.py +158 -0
  37. moozy/data/stage2/types.py +35 -0
  38. moozy/encoding.py +301 -0
  39. moozy/hf_hub.py +19 -0
  40. moozy/models/__init__.py +0 -0
  41. moozy/models/case_transformer.py +219 -0
  42. moozy/models/encoder.py +42 -0
  43. moozy/models/factory.py +222 -0
  44. moozy/models/heads.py +100 -0
  45. moozy/models/layers.py +43 -0
  46. moozy/models/moozy_slide_encoder.py +420 -0
  47. moozy/models/serialization.py +236 -0
  48. moozy/models/stage1_encoding.py +202 -0
  49. moozy/models/stage1_ssl.py +252 -0
  50. moozy/models/stage2_encoding.py +82 -0
  51. moozy/models/stage2_supervised.py +159 -0
  52. moozy/models/stage2_tasks.py +96 -0
  53. moozy/models/variants.py +50 -0
  54. moozy/py.typed +0 -0
  55. moozy/tasks/__init__.py +18 -0
  56. moozy/tasks/coverage.py +41 -0
  57. moozy/tasks/loader.py +308 -0
  58. moozy/tasks/matrices.py +63 -0
  59. moozy/tasks/resolution.py +41 -0
  60. moozy/tasks/splits.py +117 -0
  61. moozy/tasks/survival.py +103 -0
  62. moozy/training/__init__.py +41 -0
  63. moozy/training/callbacks/__init__.py +15 -0
  64. moozy/training/callbacks/checkpoint.py +307 -0
  65. moozy/training/callbacks/logging.py +230 -0
  66. moozy/training/engine/__init__.py +9 -0
  67. moozy/training/engine/base.py +39 -0
  68. moozy/training/engine/stage1.py +302 -0
  69. moozy/training/engine/stage2.py +530 -0
  70. moozy/training/logging.py +119 -0
  71. moozy/training/loss/__init__.py +27 -0
  72. moozy/training/loss/classification.py +45 -0
  73. moozy/training/loss/distillation.py +96 -0
  74. moozy/training/loss/survival.py +124 -0
  75. moozy/training/metrics.py +110 -0
  76. moozy/training/optimization.py +227 -0
  77. moozy/training/runners/__init__.py +7 -0
  78. moozy/training/runners/stage1.py +439 -0
  79. moozy/training/runners/stage2.py +472 -0
  80. moozy/training/runtime.py +243 -0
  81. moozy-0.1.0.dist-info/METADATA +685 -0
  82. moozy-0.1.0.dist-info/RECORD +86 -0
  83. moozy-0.1.0.dist-info/WHEEL +5 -0
  84. moozy-0.1.0.dist-info/entry_points.txt +2 -0
  85. moozy-0.1.0.dist-info/licenses/LICENSE +437 -0
  86. moozy-0.1.0.dist-info/top_level.txt +1 -0
moozy/__init__.py ADDED
File without changes
moozy/__main__.py ADDED
@@ -0,0 +1,4 @@
1
+ from moozy.cli.main import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
moozy/cli/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ from .main import app, main
2
+
3
+ __all__ = ["app", "main"]
moozy/cli/_types.py ADDED
@@ -0,0 +1,65 @@
1
+ from enum import Enum
2
+
3
+
4
+ class H5Format(str, Enum):
5
+ """Feature H5 schema to load."""
6
+
7
+ auto = "auto"
8
+ trident = "trident"
9
+ atlaspatch = "atlaspatch"
10
+
11
+
12
+ class Schedule(str, Enum):
13
+ """Learning-rate or momentum schedule."""
14
+
15
+ linear = "linear"
16
+ cosine = "cosine"
17
+
18
+
19
+ class WDSchedule(str, Enum):
20
+ """Weight-decay schedule (includes *none* to disable)."""
21
+
22
+ linear = "linear"
23
+ cosine = "cosine"
24
+ none = "none"
25
+
26
+
27
+ class OptimizerChoice(str, Enum):
28
+ """Optimizer algorithm."""
29
+
30
+ adamw = "adamw"
31
+ adam = "adam"
32
+ sgd = "sgd"
33
+
34
+
35
+ class Backend(str, Enum):
36
+ """Distributed training backend."""
37
+
38
+ nccl = "nccl"
39
+ gloo = "gloo"
40
+
41
+
42
+ class NormType(str, Enum):
43
+ """Normalization type for projection head layers."""
44
+
45
+ none = "none"
46
+ ln = "ln"
47
+
48
+
49
+ class HeadType(str, Enum):
50
+ """Task-head architecture."""
51
+
52
+ linear = "linear"
53
+ mlp = "mlp"
54
+
55
+
56
+ class TokenCapSampling(str, Enum):
57
+ """Sampling strategy when a slide exceeds the valid-token cap."""
58
+
59
+ deterministic = "deterministic"
60
+ random_stratified = "random_stratified"
61
+
62
+
63
+ def enum_val(v: object) -> str:
64
+ """Return the plain string value of a CLI enum, or ``str(v)`` for non-enums."""
65
+ return v.value if isinstance(v, Enum) else str(v)
moozy/cli/encode.py ADDED
@@ -0,0 +1,47 @@
1
+ from typing import Annotated
2
+
3
+ import typer
4
+
5
+
6
+ def encode_command(
7
+ slides: Annotated[
8
+ list[str],
9
+ typer.Argument(
10
+ help="H5 feature files or raw slides (.svs, .tiff, etc.) to process as a single case.",
11
+ ),
12
+ ],
13
+ output: Annotated[
14
+ str,
15
+ typer.Option("--output", "-o", help="Output H5 path for the case embedding."),
16
+ ],
17
+ mixed_precision: Annotated[
18
+ bool,
19
+ typer.Option("--mixed_precision/--no_mixed_precision", help="Enable bf16 autocast."),
20
+ ] = False,
21
+ target_mag: Annotated[
22
+ int,
23
+ typer.Option("--target_mag", help="Target magnification for patching (raw slides only)."),
24
+ ] = 20,
25
+ step_size: Annotated[
26
+ int,
27
+ typer.Option("--step_size", help="Stride between patches in pixels (raw slides only)."),
28
+ ] = 224,
29
+ mpp_csv: Annotated[
30
+ str,
31
+ typer.Option("--mpp_csv", help="CSV with wsi,mpp columns for microns-per-pixel override (raw slides only)."),
32
+ ] = "",
33
+ ) -> None:
34
+ """Encode slides into a case-level embedding with a MOOZY checkpoint."""
35
+ import logging
36
+
37
+ from moozy.encoding import run_encoding
38
+
39
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
40
+ run_encoding(
41
+ slide_paths=list(slides),
42
+ output_path=output,
43
+ mixed_precision=mixed_precision,
44
+ target_mag=target_mag,
45
+ step_size=step_size,
46
+ mpp_csv=mpp_csv or None,
47
+ )
moozy/cli/main.py ADDED
@@ -0,0 +1,16 @@
1
+ import typer
2
+
3
+ from .encode import encode_command
4
+ from .train import app as train_app
5
+
6
+ app = typer.Typer(
7
+ add_completion=False,
8
+ no_args_is_help=True,
9
+ help="MOOZY command-line interface.",
10
+ )
11
+ app.add_typer(train_app, name="train")
12
+ app.command(name="encode", help="Encode slides into a case-level embedding.")(encode_command)
13
+
14
+
15
+ def main() -> None:
16
+ app()
@@ -0,0 +1,29 @@
1
+ import typer
2
+
3
+ from ._stage1 import stage1_command
4
+ from ._stage2 import stage2_command
5
+
6
+ app = typer.Typer(
7
+ add_completion=False,
8
+ help="Train a MOOZY model.",
9
+ invoke_without_command=True,
10
+ )
11
+
12
+
13
+ @app.callback()
14
+ def _train_callback(ctx: typer.Context) -> None:
15
+ """Show help when invoked without a sub-command."""
16
+ if ctx.invoked_subcommand is None:
17
+ typer.echo(ctx.get_help(), nl=False)
18
+ raise typer.Exit(0)
19
+
20
+
21
+ # Primary commands
22
+ app.command(name="stage1")(stage1_command)
23
+ app.command(name="stage2")(stage2_command)
24
+
25
+ # Hidden aliases so users can type ``moozy train 1``, ``moozy train stage-1``, etc.
26
+ for _alias in ("1", "stage-1", "stage_1"):
27
+ app.command(name=_alias, hidden=True)(stage1_command)
28
+ for _alias in ("2", "stage-2", "stage_2"):
29
+ app.command(name=_alias, hidden=True)(stage2_command)
@@ -0,0 +1,317 @@
1
+ from typing import Annotated
2
+
3
+ import typer
4
+
5
+ from .._types import Backend, H5Format, NormType, OptimizerChoice, Schedule, WDSchedule, enum_val
6
+
7
+
8
+ def stage1_command(
9
+ feature_dirs: Annotated[
10
+ list[str],
11
+ typer.Option("--feature_dirs", help="Directories containing h5 feature files."),
12
+ ],
13
+ feature_h5_format: Annotated[
14
+ H5Format,
15
+ typer.Option("--feature_h5_format", help="Feature file H5 schema to load."),
16
+ ] = H5Format.auto,
17
+ feature_h5_key: Annotated[
18
+ str,
19
+ typer.Option("--feature_h5_key", help="Optional AtlasPatch feature key under features/."),
20
+ ] = "",
21
+ batch_size: Annotated[int, typer.Option("--batch_size", help="Batch size for training.")] = 64,
22
+ num_workers: Annotated[int, typer.Option("--num_workers", help="Number of data-loading workers.")] = 4,
23
+ prefetch_factor: Annotated[int, typer.Option("--prefetch_factor", help="Batches to prefetch per worker.")] = 4,
24
+ lazy_feature_loading: Annotated[
25
+ bool,
26
+ typer.Option("--lazy_feature_loading/--no_lazy_feature_loading", help="Load feature grids on demand."),
27
+ ] = False,
28
+ max_cached_slides: Annotated[
29
+ int,
30
+ typer.Option("--max_cached_slides", help="Max lazily-decoded slides to cache per process (0 disables)."),
31
+ ] = 0,
32
+ global_crop_size: Annotated[int, typer.Option("--global_crop_size", help="Size of global crops.")] = 20,
33
+ local_crop_size: Annotated[int, typer.Option("--local_crop_size", help="Size of local crops.")] = 12,
34
+ num_global_crops: Annotated[int, typer.Option("--num_global_crops", help="Global crops per sample.")] = 2,
35
+ num_local_crops: Annotated[int, typer.Option("--num_local_crops", help="Local crops per sample.")] = 4,
36
+ mask_ratio_min: Annotated[
37
+ float, typer.Option("--mask_ratio_min", help="Minimum mask ratio per masked global crop.")
38
+ ] = 0.1,
39
+ mask_ratio_max: Annotated[
40
+ float, typer.Option("--mask_ratio_max", help="Maximum mask ratio per masked global crop.")
41
+ ] = 0.5,
42
+ min_num_mask_patches: Annotated[
43
+ int, typer.Option("--min_num_mask_patches", help="Min patches per masking block.")
44
+ ] = 4,
45
+ max_num_mask_patches: Annotated[
46
+ int, typer.Option("--max_num_mask_patches", help="Max patches per masking block (-1 disables cap).")
47
+ ] = -1,
48
+ mask_min_aspect: Annotated[
49
+ float, typer.Option("--mask_min_aspect", help="Min aspect ratio for block-masking rectangles.")
50
+ ] = 0.3,
51
+ mask_max_aspect: Annotated[
52
+ float | None,
53
+ typer.Option("--mask_max_aspect", help="Max aspect ratio (defaults to 1/mask_min_aspect)."),
54
+ ] = None,
55
+ mask_sample_probability: Annotated[
56
+ float,
57
+ typer.Option("--mask_sample_probability", help="Probability of masking each global crop."),
58
+ ] = 0.5,
59
+ min_window_patch_ratio: Annotated[
60
+ float,
61
+ typer.Option("--min_window_patch_ratio", help="Min fraction of non-zero patches per crop."),
62
+ ] = 0.25,
63
+ crop_resample_attempts: Annotated[
64
+ int, typer.Option("--crop_resample_attempts", help="Attempts to resample a sparse crop.")
65
+ ] = 3,
66
+ hflip_prob: Annotated[float, typer.Option("--hflip_prob", help="Horizontal flip probability.")] = 0.5,
67
+ vflip_prob: Annotated[float, typer.Option("--vflip_prob", help="Vertical flip probability.")] = 0.5,
68
+ rotate_prob: Annotated[float, typer.Option("--rotate_prob", help="Probability of 90/180/270° rotation.")] = 0.5,
69
+ encoder_variant: Annotated[
70
+ str,
71
+ typer.Option("--encoder_variant", help="Encoder variant preset (e.g. base_half_depth, tiny, large)."),
72
+ ] = "base_half_depth",
73
+ output_dim: Annotated[int, typer.Option("--output_dim", help="Output dimension of the projection head.")] = 8192,
74
+ proj_hidden_dim: Annotated[
75
+ int, typer.Option("--proj_hidden_dim", help="Hidden dimension of the projection MLP.")
76
+ ] = 2048,
77
+ proj_bottleneck_dim: Annotated[
78
+ int, typer.Option("--proj_bottleneck_dim", help="Bottleneck dimension before final weight-normalised layer.")
79
+ ] = 256,
80
+ proj_norm_last_layer: Annotated[
81
+ bool,
82
+ typer.Option(
83
+ "--proj_norm_last_layer/--no_proj_norm_last_layer",
84
+ help="Freeze weight_norm gain in the projection head's last layer.",
85
+ ),
86
+ ] = True,
87
+ proj_norm: Annotated[
88
+ NormType,
89
+ typer.Option("--proj_norm", help="Normalisation inside projection MLP after fc1/fc2."),
90
+ ] = NormType.none,
91
+ proj_last_norm: Annotated[
92
+ NormType,
93
+ typer.Option("--proj_last_norm", help="Optional normalisation after the final projection layer."),
94
+ ] = NormType.none,
95
+ num_registers: Annotated[
96
+ int, typer.Option("--num_registers", help="Register tokens prepended after CLS (0 disables).")
97
+ ] = 4,
98
+ layer_drop: Annotated[float, typer.Option("--layer_drop", help="Stochastic depth (layer drop) rate.")] = 0.1,
99
+ dropout: Annotated[float, typer.Option("--dropout", help="MLP dropout rate inside transformer blocks.")] = 0.1,
100
+ attn_dropout: Annotated[float, typer.Option("--attn_dropout", help="Attention dropout probability.")] = 0.0,
101
+ qk_norm: Annotated[
102
+ bool, typer.Option("--qk_norm/--no_qk_norm", help="Per-head LayerNorm on q/k projections.")
103
+ ] = False,
104
+ layerscale_init: Annotated[
105
+ float, typer.Option("--layerscale_init", help="Initial LayerScale gamma (<=0 disables).")
106
+ ] = 0.0,
107
+ learnable_alibi: Annotated[
108
+ bool,
109
+ typer.Option("--learnable_alibi/--no_learnable_alibi", help="Make ALiBi slopes trainable."),
110
+ ] = False,
111
+ ema_momentum_start: Annotated[
112
+ float, typer.Option("--ema_momentum_start", help="Initial EMA momentum for teacher update.")
113
+ ] = 0.996,
114
+ ema_momentum: Annotated[float, typer.Option("--ema_momentum", help="Final EMA momentum for teacher update.")] = 1.0,
115
+ momentum_schedule: Annotated[
116
+ Schedule, typer.Option("--momentum_schedule", help="Teacher EMA momentum schedule.")
117
+ ] = Schedule.cosine,
118
+ student_temp: Annotated[float, typer.Option("--student_temp", help="Student temperature.")] = 0.1,
119
+ teacher_temp: Annotated[float, typer.Option("--teacher_temp", help="Final teacher CLS temperature.")] = 0.07,
120
+ teacher_patch_temp: Annotated[
121
+ float, typer.Option("--teacher_patch_temp", help="Final teacher patch temperature.")
122
+ ] = 0.07,
123
+ warmup_teacher_temp: Annotated[
124
+ float, typer.Option("--warmup_teacher_temp", help="Starting teacher CLS temperature during warmup.")
125
+ ] = 0.04,
126
+ warmup_teacher_patch_temp: Annotated[
127
+ float, typer.Option("--warmup_teacher_patch_temp", help="Starting teacher patch temperature during warmup.")
128
+ ] = 0.04,
129
+ warmup_teacher_temp_epochs: Annotated[
130
+ int, typer.Option("--warmup_teacher_temp_epochs", help="Epochs to warm up teacher temperatures (0 disables).")
131
+ ] = 30,
132
+ center_momentum: Annotated[float, typer.Option("--center_momentum", help="Momentum for center updates.")] = 0.9,
133
+ total_steps: Annotated[
134
+ int,
135
+ typer.Option("--total_steps", help="Total training steps when --epochs is 0 or negative."),
136
+ ] = 0,
137
+ epochs: Annotated[
138
+ float,
139
+ typer.Option("--epochs", help="Number of epochs. Positive values override --total_steps. Supports fractional."),
140
+ ] = 200,
141
+ lr: Annotated[float, typer.Option("--lr", help="Base learning rate.")] = 5e-4,
142
+ lr_min: Annotated[float, typer.Option("--lr_min", help="Minimum learning rate at end of decay.")] = 2e-6,
143
+ lr_schedule: Annotated[
144
+ Schedule, typer.Option("--lr_schedule", help="LR decay schedule after warmup.")
145
+ ] = Schedule.cosine,
146
+ lr_base_batch_size: Annotated[
147
+ int, typer.Option("--lr_base_batch_size", help="Reference global batch size for LR scaling.")
148
+ ] = 256,
149
+ weight_decay: Annotated[float, typer.Option("--weight_decay", help="Global weight decay.")] = 0.04,
150
+ weight_decay_start: Annotated[float, typer.Option("--weight_decay_start", help="Starting weight decay.")] = 0.04,
151
+ weight_decay_end: Annotated[float, typer.Option("--weight_decay_end", help="Final weight decay.")] = 0.4,
152
+ wd_schedule: Annotated[
153
+ WDSchedule, typer.Option("--wd_schedule", help="Weight-decay schedule type.")
154
+ ] = WDSchedule.cosine,
155
+ warmup_steps: Annotated[int, typer.Option("--warmup_steps", help="Linear warmup steps for LR.")] = 0,
156
+ warmup_epochs: Annotated[
157
+ float,
158
+ typer.Option("--warmup_epochs", help="LR warmup epochs when --warmup_steps is 0."),
159
+ ] = 5,
160
+ grad_clip: Annotated[float, typer.Option("--grad_clip", help="Gradient clipping max norm (0 disables).")] = 0.3,
161
+ grad_accumulation_steps: Annotated[
162
+ int, typer.Option("--grad_accumulation_steps", help="Gradient accumulation steps.")
163
+ ] = 1,
164
+ freeze_last_layer_steps: Annotated[
165
+ int | None,
166
+ typer.Option("--freeze_last_layer_steps", help="Freeze last projection layer for N steps."),
167
+ ] = None,
168
+ freeze_last_layer_epochs: Annotated[
169
+ float,
170
+ typer.Option("--freeze_last_layer_epochs", help="Freeze last projection layer for N epochs."),
171
+ ] = 3.0,
172
+ optimizer: Annotated[
173
+ OptimizerChoice, typer.Option("--optimizer", help="Optimiser algorithm.")
174
+ ] = OptimizerChoice.adamw,
175
+ log_every: Annotated[int, typer.Option("--log_every", help="Log training loss every N steps.")] = 72,
176
+ val_ratio: Annotated[float, typer.Option("--val_ratio", help="Validation split ratio.")] = 0.05,
177
+ val_every: Annotated[int, typer.Option("--val_every", help="Validate every N steps.")] = 1000,
178
+ mixed_precision: Annotated[
179
+ bool,
180
+ typer.Option("--mixed_precision/--no_mixed_precision", help="Enable mixed-precision training (bf16)."),
181
+ ] = False,
182
+ save_every: Annotated[int, typer.Option("--save_every", help="Save checkpoint every N steps (0 disables).")] = 72,
183
+ resume_from: Annotated[str | None, typer.Option("--resume_from", help="Path to checkpoint to resume from.")] = None,
184
+ keep_last_n: Annotated[int, typer.Option("--keep_last_n", help="Keep only last N checkpoints.")] = 50,
185
+ save_teacher: Annotated[
186
+ bool,
187
+ typer.Option("--save_teacher/--no_save_teacher", help="Save teacher-only checkpoint at each save."),
188
+ ] = True,
189
+ teacher_save_prefix: Annotated[
190
+ str, typer.Option("--teacher_save_prefix", help="Filename prefix for teacher-only checkpoints.")
191
+ ] = "teacher_step",
192
+ distributed: Annotated[
193
+ bool, typer.Option("--distributed/--no_distributed", help="Enable distributed training.")
194
+ ] = False,
195
+ backend: Annotated[Backend, typer.Option("--backend", help="Distributed backend.")] = Backend.nccl,
196
+ local_rank: Annotated[int, typer.Option("--local_rank", help="Local rank for distributed training.")] = 0,
197
+ output_dir: Annotated[
198
+ str, typer.Option("--output_dir", help="Output directory for logs and checkpoints.")
199
+ ] = "./results",
200
+ wandb: Annotated[bool, typer.Option("--wandb/--no_wandb", help="Enable Weights & Biases logging.")] = False,
201
+ wandb_project: Annotated[str, typer.Option("--wandb_project", help="Weights & Biases project name.")] = "moozy",
202
+ wandb_tags: Annotated[
203
+ str,
204
+ typer.Option("--wandb_tags", help="Space or comma-separated tags for Weights & Biases run."),
205
+ ] = "",
206
+ seed: Annotated[int, typer.Option("--seed", help="Random seed.")] = 42,
207
+ debug: Annotated[bool, typer.Option("--debug/--no_debug", help="Debug mode with limited data.")] = False,
208
+ ) -> None:
209
+ """Train the Stage-1 MOOZY self-supervised pretraining model."""
210
+ from moozy.config.data import Stage1DataConfig
211
+ from moozy.config.model import ProjectionConfig, SlideEncoderConfig
212
+ from moozy.config.stage1 import Stage1TrainConfig
213
+ from moozy.config.training import CheckpointConfig, OptimizationConfig, SchedulerConfig
214
+
215
+ parsed_wandb_tags = [t for raw in wandb_tags.replace(",", " ").split() if (t := raw.strip())] if wandb_tags else []
216
+
217
+ config = Stage1TrainConfig(
218
+ slide_encoder=SlideEncoderConfig(
219
+ variant=encoder_variant,
220
+ num_registers=num_registers,
221
+ dropout=dropout,
222
+ attn_dropout=attn_dropout,
223
+ layer_drop=layer_drop,
224
+ qk_norm=qk_norm,
225
+ layerscale_init=layerscale_init,
226
+ learnable_alibi=learnable_alibi,
227
+ ).resolve_variant(),
228
+ projection=ProjectionConfig(
229
+ output_dim=output_dim,
230
+ proj_hidden_dim=proj_hidden_dim,
231
+ proj_bottleneck_dim=proj_bottleneck_dim,
232
+ proj_norm_last_layer=proj_norm_last_layer,
233
+ proj_norm=enum_val(proj_norm),
234
+ proj_last_norm=enum_val(proj_last_norm),
235
+ ),
236
+ data=Stage1DataConfig(
237
+ feature_dirs=list(feature_dirs),
238
+ feature_h5_format=enum_val(feature_h5_format),
239
+ feature_h5_key=feature_h5_key,
240
+ batch_size=batch_size,
241
+ num_workers=num_workers,
242
+ prefetch_factor=prefetch_factor,
243
+ lazy_feature_loading=lazy_feature_loading,
244
+ max_cached_slides=max_cached_slides,
245
+ global_crop_size=global_crop_size,
246
+ local_crop_size=local_crop_size,
247
+ num_global_crops=num_global_crops,
248
+ num_local_crops=num_local_crops,
249
+ mask_ratio_min=mask_ratio_min,
250
+ mask_ratio_max=mask_ratio_max,
251
+ min_num_mask_patches=min_num_mask_patches,
252
+ max_num_mask_patches=max_num_mask_patches,
253
+ mask_min_aspect=mask_min_aspect,
254
+ mask_max_aspect=mask_max_aspect,
255
+ mask_sample_probability=mask_sample_probability,
256
+ min_window_patch_ratio=min_window_patch_ratio,
257
+ crop_resample_attempts=crop_resample_attempts,
258
+ hflip_prob=hflip_prob,
259
+ vflip_prob=vflip_prob,
260
+ rotate_prob=rotate_prob,
261
+ ),
262
+ optimization=OptimizationConfig(
263
+ optimizer=enum_val(optimizer),
264
+ lr=lr,
265
+ lr_min=lr_min,
266
+ lr_base_batch_size=lr_base_batch_size,
267
+ weight_decay=weight_decay,
268
+ grad_clip=grad_clip,
269
+ grad_accumulation_steps=grad_accumulation_steps,
270
+ mixed_precision=mixed_precision,
271
+ ),
272
+ scheduler=SchedulerConfig(
273
+ lr_schedule=enum_val(lr_schedule),
274
+ warmup_steps=warmup_steps,
275
+ warmup_epochs=warmup_epochs,
276
+ weight_decay_start=weight_decay_start,
277
+ weight_decay_end=weight_decay_end,
278
+ wd_schedule=enum_val(wd_schedule),
279
+ ema_momentum_start=ema_momentum_start,
280
+ ema_momentum=ema_momentum,
281
+ momentum_schedule=enum_val(momentum_schedule),
282
+ student_temp=student_temp,
283
+ teacher_temp=teacher_temp,
284
+ teacher_patch_temp=teacher_patch_temp,
285
+ warmup_teacher_temp=warmup_teacher_temp,
286
+ warmup_teacher_patch_temp=warmup_teacher_patch_temp,
287
+ warmup_teacher_temp_epochs=warmup_teacher_temp_epochs,
288
+ center_momentum=center_momentum,
289
+ freeze_last_layer_steps=freeze_last_layer_steps,
290
+ freeze_last_layer_epochs=freeze_last_layer_epochs,
291
+ ),
292
+ checkpoint=CheckpointConfig(
293
+ save_every=save_every,
294
+ resume_from=resume_from,
295
+ keep_last_n=keep_last_n,
296
+ save_teacher=save_teacher,
297
+ teacher_save_prefix=teacher_save_prefix,
298
+ ),
299
+ total_steps=total_steps,
300
+ epochs=epochs,
301
+ log_every=log_every,
302
+ val_ratio=val_ratio,
303
+ val_every=val_every,
304
+ distributed=distributed,
305
+ backend=enum_val(backend),
306
+ local_rank=local_rank,
307
+ output_dir=output_dir,
308
+ seed=seed,
309
+ debug=debug,
310
+ wandb=wandb,
311
+ wandb_project=wandb_project,
312
+ wandb_tags=parsed_wandb_tags,
313
+ )
314
+
315
+ from moozy.training.runners.stage1 import run_stage1
316
+
317
+ run_stage1(config)