Upload folder using huggingface_hub
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- OpenMidnight/.gitignore +0 -46
- OpenMidnight/HEST_evaluation.py +0 -51
- OpenMidnight/LICENSE +0 -203
- OpenMidnight/README.md +0 -225
- OpenMidnight/_utils.py +0 -166
- OpenMidnight/dinov2/__init__.py +0 -6
- OpenMidnight/dinov2/configs/__init__.py +0 -22
- OpenMidnight/dinov2/configs/eval/vitb14_pretrain.yaml +0 -6
- OpenMidnight/dinov2/configs/eval/vitb14_reg4_pretrain.yaml +0 -9
- OpenMidnight/dinov2/configs/eval/vitg14_pretrain.yaml +0 -7
- OpenMidnight/dinov2/configs/eval/vitg14_reg4_pretrain.yaml +0 -10
- OpenMidnight/dinov2/configs/eval/vitl14_pretrain.yaml +0 -6
- OpenMidnight/dinov2/configs/eval/vitl14_reg4_pretrain.yaml +0 -9
- OpenMidnight/dinov2/configs/eval/vits14_pretrain.yaml +0 -6
- OpenMidnight/dinov2/configs/eval/vits14_reg4_pretrain.yaml +0 -9
- OpenMidnight/dinov2/configs/ssl_default_config.yaml +0 -129
- OpenMidnight/dinov2/configs/train/openpath_vitg14.yaml +0 -95
- OpenMidnight/dinov2/configs/train/openpath_vitg14_bs64_test.yaml +0 -84
- OpenMidnight/dinov2/configs/train/openpath_vitg14_curated.yaml +0 -57
- OpenMidnight/dinov2/configs/train/openpath_vitg14_final.yaml +0 -80
- OpenMidnight/dinov2/configs/train/openpath_vitg14_final_1ep.yaml +0 -59
- OpenMidnight/dinov2/configs/train/openpath_vitg14_final_1ep_bs64.yaml +0 -59
- OpenMidnight/dinov2/configs/train/openpath_vitg14_full.yaml +0 -55
- OpenMidnight/dinov2/configs/train/openpath_vitg14_full_cptac.yaml +0 -55
- OpenMidnight/dinov2/configs/train/openpath_vitg14_run2.yaml +0 -84
- OpenMidnight/dinov2/configs/train/openpath_vitg14_run3_gram.yaml +0 -92
- OpenMidnight/dinov2/configs/train/openpath_vitg14_run3_smoke.yaml +0 -92
- OpenMidnight/dinov2/configs/train/openpath_vitg14_run4_gram.yaml +0 -92
- OpenMidnight/dinov2/configs/train/openpath_vitg14_run5_native.yaml +0 -84
- OpenMidnight/dinov2/configs/train/openpath_vitg14_run6_native_gram.yaml +0 -93
- OpenMidnight/dinov2/configs/train/openpath_vitg14_testB.yaml +0 -54
- OpenMidnight/dinov2/configs/train/openpath_vitl14_reg4.yaml +0 -58
- OpenMidnight/dinov2/configs/train/openpath_vitl14_reg4_curated.yaml +0 -58
- OpenMidnight/dinov2/configs/train/openpath_vitl14_testB.yaml +0 -59
- OpenMidnight/dinov2/configs/train/vitg14_reg4.yaml +0 -51
- OpenMidnight/dinov2/configs/train/vits14_reg4.yaml +0 -51
- OpenMidnight/dinov2/data/__init__.py +0 -10
- OpenMidnight/dinov2/data/adapters.py +0 -28
- OpenMidnight/dinov2/data/augmentations.py +0 -383
- OpenMidnight/dinov2/data/collate.py +0 -62
- OpenMidnight/dinov2/data/datasets/__init__.py +0 -9
- OpenMidnight/dinov2/data/datasets/decoders.py +0 -31
- OpenMidnight/dinov2/data/datasets/extended.py +0 -38
- OpenMidnight/dinov2/data/datasets/image_net.py +0 -290
- OpenMidnight/dinov2/data/datasets/image_net_22k.py +0 -302
- OpenMidnight/dinov2/data/datasets/slide_dataset.py +0 -99
- OpenMidnight/dinov2/data/loaders.py +0 -229
- OpenMidnight/dinov2/data/masking.py +0 -86
- OpenMidnight/dinov2/data/openpath_wds.py +0 -229
- OpenMidnight/dinov2/data/samplers.py +0 -229
OpenMidnight/.gitignore
DELETED
|
@@ -1,46 +0,0 @@
|
|
| 1 |
-
# Byte-compiled / optimized / DLL files
|
| 2 |
-
__pycache__/
|
| 3 |
-
*.py[cod]
|
| 4 |
-
|
| 5 |
-
# Distribution / packaging
|
| 6 |
-
build/
|
| 7 |
-
dist/
|
| 8 |
-
*.egg-info/
|
| 9 |
-
_version.py
|
| 10 |
-
|
| 11 |
-
# IDE
|
| 12 |
-
.vscode/
|
| 13 |
-
|
| 14 |
-
# Virtual environment
|
| 15 |
-
.venv/
|
| 16 |
-
.python-version
|
| 17 |
-
|
| 18 |
-
# Environment variables
|
| 19 |
-
.env
|
| 20 |
-
.env.*
|
| 21 |
-
|
| 22 |
-
# Jupyter Notebook
|
| 23 |
-
.ipynb_checkpoints
|
| 24 |
-
|
| 25 |
-
# Local data
|
| 26 |
-
slurms
|
| 27 |
-
checkpoints
|
| 28 |
-
wandb
|
| 29 |
-
jobs
|
| 30 |
-
|
| 31 |
-
# Local scratch/tmp
|
| 32 |
-
.scratch
|
| 33 |
-
tmp
|
| 34 |
-
.tmp*
|
| 35 |
-
|
| 36 |
-
# specific files
|
| 37 |
-
output*
|
| 38 |
-
sample_dataset_30.txt
|
| 39 |
-
uv.lock
|
| 40 |
-
watch_run.sh
|
| 41 |
-
wandb/
|
| 42 |
-
HEST/
|
| 43 |
-
eva-probe/
|
| 44 |
-
eval/
|
| 45 |
-
CLAUDE.md
|
| 46 |
-
AGENTS.md
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/HEST_evaluation.py
DELETED
|
@@ -1,51 +0,0 @@
|
|
| 1 |
-
from hest.bench import benchmark
|
| 2 |
-
import torch
|
| 3 |
-
from torchvision import transforms
|
| 4 |
-
|
| 5 |
-
print("loading base")
|
| 6 |
-
dinov2 = torch.hub.load('facebookresearch/dinov2', 'dinov2_vitg14_reg')
|
| 7 |
-
|
| 8 |
-
ours = torch.load("checkpoints/teacher_epoch250000.pth")
|
| 9 |
-
checkpoint = ours["teacher"]
|
| 10 |
-
checkpoint_new = {}
|
| 11 |
-
|
| 12 |
-
for key in list(checkpoint.keys()):
|
| 13 |
-
if "dino" in str(key) or "ibot" in str(key):
|
| 14 |
-
checkpoint.pop(key, None)
|
| 15 |
-
|
| 16 |
-
for key, keyb in zip(checkpoint.keys(), dinov2.state_dict().keys()):
|
| 17 |
-
checkpoint_new[keyb] = checkpoint[key]
|
| 18 |
-
|
| 19 |
-
checkpoint = checkpoint_new
|
| 20 |
-
|
| 21 |
-
new_shape = checkpoint["pos_embed"] #The pos embed is the only different shape
|
| 22 |
-
dinov2.pos_embed = torch.nn.parameter.Parameter(new_shape)
|
| 23 |
-
|
| 24 |
-
dinov2.load_state_dict(checkpoint)
|
| 25 |
-
|
| 26 |
-
PATH_TO_CONFIG = "./HEST/bench_config/bench_config.yaml"
|
| 27 |
-
model = dinov2
|
| 28 |
-
|
| 29 |
-
RESIZE_DIM = 224
|
| 30 |
-
NORMALIZE_MEAN = [0.485, 0.456, 0.406]
|
| 31 |
-
NORMALIZE_STD = [0.229, 0.224, 0.225]
|
| 32 |
-
|
| 33 |
-
model_transforms = transforms.Compose([
|
| 34 |
-
#transforms.Resize(224), # Resize the smaller side of the image to 256
|
| 35 |
-
#transforms.CenterCrop(RESIZE_DIM), # Crop the center of the image to 224x224
|
| 36 |
-
|
| 37 |
-
# Step 2: Convert the image (PIL/numpy) to a PyTorch tensor
|
| 38 |
-
transforms.ToTensor(),
|
| 39 |
-
|
| 40 |
-
# Step 3: Normalize the tensor
|
| 41 |
-
transforms.Normalize(
|
| 42 |
-
mean=NORMALIZE_MEAN,
|
| 43 |
-
std=NORMALIZE_STD)
|
| 44 |
-
])
|
| 45 |
-
|
| 46 |
-
benchmark(
|
| 47 |
-
model,
|
| 48 |
-
model_transforms,
|
| 49 |
-
torch.float32,
|
| 50 |
-
config=PATH_TO_CONFIG,
|
| 51 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/LICENSE
DELETED
|
@@ -1,203 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
Apache License
|
| 4 |
-
Version 2.0, January 2004
|
| 5 |
-
http://www.apache.org/licenses/
|
| 6 |
-
|
| 7 |
-
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 8 |
-
|
| 9 |
-
1. Definitions.
|
| 10 |
-
|
| 11 |
-
"License" shall mean the terms and conditions for use, reproduction,
|
| 12 |
-
and distribution as defined by Sections 1 through 9 of this document.
|
| 13 |
-
|
| 14 |
-
"Licensor" shall mean the copyright owner or entity authorized by
|
| 15 |
-
the copyright owner that is granting the License.
|
| 16 |
-
|
| 17 |
-
"Legal Entity" shall mean the union of the acting entity and all
|
| 18 |
-
other entities that control, are controlled by, or are under common
|
| 19 |
-
control with that entity. For the purposes of this definition,
|
| 20 |
-
"control" means (i) the power, direct or indirect, to cause the
|
| 21 |
-
direction or management of such entity, whether by contract or
|
| 22 |
-
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 23 |
-
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 24 |
-
|
| 25 |
-
"You" (or "Your") shall mean an individual or Legal Entity
|
| 26 |
-
exercising permissions granted by this License.
|
| 27 |
-
|
| 28 |
-
"Source" form shall mean the preferred form for making modifications,
|
| 29 |
-
including but not limited to software source code, documentation
|
| 30 |
-
source, and configuration files.
|
| 31 |
-
|
| 32 |
-
"Object" form shall mean any form resulting from mechanical
|
| 33 |
-
transformation or translation of a Source form, including but
|
| 34 |
-
not limited to compiled object code, generated documentation,
|
| 35 |
-
and conversions to other media types.
|
| 36 |
-
|
| 37 |
-
"Work" shall mean the work of authorship, whether in Source or
|
| 38 |
-
Object form, made available under the License, as indicated by a
|
| 39 |
-
copyright notice that is included in or attached to the work
|
| 40 |
-
(an example is provided in the Appendix below).
|
| 41 |
-
|
| 42 |
-
"Derivative Works" shall mean any work, whether in Source or Object
|
| 43 |
-
form, that is based on (or derived from) the Work and for which the
|
| 44 |
-
editorial revisions, annotations, elaborations, or other modifications
|
| 45 |
-
represent, as a whole, an original work of authorship. For the purposes
|
| 46 |
-
of this License, Derivative Works shall not include works that remain
|
| 47 |
-
separable from, or merely link (or bind by name) to the interfaces of,
|
| 48 |
-
the Work and Derivative Works thereof.
|
| 49 |
-
|
| 50 |
-
"Contribution" shall mean any work of authorship, including
|
| 51 |
-
the original version of the Work and any modifications or additions
|
| 52 |
-
to that Work or Derivative Works thereof, that is intentionally
|
| 53 |
-
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 54 |
-
or by an individual or Legal Entity authorized to submit on behalf of
|
| 55 |
-
the copyright owner. For the purposes of this definition, "submitted"
|
| 56 |
-
means any form of electronic, verbal, or written communication sent
|
| 57 |
-
to the Licensor or its representatives, including but not limited to
|
| 58 |
-
communication on electronic mailing lists, source code control systems,
|
| 59 |
-
and issue tracking systems that are managed by, or on behalf of, the
|
| 60 |
-
Licensor for the purpose of discussing and improving the Work, but
|
| 61 |
-
excluding communication that is conspicuously marked or otherwise
|
| 62 |
-
designated in writing by the copyright owner as "Not a Contribution."
|
| 63 |
-
|
| 64 |
-
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 65 |
-
on behalf of whom a Contribution has been received by Licensor and
|
| 66 |
-
subsequently incorporated within the Work.
|
| 67 |
-
|
| 68 |
-
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 69 |
-
this License, each Contributor hereby grants to You a perpetual,
|
| 70 |
-
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 71 |
-
copyright license to reproduce, prepare Derivative Works of,
|
| 72 |
-
publicly display, publicly perform, sublicense, and distribute the
|
| 73 |
-
Work and such Derivative Works in Source or Object form.
|
| 74 |
-
|
| 75 |
-
3. Grant of Patent License. Subject to the terms and conditions of
|
| 76 |
-
this License, each Contributor hereby grants to You a perpetual,
|
| 77 |
-
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 78 |
-
(except as stated in this section) patent license to make, have made,
|
| 79 |
-
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 80 |
-
where such license applies only to those patent claims licensable
|
| 81 |
-
by such Contributor that are necessarily infringed by their
|
| 82 |
-
Contribution(s) alone or by combination of their Contribution(s)
|
| 83 |
-
with the Work to which such Contribution(s) was submitted. If You
|
| 84 |
-
institute patent litigation against any entity (including a
|
| 85 |
-
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 86 |
-
or a Contribution incorporated within the Work constitutes direct
|
| 87 |
-
or contributory patent infringement, then any patent licenses
|
| 88 |
-
granted to You under this License for that Work shall terminate
|
| 89 |
-
as of the date such litigation is filed.
|
| 90 |
-
|
| 91 |
-
4. Redistribution. You may reproduce and distribute copies of the
|
| 92 |
-
Work or Derivative Works thereof in any medium, with or without
|
| 93 |
-
modifications, and in Source or Object form, provided that You
|
| 94 |
-
meet the following conditions:
|
| 95 |
-
|
| 96 |
-
(a) You must give any other recipients of the Work or
|
| 97 |
-
Derivative Works a copy of this License; and
|
| 98 |
-
|
| 99 |
-
(b) You must cause any modified files to carry prominent notices
|
| 100 |
-
stating that You changed the files; and
|
| 101 |
-
|
| 102 |
-
(c) You must retain, in the Source form of any Derivative Works
|
| 103 |
-
that You distribute, all copyright, patent, trademark, and
|
| 104 |
-
attribution notices from the Source form of the Work,
|
| 105 |
-
excluding those notices that do not pertain to any part of
|
| 106 |
-
the Derivative Works; and
|
| 107 |
-
|
| 108 |
-
(d) If the Work includes a "NOTICE" text file as part of its
|
| 109 |
-
distribution, then any Derivative Works that You distribute must
|
| 110 |
-
include a readable copy of the attribution notices contained
|
| 111 |
-
within such NOTICE file, excluding those notices that do not
|
| 112 |
-
pertain to any part of the Derivative Works, in at least one
|
| 113 |
-
of the following places: within a NOTICE text file distributed
|
| 114 |
-
as part of the Derivative Works; within the Source form or
|
| 115 |
-
documentation, if provided along with the Derivative Works; or,
|
| 116 |
-
within a display generated by the Derivative Works, if and
|
| 117 |
-
wherever such third-party notices normally appear. The contents
|
| 118 |
-
of the NOTICE file are for informational purposes only and
|
| 119 |
-
do not modify the License. You may add Your own attribution
|
| 120 |
-
notices within Derivative Works that You distribute, alongside
|
| 121 |
-
or as an addendum to the NOTICE text from the Work, provided
|
| 122 |
-
that such additional attribution notices cannot be construed
|
| 123 |
-
as modifying the License.
|
| 124 |
-
|
| 125 |
-
You may add Your own copyright statement to Your modifications and
|
| 126 |
-
may provide additional or different license terms and conditions
|
| 127 |
-
for use, reproduction, or distribution of Your modifications, or
|
| 128 |
-
for any such Derivative Works as a whole, provided Your use,
|
| 129 |
-
reproduction, and distribution of the Work otherwise complies with
|
| 130 |
-
the conditions stated in this License.
|
| 131 |
-
|
| 132 |
-
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 133 |
-
any Contribution intentionally submitted for inclusion in the Work
|
| 134 |
-
by You to the Licensor shall be under the terms and conditions of
|
| 135 |
-
this License, without any additional terms or conditions.
|
| 136 |
-
Notwithstanding the above, nothing herein shall supersede or modify
|
| 137 |
-
the terms of any separate license agreement you may have executed
|
| 138 |
-
with Licensor regarding such Contributions.
|
| 139 |
-
|
| 140 |
-
6. Trademarks. This License does not grant permission to use the trade
|
| 141 |
-
names, trademarks, service marks, or product names of the Licensor,
|
| 142 |
-
except as required for reasonable and customary use in describing the
|
| 143 |
-
origin of the Work and reproducing the content of the NOTICE file.
|
| 144 |
-
|
| 145 |
-
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 146 |
-
agreed to in writing, Licensor provides the Work (and each
|
| 147 |
-
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 148 |
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 149 |
-
implied, including, without limitation, any warranties or conditions
|
| 150 |
-
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 151 |
-
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 152 |
-
appropriateness of using or redistributing the Work and assume any
|
| 153 |
-
risks associated with Your exercise of permissions under this License.
|
| 154 |
-
|
| 155 |
-
8. Limitation of Liability. In no event and under no legal theory,
|
| 156 |
-
whether in tort (including negligence), contract, or otherwise,
|
| 157 |
-
unless required by applicable law (such as deliberate and grossly
|
| 158 |
-
negligent acts) or agreed to in writing, shall any Contributor be
|
| 159 |
-
liable to You for damages, including any direct, indirect, special,
|
| 160 |
-
incidental, or consequential damages of any character arising as a
|
| 161 |
-
result of this License or out of the use or inability to use the
|
| 162 |
-
Work (including but not limited to damages for loss of goodwill,
|
| 163 |
-
work stoppage, computer failure or malfunction, or any and all
|
| 164 |
-
other commercial damages or losses), even if such Contributor
|
| 165 |
-
has been advised of the possibility of such damages.
|
| 166 |
-
|
| 167 |
-
9. Accepting Warranty or Additional Liability. While redistributing
|
| 168 |
-
the Work or Derivative Works thereof, You may choose to offer,
|
| 169 |
-
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 170 |
-
or other liability obligations and/or rights consistent with this
|
| 171 |
-
License. However, in accepting such obligations, You may act only
|
| 172 |
-
on Your own behalf and on Your sole responsibility, not on behalf
|
| 173 |
-
of any other Contributor, and only if You agree to indemnify,
|
| 174 |
-
defend, and hold each Contributor harmless for any liability
|
| 175 |
-
incurred by, or claims asserted against, such Contributor by reason
|
| 176 |
-
of your accepting any such warranty or additional liability.
|
| 177 |
-
|
| 178 |
-
END OF TERMS AND CONDITIONS
|
| 179 |
-
|
| 180 |
-
APPENDIX: How to apply the Apache License to your work.
|
| 181 |
-
|
| 182 |
-
To apply the Apache License to your work, attach the following
|
| 183 |
-
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 184 |
-
replaced with your own identifying information. (Don't include
|
| 185 |
-
the brackets!) The text should be enclosed in the appropriate
|
| 186 |
-
comment syntax for the file format. We also recommend that a
|
| 187 |
-
file or class name and description of purpose be included on the
|
| 188 |
-
same "printed page" as the copyright notice for easier
|
| 189 |
-
identification within third-party archives.
|
| 190 |
-
|
| 191 |
-
Copyright [yyyy] [name of copyright owner]
|
| 192 |
-
|
| 193 |
-
Licensed under the Apache License, Version 2.0 (the "License");
|
| 194 |
-
you may not use this file except in compliance with the License.
|
| 195 |
-
You may obtain a copy of the License at
|
| 196 |
-
|
| 197 |
-
http://www.apache.org/licenses/LICENSE-2.0
|
| 198 |
-
|
| 199 |
-
Unless required by applicable law or agreed to in writing, software
|
| 200 |
-
distributed under the License is distributed on an "AS IS" BASIS,
|
| 201 |
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 202 |
-
See the License for the specific language governing permissions and
|
| 203 |
-
limitations under the License.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/README.md
DELETED
|
@@ -1,225 +0,0 @@
|
|
| 1 |
-
<h1> OpenMidnight </h1>
|
| 2 |
-
|
| 3 |
-
<p align="center"><img width="735" height="400" alt="2PJpVl2k51pfjFg2Vs-oS" src="https://github.com/user-attachments/assets/fd00a9f5-3248-46ed-883a-f423e54ac2b6" /></p>
|
| 4 |
-
<p align="center">Fully open-source and improved replication of Kaiko.AI's pathology foundation model <a href="https://arxiv.org/abs/2504.05186v1">Midnight</a>.</p>
|
| 5 |
-
<div id="badges" align="center">
|
| 6 |
-
<a href="https://sophont.med/blog/openmidnight"><img src="https://img.shields.io/badge/Blog-Training%20SOTA%20Pathology%20Foundation%20Model%20with%20%241.6k-111827?style=for-the-badge&logo=read.cv&logoColor=white" /> </a>
|
| 7 |
-
<a href="https://discord.gg/tVR4TWnRM9"><img src="https://img.shields.io/badge/Discord-Collaborate%20with%20us-5865F2?style=for-the-badge&logo=discord&logoColor=white" /></a>
|
| 8 |
-
</div>
|
| 9 |
-
<p align="center"> <a href="https://sophont.med">Sophont</a> · <a href="https://medarc.ai">MedARC</a> </p>
|
| 10 |
-
|
| 11 |
-
This is a publicly developed, open-source project by [MedARC](https://www.medarc.ai/). If you are interested in helping out, [join our Discord server](https://discord.gg/tVR4TWnRM9) and introduce yourself in our `#path-fm` channel.
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
## Features
|
| 15 |
-
- Trains faster with improved average benchmarking performance compared to the original Midnight-12K model (~3 days to train using 1×8×H100)
|
| 16 |
-
- Supports single‑GPU up to multi‑node training with FSDP
|
| 17 |
-
- Robust resuming from last checkpoint functionality if training gets interrupted
|
| 18 |
-
- Weights & Biases (wandb) logging for monitoring/tracking model training
|
| 19 |
-
- Optionally stream data from Hugging Face so no need to download any data in advance (TCGA-12K is approximately 12 TB)
|
| 20 |
-
|
| 21 |
-
# Installation
|
| 22 |
-
|
| 23 |
-
Clone the repository:
|
| 24 |
-
|
| 25 |
-
```bash
|
| 26 |
-
git clone https://github.com/MedARC-AI/openmidnight.git
|
| 27 |
-
```
|
| 28 |
-
|
| 29 |
-
Change into the directory, then run the installation script:
|
| 30 |
-
|
| 31 |
-
```bash
|
| 32 |
-
./install.sh
|
| 33 |
-
```
|
| 34 |
-
|
| 35 |
-
This will create a virtual environment, "pathologydino", with all necessary packages pre-installed, located as a .venv folder in the same directory as openmidnight. It will automatically detect your machine's CUDA version and install packages appropriately.
|
| 36 |
-
|
| 37 |
-
Note: We have only personally verified training works as intended with this repository using H100 GPUs.
|
| 38 |
-
|
| 39 |
-
```bash
|
| 40 |
-
source .venv/bin/activate
|
| 41 |
-
wandb init
|
| 42 |
-
```
|
| 43 |
-
|
| 44 |
-
By default, we log model training to wandb. Run `wandb init` inside of `openmidnight/` before starting your training run so that wandb is properly configured.
|
| 45 |
-
|
| 46 |
-
You can now run one of our `run*.sh` scripts to train your model (see Training section below), using the YAML config specified in that script.
|
| 47 |
-
|
| 48 |
-
Once you have successfully completed model training (or have downloaded [our pretrained checkpoint](https://huggingface.co/SophontAI/OpenMidnight/blob/main/teacher_checkpoint.pth)), you can evaluate using [Kaiko.AI's eva framework](https://github.com/kaiko-ai/eva) and the [Mahmood Lab's HEST benchmark](https://github.com/mahmoodlab/HEST) (skip to the Evaluation section below).
|
| 49 |
-
|
| 50 |
-
# Training
|
| 51 |
-
|
| 52 |
-
We use `run*.sh` to initiate training runs via torchrun. Depending on your compute setting, modify and run the corresponding shell script described in the relevant subsection below.
|
| 53 |
-
|
| 54 |
-
We use YAML configs for specifying important hyperparameters during training. These files are located in `dinov2/configs/train/` (not in `eval_configs/`, which stores YAML configs for eva evaluation benchmarking). Our replication checkpoint specifically used `dinov2/configs/train/vitg14_reg4.yaml`.
|
| 55 |
-
|
| 56 |
-
There are some variables that are specified in `run*.sh` directly (as opposed to the YAML config), such as the output directory for saving checkpoints, whether to enable resume functionality, and the specific CUDA devices you want to train with.
|
| 57 |
-
|
| 58 |
-
If you are getting rate limited by huggingface, one easy method to increase your rate is to first `export HF_TOKEN=<your HF token here>` before running your code (https://huggingface.co/settings/tokens).
|
| 59 |
-
|
| 60 |
-
## Dataset prep
|
| 61 |
-
|
| 62 |
-
If you are wanting to exactly replicate our checkpoint, note that we did not train via streaming from huggingface. This feature was subsequently added and needs to be enabled in your YAML config (`train.streaming_from_hf=[True,False]`). Unless you enable this, you will need to first locally download the TCGA-12K dataset (~12TB) and then use the scripts provided in `prepatching_scripts/` to create a txt file containing the svs filepaths and locations/magnitude from which to create patches on-the-fly during model training. You can alternatively use [our original sample_dataset_30.txt file](https://huggingface.co/SophontAI/OpenMidnight/blob/main/sample_dataset_30.txt), but note you would need to modify that txt to correct its use of absolute filepaths.
|
| 63 |
-
|
| 64 |
-
## Training Single GPU (Short Config)
|
| 65 |
-
|
| 66 |
-
```bash
|
| 67 |
-
./run_ablation.sh
|
| 68 |
-
```
|
| 69 |
-
|
| 70 |
-
This does finetuning with a YAML config (`dinov2/configs/train/vits14_reg_ablations.yaml`) tweaked to support an informative, short training run on a single GPU that can be completed in under 24 hours. We hope this can be particularly useful for debugging and ablation experiments.
|
| 71 |
-
|
| 72 |
-
## Training Single Node, Multi‑GPU (Full Reproduction)
|
| 73 |
-
|
| 74 |
-
```bash
|
| 75 |
-
./run_1node.sh
|
| 76 |
-
```
|
| 77 |
-
|
| 78 |
-
Train across multiple GPUs on a single node. Our released checkpoint used this script for training.
|
| 79 |
-
|
| 80 |
-
## Training Multi‑Node (Full Reproduction)
|
| 81 |
-
|
| 82 |
-
Same as training single‑node multi‑GPU, except increase `NNODES` in both `run_master_node.sh` and `run_other_nodes.sh` to the number of total nodes you are training across. Then run the corresponding scripts.
|
| 83 |
-
|
| 84 |
-
```bash
|
| 85 |
-
./run_master_node.sh # on master node
|
| 86 |
-
```
|
| 87 |
-
```bash
|
| 88 |
-
./run_other_nodes.sh # on non-master nodes
|
| 89 |
-
```
|
| 90 |
-
|
| 91 |
-
If during training you get HTTP Error 429, try reducing the number of workers (set in the YAML config) and lowering the DataLoader's `prefetch_factor` (defined in `dinov2/train/train.py`). This error can happen when Hugging Face is being pinged too frequently during streaming. Another solution is to [download the data locally](https://huggingface.co/datasets/medarc/TCGA-12K-parquet) and replace `medarc/TCGA-12K-parquet` in `dinov2/train/train.py` with the full path to your locally downloaded dataset folder.
|
| 92 |
-
|
| 93 |
-
# Methods / Training Recipe
|
| 94 |
-
|
| 95 |
-
Below is a high‑level overview of our training recipe, with particular attention to deviations from the original DINOv2 paper. For additional context, refer to the [Midnight paper](https://arxiv.org/abs/2504.05186).
|
| 96 |
-
|
| 97 |
-
- Base model + init
|
| 98 |
-
- Student/teacher are ViT‑G/14 with 4 register tokens.
|
| 99 |
-
- We initialize the student backbone from Meta’s DINOv2 ViT‑G/14 register checkpoint via `torch.hub`.
|
| 100 |
-
- Heads are re‑initialized, as Meta only shared pretrained weights for their model backbone.
|
| 101 |
-
|
| 102 |
-
- Objectives and heads
|
| 103 |
-
- DINO self‑distillation on CLS tokens, with 131072 prototypes and a 384‑dim bottleneck head. iBOT masked patch prediction on global crops.
|
| 104 |
-
- DINOv2's KoLeo regularization is replaced by a KDE‑based entropy regularizer as done in the Midnight paper.
|
| 105 |
-
|
| 106 |
-
- Data and augmentations
|
| 107 |
-
- Streaming directly from a Parquet dataset of pre-patched TCGA slides hosted on Hugging Face (medarc/TCGA-12K-parquet). See `prepatching_scripts/` and read our [hf dataset card](https://huggingface.co/datasets/medarc/TCGA-12K-parquet/blob/main/README.md) for more information on pre-patching. We read `image_bytes`, decode to RGB, and apply DINO augmentations.
|
| 108 |
-
- H&E augmentation: before normalization, images are converted to HED space and perturbed, then converted back to RGB. See `DataAugmentationDINO` in `dinov2/data/augmentations.py`.
|
| 109 |
-
- Multi‑crop: 2 global crops (224) and multiple local crops (98). iBOT masks are sampled per‑image with ratios drawn uniformly from a min/max range.
|
| 110 |
-
|
| 111 |
-
- Optimization and schedules
|
| 112 |
-
- LR is scaled with sqrt(batch/1024) from a base LR of 2e‑4. Midnight paper originally used a base LR of 3.5e-4 but we observed that this led to training collapse.
|
| 113 |
-
- We train for 8000 “epochs” by schedule (1 epoch = 1250 steps), but early‑stop at 200 epochs. We found early stopping was necessary to prevent worsening downstream performance with longer model training.
|
| 114 |
-
|
| 115 |
-
- Checkpointing, evaluation, logging
|
| 116 |
-
- We save LOCAL_STATE_DICT FSDP checkpoints per rank and tag `last_checkpoint.rank_*` files (for resuming functionality).
|
| 117 |
-
- The teacher weights are exported every cfg.evaluation.eval_period_iterations steps to `output_dir/eval/training_<iter>/teacher_checkpoint.pth`.
|
| 118 |
-
- Weights & Biases logging is enabled with a persistent run id stored in the output directory to support resuming, in case model training gets interrupted.
|
| 119 |
-
|
| 120 |
-
# Downstream Evaluation
|
| 121 |
-
|
| 122 |
-
## eva Benchmarks
|
| 123 |
-
|
| 124 |
-
First ensure you have a checkpoint ready to be evaluated. Place your .pth file for your teacher checkpoint in the /checkpoints folder. You can download our pretrained checkpoint here: https://huggingface.co/SophontAI/OpenMidnight/blob/main/teacher_checkpoint.pth
|
| 125 |
-
|
| 126 |
-
Then, `cd` into the same `openmidnight` folder cloned from our Installation steps and clone our modified GitHub repo forked from the original [kaiko-eva](https://github.com/kaiko-ai/eva):
|
| 127 |
-
|
| 128 |
-
```bash
|
| 129 |
-
cd openmidnight
|
| 130 |
-
source .venv/bin/activate
|
| 131 |
-
git clone https://github.com/MedARC-AI/eva-probe
|
| 132 |
-
```
|
| 133 |
-
|
| 134 |
-
Then, install the eva framework to enable use of the `eva` command in your terminal (using `--no-deps` because the `openmidnight` virtual environment already contains the necessary packages):
|
| 135 |
-
|
| 136 |
-
```bash
|
| 137 |
-
uv pip install -e './eva-probe[vision]' --no-deps
|
| 138 |
-
```
|
| 139 |
-
|
| 140 |
-
We provide every YAML config file we used in our replication in `openmidnight/eval_configs/`. Not every dataset permits automatic downloading. For datasets like BACH that do, we automatically download the dataset when you specify `DOWNLOAD_DATA=true` when calling `eva predict_fit`. For other datasets, follow the manual download steps described in the [eva datasets documentation](https://kaiko-ai.github.io/eva/main/datasets/) and revise each YAML config to provide the path to your downloaded dataset prior to eva benchmarking.
|
| 141 |
-
|
| 142 |
-
Below are the steps for running the [BACH](https://kaiko-ai.github.io/eva/main/datasets/bach/) evaluation. If your teacher checkpoint is not stored as `openmidnight/checkpoints/teacher_epoch250000.pth`, you will first need to modify your eva YAML file's `checkpoint_path` variable to specify the path to your model weights.
|
| 143 |
-
|
| 144 |
-
```bash
|
| 145 |
-
cd eva-probe # should be located in openmidnight/eva-probe
|
| 146 |
-
CUDA_VISIBLE_DEVICES=0 DOWNLOAD_DATA=true eva predict_fit --config ../eval_configs/bach.yaml
|
| 147 |
-
```
|
| 148 |
-
|
| 149 |
-
All eva evaluations should be run on a single GPU by setting `CUDA_VISIBLE_DEVICES=0`. We observed inconsistent and worse results when trying to evaluate using multiple GPUs.
|
| 150 |
-
|
| 151 |
-
## HEST Benchmark
|
| 152 |
-
|
| 153 |
-
First ensure you have a checkpoint ready to be evaluated. Place the .pth file for your teacher model in the /checkpoints folder. You can download our pretrained checkpoint here: https://huggingface.co/SophontAI/OpenMidnight/blob/main/teacher_checkpoint.pth
|
| 154 |
-
|
| 155 |
-
Then, `cd` into the same `openmidnight` folder cloned from our Installation steps and clone the [Mahmood Lab's HEST GitHub repo](https://github.com/mahmoodlab/HEST):
|
| 156 |
-
|
| 157 |
-
```bash
|
| 158 |
-
cd openmidnight
|
| 159 |
-
source .venv/bin/activate
|
| 160 |
-
git clone https://github.com/mahmoodlab/HEST.git
|
| 161 |
-
cd HEST
|
| 162 |
-
git checkout afd42c3143092c51e6bcc0f1df65bbf58a467e5e
|
| 163 |
-
cd .. # cd back to openmidnight/ for subsequent install steps
|
| 164 |
-
```
|
| 165 |
-
|
| 166 |
-
Then install HEST framework so that we can import invoke their benchmark function (using `--no-deps` because the `openmidnight` virtual environment already contains the necessary packages).
|
| 167 |
-
|
| 168 |
-
```bash
|
| 169 |
-
uv pip install -e ./HEST --no-deps
|
| 170 |
-
```
|
| 171 |
-
|
| 172 |
-
Now uncomment out specific lines in the HEST YAML config to enable PCA dimensionality reduction, benchmark across all HEST datasets used in the original paper, and solely benchmark our DINOv2 model as the encoder:
|
| 173 |
-
|
| 174 |
-
```bash
|
| 175 |
-
sed -i -E '/^datasets:/,/^\]/{s/^([[:space:]]*)#([[:space:]]*)"([^"]+)",/\1"\3",/;s/^([[:space:]]*)"HCC"(,?)/\1# "HCC"\2/}; s/^[[:space:]]*#([[:space:]]*dimreduce:[[:space:]]*".*")/\1/; /^encoders:/,/^\]/{s/^([[:space:]]*)"resnet50"(,?)/\1# "resnet50"\2/}' ./HEST/bench_config/bench_config.yaml
|
| 176 |
-
```
|
| 177 |
-
|
| 178 |
-
Then finally run our HEST_evaluation.py script to benchmark your checkpoint. Running HEST_evaluation.py will automatically download the necessary preprocessed patches and patch encoders and output results into a new `openmidnight/eval` folder (specifically, the benchmark results dump to the `ST_pred_results/` subfolder).
|
| 179 |
-
|
| 180 |
-
```bash
|
| 181 |
-
python HEST_evaluation.py
|
| 182 |
-
```
|
| 183 |
-
|
| 184 |
-
## Related Work / Citation
|
| 185 |
-
|
| 186 |
-
This repository adapts and extends Meta AI's DINOv2 codebase and follows modifications introduced by Kaiko's Midnight work. If you use this repository or models in academic work, please cite their and our work:
|
| 187 |
-
|
| 188 |
-
Kaplan, D., Grandhi, R. S., Lane, C., Warner, B., Abraham, T. M., & Scotti, P. S. (2025). How to train a state-of-the-art pathology foundation model with $1.6k. Sophont. https://sophont.med/blog/openmidnight
|
| 189 |
-
|
| 190 |
-
```
|
| 191 |
-
@article{kaplan2025openmidnight,
|
| 192 |
-
author = {Kaplan, Daniel and Grandhi, Ratna Sagari and Lane, Connor and Warner, Benjamin and Abraham, Tanishq Mathew and Scotti, Paul S.},
|
| 193 |
-
title = {How to Train a State-of-the-Art Pathology Foundation Model with \$1.6k},
|
| 194 |
-
year = {2025},
|
| 195 |
-
url = {https://sophont.med/blog/openmidnight},
|
| 196 |
-
}
|
| 197 |
-
```
|
| 198 |
-
|
| 199 |
-
Karasikov, M., van Doorn, J., Känzig, N., Erdal Cesur, M., Horlings, H. M., Berke, R., ... & Otálora, S. (2025). Training state-of-the-art pathology foundation models with orders of magnitude less data. In International Conference on Medical Image Computing and Computer-Assisted Intervention (pp. 573-583). Cham: Springer Nature Switzerland.
|
| 200 |
-
|
| 201 |
-
```
|
| 202 |
-
@inproceedings{karasikov2025training,
|
| 203 |
-
title={Training state-of-the-art pathology foundation models with orders of magnitude less data},
|
| 204 |
-
author={Karasikov, Mikhail and van Doorn, Joost and K{\"a}nzig, Nicolas and Erdal Cesur, Melis and Horlings, Hugo Mark and Berke, Robert and Tang, Fei and Ot{\'a}lora, Sebastian},
|
| 205 |
-
booktitle={International Conference on Medical Image Computing and Computer-Assisted Intervention},
|
| 206 |
-
pages={573—583},
|
| 207 |
-
year={2025},
|
| 208 |
-
organization={Springer}
|
| 209 |
-
}
|
| 210 |
-
```
|
| 211 |
-
|
| 212 |
-
Oquab, M., Darcet, T., Moutakanni, T., Vo, H., Szafraniec, M., Khalidov, V., ... & Bojanowski, P. (2023). DINOv2: Learning robust visual features without supervision. arXiv preprint arXiv:2304.07193.
|
| 213 |
-
|
| 214 |
-
```
|
| 215 |
-
@article{oquab2023dinov2,
|
| 216 |
-
title={DINOv2: Learning Robust Visual Features without Supervision},
|
| 217 |
-
author={Maxime Oquab and Timothée Darcet and Théo Moutakanni and Huy Vo and Marc Szafraniec and Vasil Khalidov and Pierre Fernandez and Daniel Haziza and Francisco Massa and Alaaeldin El-Nouby and Mahmoud Assran and Nicolas Ballas and Wojciech Galuba and Russell Howes and Po-Yao Huang and Shang-Wen Li and Ishan Misra and Michael Rabbat and Vasu Sharma and Gabriel Synnaeve and Hu Xu and Hervé Jegou and Julien Mairal and Patrick Labatut and Armand Joulin and Piotr Bojanowski},
|
| 218 |
-
year={2024},
|
| 219 |
-
eprint={2304.07193},
|
| 220 |
-
archivePrefix={arXiv},
|
| 221 |
-
primaryClass={cs.CV},
|
| 222 |
-
url={https://arxiv.org/abs/2304.07193},
|
| 223 |
-
}
|
| 224 |
-
```
|
| 225 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/_utils.py
DELETED
|
@@ -1,166 +0,0 @@
|
|
| 1 |
-
"""Utilities and helper functions for models."""
|
| 2 |
-
|
| 3 |
-
#/home/daniel/pathologyDino/dino_env/lib/python3.11/site-packages/eva/core/models/wrappers
|
| 4 |
-
|
| 5 |
-
import hashlib
|
| 6 |
-
import os
|
| 7 |
-
import sys
|
| 8 |
-
from typing import Any, Dict
|
| 9 |
-
|
| 10 |
-
import torch
|
| 11 |
-
from fsspec.core import url_to_fs
|
| 12 |
-
from lightning_fabric.utilities import cloud_io
|
| 13 |
-
from loguru import logger
|
| 14 |
-
from torch import hub, nn
|
| 15 |
-
|
| 16 |
-
from eva.core.utils.progress_bar import tqdm
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
def load_model_weights(model: nn.Module, checkpoint_path: str) -> None:
|
| 20 |
-
"""Loads (local or remote) weights to the model in-place.
|
| 21 |
-
|
| 22 |
-
Args:
|
| 23 |
-
model: The model to load the weights to.
|
| 24 |
-
checkpoint_path: The path to the model weights/checkpoint.
|
| 25 |
-
"""
|
| 26 |
-
logger.info(f"Loading '{model.__class__.__name__}' model from checkpoint '{checkpoint_path}'")
|
| 27 |
-
|
| 28 |
-
print("tstingi")
|
| 29 |
-
print(model.state_dict().keys())
|
| 30 |
-
fs = cloud_io.get_filesystem(checkpoint_path)
|
| 31 |
-
with fs.open(checkpoint_path, "rb") as file:
|
| 32 |
-
checkpoint = cloud_io._load(file, map_location="cpu") # type: ignore
|
| 33 |
-
if isinstance(checkpoint, dict) and "state_dict" in checkpoint:
|
| 34 |
-
checkpoint = checkpoint["state_dict"]
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
if "teacher" in checkpoint:
|
| 38 |
-
checkpoint = checkpoint["teacher"]
|
| 39 |
-
#Need to remove the word backbone from everything I think?
|
| 40 |
-
checkpoint_new = {}
|
| 41 |
-
for key in list(checkpoint.keys()):
|
| 42 |
-
if "dino" in str(key) or "ibot" in str(key):
|
| 43 |
-
checkpoint.pop(key, None)
|
| 44 |
-
for key, keyb in zip(checkpoint.keys(), model.state_dict().keys()):
|
| 45 |
-
checkpoint_new[keyb] = checkpoint[key]
|
| 46 |
-
|
| 47 |
-
checkpoint = checkpoint_new
|
| 48 |
-
#The pos embed is the only different one, idk why
|
| 49 |
-
new_shape = checkpoint["pos_embed"]
|
| 50 |
-
model.pos_embed = torch.nn.parameter.Parameter(new_shape)
|
| 51 |
-
|
| 52 |
-
model.load_state_dict(checkpoint, strict=True)
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
logger.info(f"Loading weights from '{checkpoint_path}' completed successfully.")
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
def load_state_dict_from_url(
|
| 59 |
-
url: str,
|
| 60 |
-
*,
|
| 61 |
-
model_dir: str | None = None,
|
| 62 |
-
filename: str | None = None,
|
| 63 |
-
progress: bool = True,
|
| 64 |
-
md5: str | None = None,
|
| 65 |
-
force: bool = False,
|
| 66 |
-
) -> Dict[str, Any]:
|
| 67 |
-
"""Loads the Torch serialized object at the given URL.
|
| 68 |
-
|
| 69 |
-
If the object is already present and valid in `model_dir`, it's
|
| 70 |
-
deserialized and returned.
|
| 71 |
-
|
| 72 |
-
The default value of ``model_dir`` is ``<hub_dir>/checkpoints`` where
|
| 73 |
-
``hub_dir`` is the directory returned by :func:`~torch.hub.get_dir`.
|
| 74 |
-
|
| 75 |
-
Args:
|
| 76 |
-
url: URL of the object to download.
|
| 77 |
-
model_dir: Directory in which to save the object.
|
| 78 |
-
filename: Name for the downloaded file. Filename from ``url`` will be used if not set.
|
| 79 |
-
progress: Whether or not to display a progress bar to stderr.
|
| 80 |
-
md5: MD5 file code to check whether the file is valid. If not, it will re-download it.
|
| 81 |
-
force: Whether to download the file regardless if it exists.
|
| 82 |
-
"""
|
| 83 |
-
model_dir = model_dir or os.path.join(hub.get_dir(), "checkpoints")
|
| 84 |
-
os.makedirs(model_dir, exist_ok=True)
|
| 85 |
-
|
| 86 |
-
cached_file = os.path.join(model_dir, filename or os.path.basename(url))
|
| 87 |
-
if force or not os.path.exists(cached_file) or not _check_integrity(cached_file, md5):
|
| 88 |
-
sys.stderr.write(f"Downloading: '{url}' to {cached_file}\n")
|
| 89 |
-
_download_url_to_file(url, cached_file, progress=progress)
|
| 90 |
-
if md5 is None or not _check_integrity(cached_file, md5):
|
| 91 |
-
sys.stderr.write(f"File MD5: {_calculate_md5(cached_file)}\n")
|
| 92 |
-
|
| 93 |
-
return torch.load(cached_file, map_location="cpu")
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
def _download_url_to_file(
|
| 97 |
-
url: str,
|
| 98 |
-
dst: str,
|
| 99 |
-
*,
|
| 100 |
-
progress: bool = True,
|
| 101 |
-
) -> None:
|
| 102 |
-
"""Download object at the given URL to a local path.
|
| 103 |
-
|
| 104 |
-
Args:
|
| 105 |
-
url: URL of the object to download.
|
| 106 |
-
dst: Full path where object will be saved.
|
| 107 |
-
chunk_size: The size of each chunk to read in bytes.
|
| 108 |
-
progress: Whether or not to display a progress bar to stderr.
|
| 109 |
-
"""
|
| 110 |
-
try:
|
| 111 |
-
_download_with_fsspec(url=url, dst=dst, progress=progress)
|
| 112 |
-
except Exception:
|
| 113 |
-
try:
|
| 114 |
-
hub.download_url_to_file(url=url, dst=dst, progress=progress)
|
| 115 |
-
except Exception as hub_e:
|
| 116 |
-
raise RuntimeError(
|
| 117 |
-
f"Failed to download file from {url} using both fsspec and hub."
|
| 118 |
-
) from hub_e
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
def _download_with_fsspec(
|
| 122 |
-
url: str,
|
| 123 |
-
dst: str,
|
| 124 |
-
*,
|
| 125 |
-
chunk_size: int = 1024 * 1024,
|
| 126 |
-
progress: bool = True,
|
| 127 |
-
) -> None:
|
| 128 |
-
"""Download object at the given URL to a local path using fsspec.
|
| 129 |
-
|
| 130 |
-
Args:
|
| 131 |
-
url: URL of the object to download.
|
| 132 |
-
dst: Full path where object will be saved.
|
| 133 |
-
chunk_size: The size of each chunk to read in bytes.
|
| 134 |
-
progress: Whether or not to display a progress bar to stderr.
|
| 135 |
-
"""
|
| 136 |
-
filesystem, _ = url_to_fs(url, anon=False)
|
| 137 |
-
total_size_bytes = filesystem.size(url)
|
| 138 |
-
with (
|
| 139 |
-
filesystem.open(url, "rb") as remote_file,
|
| 140 |
-
tqdm(
|
| 141 |
-
total=total_size_bytes,
|
| 142 |
-
unit="iB",
|
| 143 |
-
unit_scale=True,
|
| 144 |
-
unit_divisor=1024,
|
| 145 |
-
disable=not progress,
|
| 146 |
-
) as pbar,
|
| 147 |
-
):
|
| 148 |
-
with open(dst, "wb") as local_file:
|
| 149 |
-
while True:
|
| 150 |
-
data = remote_file.read(chunk_size)
|
| 151 |
-
if not data:
|
| 152 |
-
break
|
| 153 |
-
|
| 154 |
-
local_file.write(data)
|
| 155 |
-
pbar.update(chunk_size)
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
def _calculate_md5(path: str) -> str:
|
| 159 |
-
"""Calculate the md5 hash of a file."""
|
| 160 |
-
with open(path, "rb") as file:
|
| 161 |
-
return hashlib.md5(file.read(), usedforsecurity=False).hexdigest()
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
def _check_integrity(path: str, md5: str | None) -> bool:
|
| 165 |
-
"""Check if the file matches the specified md5 hash."""
|
| 166 |
-
return (md5 is None) or (md5 == _calculate_md5(path))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/__init__.py
DELETED
|
@@ -1,6 +0,0 @@
|
|
| 1 |
-
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
-
#
|
| 3 |
-
# This source code is licensed under the Apache License, Version 2.0
|
| 4 |
-
# found in the LICENSE file in the root directory of this source tree.
|
| 5 |
-
|
| 6 |
-
__version__ = "0.0.1"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/__init__.py
DELETED
|
@@ -1,22 +0,0 @@
|
|
| 1 |
-
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
-
#
|
| 3 |
-
# This source code is licensed under the Apache License, Version 2.0
|
| 4 |
-
# found in the LICENSE file in the root directory of this source tree.
|
| 5 |
-
|
| 6 |
-
import pathlib
|
| 7 |
-
|
| 8 |
-
from omegaconf import OmegaConf
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
def load_config(config_name: str):
|
| 12 |
-
config_filename = config_name + ".yaml"
|
| 13 |
-
return OmegaConf.load(pathlib.Path(__file__).parent.resolve() / config_filename)
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
dinov2_default_config = load_config("ssl_default_config")
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
def load_and_merge_config(config_name: str):
|
| 20 |
-
default_config = OmegaConf.create(dinov2_default_config)
|
| 21 |
-
loaded_config = load_config(config_name)
|
| 22 |
-
return OmegaConf.merge(default_config, loaded_config)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/eval/vitb14_pretrain.yaml
DELETED
|
@@ -1,6 +0,0 @@
|
|
| 1 |
-
student:
|
| 2 |
-
arch: vit_base
|
| 3 |
-
patch_size: 14
|
| 4 |
-
crops:
|
| 5 |
-
global_crops_size: 518 # this is to set up the position embeddings properly
|
| 6 |
-
local_crops_size: 98
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/eval/vitb14_reg4_pretrain.yaml
DELETED
|
@@ -1,9 +0,0 @@
|
|
| 1 |
-
student:
|
| 2 |
-
arch: vit_base
|
| 3 |
-
patch_size: 14
|
| 4 |
-
num_register_tokens: 4
|
| 5 |
-
interpolate_antialias: true
|
| 6 |
-
interpolate_offset: 0.0
|
| 7 |
-
crops:
|
| 8 |
-
global_crops_size: 518 # this is to set up the position embeddings properly
|
| 9 |
-
local_crops_size: 98
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/eval/vitg14_pretrain.yaml
DELETED
|
@@ -1,7 +0,0 @@
|
|
| 1 |
-
student:
|
| 2 |
-
arch: vit_giant2
|
| 3 |
-
patch_size: 14
|
| 4 |
-
ffn_layer: swiglufused
|
| 5 |
-
crops:
|
| 6 |
-
global_crops_size: 518 # this is to set up the position embeddings properly
|
| 7 |
-
local_crops_size: 98
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/eval/vitg14_reg4_pretrain.yaml
DELETED
|
@@ -1,10 +0,0 @@
|
|
| 1 |
-
student:
|
| 2 |
-
arch: vit_giant2
|
| 3 |
-
patch_size: 14
|
| 4 |
-
ffn_layer: swiglufused
|
| 5 |
-
num_register_tokens: 4
|
| 6 |
-
interpolate_antialias: true
|
| 7 |
-
interpolate_offset: 0.0
|
| 8 |
-
crops:
|
| 9 |
-
global_crops_size: 518 # this is to set up the position embeddings properly
|
| 10 |
-
local_crops_size: 98
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/eval/vitl14_pretrain.yaml
DELETED
|
@@ -1,6 +0,0 @@
|
|
| 1 |
-
student:
|
| 2 |
-
arch: vit_large
|
| 3 |
-
patch_size: 14
|
| 4 |
-
crops:
|
| 5 |
-
global_crops_size: 518 # this is to set up the position embeddings properly
|
| 6 |
-
local_crops_size: 98
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/eval/vitl14_reg4_pretrain.yaml
DELETED
|
@@ -1,9 +0,0 @@
|
|
| 1 |
-
student:
|
| 2 |
-
arch: vit_large
|
| 3 |
-
patch_size: 14
|
| 4 |
-
num_register_tokens: 4
|
| 5 |
-
interpolate_antialias: true
|
| 6 |
-
interpolate_offset: 0.0
|
| 7 |
-
crops:
|
| 8 |
-
global_crops_size: 518 # this is to set up the position embeddings properly
|
| 9 |
-
local_crops_size: 98
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/eval/vits14_pretrain.yaml
DELETED
|
@@ -1,6 +0,0 @@
|
|
| 1 |
-
student:
|
| 2 |
-
arch: vit_small
|
| 3 |
-
patch_size: 14
|
| 4 |
-
crops:
|
| 5 |
-
global_crops_size: 518 # this is to set up the position embeddings properly
|
| 6 |
-
local_crops_size: 98
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/eval/vits14_reg4_pretrain.yaml
DELETED
|
@@ -1,9 +0,0 @@
|
|
| 1 |
-
student:
|
| 2 |
-
arch: vit_small
|
| 3 |
-
patch_size: 14
|
| 4 |
-
num_register_tokens: 4
|
| 5 |
-
interpolate_antialias: true
|
| 6 |
-
interpolate_offset: 0.0
|
| 7 |
-
crops:
|
| 8 |
-
global_crops_size: 518 # this is to set up the position embeddings properly
|
| 9 |
-
local_crops_size: 98
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/ssl_default_config.yaml
DELETED
|
@@ -1,129 +0,0 @@
|
|
| 1 |
-
MODEL:
|
| 2 |
-
WEIGHTS: ''
|
| 3 |
-
compute_precision:
|
| 4 |
-
grad_scaler: true
|
| 5 |
-
teacher:
|
| 6 |
-
backbone:
|
| 7 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 8 |
-
mixed_precision:
|
| 9 |
-
param_dtype: fp16
|
| 10 |
-
reduce_dtype: fp16
|
| 11 |
-
buffer_dtype: fp32
|
| 12 |
-
dino_head:
|
| 13 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 14 |
-
mixed_precision:
|
| 15 |
-
param_dtype: fp16
|
| 16 |
-
reduce_dtype: fp16
|
| 17 |
-
buffer_dtype: fp32
|
| 18 |
-
ibot_head:
|
| 19 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 20 |
-
mixed_precision:
|
| 21 |
-
param_dtype: fp16
|
| 22 |
-
reduce_dtype: fp16
|
| 23 |
-
buffer_dtype: fp32
|
| 24 |
-
student:
|
| 25 |
-
backbone:
|
| 26 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 27 |
-
mixed_precision:
|
| 28 |
-
param_dtype: fp16
|
| 29 |
-
reduce_dtype: fp16
|
| 30 |
-
buffer_dtype: fp32
|
| 31 |
-
dino_head:
|
| 32 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 33 |
-
mixed_precision:
|
| 34 |
-
param_dtype: fp16
|
| 35 |
-
reduce_dtype: fp32
|
| 36 |
-
buffer_dtype: fp32
|
| 37 |
-
ibot_head:
|
| 38 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 39 |
-
mixed_precision:
|
| 40 |
-
param_dtype: fp16
|
| 41 |
-
reduce_dtype: fp32
|
| 42 |
-
buffer_dtype: fp32
|
| 43 |
-
dino:
|
| 44 |
-
loss_weight: 1.0
|
| 45 |
-
head_n_prototypes: 65536
|
| 46 |
-
head_bottleneck_dim: 256
|
| 47 |
-
head_nlayers: 3
|
| 48 |
-
head_hidden_dim: 2048
|
| 49 |
-
koleo_loss_weight: 0.1
|
| 50 |
-
ibot:
|
| 51 |
-
loss_weight: 1.0
|
| 52 |
-
mask_sample_probability: 0.5
|
| 53 |
-
mask_ratio_min_max:
|
| 54 |
-
- 0.1
|
| 55 |
-
- 0.5
|
| 56 |
-
separate_head: false
|
| 57 |
-
head_n_prototypes: 65536
|
| 58 |
-
head_bottleneck_dim: 256
|
| 59 |
-
head_nlayers: 3
|
| 60 |
-
head_hidden_dim: 2048
|
| 61 |
-
train:
|
| 62 |
-
batch_size_per_gpu: 64
|
| 63 |
-
dataset_path: ImageNet:split=TRAIN
|
| 64 |
-
output_dir: .
|
| 65 |
-
saveckp_freq: 20
|
| 66 |
-
skip_checkpointer: null # if null, auto-skip when running on a single GPU
|
| 67 |
-
seed: 0
|
| 68 |
-
num_workers: 10
|
| 69 |
-
OFFICIAL_EPOCH_LENGTH: 1250
|
| 70 |
-
cache_dataset: true
|
| 71 |
-
centering: "centering" # or "sinkhorn_knopp"
|
| 72 |
-
unfreeze_last_n_blocks: 40
|
| 73 |
-
student:
|
| 74 |
-
arch: vit_large
|
| 75 |
-
patch_size: 16
|
| 76 |
-
drop_path_rate: 0.3
|
| 77 |
-
layerscale: 1.0e-05
|
| 78 |
-
drop_path_uniform: true
|
| 79 |
-
pretrained_weights: ''
|
| 80 |
-
ffn_layer: "mlp"
|
| 81 |
-
block_chunks: 0
|
| 82 |
-
qkv_bias: true
|
| 83 |
-
proj_bias: true
|
| 84 |
-
ffn_bias: true
|
| 85 |
-
num_register_tokens: 0
|
| 86 |
-
interpolate_antialias: false
|
| 87 |
-
interpolate_offset: 0.1
|
| 88 |
-
teacher:
|
| 89 |
-
momentum_teacher: 0.992
|
| 90 |
-
final_momentum_teacher: 1
|
| 91 |
-
warmup_teacher_temp: 0.04
|
| 92 |
-
teacher_temp: 0.07
|
| 93 |
-
warmup_teacher_temp_epochs: 30
|
| 94 |
-
optim:
|
| 95 |
-
epochs: 100
|
| 96 |
-
weight_decay: 0.04
|
| 97 |
-
weight_decay_end: 0.4
|
| 98 |
-
base_lr: 0.004 # learning rate for a batch size of 1024
|
| 99 |
-
lr: 0. # will be set after applying scaling rule
|
| 100 |
-
warmup_epochs: 10
|
| 101 |
-
min_lr: 1.0e-06
|
| 102 |
-
clip_grad: 3.0
|
| 103 |
-
freeze_last_layer_epochs: 1
|
| 104 |
-
scaling_rule: sqrt_wrt_1024
|
| 105 |
-
patch_embed_lr_mult: 0.2
|
| 106 |
-
layerwise_decay: 0.9
|
| 107 |
-
adamw_beta1: 0.9
|
| 108 |
-
adamw_beta2: 0.999
|
| 109 |
-
crops:
|
| 110 |
-
global_crops_scale:
|
| 111 |
-
- 0.32
|
| 112 |
-
- 1.0
|
| 113 |
-
local_crops_number: 8
|
| 114 |
-
local_crops_scale:
|
| 115 |
-
- 0.05
|
| 116 |
-
- 0.32
|
| 117 |
-
global_crops_size: 224
|
| 118 |
-
local_crops_size: 96
|
| 119 |
-
evaluation:
|
| 120 |
-
eval_period_iterations: 12500
|
| 121 |
-
bach_root: /data/eva-data/bach
|
| 122 |
-
gram:
|
| 123 |
-
use_loss: false
|
| 124 |
-
normalized: true
|
| 125 |
-
remove_neg: true
|
| 126 |
-
loss_weight: 1.0
|
| 127 |
-
it_first_update: 34000
|
| 128 |
-
ramp_iters: 5000
|
| 129 |
-
ckpt: ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/train/openpath_vitg14.yaml
DELETED
|
@@ -1,95 +0,0 @@
|
|
| 1 |
-
# OpenPath — ViT-g/14 pathology FM pre-training config.
|
| 2 |
-
# DINOv2 (DINO + iBOT + KDE) + gram anchoring, warm-started from Meta DINOv2 ViT-g/14-reg,
|
| 3 |
-
# on the public OpenPath corpus (native 40x tiles). Global batch = 64/GPU x 40 GPU = 2560.
|
| 4 |
-
#
|
| 5 |
-
# LR regime: near-constant (flat). `epochs` only sets the schedule horizon (very large), so the
|
| 6 |
-
# cosine barely descends; the run actually stops at `early_stop`. This keeps LR/wd/momentum
|
| 7 |
-
# near their initial values throughout training.
|
| 8 |
-
compute_precision:
|
| 9 |
-
grad_scaler: false
|
| 10 |
-
teacher:
|
| 11 |
-
backbone:
|
| 12 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 13 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 14 |
-
dino_head:
|
| 15 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 16 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 17 |
-
ibot_head:
|
| 18 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 19 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 20 |
-
student:
|
| 21 |
-
backbone:
|
| 22 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 23 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 24 |
-
dino_head:
|
| 25 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 26 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: fp32, buffer_dtype: fp32}
|
| 27 |
-
ibot_head:
|
| 28 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 29 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: fp32, buffer_dtype: fp32}
|
| 30 |
-
dino:
|
| 31 |
-
head_n_prototypes: 131072
|
| 32 |
-
head_bottleneck_dim: 384
|
| 33 |
-
do_kde: True
|
| 34 |
-
kde_loss_weight: .05
|
| 35 |
-
koleo_loss_weight: 0
|
| 36 |
-
do_koleo: False
|
| 37 |
-
ibot:
|
| 38 |
-
loss_weight: 1.0
|
| 39 |
-
mask_sample_probability: 0.5
|
| 40 |
-
mask_ratio_min_max:
|
| 41 |
-
- 0.1
|
| 42 |
-
- 0.45
|
| 43 |
-
separate_head: true
|
| 44 |
-
head_n_prototypes: 131072
|
| 45 |
-
train:
|
| 46 |
-
# OpenPath corpus (taejoon89/openpath-corpus). Point to the downloaded shards:
|
| 47 |
-
sample_list_path: "openpath:glob=/path/to/openpath-corpus/*/tiles/shards/w*/*.tar"
|
| 48 |
-
streaming_from_hf: false
|
| 49 |
-
batch_size_per_gpu: 64 # x 40 GPU = global 2560
|
| 50 |
-
centering: sinkhorn_knopp
|
| 51 |
-
use_pretrained: True # Meta DINOv2 ViT-g/14-reg warm-start (set MODEL.WEIGHTS)
|
| 52 |
-
OFFICIAL_EPOCH_LENGTH: 1250
|
| 53 |
-
saveckp_freq: 8 # resume ckpt every 8 x 1250 = 10,000 iters
|
| 54 |
-
num_workers: 10
|
| 55 |
-
prefetch_factor: 4
|
| 56 |
-
skip_checkpointer: false
|
| 57 |
-
student:
|
| 58 |
-
arch: vit_giant2
|
| 59 |
-
patch_size: 14
|
| 60 |
-
drop_path_rate: 0.4
|
| 61 |
-
ffn_layer: swiglufused
|
| 62 |
-
block_chunks: 4
|
| 63 |
-
num_register_tokens: 4
|
| 64 |
-
interpolate_antialias: true
|
| 65 |
-
interpolate_offset: 0.0
|
| 66 |
-
teacher:
|
| 67 |
-
momentum_teacher: 0.994
|
| 68 |
-
warmup_teacher_temp_epochs: 41 # 0.27 epoch = 51,250 iters
|
| 69 |
-
optim:
|
| 70 |
-
epochs: 8000 # schedule horizon only (=10,000,000 iter); LR/wd/momentum near-constant
|
| 71 |
-
early_stop: 276 # actual stop: 276 x 1250 = 345,000 iters (~1 native epoch)
|
| 72 |
-
weight_decay_end: 0.2 # not reached within early_stop (wd stays ~0.040)
|
| 73 |
-
base_lr: 2.0e-04 # sqrt_wrt_1024 -> effective ~3.16e-4 @ global 2560
|
| 74 |
-
warmup_epochs: 9 # ~11,250 iter (~4.9%); must be an integer
|
| 75 |
-
freeze_last_layer_epochs: 1
|
| 76 |
-
layerwise_decay: 1.0
|
| 77 |
-
crops:
|
| 78 |
-
local_crops_size: 98
|
| 79 |
-
evaluation:
|
| 80 |
-
eval_period_iterations: 5750 # save a teacher checkpoint every 5,750 iters
|
| 81 |
-
bach_root: ""
|
| 82 |
-
breakhis_root: ""
|
| 83 |
-
pcam_root: ""
|
| 84 |
-
|
| 85 |
-
# Gram anchoring (ported from DINOv3): MSE between L2-normalized patch-token Gram matrices of
|
| 86 |
-
# student vs a frozen anchor. Dampens dense-feature degradation during long training.
|
| 87 |
-
gram:
|
| 88 |
-
use_loss: true
|
| 89 |
-
normalized: true
|
| 90 |
-
remove_neg: true
|
| 91 |
-
loss_weight: 40.0 # ~5-8% of total loss; lower if it over-constrains DINO/iBOT
|
| 92 |
-
it_first_update: 57500 # activate near the dense-feature peak
|
| 93 |
-
ramp_iters: 3000 # linear ramp of the gram weight
|
| 94 |
-
# Anchor = a strong earlier OpenPath teacher checkpoint (see taejoon89/openpath-checkpoints):
|
| 95 |
-
ckpt: /path/to/openpath-checkpoints/training_63250/teacher_checkpoint.pth
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/train/openpath_vitg14_bs64_test.yaml
DELETED
|
@@ -1,84 +0,0 @@
|
|
| 1 |
-
# bs=64 대용량 batch 검증 — full+CPTAC(582.0M) ViT-g/14, 48 GPU, bs=64/GPU(global 3072).
|
| 2 |
-
# ★ 공정비교: batch 8배↑ → steps 1/8↓ = 같은 데이터노출. bs=8 full+cptac 137.5k스케줄을 통째로 1/8 축소.
|
| 3 |
-
# = OFFICIAL_EPOCH_LENGTH 1250→156(=1250/8), 나머지 epoch-단위 파라미터는 bs=8 config와 동일.
|
| 4 |
-
# → 데이터·스케줄위치 완벽 1/8 복제. LR만 sqrt스케일로 자동상향(1.84e-4→5.20e-4).
|
| 5 |
-
# 판정: early_stop 80(=12,480 iters ≈ bs8의 100k/8, 동일 38.4M samples)서 정지 → HEST probe.
|
| 6 |
-
# bs=8 full+cptac 100k=0.3753(full 100k=0.3834)와 비교. 유지되면 bs=64로 1epoch 최종학습.
|
| 7 |
-
# ★ fp16→bf16 전환(아래 compute_precision): 1차 bs=64 런이 iter~8695서 fp16 오버플로로 NaN 크래시
|
| 8 |
-
# (loss는 7.9서 평탄·안정하다 단발 NaN=고LR발산 아님). bf16은 fp32 표현범위라 오버플로 원천차단.
|
| 9 |
-
# bf16이므로 grad_scaler off(loss scaling 불필요). LR·batch·스케줄은 1차와 동일(bf16만 변경).
|
| 10 |
-
compute_precision:
|
| 11 |
-
grad_scaler: false # bf16은 loss scaling 불필요 → off
|
| 12 |
-
teacher:
|
| 13 |
-
backbone:
|
| 14 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 15 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 16 |
-
dino_head:
|
| 17 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 18 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 19 |
-
ibot_head:
|
| 20 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 21 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 22 |
-
student:
|
| 23 |
-
backbone:
|
| 24 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 25 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 26 |
-
dino_head:
|
| 27 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 28 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: fp32, buffer_dtype: fp32}
|
| 29 |
-
ibot_head:
|
| 30 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 31 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: fp32, buffer_dtype: fp32}
|
| 32 |
-
dino:
|
| 33 |
-
head_n_prototypes: 131072
|
| 34 |
-
head_bottleneck_dim: 384
|
| 35 |
-
do_kde: True
|
| 36 |
-
kde_loss_weight: .05
|
| 37 |
-
koleo_loss_weight: 0
|
| 38 |
-
do_koleo: False
|
| 39 |
-
ibot:
|
| 40 |
-
loss_weight: 1.0
|
| 41 |
-
mask_sample_probability: 0.5
|
| 42 |
-
mask_ratio_min_max:
|
| 43 |
-
- 0.1
|
| 44 |
-
- 0.45
|
| 45 |
-
separate_head: true
|
| 46 |
-
head_n_prototypes: 131072
|
| 47 |
-
train:
|
| 48 |
-
sample_list_path: "openpath:glob=/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data/tiles/shards/w*/*.tar,/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data_cptac/tiles/shards/w*/*.tar"
|
| 49 |
-
streaming_from_hf: false
|
| 50 |
-
batch_size_per_gpu: 64 # × 48 GPU = global 3072 (bs=8의 8배)
|
| 51 |
-
centering: sinkhorn_knopp
|
| 52 |
-
use_pretrained: True # ★ Meta DINOv2 ViT-g/14-reg warm-start
|
| 53 |
-
OFFICIAL_EPOCH_LENGTH: 156 # = 1250/8 → 전 스케줄 1/8 축소(데이터노출 동일)
|
| 54 |
-
saveckp_freq: 16 # resume ckpt = 16×156 = 2,496 iters
|
| 55 |
-
num_workers: 10
|
| 56 |
-
prefetch_factor: 4
|
| 57 |
-
skip_checkpointer: false
|
| 58 |
-
student:
|
| 59 |
-
arch: vit_giant2
|
| 60 |
-
patch_size: 14
|
| 61 |
-
drop_path_rate: 0.4
|
| 62 |
-
ffn_layer: swiglufused
|
| 63 |
-
block_chunks: 4
|
| 64 |
-
num_register_tokens: 4
|
| 65 |
-
interpolate_antialias: true
|
| 66 |
-
interpolate_offset: 0.0
|
| 67 |
-
teacher:
|
| 68 |
-
momentum_teacher: 0.994
|
| 69 |
-
warmup_teacher_temp_epochs: 30 # = bs=8 동일(30×156=4,680 iters = 37,500/8)
|
| 70 |
-
optim:
|
| 71 |
-
epochs: 110 # × 156 = 17,160 iters (≈137.5k/8) — schedule 지평
|
| 72 |
-
early_stop: 80 # ★ 12,480 iters서 정지(=100k/8, 동일 38.4M samples) → probe
|
| 73 |
-
weight_decay_end: 0.2
|
| 74 |
-
base_lr: 3.0e-04 # 원래값 유지(LR 안 건드림). NaN 원인은 LR 아닌 fp16 오버플로였음 → bf16으로 해결
|
| 75 |
-
warmup_epochs: 10 # = bs=8 동일(10×156=1,560 = 12,500/8)
|
| 76 |
-
freeze_last_layer_epochs: 1 # 156 iters (=1,250/8)
|
| 77 |
-
layerwise_decay: 1.0
|
| 78 |
-
crops:
|
| 79 |
-
local_crops_size: 98
|
| 80 |
-
evaluation:
|
| 81 |
-
eval_period_iterations: 2496 # probe ckpt at 2496,4992,7488,9984,12480 (~5개)
|
| 82 |
-
bach_root: ""
|
| 83 |
-
breakhis_root: ""
|
| 84 |
-
pcam_root: ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/train/openpath_vitg14_curated.yaml
DELETED
|
@@ -1,57 +0,0 @@
|
|
| 1 |
-
# OpenPath × OpenMidnight — ViT-g/14 warm-start, 우리 mag20 데이터(큐레이션).
|
| 2 |
-
# 목적: 2×2의 빈 칸 채우기 = ViT-g × 우리데이터(mag20).
|
| 3 |
-
# ViT-L 우리데이터=0.264, ViT-g 그들데이터(native)=0.382. 이 런이 ViT-g 우리데이터.
|
| 4 |
-
# 결과로 "남은 격차 중 데이터 해상도 몫"을 분리 → native 재추출 가치 판정.
|
| 5 |
-
# 레시피·아키텍처는 openpath_vitg14_testB.yaml과 동일(깨끗한 A/B), 데이터만 우리 TAR(큐레이션 암@20x).
|
| 6 |
-
dino:
|
| 7 |
-
head_n_prototypes: 131072
|
| 8 |
-
head_bottleneck_dim: 384
|
| 9 |
-
do_kde: True
|
| 10 |
-
kde_loss_weight: .05
|
| 11 |
-
koleo_loss_weight: 0
|
| 12 |
-
do_koleo: False
|
| 13 |
-
ibot:
|
| 14 |
-
loss_weight: 1.0
|
| 15 |
-
mask_sample_probability: 0.5
|
| 16 |
-
mask_ratio_min_max:
|
| 17 |
-
- 0.1
|
| 18 |
-
- 0.45
|
| 19 |
-
separate_head: true
|
| 20 |
-
head_n_prototypes: 131072
|
| 21 |
-
train:
|
| 22 |
-
# openpath: 분기 → openpath_wds.py TAR 로더(interleave=24 수정 = ~22 WSI/배치 다양성).
|
| 23 |
-
# 큐레이션: 암 41659 WSI @ 20x (GTEx 제거 + mag20). ViT-L 0.264와 동일 데이터 셀.
|
| 24 |
-
sample_list_path: "openpath:glob=/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data/tiles/shards/w*/*.tar:split=/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data/splits/pretrain_train_cancer.txt:mag=20"
|
| 25 |
-
streaming_from_hf: false
|
| 26 |
-
batch_size_per_gpu: 8 # × 48 GPU = global 384 (Test B와 동일)
|
| 27 |
-
centering: sinkhorn_knopp
|
| 28 |
-
use_pretrained: True # ★ Meta DINOv2 ViT-g/14-reg warm-start
|
| 29 |
-
OFFICIAL_EPOCH_LENGTH: 1250
|
| 30 |
-
num_workers: 10 # 다운로드 타일워커와 CPU 공유 → 약간 보수적
|
| 31 |
-
prefetch_factor: 4
|
| 32 |
-
skip_checkpointer: false # resume 가능
|
| 33 |
-
student:
|
| 34 |
-
arch: vit_giant2
|
| 35 |
-
patch_size: 14
|
| 36 |
-
drop_path_rate: 0.4
|
| 37 |
-
ffn_layer: swiglufused # ★ Meta vitg14 = swiglu
|
| 38 |
-
block_chunks: 4
|
| 39 |
-
num_register_tokens: 4
|
| 40 |
-
interpolate_antialias: true
|
| 41 |
-
interpolate_offset: 0.0
|
| 42 |
-
teacher:
|
| 43 |
-
momentum_teacher: 0.994
|
| 44 |
-
optim:
|
| 45 |
-
epochs: 110
|
| 46 |
-
early_stop: 110
|
| 47 |
-
weight_decay_end: 0.2
|
| 48 |
-
base_lr: 3.0e-04
|
| 49 |
-
warmup_epochs: 10
|
| 50 |
-
layerwise_decay: 1.0
|
| 51 |
-
crops:
|
| 52 |
-
local_crops_size: 98
|
| 53 |
-
evaluation:
|
| 54 |
-
eval_period_iterations: 2500
|
| 55 |
-
bach_root: ""
|
| 56 |
-
breakhis_root: ""
|
| 57 |
-
pcam_root: ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/train/openpath_vitg14_final.yaml
DELETED
|
@@ -1,80 +0,0 @@
|
|
| 1 |
-
# ★★ OpenPath 최종 학습 — full+CPTAC(582.0M) ViT-g/14, 48 GPU, bs=64+bf16, 1 EPOCH.
|
| 2 |
-
# 검증완료(2026-07-01): bs=64+bf16 @12,480 = 0.3721 ≈ bs=8 @100k 0.3753(품질유지). throughput 4배(~1.8일).
|
| 3 |
-
# 1 epoch = 582.0M / global3072 = 189,453 iters. schedule 지평 190,000(OEL 1250 × epochs 152).
|
| 4 |
-
# 체크포인트 0.05 epoch(=9,473 iters)마다 = eval_period 9500 → 정확히 20개 teacher_checkpoint(probe용).
|
| 5 |
-
# ★ bf16 필수(fp16은 bs=64서 오버플로 NaN). grad_scaler off. LR=sqrt스케일 eff 5.2e-4(검증런 동일).
|
| 6 |
-
compute_precision:
|
| 7 |
-
grad_scaler: false
|
| 8 |
-
teacher:
|
| 9 |
-
backbone:
|
| 10 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 11 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 12 |
-
dino_head:
|
| 13 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 14 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 15 |
-
ibot_head:
|
| 16 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 17 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 18 |
-
student:
|
| 19 |
-
backbone:
|
| 20 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 21 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 22 |
-
dino_head:
|
| 23 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 24 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: fp32, buffer_dtype: fp32}
|
| 25 |
-
ibot_head:
|
| 26 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 27 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: fp32, buffer_dtype: fp32}
|
| 28 |
-
dino:
|
| 29 |
-
head_n_prototypes: 131072
|
| 30 |
-
head_bottleneck_dim: 384
|
| 31 |
-
do_kde: True
|
| 32 |
-
kde_loss_weight: .05
|
| 33 |
-
koleo_loss_weight: 0
|
| 34 |
-
do_koleo: False
|
| 35 |
-
ibot:
|
| 36 |
-
loss_weight: 1.0
|
| 37 |
-
mask_sample_probability: 0.5
|
| 38 |
-
mask_ratio_min_max:
|
| 39 |
-
- 0.1
|
| 40 |
-
- 0.45
|
| 41 |
-
separate_head: true
|
| 42 |
-
head_n_prototypes: 131072
|
| 43 |
-
train:
|
| 44 |
-
sample_list_path: "openpath:glob=/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data/tiles/shards/w*/*.tar,/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data_cptac/tiles/shards/w*/*.tar"
|
| 45 |
-
streaming_from_hf: false
|
| 46 |
-
batch_size_per_gpu: 64 # × 48 GPU = global 3072
|
| 47 |
-
centering: sinkhorn_knopp
|
| 48 |
-
use_pretrained: True # ★ Meta DINOv2 ViT-g/14-reg warm-start
|
| 49 |
-
OFFICIAL_EPOCH_LENGTH: 1250
|
| 50 |
-
saveckp_freq: 8 # resume ckpt = 8×1250 = 10,000 iters ≈ 0.05 epoch
|
| 51 |
-
num_workers: 10
|
| 52 |
-
prefetch_factor: 4
|
| 53 |
-
skip_checkpointer: false
|
| 54 |
-
student:
|
| 55 |
-
arch: vit_giant2
|
| 56 |
-
patch_size: 14
|
| 57 |
-
drop_path_rate: 0.4
|
| 58 |
-
ffn_layer: swiglufused
|
| 59 |
-
block_chunks: 4
|
| 60 |
-
num_register_tokens: 4
|
| 61 |
-
interpolate_antialias: true
|
| 62 |
-
interpolate_offset: 0.0
|
| 63 |
-
teacher:
|
| 64 |
-
momentum_teacher: 0.994
|
| 65 |
-
warmup_teacher_temp_epochs: 41 # 0.27 epoch (검증 27% 비율) = 51,250 iters
|
| 66 |
-
optim:
|
| 67 |
-
epochs: 152 # × 1250 = 190,000 iters ≈ 1 epoch(189,453)
|
| 68 |
-
early_stop: 152 # 완주(테스트의 early_stop 80 제거)
|
| 69 |
-
weight_decay_end: 0.2
|
| 70 |
-
base_lr: 3.0e-04 # sqrt_wrt_1024 → 실효 5.20e-4 (검증런 동일)
|
| 71 |
-
warmup_epochs: 14 # 0.09 epoch (검증 9% 비율) = 17,500 iters
|
| 72 |
-
freeze_last_layer_epochs: 1 # 1250 iters
|
| 73 |
-
layerwise_decay: 1.0
|
| 74 |
-
crops:
|
| 75 |
-
local_crops_size: 98
|
| 76 |
-
evaluation:
|
| 77 |
-
eval_period_iterations: 9500 # 0.05 epoch마다 teacher_checkpoint(probe용) → 190000/9500 = 정확히 20개
|
| 78 |
-
bach_root: ""
|
| 79 |
-
breakhis_root: ""
|
| 80 |
-
pcam_root: ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/train/openpath_vitg14_final_1ep.yaml
DELETED
|
@@ -1,59 +0,0 @@
|
|
| 1 |
-
# ★ OpenPath 최종 학습 — full + CPTAC 코퍼스(582.0M 타일) ViT-g/14, 48 GPU, 1 EPOCH.
|
| 2 |
-
# 코퍼스: full(566.2M, 전 슬라이드·전 배율) + CPTAC(15.79M, 6암종) = 582.0M 타일.
|
| 3 |
-
# 1 epoch 정의: 582.0M / global384 = 1,516,250 iters (OFFICIAL_EPOCH_LENGTH 1250 × epochs 1213).
|
| 4 |
-
# per-epoch 단위 파라미터(warmup/ttemp/freeze)는 1250-iter 단위 유지 → 검증레시피(0.3873) 비율을 1ep로 stretch.
|
| 5 |
-
# 체크포인트: 0.1 epoch(=151,250 iters)마다 = saveckp_freq 121 + eval_period 151250 → 약 10개.
|
| 6 |
-
dino:
|
| 7 |
-
head_n_prototypes: 131072
|
| 8 |
-
head_bottleneck_dim: 384
|
| 9 |
-
do_kde: True
|
| 10 |
-
kde_loss_weight: .05
|
| 11 |
-
koleo_loss_weight: 0
|
| 12 |
-
do_koleo: False
|
| 13 |
-
ibot:
|
| 14 |
-
loss_weight: 1.0
|
| 15 |
-
mask_sample_probability: 0.5
|
| 16 |
-
mask_ratio_min_max:
|
| 17 |
-
- 0.1
|
| 18 |
-
- 0.45
|
| 19 |
-
separate_head: true
|
| 20 |
-
head_n_prototypes: 131072
|
| 21 |
-
train:
|
| 22 |
-
# openpath TAR 로더(interleave=24). 콤마구분 다중 glob = full + cptac union. split·mag 필터 없음.
|
| 23 |
-
sample_list_path: "openpath:glob=/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data/tiles/shards/w*/*.tar,/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data_cptac/tiles/shards/w*/*.tar"
|
| 24 |
-
streaming_from_hf: false
|
| 25 |
-
batch_size_per_gpu: 8 # × 48 GPU = global 384 (검증 레시피 동일)
|
| 26 |
-
centering: sinkhorn_knopp
|
| 27 |
-
use_pretrained: True # ★ Meta DINOv2 ViT-g/14-reg warm-start
|
| 28 |
-
OFFICIAL_EPOCH_LENGTH: 1250 # 스케줄 단위(검증런과 동일) — per-epoch 파라미터 절대 iter값 보존
|
| 29 |
-
saveckp_freq: 121 # resume 체크포인트 = 121×1250 = 151,250 iters ≈ 0.1 epoch
|
| 30 |
-
num_workers: 10
|
| 31 |
-
prefetch_factor: 4
|
| 32 |
-
skip_checkpointer: false # resume 가능
|
| 33 |
-
student:
|
| 34 |
-
arch: vit_giant2
|
| 35 |
-
patch_size: 14
|
| 36 |
-
drop_path_rate: 0.4
|
| 37 |
-
ffn_layer: swiglufused
|
| 38 |
-
block_chunks: 4
|
| 39 |
-
num_register_tokens: 4
|
| 40 |
-
interpolate_antialias: true
|
| 41 |
-
interpolate_offset: 0.0
|
| 42 |
-
teacher:
|
| 43 |
-
momentum_teacher: 0.994
|
| 44 |
-
warmup_teacher_temp_epochs: 328 # 0.27 epoch (검증런 30/110=27% 비율 그대로) = 410k iters
|
| 45 |
-
optim:
|
| 46 |
-
epochs: 1213 # × 1250 = 1,516,250 iters ≈ 1 epoch(582.0M/384)
|
| 47 |
-
early_stop: 1213
|
| 48 |
-
weight_decay_end: 0.2
|
| 49 |
-
base_lr: 3.0e-04 # sqrt_wrt_1024 스케일 → 실효 lr 1.84e-4 (검증런 동일)
|
| 50 |
-
warmup_epochs: 109 # 0.09 epoch (검증런 10/110=9% 비율) = 136,250 iters warmup
|
| 51 |
-
freeze_last_layer_epochs: 1 # 1250 iters last-layer freeze (검증런 동일, warm-start 안정화)
|
| 52 |
-
layerwise_decay: 1.0
|
| 53 |
-
crops:
|
| 54 |
-
local_crops_size: 98
|
| 55 |
-
evaluation:
|
| 56 |
-
eval_period_iterations: 151250 # 0.1 epoch마다 teacher_checkpoint 저장(probe용) → ~10개
|
| 57 |
-
bach_root: ""
|
| 58 |
-
breakhis_root: ""
|
| 59 |
-
pcam_root: ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/train/openpath_vitg14_final_1ep_bs64.yaml
DELETED
|
@@ -1,59 +0,0 @@
|
|
| 1 |
-
# ★ OpenPath 최종학습 후보 — full+CPTAC(582.0M) ViT-g/14, 48 GPU, bs=64/GPU(global 3072), 1 EPOCH.
|
| 2 |
-
# 목적: 대용량 batch(3072)가 (1) throughput↑로 epoch 단축, (2) HEST 성능 유지하는지 100k서 검증.
|
| 3 |
-
# 1 epoch = 582.0M / 3072 = 189,453 iters (bs=8 1.516M의 1/8 스텝). schedule은 1ep 지평.
|
| 4 |
-
# ★ early_stop=80(=100,000 iters)에서 멈춰 판정 → 성능 유지되면 early_stop 늘려 resume·완주.
|
| 5 |
-
# 체크포인트 0.1 epoch(=18,750 iters)마다(saveckp_freq 15 + eval_period 18750).
|
| 6 |
-
# per-epoch 파라미터는 1250-iter 단위 유지 → 검증레시피(0.3873) 비율을 1ep로 stretch. LR=sqrt스케일 자동상향.
|
| 7 |
-
dino:
|
| 8 |
-
head_n_prototypes: 131072
|
| 9 |
-
head_bottleneck_dim: 384
|
| 10 |
-
do_kde: True
|
| 11 |
-
kde_loss_weight: .05
|
| 12 |
-
koleo_loss_weight: 0
|
| 13 |
-
do_koleo: False
|
| 14 |
-
ibot:
|
| 15 |
-
loss_weight: 1.0
|
| 16 |
-
mask_sample_probability: 0.5
|
| 17 |
-
mask_ratio_min_max:
|
| 18 |
-
- 0.1
|
| 19 |
-
- 0.45
|
| 20 |
-
separate_head: true
|
| 21 |
-
head_n_prototypes: 131072
|
| 22 |
-
train:
|
| 23 |
-
sample_list_path: "openpath:glob=/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data/tiles/shards/w*/*.tar,/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data_cptac/tiles/shards/w*/*.tar"
|
| 24 |
-
streaming_from_hf: false
|
| 25 |
-
batch_size_per_gpu: 64 # × 48 GPU = global 3072
|
| 26 |
-
centering: sinkhorn_knopp
|
| 27 |
-
use_pretrained: True # ★ Meta DINOv2 ViT-g/14-reg warm-start
|
| 28 |
-
OFFICIAL_EPOCH_LENGTH: 1250
|
| 29 |
-
saveckp_freq: 15 # resume ckpt = 15×1250 = 18,750 iters ≈ 0.1 epoch
|
| 30 |
-
num_workers: 10
|
| 31 |
-
prefetch_factor: 4
|
| 32 |
-
skip_checkpointer: false
|
| 33 |
-
student:
|
| 34 |
-
arch: vit_giant2
|
| 35 |
-
patch_size: 14
|
| 36 |
-
drop_path_rate: 0.4
|
| 37 |
-
ffn_layer: swiglufused
|
| 38 |
-
block_chunks: 4
|
| 39 |
-
num_register_tokens: 4
|
| 40 |
-
interpolate_antialias: true
|
| 41 |
-
interpolate_offset: 0.0
|
| 42 |
-
teacher:
|
| 43 |
-
momentum_teacher: 0.994
|
| 44 |
-
warmup_teacher_temp_epochs: 41 # 0.27 epoch (검증 27% 비율) = 51,250 iters
|
| 45 |
-
optim:
|
| 46 |
-
epochs: 152 # × 1250 = 190,000 iters ≈ 1 epoch(189,453) — schedule 지평
|
| 47 |
-
early_stop: 80 # ★ 100,000 iters서 정지(bs=64 검증). 통과시 152로 늘려 resume
|
| 48 |
-
weight_decay_end: 0.2
|
| 49 |
-
base_lr: 3.0e-04 # sqrt_wrt_1024 → 실효 lr 5.20e-4 (global 3072)
|
| 50 |
-
warmup_epochs: 14 # 0.09 epoch (검증 9% 비율) = 17,500 iters warmup
|
| 51 |
-
freeze_last_layer_epochs: 1 # 1250 iters last-layer freeze (warm-start 안정화)
|
| 52 |
-
layerwise_decay: 1.0
|
| 53 |
-
crops:
|
| 54 |
-
local_crops_size: 98
|
| 55 |
-
evaluation:
|
| 56 |
-
eval_period_iterations: 18750 # 0.1 epoch마다 teacher_checkpoint(probe용)
|
| 57 |
-
bach_root: ""
|
| 58 |
-
breakhis_root: ""
|
| 59 |
-
pcam_root: ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/train/openpath_vitg14_full.yaml
DELETED
|
@@ -1,55 +0,0 @@
|
|
| 1 |
-
# 풀코퍼스 ViT-g/14 — 전 슬라이드(GTEx 정상 포함 ~68k) + 전 배율(20x/10x/5x).
|
| 2 |
-
# 목적: 큐레이션(암@20x)과 동일 레시피·스케줄(137.5k 코사인)로 데이터만 풀코퍼스 → 100k서 probe.
|
| 3 |
-
# 비교기준 = 큐레이션 100k=0.3765. 데이터 다양성(멀티스케일+정상)이 HEST 유지/향상하는지 검증.
|
| 4 |
-
# 큐레이션과의 유일 차이 = sample_list_path에서 :split·:mag 필터 제거(전 타일 사용).
|
| 5 |
-
dino:
|
| 6 |
-
head_n_prototypes: 131072
|
| 7 |
-
head_bottleneck_dim: 384
|
| 8 |
-
do_kde: True
|
| 9 |
-
kde_loss_weight: .05
|
| 10 |
-
koleo_loss_weight: 0
|
| 11 |
-
do_koleo: False
|
| 12 |
-
ibot:
|
| 13 |
-
loss_weight: 1.0
|
| 14 |
-
mask_sample_probability: 0.5
|
| 15 |
-
mask_ratio_min_max:
|
| 16 |
-
- 0.1
|
| 17 |
-
- 0.45
|
| 18 |
-
separate_head: true
|
| 19 |
-
head_n_prototypes: 131072
|
| 20 |
-
train:
|
| 21 |
-
# openpath: TAR 로더(interleave=24). split·mag 필터 없음 → 전 슬라이드·전 배율(565.9M 타일).
|
| 22 |
-
sample_list_path: "openpath:glob=/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data/tiles/shards/w*/*.tar"
|
| 23 |
-
streaming_from_hf: false
|
| 24 |
-
batch_size_per_gpu: 8 # × 48 GPU = global 384 (큐레이션 동일)
|
| 25 |
-
centering: sinkhorn_knopp
|
| 26 |
-
use_pretrained: True # ★ Meta DINOv2 ViT-g/14-reg warm-start
|
| 27 |
-
OFFICIAL_EPOCH_LENGTH: 1250
|
| 28 |
-
num_workers: 10
|
| 29 |
-
prefetch_factor: 4
|
| 30 |
-
skip_checkpointer: false # resume 가능
|
| 31 |
-
student:
|
| 32 |
-
arch: vit_giant2
|
| 33 |
-
patch_size: 14
|
| 34 |
-
drop_path_rate: 0.4
|
| 35 |
-
ffn_layer: swiglufused
|
| 36 |
-
block_chunks: 4
|
| 37 |
-
num_register_tokens: 4
|
| 38 |
-
interpolate_antialias: true
|
| 39 |
-
interpolate_offset: 0.0
|
| 40 |
-
teacher:
|
| 41 |
-
momentum_teacher: 0.994
|
| 42 |
-
optim:
|
| 43 |
-
epochs: 110 # 137.5k 스케줄(큐레이션 동일) — 100k서 probe해 LR시점 일치
|
| 44 |
-
early_stop: 110
|
| 45 |
-
weight_decay_end: 0.2
|
| 46 |
-
base_lr: 3.0e-04
|
| 47 |
-
warmup_epochs: 10
|
| 48 |
-
layerwise_decay: 1.0
|
| 49 |
-
crops:
|
| 50 |
-
local_crops_size: 98
|
| 51 |
-
evaluation:
|
| 52 |
-
eval_period_iterations: 2500
|
| 53 |
-
bach_root: ""
|
| 54 |
-
breakhis_root: ""
|
| 55 |
-
pcam_root: ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/train/openpath_vitg14_full_cptac.yaml
DELETED
|
@@ -1,55 +0,0 @@
|
|
| 1 |
-
# 풀코퍼스 + CPTAC ViT-g/14 — full(전 슬라이드·전 배율, 565.9M 타일) + CPTAC(15.79M 타일, 6암종).
|
| 2 |
-
# 목적: 본모델(full final HEST 0.3873)에 CPTAC 추가가 HEST를 더 올리는지 검증.
|
| 3 |
-
# full 대비 유일 차이 = sample_list_path에 data_cptac 샤드 glob을 콤마로 추가(union).
|
| 4 |
-
# 레시피·스케줄은 full과 100% 동일(137.5k 코사인, ViT-g warm-start) → 데이터만의 효과를 분리.
|
| 5 |
-
dino:
|
| 6 |
-
head_n_prototypes: 131072
|
| 7 |
-
head_bottleneck_dim: 384
|
| 8 |
-
do_kde: True
|
| 9 |
-
kde_loss_weight: .05
|
| 10 |
-
koleo_loss_weight: 0
|
| 11 |
-
do_koleo: False
|
| 12 |
-
ibot:
|
| 13 |
-
loss_weight: 1.0
|
| 14 |
-
mask_sample_probability: 0.5
|
| 15 |
-
mask_ratio_min_max:
|
| 16 |
-
- 0.1
|
| 17 |
-
- 0.45
|
| 18 |
-
separate_head: true
|
| 19 |
-
head_n_prototypes: 131072
|
| 20 |
-
train:
|
| 21 |
-
# openpath TAR 로더(interleave=24). 콤마구분 다중 glob = full + cptac union. split·mag 필터 없음.
|
| 22 |
-
sample_list_path: "openpath:glob=/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data/tiles/shards/w*/*.tar,/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data_cptac/tiles/shards/w*/*.tar"
|
| 23 |
-
streaming_from_hf: false
|
| 24 |
-
batch_size_per_gpu: 8 # × 48 GPU = global 384 (full 동일)
|
| 25 |
-
centering: sinkhorn_knopp
|
| 26 |
-
use_pretrained: True # ★ Meta DINOv2 ViT-g/14-reg warm-start
|
| 27 |
-
OFFICIAL_EPOCH_LENGTH: 1250
|
| 28 |
-
num_workers: 10
|
| 29 |
-
prefetch_factor: 4
|
| 30 |
-
skip_checkpointer: false # resume 가능
|
| 31 |
-
student:
|
| 32 |
-
arch: vit_giant2
|
| 33 |
-
patch_size: 14
|
| 34 |
-
drop_path_rate: 0.4
|
| 35 |
-
ffn_layer: swiglufused
|
| 36 |
-
block_chunks: 4
|
| 37 |
-
num_register_tokens: 4
|
| 38 |
-
interpolate_antialias: true
|
| 39 |
-
interpolate_offset: 0.0
|
| 40 |
-
teacher:
|
| 41 |
-
momentum_teacher: 0.994
|
| 42 |
-
optim:
|
| 43 |
-
epochs: 110 # 137.5k 스케줄(full 동일) — 100k서 probe해 LR시점 일치
|
| 44 |
-
early_stop: 110
|
| 45 |
-
weight_decay_end: 0.2
|
| 46 |
-
base_lr: 3.0e-04
|
| 47 |
-
warmup_epochs: 10
|
| 48 |
-
layerwise_decay: 1.0
|
| 49 |
-
crops:
|
| 50 |
-
local_crops_size: 98
|
| 51 |
-
evaluation:
|
| 52 |
-
eval_period_iterations: 2500
|
| 53 |
-
bach_root: ""
|
| 54 |
-
breakhis_root: ""
|
| 55 |
-
pcam_root: ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/train/openpath_vitg14_run2.yaml
DELETED
|
@@ -1,84 +0,0 @@
|
|
| 1 |
-
# ★★ OpenPath run 2 — full+CPTAC(582M) ViT-g/14, ★40 GPU(TJ-2~6, 5노드), bs=64+bf16, 1 EPOCH.
|
| 2 |
-
# TJ-1은 학습 제외(에이전트+실시간 eval watcher 전용). global batch = 64×40 = 2560.
|
| 3 |
-
# run1(0.3792 peak@161500 후 하락, 전체최고는 28500=0.3816) 대비 = "OpenMidnight식 LR 레짐"만 교체(코퍼스·bs 동일).
|
| 4 |
-
# ① base_lr 3e-4→2e-4 (실효 → 2e-4×sqrt(2560/1024)=3.16e-4, OpenMidnight base값).
|
| 5 |
-
# ② ★ anneal 제거: epochs 8000(스케줄 지평만 확대=10M iter) + early_stop 182(=227,500 iter≈1 epoch서 실제 정지).
|
| 6 |
-
# → LR이 227.5k/10M=2.3%만 밟아 peak의 ~99.7% 유지(near-constant). wd·teacher-momentum도 near-initial(OM 레짐).
|
| 7 |
-
# ③ warmup 5% (warmup_epochs 9.1 = 11,375 iter).
|
| 8 |
-
# ④ eval_period 5700 → ~40 teacher_checkpoint(0.025ep마다). TJ-1 watcher가 실시간 HEST 곡선.
|
| 9 |
-
# ★ 1 epoch = 582.0M / 2560 = 227,343 iter. 검증필요: 3.16e-4 flat+1ep under-training 여부 → 실시간 곡선으로 조기판단, 상승지속시 early_stop 상향.
|
| 10 |
-
compute_precision:
|
| 11 |
-
grad_scaler: false
|
| 12 |
-
teacher:
|
| 13 |
-
backbone:
|
| 14 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 15 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 16 |
-
dino_head:
|
| 17 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 18 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 19 |
-
ibot_head:
|
| 20 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 21 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 22 |
-
student:
|
| 23 |
-
backbone:
|
| 24 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 25 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 26 |
-
dino_head:
|
| 27 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 28 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: fp32, buffer_dtype: fp32}
|
| 29 |
-
ibot_head:
|
| 30 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 31 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: fp32, buffer_dtype: fp32}
|
| 32 |
-
dino:
|
| 33 |
-
head_n_prototypes: 131072
|
| 34 |
-
head_bottleneck_dim: 384
|
| 35 |
-
do_kde: True
|
| 36 |
-
kde_loss_weight: .05
|
| 37 |
-
koleo_loss_weight: 0
|
| 38 |
-
do_koleo: False
|
| 39 |
-
ibot:
|
| 40 |
-
loss_weight: 1.0
|
| 41 |
-
mask_sample_probability: 0.5
|
| 42 |
-
mask_ratio_min_max:
|
| 43 |
-
- 0.1
|
| 44 |
-
- 0.45
|
| 45 |
-
separate_head: true
|
| 46 |
-
head_n_prototypes: 131072
|
| 47 |
-
train:
|
| 48 |
-
sample_list_path: "openpath:glob=/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data/tiles/shards/w*/*.tar,/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data_cptac/tiles/shards/w*/*.tar"
|
| 49 |
-
streaming_from_hf: false
|
| 50 |
-
batch_size_per_gpu: 64 # × 40 GPU(TJ-2~6) = global 2560
|
| 51 |
-
centering: sinkhorn_knopp
|
| 52 |
-
use_pretrained: True # ★ Meta DINOv2 ViT-g/14-reg warm-start
|
| 53 |
-
OFFICIAL_EPOCH_LENGTH: 1250
|
| 54 |
-
saveckp_freq: 8 # resume ckpt = 8×1250 = 10,000 iters
|
| 55 |
-
num_workers: 10
|
| 56 |
-
prefetch_factor: 4
|
| 57 |
-
skip_checkpointer: false
|
| 58 |
-
student:
|
| 59 |
-
arch: vit_giant2
|
| 60 |
-
patch_size: 14
|
| 61 |
-
drop_path_rate: 0.4
|
| 62 |
-
ffn_layer: swiglufused
|
| 63 |
-
block_chunks: 4
|
| 64 |
-
num_register_tokens: 4
|
| 65 |
-
interpolate_antialias: true
|
| 66 |
-
interpolate_offset: 0.0
|
| 67 |
-
teacher:
|
| 68 |
-
momentum_teacher: 0.994
|
| 69 |
-
warmup_teacher_temp_epochs: 41 # 0.27 epoch = 51,250 iters (epochs와 독립, run1과 동일)
|
| 70 |
-
optim:
|
| 71 |
-
epochs: 8000 # ★ 스케줄 지평만 확대(=10,000,000 iter). LR/wd/momentum near-constant.
|
| 72 |
-
early_stop: 184 # ★ 정지 = 184×1250 = 230,000 iter ≈ 1.01 epoch. eval_period 5750의 배수(40×5750)=정지점서 최종ckpt 저장
|
| 73 |
-
weight_decay_end: 0.2 # 8000ep 지평이라 227.5k선 wd≈0.040(near-initial, 미도달)
|
| 74 |
-
base_lr: 2.0e-04 # ★ sqrt_wrt_1024 → 실효 3.16e-4 @batch2560 (OpenMidnight base)
|
| 75 |
-
warmup_epochs: 9 # ★ 11,250 iter ≈ 4.9% (정수 필수: np.linspace(...,warmup_iters)가 int만 허용. 9.1이면 crash)
|
| 76 |
-
freeze_last_layer_epochs: 1 # 1250 iters
|
| 77 |
-
layerwise_decay: 1.0
|
| 78 |
-
crops:
|
| 79 |
-
local_crops_size: 98
|
| 80 |
-
evaluation:
|
| 81 |
-
eval_period_iterations: 5750 # ★ teacher_checkpoint 40개(5750×1..40=230,000, 정지점 포함). TJ-1 watcher가 실시간 probe.
|
| 82 |
-
bach_root: ""
|
| 83 |
-
breakhis_root: ""
|
| 84 |
-
pcam_root: ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/train/openpath_vitg14_run3_gram.yaml
DELETED
|
@@ -1,92 +0,0 @@
|
|
| 1 |
-
# ★★ OpenPath run 2 — full+CPTAC(582M) ViT-g/14, ★40 GPU(TJ-2~6, 5노드), bs=64+bf16, 1 EPOCH.
|
| 2 |
-
# TJ-1은 학습 제외(에이전트+실시간 eval watcher 전용). global batch = 64×40 = 2560.
|
| 3 |
-
# run1(0.3792 peak@161500 후 하락, 전체최고는 28500=0.3816) 대비 = "OpenMidnight식 LR 레짐"만 교체(코퍼스·bs 동일).
|
| 4 |
-
# ① base_lr 3e-4→2e-4 (실효 → 2e-4×sqrt(2560/1024)=3.16e-4, OpenMidnight base값).
|
| 5 |
-
# ② ★ anneal 제거: epochs 8000(스케줄 지평만 확대=10M iter) + early_stop 182(=227,500 iter≈1 epoch서 실제 정지).
|
| 6 |
-
# → LR이 227.5k/10M=2.3%만 밟아 peak의 ~99.7% 유지(near-constant). wd·teacher-momentum도 near-initial(OM 레짐).
|
| 7 |
-
# ③ warmup 5% (warmup_epochs 9.1 = 11,375 iter).
|
| 8 |
-
# ④ eval_period 5700 → ~40 teacher_checkpoint(0.025ep마다). TJ-1 watcher가 실시간 HEST 곡선.
|
| 9 |
-
# ★ 1 epoch = 582.0M / 2560 = 227,343 iter. 검증필요: 3.16e-4 flat+1ep under-training 여부 → 실시간 곡선으로 조기판단, 상승지속시 early_stop 상향.
|
| 10 |
-
compute_precision:
|
| 11 |
-
grad_scaler: false
|
| 12 |
-
teacher:
|
| 13 |
-
backbone:
|
| 14 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 15 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 16 |
-
dino_head:
|
| 17 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 18 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 19 |
-
ibot_head:
|
| 20 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 21 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 22 |
-
student:
|
| 23 |
-
backbone:
|
| 24 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 25 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 26 |
-
dino_head:
|
| 27 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 28 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: fp32, buffer_dtype: fp32}
|
| 29 |
-
ibot_head:
|
| 30 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 31 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: fp32, buffer_dtype: fp32}
|
| 32 |
-
dino:
|
| 33 |
-
head_n_prototypes: 131072
|
| 34 |
-
head_bottleneck_dim: 384
|
| 35 |
-
do_kde: True
|
| 36 |
-
kde_loss_weight: .05
|
| 37 |
-
koleo_loss_weight: 0
|
| 38 |
-
do_koleo: False
|
| 39 |
-
ibot:
|
| 40 |
-
loss_weight: 1.0
|
| 41 |
-
mask_sample_probability: 0.5
|
| 42 |
-
mask_ratio_min_max:
|
| 43 |
-
- 0.1
|
| 44 |
-
- 0.45
|
| 45 |
-
separate_head: true
|
| 46 |
-
head_n_prototypes: 131072
|
| 47 |
-
train:
|
| 48 |
-
sample_list_path: "openpath:glob=/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data/tiles/shards/w*/*.tar,/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data_cptac/tiles/shards/w*/*.tar"
|
| 49 |
-
streaming_from_hf: false
|
| 50 |
-
batch_size_per_gpu: 64 # × 40 GPU(TJ-2~6) = global 2560
|
| 51 |
-
centering: sinkhorn_knopp
|
| 52 |
-
use_pretrained: True # ★ Meta DINOv2 ViT-g/14-reg warm-start
|
| 53 |
-
OFFICIAL_EPOCH_LENGTH: 1250
|
| 54 |
-
saveckp_freq: 8 # resume ckpt = 8×1250 = 10,000 iters
|
| 55 |
-
num_workers: 10
|
| 56 |
-
prefetch_factor: 4
|
| 57 |
-
skip_checkpointer: false
|
| 58 |
-
student:
|
| 59 |
-
arch: vit_giant2
|
| 60 |
-
patch_size: 14
|
| 61 |
-
drop_path_rate: 0.4
|
| 62 |
-
ffn_layer: swiglufused
|
| 63 |
-
block_chunks: 4
|
| 64 |
-
num_register_tokens: 4
|
| 65 |
-
interpolate_antialias: true
|
| 66 |
-
interpolate_offset: 0.0
|
| 67 |
-
teacher:
|
| 68 |
-
momentum_teacher: 0.994
|
| 69 |
-
warmup_teacher_temp_epochs: 41 # 0.27 epoch = 51,250 iters (epochs와 독립, run1과 동일)
|
| 70 |
-
optim:
|
| 71 |
-
epochs: 8000 # ★ 스케줄 지평만 확대(=10,000,000 iter). LR/wd/momentum near-constant.
|
| 72 |
-
early_stop: 184 # ★ 정지 = 184×1250 = 230,000 iter ≈ 1.01 epoch. eval_period 5750의 배수(40×5750)=정지점서 최종ckpt 저장
|
| 73 |
-
weight_decay_end: 0.2 # 8000ep 지평이라 227.5k선 wd≈0.040(near-initial, 미도달)
|
| 74 |
-
base_lr: 2.0e-04 # ★ sqrt_wrt_1024 → 실효 3.16e-4 @batch2560 (OpenMidnight base)
|
| 75 |
-
warmup_epochs: 9 # ★ 11,250 iter ≈ 4.9% (정수 필수: np.linspace(...,warmup_iters)가 int만 허용. 9.1이면 crash)
|
| 76 |
-
freeze_last_layer_epochs: 1 # 1250 iters
|
| 77 |
-
layerwise_decay: 1.0
|
| 78 |
-
crops:
|
| 79 |
-
local_crops_size: 98
|
| 80 |
-
evaluation:
|
| 81 |
-
eval_period_iterations: 5750 # ★ teacher_checkpoint 40개(5750×1..40=230,000, 정지점 포함). TJ-1 watcher가 실시간 probe.
|
| 82 |
-
bach_root: ""
|
| 83 |
-
breakhis_root: ""
|
| 84 |
-
pcam_root: ""
|
| 85 |
-
gram:
|
| 86 |
-
use_loss: true # ★ gram anchoring 켜기
|
| 87 |
-
normalized: true
|
| 88 |
-
remove_neg: true
|
| 89 |
-
loss_weight: 1.0
|
| 90 |
-
it_first_update: 34000 # ~0.15 epoch: 우리 peak 부근서 gram 활성 → 이후 하락 방지
|
| 91 |
-
ramp_iters: 5000 # 34k~39k 선형 ramp
|
| 92 |
-
ckpt: /NHNHOME/WORKSPACE/0526040027_A/OpenPath/data/runs/om_vitg14_final/eval/training_28500/teacher_checkpoint.pth
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/train/openpath_vitg14_run3_smoke.yaml
DELETED
|
@@ -1,92 +0,0 @@
|
|
| 1 |
-
# ★★ OpenPath run 2 — full+CPTAC(582M) ViT-g/14, ★40 GPU(TJ-2~6, 5노드), bs=64+bf16, 1 EPOCH.
|
| 2 |
-
# TJ-1은 학습 제외(에이전트+실시간 eval watcher 전용). global batch = 64×40 = 2560.
|
| 3 |
-
# run1(0.3792 peak@161500 후 하락, 전체최고는 28500=0.3816) 대비 = "OpenMidnight식 LR 레짐"만 교체(코퍼스·bs 동일).
|
| 4 |
-
# ① base_lr 3e-4→2e-4 (실효 → 2e-4×sqrt(2560/1024)=3.16e-4, OpenMidnight base값).
|
| 5 |
-
# ② ★ anneal 제거: epochs 8000(스케줄 지평만 확대=10M iter) + early_stop 182(=227,500 iter≈1 epoch서 실제 정지).
|
| 6 |
-
# → LR이 227.5k/10M=2.3%만 밟아 peak의 ~99.7% 유지(near-constant). wd·teacher-momentum도 near-initial(OM 레짐).
|
| 7 |
-
# ③ warmup 5% (warmup_epochs 9.1 = 11,375 iter).
|
| 8 |
-
# ④ eval_period 5700 → ~40 teacher_checkpoint(0.025ep마다). TJ-1 watcher가 실시간 HEST 곡선.
|
| 9 |
-
# ★ 1 epoch = 582.0M / 2560 = 227,343 iter. 검증필요: 3.16e-4 flat+1ep under-training 여부 → 실시간 곡선으로 조기판단, 상승지속시 early_stop 상향.
|
| 10 |
-
compute_precision:
|
| 11 |
-
grad_scaler: false
|
| 12 |
-
teacher:
|
| 13 |
-
backbone:
|
| 14 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 15 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 16 |
-
dino_head:
|
| 17 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 18 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 19 |
-
ibot_head:
|
| 20 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 21 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 22 |
-
student:
|
| 23 |
-
backbone:
|
| 24 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 25 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 26 |
-
dino_head:
|
| 27 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 28 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: fp32, buffer_dtype: fp32}
|
| 29 |
-
ibot_head:
|
| 30 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 31 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: fp32, buffer_dtype: fp32}
|
| 32 |
-
dino:
|
| 33 |
-
head_n_prototypes: 131072
|
| 34 |
-
head_bottleneck_dim: 384
|
| 35 |
-
do_kde: True
|
| 36 |
-
kde_loss_weight: .05
|
| 37 |
-
koleo_loss_weight: 0
|
| 38 |
-
do_koleo: False
|
| 39 |
-
ibot:
|
| 40 |
-
loss_weight: 1.0
|
| 41 |
-
mask_sample_probability: 0.5
|
| 42 |
-
mask_ratio_min_max:
|
| 43 |
-
- 0.1
|
| 44 |
-
- 0.45
|
| 45 |
-
separate_head: true
|
| 46 |
-
head_n_prototypes: 131072
|
| 47 |
-
train:
|
| 48 |
-
sample_list_path: "openpath:glob=/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data/tiles/shards/w*/*.tar,/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data_cptac/tiles/shards/w*/*.tar"
|
| 49 |
-
streaming_from_hf: false
|
| 50 |
-
batch_size_per_gpu: 64 # × 40 GPU(TJ-2~6) = global 2560
|
| 51 |
-
centering: sinkhorn_knopp
|
| 52 |
-
use_pretrained: True # ★ Meta DINOv2 ViT-g/14-reg warm-start
|
| 53 |
-
OFFICIAL_EPOCH_LENGTH: 1250
|
| 54 |
-
saveckp_freq: 8 # resume ckpt = 8×1250 = 10,000 iters
|
| 55 |
-
num_workers: 10
|
| 56 |
-
prefetch_factor: 4
|
| 57 |
-
skip_checkpointer: false
|
| 58 |
-
student:
|
| 59 |
-
arch: vit_giant2
|
| 60 |
-
patch_size: 14
|
| 61 |
-
drop_path_rate: 0.4
|
| 62 |
-
ffn_layer: swiglufused
|
| 63 |
-
block_chunks: 4
|
| 64 |
-
num_register_tokens: 4
|
| 65 |
-
interpolate_antialias: true
|
| 66 |
-
interpolate_offset: 0.0
|
| 67 |
-
teacher:
|
| 68 |
-
momentum_teacher: 0.994
|
| 69 |
-
warmup_teacher_temp_epochs: 41 # 0.27 epoch = 51,250 iters (epochs와 독립, run1과 동일)
|
| 70 |
-
optim:
|
| 71 |
-
epochs: 8000 # ★ 스케줄 지평만 확대(=10,000,000 iter). LR/wd/momentum near-constant.
|
| 72 |
-
early_stop: 184 # ★ 정지 = 184×1250 = 230,000 iter ≈ 1.01 epoch. eval_period 5750의 배수(40×5750)=정지점서 최종ckpt 저장
|
| 73 |
-
weight_decay_end: 0.2 # 8000ep 지평이라 227.5k선 wd≈0.040(near-initial, 미도달)
|
| 74 |
-
base_lr: 2.0e-04 # ★ sqrt_wrt_1024 → 실효 3.16e-4 @batch2560 (OpenMidnight base)
|
| 75 |
-
warmup_epochs: 9 # ★ 11,250 iter ≈ 4.9% (정수 필수: np.linspace(...,warmup_iters)가 int만 허용. 9.1이면 crash)
|
| 76 |
-
freeze_last_layer_epochs: 1 # 1250 iters
|
| 77 |
-
layerwise_decay: 1.0
|
| 78 |
-
crops:
|
| 79 |
-
local_crops_size: 98
|
| 80 |
-
evaluation:
|
| 81 |
-
eval_period_iterations: 5750 # ★ teacher_checkpoint 40개(5750×1..40=230,000, 정지점 포함). TJ-1 watcher가 실시간 probe.
|
| 82 |
-
bach_root: ""
|
| 83 |
-
breakhis_root: ""
|
| 84 |
-
pcam_root: ""
|
| 85 |
-
gram:
|
| 86 |
-
use_loss: true # ★ gram anchoring 켜기
|
| 87 |
-
normalized: true
|
| 88 |
-
remove_neg: true
|
| 89 |
-
loss_weight: 1.0
|
| 90 |
-
it_first_update: 5
|
| 91 |
-
ramp_iters: 3
|
| 92 |
-
ckpt: /NHNHOME/WORKSPACE/0526040027_A/OpenPath/data/runs/om_vitg14_final/eval/training_28500/teacher_checkpoint.pth
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/train/openpath_vitg14_run4_gram.yaml
DELETED
|
@@ -1,92 +0,0 @@
|
|
| 1 |
-
# ★★ OpenPath run 2 — full+CPTAC(582M) ViT-g/14, ★40 GPU(TJ-2~6, 5노드), bs=64+bf16, 1 EPOCH.
|
| 2 |
-
# TJ-1은 학습 제외(에이전트+실시간 eval watcher 전용). global batch = 64×40 = 2560.
|
| 3 |
-
# run1(0.3792 peak@161500 후 하락, 전체최고는 28500=0.3816) 대비 = "OpenMidnight식 LR 레짐"만 교체(코퍼스·bs 동일).
|
| 4 |
-
# ① base_lr 3e-4→2e-4 (실효 → 2e-4×sqrt(2560/1024)=3.16e-4, OpenMidnight base값).
|
| 5 |
-
# ② ★ anneal 제거: epochs 8000(스케줄 지평만 확대=10M iter) + early_stop 182(=227,500 iter≈1 epoch서 실제 정지).
|
| 6 |
-
# → LR이 227.5k/10M=2.3%만 밟아 peak의 ~99.7% 유지(near-constant). wd·teacher-momentum도 near-initial(OM 레짐).
|
| 7 |
-
# ③ warmup 5% (warmup_epochs 9.1 = 11,375 iter).
|
| 8 |
-
# ④ eval_period 5700 → ~40 teacher_checkpoint(0.025ep마다). TJ-1 watcher가 실시간 HEST 곡선.
|
| 9 |
-
# ★ 1 epoch = 582.0M / 2560 = 227,343 iter. 검증필요: 3.16e-4 flat+1ep under-training 여부 → 실시간 곡선으로 조기판단, 상승지속시 early_stop 상향.
|
| 10 |
-
compute_precision:
|
| 11 |
-
grad_scaler: false
|
| 12 |
-
teacher:
|
| 13 |
-
backbone:
|
| 14 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 15 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 16 |
-
dino_head:
|
| 17 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 18 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 19 |
-
ibot_head:
|
| 20 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 21 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 22 |
-
student:
|
| 23 |
-
backbone:
|
| 24 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 25 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 26 |
-
dino_head:
|
| 27 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 28 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: fp32, buffer_dtype: fp32}
|
| 29 |
-
ibot_head:
|
| 30 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 31 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: fp32, buffer_dtype: fp32}
|
| 32 |
-
dino:
|
| 33 |
-
head_n_prototypes: 131072
|
| 34 |
-
head_bottleneck_dim: 384
|
| 35 |
-
do_kde: True
|
| 36 |
-
kde_loss_weight: .05
|
| 37 |
-
koleo_loss_weight: 0
|
| 38 |
-
do_koleo: False
|
| 39 |
-
ibot:
|
| 40 |
-
loss_weight: 1.0
|
| 41 |
-
mask_sample_probability: 0.5
|
| 42 |
-
mask_ratio_min_max:
|
| 43 |
-
- 0.1
|
| 44 |
-
- 0.45
|
| 45 |
-
separate_head: true
|
| 46 |
-
head_n_prototypes: 131072
|
| 47 |
-
train:
|
| 48 |
-
sample_list_path: "openpath:glob=/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data/tiles/shards/w*/*.tar,/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data_cptac/tiles/shards/w*/*.tar"
|
| 49 |
-
streaming_from_hf: false
|
| 50 |
-
batch_size_per_gpu: 64 # × 40 GPU(TJ-2~6) = global 2560
|
| 51 |
-
centering: sinkhorn_knopp
|
| 52 |
-
use_pretrained: True # ★ Meta DINOv2 ViT-g/14-reg warm-start
|
| 53 |
-
OFFICIAL_EPOCH_LENGTH: 1250
|
| 54 |
-
saveckp_freq: 8 # resume ckpt = 8×1250 = 10,000 iters
|
| 55 |
-
num_workers: 10
|
| 56 |
-
prefetch_factor: 4
|
| 57 |
-
skip_checkpointer: false
|
| 58 |
-
student:
|
| 59 |
-
arch: vit_giant2
|
| 60 |
-
patch_size: 14
|
| 61 |
-
drop_path_rate: 0.4
|
| 62 |
-
ffn_layer: swiglufused
|
| 63 |
-
block_chunks: 4
|
| 64 |
-
num_register_tokens: 4
|
| 65 |
-
interpolate_antialias: true
|
| 66 |
-
interpolate_offset: 0.0
|
| 67 |
-
teacher:
|
| 68 |
-
momentum_teacher: 0.994
|
| 69 |
-
warmup_teacher_temp_epochs: 41 # 0.27 epoch = 51,250 iters (epochs와 독립, run1과 동일)
|
| 70 |
-
optim:
|
| 71 |
-
epochs: 8000 # ★ 스케줄 지평만 확대(=10,000,000 iter). LR/wd/momentum near-constant.
|
| 72 |
-
early_stop: 184 # ★ 정지 = 184×1250 = 230,000 iter ≈ 1.01 epoch. eval_period 5750의 배수(40×5750)=정지점서 최종ckpt 저장
|
| 73 |
-
weight_decay_end: 0.2 # 8000ep 지평이라 227.5k선 wd≈0.040(near-initial, 미도달)
|
| 74 |
-
base_lr: 2.0e-04 # ★ sqrt_wrt_1024 → 실효 3.16e-4 @batch2560 (OpenMidnight base)
|
| 75 |
-
warmup_epochs: 9 # ★ 11,250 iter ≈ 4.9% (정수 필수: np.linspace(...,warmup_iters)가 int만 허용. 9.1이면 crash)
|
| 76 |
-
freeze_last_layer_epochs: 1 # 1250 iters
|
| 77 |
-
layerwise_decay: 1.0
|
| 78 |
-
crops:
|
| 79 |
-
local_crops_size: 98
|
| 80 |
-
evaluation:
|
| 81 |
-
eval_period_iterations: 5750 # ★ teacher_checkpoint 40개(5750×1..40=230,000, 정지점 포함). TJ-1 watcher가 실시간 probe.
|
| 82 |
-
bach_root: ""
|
| 83 |
-
breakhis_root: ""
|
| 84 |
-
pcam_root: ""
|
| 85 |
-
gram:
|
| 86 |
-
use_loss: true
|
| 87 |
-
normalized: true
|
| 88 |
-
remove_neg: true
|
| 89 |
-
loss_weight: 20.0 # ★ run3(1.0)의 20배 — 앵커서 ~3.4%, drift시 ~10%+로 물게. 과제약이면 실시간곡선서 감지→하향
|
| 90 |
-
it_first_update: 23000 # ★ peak(23k)에서 gram 활성(run3는 34k 지각 활성해 peak·dip 놓침)
|
| 91 |
-
ramp_iters: 3000 # 23k~26k ramp
|
| 92 |
-
ckpt: /NHNHOME/WORKSPACE/0526040027_A/OpenPath/data/runs/om_vitg14_run3/eval/training_23000/teacher_checkpoint.pth
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/train/openpath_vitg14_run5_native.yaml
DELETED
|
@@ -1,84 +0,0 @@
|
|
| 1 |
-
# ★★ OpenPath run5 — ★native 40× 재추출 코퍼스(33,991 shard, 암85%) + flat-LR 40GPU + early-stop(gram 없음). 데이터 품질 레버.
|
| 2 |
-
# TJ-1은 학습 제외(에이전트+실시간 eval watcher 전용). global batch = 64×40 = 2560.
|
| 3 |
-
# run1(0.3792 peak@161500 후 하락, 전체최고는 28500=0.3816) 대비 = "OpenMidnight식 LR 레짐"만 교체(코퍼스·bs 동일).
|
| 4 |
-
# ① base_lr 3e-4→2e-4 (실효 → 2e-4×sqrt(2560/1024)=3.16e-4, OpenMidnight base값).
|
| 5 |
-
# ② ★ anneal 제거: epochs 8000(스케줄 지평만 확대=10M iter) + early_stop 182(=227,500 iter≈1 epoch서 실제 정지).
|
| 6 |
-
# → LR이 227.5k/10M=2.3%만 밟아 peak의 ~99.7% 유지(near-constant). wd·teacher-momentum도 near-initial(OM 레짐).
|
| 7 |
-
# ③ warmup 5% (warmup_epochs 9.1 = 11,375 iter).
|
| 8 |
-
# ④ eval_period 5700 → ~40 teacher_checkpoint(0.025ep마다). TJ-1 watcher가 실시간 HEST 곡선.
|
| 9 |
-
# ★ 1 epoch = 582.0M / 2560 = 227,343 iter. 검증필요: 3.16e-4 flat+1ep under-training 여부 → 실시간 곡선으로 조기판단, 상승지속시 early_stop 상향.
|
| 10 |
-
compute_precision:
|
| 11 |
-
grad_scaler: false
|
| 12 |
-
teacher:
|
| 13 |
-
backbone:
|
| 14 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 15 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 16 |
-
dino_head:
|
| 17 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 18 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 19 |
-
ibot_head:
|
| 20 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 21 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 22 |
-
student:
|
| 23 |
-
backbone:
|
| 24 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 25 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 26 |
-
dino_head:
|
| 27 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 28 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: fp32, buffer_dtype: fp32}
|
| 29 |
-
ibot_head:
|
| 30 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 31 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: fp32, buffer_dtype: fp32}
|
| 32 |
-
dino:
|
| 33 |
-
head_n_prototypes: 131072
|
| 34 |
-
head_bottleneck_dim: 384
|
| 35 |
-
do_kde: True
|
| 36 |
-
kde_loss_weight: .05
|
| 37 |
-
koleo_loss_weight: 0
|
| 38 |
-
do_koleo: False
|
| 39 |
-
ibot:
|
| 40 |
-
loss_weight: 1.0
|
| 41 |
-
mask_sample_probability: 0.5
|
| 42 |
-
mask_ratio_min_max:
|
| 43 |
-
- 0.1
|
| 44 |
-
- 0.45
|
| 45 |
-
separate_head: true
|
| 46 |
-
head_n_prototypes: 131072
|
| 47 |
-
train:
|
| 48 |
-
sample_list_path: "openpath:glob=/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data_native/*/tiles/shards/w*/*.tar"
|
| 49 |
-
streaming_from_hf: false
|
| 50 |
-
batch_size_per_gpu: 64 # × 40 GPU(TJ-2~6) = global 2560
|
| 51 |
-
centering: sinkhorn_knopp
|
| 52 |
-
use_pretrained: True # ★ Meta DINOv2 ViT-g/14-reg warm-start
|
| 53 |
-
OFFICIAL_EPOCH_LENGTH: 1250
|
| 54 |
-
saveckp_freq: 8 # resume ckpt = 8×1250 = 10,000 iters
|
| 55 |
-
num_workers: 10
|
| 56 |
-
prefetch_factor: 4
|
| 57 |
-
skip_checkpointer: false
|
| 58 |
-
student:
|
| 59 |
-
arch: vit_giant2
|
| 60 |
-
patch_size: 14
|
| 61 |
-
drop_path_rate: 0.4
|
| 62 |
-
ffn_layer: swiglufused
|
| 63 |
-
block_chunks: 4
|
| 64 |
-
num_register_tokens: 4
|
| 65 |
-
interpolate_antialias: true
|
| 66 |
-
interpolate_offset: 0.0
|
| 67 |
-
teacher:
|
| 68 |
-
momentum_teacher: 0.994
|
| 69 |
-
warmup_teacher_temp_epochs: 41 # 0.27 epoch = 51,250 iters (epochs와 독립, run1과 동일)
|
| 70 |
-
optim:
|
| 71 |
-
epochs: 8000 # ★ 스케줄 지평만 확대(=10,000,000 iter). LR/wd/momentum near-constant.
|
| 72 |
-
early_stop: 276 # ★ native 1 full epoch: 276×1250=345,000 iter(=1.06ep, 5750×60 정렬). run2값 184(230k=0.71ep native) 교정
|
| 73 |
-
weight_decay_end: 0.2 # 8000ep 지평이라 227.5k선 wd≈0.040(near-initial, 미도달)
|
| 74 |
-
base_lr: 2.0e-04 # ★ sqrt_wrt_1024 → 실효 3.16e-4 @batch2560 (OpenMidnight base)
|
| 75 |
-
warmup_epochs: 9 # ★ 11,250 iter ≈ 4.9% (정수 필수: np.linspace(...,warmup_iters)가 int만 허용. 9.1이면 crash)
|
| 76 |
-
freeze_last_layer_epochs: 1 # 1250 iters
|
| 77 |
-
layerwise_decay: 1.0
|
| 78 |
-
crops:
|
| 79 |
-
local_crops_size: 98
|
| 80 |
-
evaluation:
|
| 81 |
-
eval_period_iterations: 5750 # ★ teacher_checkpoint 40개(5750×1..40=230,000, 정지점 포함). TJ-1 watcher가 실시간 probe.
|
| 82 |
-
bach_root: ""
|
| 83 |
-
breakhis_root: ""
|
| 84 |
-
pcam_root: ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/train/openpath_vitg14_run6_native_gram.yaml
DELETED
|
@@ -1,93 +0,0 @@
|
|
| 1 |
-
# ★★ OpenPath run6 — native 40× 코퍼스 + gram anchoring(weight 40, 앵커=run5 native peak 0.3834). native 하락 방지 시도.
|
| 2 |
-
# TJ-1은 학습 제외(에이전트+실시간 eval watcher 전용). global batch = 64×40 = 2560.
|
| 3 |
-
# run1(0.3792 peak@161500 후 하락, 전체최고는 28500=0.3816) 대비 = "OpenMidnight식 LR 레짐"만 교체(코퍼스·bs 동일).
|
| 4 |
-
# ① base_lr 3e-4→2e-4 (실효 → 2e-4×sqrt(2560/1024)=3.16e-4, OpenMidnight base값).
|
| 5 |
-
# ② ★ anneal 제거: epochs 8000(스케줄 지평만 확대=10M iter) + early_stop 182(=227,500 iter≈1 epoch서 실제 정지).
|
| 6 |
-
# → LR이 227.5k/10M=2.3%만 밟아 peak의 ~99.7% 유지(near-constant). wd·teacher-momentum도 near-initial(OM 레짐).
|
| 7 |
-
# ③ warmup 5% (warmup_epochs 9.1 = 11,375 iter).
|
| 8 |
-
# ④ eval_period 5700 → ~40 teacher_checkpoint(0.025ep마다). TJ-1 watcher가 실시간 HEST 곡선.
|
| 9 |
-
# ★ 1 epoch = 582.0M / 2560 = 227,343 iter. 검증필요: 3.16e-4 flat+1ep under-training 여부 → 실시간 곡선으로 조기판단, 상승지속시 early_stop 상향.
|
| 10 |
-
compute_precision:
|
| 11 |
-
grad_scaler: false
|
| 12 |
-
teacher:
|
| 13 |
-
backbone:
|
| 14 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 15 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 16 |
-
dino_head:
|
| 17 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 18 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 19 |
-
ibot_head:
|
| 20 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 21 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 22 |
-
student:
|
| 23 |
-
backbone:
|
| 24 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 25 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
|
| 26 |
-
dino_head:
|
| 27 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 28 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: fp32, buffer_dtype: fp32}
|
| 29 |
-
ibot_head:
|
| 30 |
-
sharding_strategy: SHARD_GRAD_OP
|
| 31 |
-
mixed_precision: {param_dtype: bf16, reduce_dtype: fp32, buffer_dtype: fp32}
|
| 32 |
-
dino:
|
| 33 |
-
head_n_prototypes: 131072
|
| 34 |
-
head_bottleneck_dim: 384
|
| 35 |
-
do_kde: True
|
| 36 |
-
kde_loss_weight: .05
|
| 37 |
-
koleo_loss_weight: 0
|
| 38 |
-
do_koleo: False
|
| 39 |
-
ibot:
|
| 40 |
-
loss_weight: 1.0
|
| 41 |
-
mask_sample_probability: 0.5
|
| 42 |
-
mask_ratio_min_max:
|
| 43 |
-
- 0.1
|
| 44 |
-
- 0.45
|
| 45 |
-
separate_head: true
|
| 46 |
-
head_n_prototypes: 131072
|
| 47 |
-
train:
|
| 48 |
-
sample_list_path: "openpath:glob=/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data_native/*/tiles/shards/w*/*.tar"
|
| 49 |
-
streaming_from_hf: false
|
| 50 |
-
batch_size_per_gpu: 64 # × 40 GPU(TJ-2~6) = global 2560
|
| 51 |
-
centering: sinkhorn_knopp
|
| 52 |
-
use_pretrained: True # ★ Meta DINOv2 ViT-g/14-reg warm-start
|
| 53 |
-
OFFICIAL_EPOCH_LENGTH: 1250
|
| 54 |
-
saveckp_freq: 8 # resume ckpt = 8×1250 = 10,000 iters
|
| 55 |
-
num_workers: 10
|
| 56 |
-
prefetch_factor: 4
|
| 57 |
-
skip_checkpointer: false
|
| 58 |
-
student:
|
| 59 |
-
arch: vit_giant2
|
| 60 |
-
patch_size: 14
|
| 61 |
-
drop_path_rate: 0.4
|
| 62 |
-
ffn_layer: swiglufused
|
| 63 |
-
block_chunks: 4
|
| 64 |
-
num_register_tokens: 4
|
| 65 |
-
interpolate_antialias: true
|
| 66 |
-
interpolate_offset: 0.0
|
| 67 |
-
teacher:
|
| 68 |
-
momentum_teacher: 0.994
|
| 69 |
-
warmup_teacher_temp_epochs: 41 # 0.27 epoch = 51,250 iters (epochs와 독립, run1과 동일)
|
| 70 |
-
optim:
|
| 71 |
-
epochs: 8000 # ★ 스케줄 지평만 확대(=10,000,000 iter). LR/wd/momentum near-constant.
|
| 72 |
-
early_stop: 276 # ★ native 1 full epoch: 276×1250=345,000 iter(=1.06ep, 5750×60 정렬). run2값 184(230k=0.71ep native) 교정
|
| 73 |
-
weight_decay_end: 0.2 # 8000ep 지평이라 227.5k선 wd≈0.040(near-initial, 미도달)
|
| 74 |
-
base_lr: 2.0e-04 # ★ sqrt_wrt_1024 → 실효 3.16e-4 @batch2560 (OpenMidnight base)
|
| 75 |
-
warmup_epochs: 9 # ★ 11,250 iter ≈ 4.9% (정수 필수: np.linspace(...,warmup_iters)가 int만 허용. 9.1이면 crash)
|
| 76 |
-
freeze_last_layer_epochs: 1 # 1250 iters
|
| 77 |
-
layerwise_decay: 1.0
|
| 78 |
-
crops:
|
| 79 |
-
local_crops_size: 98
|
| 80 |
-
evaluation:
|
| 81 |
-
eval_period_iterations: 5750 # ★ teacher_checkpoint 40개(5750×1..40=230,000, 정지점 포함). TJ-1 watcher가 실시간 probe.
|
| 82 |
-
bach_root: ""
|
| 83 |
-
breakhis_root: ""
|
| 84 |
-
pcam_root: ""
|
| 85 |
-
|
| 86 |
-
gram:
|
| 87 |
-
use_loss: true
|
| 88 |
-
normalized: true
|
| 89 |
-
remove_neg: true
|
| 90 |
-
loss_weight: 40.0 # ★ run4(20)의 2배 — native 가파른 하락(0.3834→0.36) 잡으려 강화. 목표 기여 ~5~8%. 과제약이면 실시간곡선서 감지→하향
|
| 91 |
-
it_first_update: 57500 # native peak(~63k) 직전 활성 → ramp 후 peak~하락 구간 커버
|
| 92 |
-
ramp_iters: 3000 # 57.5k~60.5k ramp
|
| 93 |
-
ckpt: /NHNHOME/WORKSPACE/0526040027_A/OpenPath/data/runs/om_vitg14_run5/eval/training_63250/teacher_checkpoint.pth
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/train/openpath_vitg14_testB.yaml
DELETED
|
@@ -1,54 +0,0 @@
|
|
| 1 |
-
# Test B (ViT-g/14) — 그들 TCGA-12K parquet + 우리 수정 파이프라인.
|
| 2 |
-
# 목적: ViT-L Test B가 100k에서 0.299로 0.39 미달 → 아키텍처(ViT-g) 격차인지 검증.
|
| 3 |
-
# OpenMidnight vitg14_reg4 레시피 그대로 + 데이터만 parquet(버퍼50000 수정 로더).
|
| 4 |
-
# 48 GPU × batch 8 = global 384 (= OpenMidnight 1-node와 동일 batch/LR, 클린 비교).
|
| 5 |
-
dino:
|
| 6 |
-
head_n_prototypes: 131072
|
| 7 |
-
head_bottleneck_dim: 384
|
| 8 |
-
do_kde: True
|
| 9 |
-
kde_loss_weight: .05
|
| 10 |
-
koleo_loss_weight: 0
|
| 11 |
-
do_koleo: False
|
| 12 |
-
ibot:
|
| 13 |
-
loss_weight: 1.0
|
| 14 |
-
mask_sample_probability: 0.5
|
| 15 |
-
mask_ratio_min_max:
|
| 16 |
-
- 0.1
|
| 17 |
-
- 0.45
|
| 18 |
-
separate_head: true
|
| 19 |
-
head_n_prototypes: 131072
|
| 20 |
-
train:
|
| 21 |
-
sample_list_path: "parquet:glob=/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data/external/tcga12k_parquet/1/*.parquet"
|
| 22 |
-
streaming_from_hf: false
|
| 23 |
-
batch_size_per_gpu: 8 # × 48 GPU = global 384 (OpenMidnight 동일)
|
| 24 |
-
centering: sinkhorn_knopp
|
| 25 |
-
use_pretrained: True # ★ Meta DINOv2 ViT-g/14-reg warm-start
|
| 26 |
-
OFFICIAL_EPOCH_LENGTH: 1250
|
| 27 |
-
num_workers: 12
|
| 28 |
-
prefetch_factor: 4
|
| 29 |
-
skip_checkpointer: true
|
| 30 |
-
student:
|
| 31 |
-
arch: vit_giant2
|
| 32 |
-
patch_size: 14
|
| 33 |
-
drop_path_rate: 0.4
|
| 34 |
-
ffn_layer: swiglufused # ★ Meta vitg14 = swiglu
|
| 35 |
-
block_chunks: 4
|
| 36 |
-
num_register_tokens: 4
|
| 37 |
-
interpolate_antialias: true
|
| 38 |
-
interpolate_offset: 0.0
|
| 39 |
-
teacher:
|
| 40 |
-
momentum_teacher: 0.994
|
| 41 |
-
optim:
|
| 42 |
-
epochs: 110
|
| 43 |
-
early_stop: 110
|
| 44 |
-
weight_decay_end: 0.2
|
| 45 |
-
base_lr: 3.0e-04
|
| 46 |
-
warmup_epochs: 10
|
| 47 |
-
layerwise_decay: 1.0
|
| 48 |
-
crops:
|
| 49 |
-
local_crops_size: 98
|
| 50 |
-
evaluation:
|
| 51 |
-
eval_period_iterations: 2500
|
| 52 |
-
bach_root: ""
|
| 53 |
-
breakhis_root: ""
|
| 54 |
-
pcam_root: ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/train/openpath_vitl14_reg4.yaml
DELETED
|
@@ -1,58 +0,0 @@
|
|
| 1 |
-
# OpenPath × OpenMidnight — ViT-L/14 warm-start 검증 런.
|
| 2 |
-
# 목적: "Meta DINOv2 warm-start + OpenMidnight 레시피(LR/reg4/sinkhorn/KDE/HED/early-stop)
|
| 3 |
-
# + 우리 full-corpus 데이터"가 v2(scratch) 실패를 뒤집는지 단일 노드에서 깨끗이 격리.
|
| 4 |
-
# v2 대비 핵심 변경: scratch→warm-start(use_pretrained), reg 0→4, centering→sinkhorn_knopp,
|
| 5 |
-
# KoLeo→KDE, 1.475M완주→137.5k early-stop. base_lr는 단일노드(global 384) 기준 보수적.
|
| 6 |
-
# 백본: ViT-L/14 (Meta dinov2_vitl14_reg에서 warm-start → ffn_layer MUST be "mlp").
|
| 7 |
-
dino:
|
| 8 |
-
head_n_prototypes: 131072
|
| 9 |
-
head_bottleneck_dim: 384
|
| 10 |
-
do_kde: True
|
| 11 |
-
kde_loss_weight: .05
|
| 12 |
-
koleo_loss_weight: 0
|
| 13 |
-
do_koleo: False
|
| 14 |
-
ibot:
|
| 15 |
-
loss_weight: 1.0
|
| 16 |
-
mask_sample_probability: 0.5
|
| 17 |
-
mask_ratio_min_max:
|
| 18 |
-
- 0.1
|
| 19 |
-
- 0.45
|
| 20 |
-
separate_head: true
|
| 21 |
-
head_n_prototypes: 131072
|
| 22 |
-
train:
|
| 23 |
-
# openpath: 분기 → dinov2/data/openpath_wds.py (tar shard 스트리밍, full corpus 66k WSI)
|
| 24 |
-
sample_list_path: "openpath:glob=/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data/tiles/shards/w*/*.tar:split=/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data/splits/pretrain_train.txt"
|
| 25 |
-
streaming_from_hf: false
|
| 26 |
-
batch_size_per_gpu: 48 # 단일 노드 8 GPU → global 384 (= OpenMidnight 1node와 동일)
|
| 27 |
-
centering: sinkhorn_knopp
|
| 28 |
-
use_pretrained: True # ★ Meta DINOv2 ViT-L/14-reg warm-start
|
| 29 |
-
OFFICIAL_EPOCH_LENGTH: 1250
|
| 30 |
-
num_workers: 16
|
| 31 |
-
prefetch_factor: 6
|
| 32 |
-
skip_checkpointer: false # resume 가능하게(다중 시간 런)
|
| 33 |
-
student:
|
| 34 |
-
arch: vit_large
|
| 35 |
-
patch_size: 14
|
| 36 |
-
drop_path_rate: 0.4
|
| 37 |
-
ffn_layer: "mlp" # ★ Meta vitl14 = MLP (swiglu 아님) — warm-start 로드 일치 필수
|
| 38 |
-
block_chunks: 4
|
| 39 |
-
num_register_tokens: 4 # ★ Meta dinov2_vitl14_reg = 4 registers
|
| 40 |
-
interpolate_antialias: true
|
| 41 |
-
interpolate_offset: 0.0
|
| 42 |
-
teacher:
|
| 43 |
-
momentum_teacher: 0.994
|
| 44 |
-
optim:
|
| 45 |
-
epochs: 110 # ×1250 = 137,500 step
|
| 46 |
-
early_stop: 110
|
| 47 |
-
weight_decay_end: 0.2
|
| 48 |
-
base_lr: 3.0e-04 # 보수적(warm-start). 실효 LR = 3e-4×sqrt(384/1024)≈1.84e-4
|
| 49 |
-
warmup_epochs: 10
|
| 50 |
-
layerwise_decay: 1.0
|
| 51 |
-
crops:
|
| 52 |
-
local_crops_size: 98
|
| 53 |
-
evaluation:
|
| 54 |
-
eval_period_iterations: 2500 # teacher 체크포인트 자주 저장(step별 probe용)
|
| 55 |
-
# in-training eva 평가는 비활성(빈 root → graceful skip). 평가는 외부 검증 probe로 수행.
|
| 56 |
-
bach_root: ""
|
| 57 |
-
breakhis_root: ""
|
| 58 |
-
pcam_root: ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/train/openpath_vitl14_reg4_curated.yaml
DELETED
|
@@ -1,58 +0,0 @@
|
|
| 1 |
-
# OpenPath × OpenMidnight — ViT-L/14 warm-start 검증 런.
|
| 2 |
-
# 목적: "Meta DINOv2 warm-start + OpenMidnight 레시피(LR/reg4/sinkhorn/KDE/HED/early-stop)
|
| 3 |
-
# + 우리 full-corpus 데이터"가 v2(scratch) 실패를 뒤집는지 단일 노드에서 깨끗이 격리.
|
| 4 |
-
# v2 대비 핵심 변경: scratch→warm-start(use_pretrained), reg 0→4, centering→sinkhorn_knopp,
|
| 5 |
-
# KoLeo→KDE, 1.475M완주→137.5k early-stop. base_lr는 단일노드(global 384) 기준 보수적.
|
| 6 |
-
# 백본: ViT-L/14 (Meta dinov2_vitl14_reg에서 warm-start → ffn_layer MUST be "mlp").
|
| 7 |
-
dino:
|
| 8 |
-
head_n_prototypes: 131072
|
| 9 |
-
head_bottleneck_dim: 384
|
| 10 |
-
do_kde: True
|
| 11 |
-
kde_loss_weight: .05
|
| 12 |
-
koleo_loss_weight: 0
|
| 13 |
-
do_koleo: False
|
| 14 |
-
ibot:
|
| 15 |
-
loss_weight: 1.0
|
| 16 |
-
mask_sample_probability: 0.5
|
| 17 |
-
mask_ratio_min_max:
|
| 18 |
-
- 0.1
|
| 19 |
-
- 0.45
|
| 20 |
-
separate_head: true
|
| 21 |
-
head_n_prototypes: 131072
|
| 22 |
-
train:
|
| 23 |
-
# openpath: 분기 → dinov2/data/openpath_wds.py (tar shard 스트리밍, 큐레이션: 암 41659 WSI @ 20x [GTEx 제거+mag20])
|
| 24 |
-
sample_list_path: "openpath:glob=/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data/tiles/shards/w*/*.tar:split=/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data/splits/pretrain_train_cancer.txt:mag=20"
|
| 25 |
-
streaming_from_hf: false
|
| 26 |
-
batch_size_per_gpu: 48 # 단일 노드 8 GPU → global 384 (= OpenMidnight 1node와 동일)
|
| 27 |
-
centering: sinkhorn_knopp
|
| 28 |
-
use_pretrained: True # ★ Meta DINOv2 ViT-L/14-reg warm-start
|
| 29 |
-
OFFICIAL_EPOCH_LENGTH: 1250
|
| 30 |
-
num_workers: 16
|
| 31 |
-
prefetch_factor: 6
|
| 32 |
-
skip_checkpointer: false # resume 가능하게(다중 시간 런)
|
| 33 |
-
student:
|
| 34 |
-
arch: vit_large
|
| 35 |
-
patch_size: 14
|
| 36 |
-
drop_path_rate: 0.4
|
| 37 |
-
ffn_layer: "mlp" # ★ Meta vitl14 = MLP (swiglu 아님) — warm-start 로드 일치 필수
|
| 38 |
-
block_chunks: 4
|
| 39 |
-
num_register_tokens: 4 # ★ Meta dinov2_vitl14_reg = 4 registers
|
| 40 |
-
interpolate_antialias: true
|
| 41 |
-
interpolate_offset: 0.0
|
| 42 |
-
teacher:
|
| 43 |
-
momentum_teacher: 0.994
|
| 44 |
-
optim:
|
| 45 |
-
epochs: 110 # ×1250 = 137,500 step
|
| 46 |
-
early_stop: 110
|
| 47 |
-
weight_decay_end: 0.2
|
| 48 |
-
base_lr: 3.0e-04 # 보수적(warm-start). 실효 LR = 3e-4×sqrt(384/1024)≈1.84e-4
|
| 49 |
-
warmup_epochs: 10
|
| 50 |
-
layerwise_decay: 1.0
|
| 51 |
-
crops:
|
| 52 |
-
local_crops_size: 98
|
| 53 |
-
evaluation:
|
| 54 |
-
eval_period_iterations: 2500 # teacher 체크포인트 자주 저장(step별 probe용)
|
| 55 |
-
# in-training eva 평가는 비활성(빈 root → graceful skip). 평가는 외부 검증 probe로 수행.
|
| 56 |
-
bach_root: ""
|
| 57 |
-
breakhis_root: ""
|
| 58 |
-
pcam_root: ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/train/openpath_vitl14_testB.yaml
DELETED
|
@@ -1,59 +0,0 @@
|
|
| 1 |
-
# OpenPath × OpenMidnight — ViT-L/14 warm-start 검증 런.
|
| 2 |
-
# 목적: "Meta DINOv2 warm-start + OpenMidnight 레시피(LR/reg4/sinkhorn/KDE/HED/early-stop)
|
| 3 |
-
# + 우리 full-corpus 데이터"가 v2(scratch) 실패를 뒤집는지 단일 노드에서 깨끗이 격리.
|
| 4 |
-
# v2 대비 핵심 변경: scratch→warm-start(use_pretrained), reg 0→4, centering→sinkhorn_knopp,
|
| 5 |
-
# KoLeo→KDE, 1.475M완주→137.5k early-stop. base_lr는 단일노드(global 384) 기준 보수적.
|
| 6 |
-
# 백본: ViT-L/14 (Meta dinov2_vitl14_reg에서 warm-start → ffn_layer MUST be "mlp").
|
| 7 |
-
dino:
|
| 8 |
-
head_n_prototypes: 131072
|
| 9 |
-
head_bottleneck_dim: 384
|
| 10 |
-
do_kde: True
|
| 11 |
-
kde_loss_weight: .05
|
| 12 |
-
koleo_loss_weight: 0
|
| 13 |
-
do_koleo: False
|
| 14 |
-
ibot:
|
| 15 |
-
loss_weight: 1.0
|
| 16 |
-
mask_sample_probability: 0.5
|
| 17 |
-
mask_ratio_min_max:
|
| 18 |
-
- 0.1
|
| 19 |
-
- 0.45
|
| 20 |
-
separate_head: true
|
| 21 |
-
head_n_prototypes: 131072
|
| 22 |
-
train:
|
| 23 |
-
# openpath: 분기 → dinov2/data/openpath_wds.py (tar shard 스트리밍, full corpus 66k WSI)
|
| 24 |
-
sample_list_path: "parquet:glob=/NHNHOME/WORKSPACE/0526040027_A/OpenPath/data/external/tcga12k_parquet/1/*.parquet"
|
| 25 |
-
streaming_from_hf: false
|
| 26 |
-
|
| 27 |
-
batch_size_per_gpu: 48 # 단일 노드 8 GPU → global 384 (= OpenMidnight 1node와 동일)
|
| 28 |
-
centering: sinkhorn_knopp
|
| 29 |
-
use_pretrained: True # ★ Meta DINOv2 ViT-L/14-reg warm-start
|
| 30 |
-
OFFICIAL_EPOCH_LENGTH: 1250
|
| 31 |
-
num_workers: 16
|
| 32 |
-
prefetch_factor: 6
|
| 33 |
-
skip_checkpointer: false # resume 가능하게(다중 시간 런)
|
| 34 |
-
student:
|
| 35 |
-
arch: vit_large
|
| 36 |
-
patch_size: 14
|
| 37 |
-
drop_path_rate: 0.4
|
| 38 |
-
ffn_layer: "mlp" # ★ Meta vitl14 = MLP (swiglu 아님) — warm-start 로드 일치 필수
|
| 39 |
-
block_chunks: 4
|
| 40 |
-
num_register_tokens: 4 # ★ Meta dinov2_vitl14_reg = 4 registers
|
| 41 |
-
interpolate_antialias: true
|
| 42 |
-
interpolate_offset: 0.0
|
| 43 |
-
teacher:
|
| 44 |
-
momentum_teacher: 0.994
|
| 45 |
-
optim:
|
| 46 |
-
epochs: 110 # ×1250 = 137,500 step
|
| 47 |
-
early_stop: 110
|
| 48 |
-
weight_decay_end: 0.2
|
| 49 |
-
base_lr: 3.0e-04 # 보수적(warm-start). 실효 LR = 3e-4×sqrt(384/1024)≈1.84e-4
|
| 50 |
-
warmup_epochs: 10
|
| 51 |
-
layerwise_decay: 1.0
|
| 52 |
-
crops:
|
| 53 |
-
local_crops_size: 98
|
| 54 |
-
evaluation:
|
| 55 |
-
eval_period_iterations: 2500 # teacher 체크포인트 자주 저장(step별 probe용)
|
| 56 |
-
# in-training eva 평가는 비활성(빈 root → graceful skip). 평가는 외부 검증 probe로 수행.
|
| 57 |
-
bach_root: ""
|
| 58 |
-
breakhis_root: ""
|
| 59 |
-
pcam_root: ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/train/vitg14_reg4.yaml
DELETED
|
@@ -1,51 +0,0 @@
|
|
| 1 |
-
dino:
|
| 2 |
-
head_n_prototypes: 131072
|
| 3 |
-
head_bottleneck_dim: 384
|
| 4 |
-
do_kde: True
|
| 5 |
-
kde_loss_weight: .05
|
| 6 |
-
koleo_loss_weight: 0
|
| 7 |
-
do_koleo: False
|
| 8 |
-
ibot:
|
| 9 |
-
loss_weight: 1.0
|
| 10 |
-
mask_sample_probability: 0.5
|
| 11 |
-
mask_ratio_min_max:
|
| 12 |
-
- 0.1
|
| 13 |
-
- 0.45
|
| 14 |
-
separate_head: true
|
| 15 |
-
head_n_prototypes: 131072
|
| 16 |
-
train:
|
| 17 |
-
sample_list_path: /block/TCGA/sample_dataset_30.txt # gives paths to svs files for data loading if streaming_from_hf=False
|
| 18 |
-
streaming_from_hf: false # if True, dataset is streamed from HuggingFace Hub, and sample_list_path is ignored. You should lower num_workers to 4
|
| 19 |
-
streaming_dataset_path: medarc/TCGA-12K-parquet
|
| 20 |
-
batch_size_per_gpu: 48
|
| 21 |
-
centering: sinkhorn_knopp
|
| 22 |
-
use_pretrained: True
|
| 23 |
-
OFFICIAL_EPOCH_LENGTH: 1250
|
| 24 |
-
num_workers: 24 # set to 4 if streaming_from_hf=True
|
| 25 |
-
prefetch_factor: 8
|
| 26 |
-
skip_checkpointer: true # set True if you don't need to resume training (this yields training speedup and less space usage)
|
| 27 |
-
student:
|
| 28 |
-
arch: vit_giant2
|
| 29 |
-
patch_size: 14
|
| 30 |
-
drop_path_rate: 0.4
|
| 31 |
-
ffn_layer: swiglufused
|
| 32 |
-
block_chunks: 4
|
| 33 |
-
num_register_tokens: 4
|
| 34 |
-
interpolate_antialias: true # positional embeddings are interpolated with antialiasing
|
| 35 |
-
interpolate_offset: 0.0
|
| 36 |
-
teacher:
|
| 37 |
-
momentum_teacher: 0.994
|
| 38 |
-
optim:
|
| 39 |
-
epochs: 110 # epochs and early_stop for 137.5k pretraining steps
|
| 40 |
-
early_stop: 110
|
| 41 |
-
weight_decay_end: 0.2
|
| 42 |
-
base_lr: 3.0e-04 # learning rate for vit_giant2
|
| 43 |
-
warmup_epochs: 10
|
| 44 |
-
layerwise_decay: 1.0
|
| 45 |
-
crops:
|
| 46 |
-
local_crops_size: 98
|
| 47 |
-
evaluation:
|
| 48 |
-
eval_period_iterations: 2500 # save checkpoints frequently for evaluation
|
| 49 |
-
bach_root: /block/eva-data/bach
|
| 50 |
-
breakhis_root: /block/eva-data/breakhis
|
| 51 |
-
pcam_root: /block/eva-data/patch_camelyon
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/configs/train/vits14_reg4.yaml
DELETED
|
@@ -1,51 +0,0 @@
|
|
| 1 |
-
dino:
|
| 2 |
-
head_n_prototypes: 131072
|
| 3 |
-
head_bottleneck_dim: 384
|
| 4 |
-
do_kde: True
|
| 5 |
-
kde_loss_weight: .05
|
| 6 |
-
koleo_loss_weight: 0
|
| 7 |
-
do_koleo: False
|
| 8 |
-
ibot:
|
| 9 |
-
loss_weight: 1.0
|
| 10 |
-
mask_sample_probability: 0.5
|
| 11 |
-
mask_ratio_min_max:
|
| 12 |
-
- 0.1
|
| 13 |
-
- 0.45
|
| 14 |
-
separate_head: true
|
| 15 |
-
head_n_prototypes: 131072
|
| 16 |
-
train:
|
| 17 |
-
sample_list_path: /block/TCGA/sample_dataset_30.txt # gives paths to svs files for data loading if streaming_from_hf=False
|
| 18 |
-
streaming_from_hf: false # if True, dataset is streamed from HuggingFace Hub, and sample_list_path is ignored. You should lower num_workers to 4
|
| 19 |
-
streaming_dataset_path: medarc/TCGA-12K-parquet
|
| 20 |
-
batch_size_per_gpu: 48
|
| 21 |
-
centering: sinkhorn_knopp
|
| 22 |
-
use_pretrained: True
|
| 23 |
-
OFFICIAL_EPOCH_LENGTH: 1250
|
| 24 |
-
num_workers: 24 # set to 4 if streaming_from_hf=True
|
| 25 |
-
prefetch_factor: 8
|
| 26 |
-
skip_checkpointer: true # set True if you don't need to resume training (this yields training speedup and less space usage)
|
| 27 |
-
student:
|
| 28 |
-
arch: vit_small
|
| 29 |
-
patch_size: 14
|
| 30 |
-
drop_path_rate: 0.1 # drop path rate for vit_small
|
| 31 |
-
ffn_layer: swiglufused
|
| 32 |
-
block_chunks: 4
|
| 33 |
-
num_register_tokens: 4
|
| 34 |
-
interpolate_antialias: true # positional embeddings are interpolated with antialiasing
|
| 35 |
-
interpolate_offset: 0.0
|
| 36 |
-
teacher:
|
| 37 |
-
momentum_teacher: 0.994
|
| 38 |
-
optim:
|
| 39 |
-
epochs: 110 # epochs and early_stop for 137.5k pretraining steps
|
| 40 |
-
early_stop: 110
|
| 41 |
-
weight_decay_end: 0.2
|
| 42 |
-
base_lr: 7.0e-04 # learning rate for vit_small
|
| 43 |
-
warmup_epochs: 10
|
| 44 |
-
layerwise_decay: 1.0
|
| 45 |
-
crops:
|
| 46 |
-
local_crops_size: 98
|
| 47 |
-
evaluation:
|
| 48 |
-
eval_period_iterations: 2500 # save checkpoints frequently for evaluation
|
| 49 |
-
bach_root: /block/eva-data/bach
|
| 50 |
-
breakhis_root: /block/eva-data/breakhis
|
| 51 |
-
pcam_root: /block/eva-data/patch_camelyon
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/data/__init__.py
DELETED
|
@@ -1,10 +0,0 @@
|
|
| 1 |
-
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
-
#
|
| 3 |
-
# This source code is licensed under the Apache License, Version 2.0
|
| 4 |
-
# found in the LICENSE file in the root directory of this source tree.
|
| 5 |
-
|
| 6 |
-
from .adapters import DatasetWithEnumeratedTargets
|
| 7 |
-
from .loaders import make_data_loader, make_dataset, SamplerType
|
| 8 |
-
from .collate import collate_data_and_cast
|
| 9 |
-
from .masking import MaskingGenerator
|
| 10 |
-
from .augmentations import DataAugmentationDINO
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/data/adapters.py
DELETED
|
@@ -1,28 +0,0 @@
|
|
| 1 |
-
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
-
#
|
| 3 |
-
# This source code is licensed under the Apache License, Version 2.0
|
| 4 |
-
# found in the LICENSE file in the root directory of this source tree.
|
| 5 |
-
|
| 6 |
-
from typing import Any, Tuple
|
| 7 |
-
|
| 8 |
-
from torch.utils.data import Dataset
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
class DatasetWithEnumeratedTargets(Dataset):
|
| 12 |
-
def __init__(self, dataset):
|
| 13 |
-
self._dataset = dataset
|
| 14 |
-
|
| 15 |
-
def get_image_data(self, index: int) -> bytes:
|
| 16 |
-
return self._dataset.get_image_data(index)
|
| 17 |
-
|
| 18 |
-
def get_target(self, index: int) -> Tuple[Any, int]:
|
| 19 |
-
target = self._dataset.get_target(index)
|
| 20 |
-
return (index, target)
|
| 21 |
-
|
| 22 |
-
def __getitem__(self, index: int) -> Tuple[Any, Tuple[Any, int]]:
|
| 23 |
-
image, target = self._dataset[index]
|
| 24 |
-
target = index if target is None else target
|
| 25 |
-
return image, (index, target)
|
| 26 |
-
|
| 27 |
-
def __len__(self) -> int:
|
| 28 |
-
return len(self._dataset)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/data/augmentations.py
DELETED
|
@@ -1,383 +0,0 @@
|
|
| 1 |
-
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
-
#
|
| 3 |
-
# This source code is licensed under the Apache License, Version 2.0
|
| 4 |
-
# found in the LICENSE file in the root directory of this source tree.
|
| 5 |
-
|
| 6 |
-
import logging
|
| 7 |
-
import cv2
|
| 8 |
-
import numpy as np
|
| 9 |
-
import random
|
| 10 |
-
import torch
|
| 11 |
-
import torchvision
|
| 12 |
-
from torchvision import transforms
|
| 13 |
-
from skimage import color as skimage_color
|
| 14 |
-
from skimage.color import rgb2hed, hed2rgb
|
| 15 |
-
from einops import rearrange
|
| 16 |
-
from PIL import Image
|
| 17 |
-
|
| 18 |
-
from .transforms import (
|
| 19 |
-
GaussianBlur,
|
| 20 |
-
make_normalize_transform,
|
| 21 |
-
)
|
| 22 |
-
|
| 23 |
-
logger = logging.getLogger("dinov2")
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
class RandStainNA(torch.nn.Module):
|
| 27 |
-
"""
|
| 28 |
-
RandStainNA: Random Stain Normalization and Augmentation.
|
| 29 |
-
Bridges stain normalization and augmentation by constraining variable stain styles
|
| 30 |
-
in a practicable range using virtual template generation.
|
| 31 |
-
|
| 32 |
-
Based on: "RandStainNA: Learning Stain-Agnostic Features from Histology Slides
|
| 33 |
-
by Bridging Stain Augmentation and Normalization" (MICCAI 2022)
|
| 34 |
-
|
| 35 |
-
Reference: https://github.com/yiqings/RandStainNA
|
| 36 |
-
"""
|
| 37 |
-
|
| 38 |
-
# Default statistics for LAB color space from CRC dataset
|
| 39 |
-
# These can be overridden by providing a yaml_file
|
| 40 |
-
DEFAULT_LAB_STATS = {
|
| 41 |
-
'L': {'avg': {'mean': 158.033, 'std': 48.792}, 'std': {'mean': 36.899, 'std': 14.383}},
|
| 42 |
-
'A': {'avg': {'mean': 151.187, 'std': 10.958}, 'std': {'mean': 8.134, 'std': 2.822}},
|
| 43 |
-
'B': {'avg': {'mean': 116.812, 'std': 6.643}, 'std': {'mean': 6.129, 'std': 2.013}},
|
| 44 |
-
}
|
| 45 |
-
|
| 46 |
-
# Default statistics for HED color space
|
| 47 |
-
DEFAULT_HED_STATS = {
|
| 48 |
-
'H': {'avg': {'mean': 0.05, 'std': 0.02}, 'std': {'mean': 0.03, 'std': 0.01}},
|
| 49 |
-
'E': {'avg': {'mean': 0.02, 'std': 0.01}, 'std': {'mean': 0.02, 'std': 0.008}},
|
| 50 |
-
'D': {'avg': {'mean': 0.0, 'std': 0.005}, 'std': {'mean': 0.01, 'std': 0.005}},
|
| 51 |
-
}
|
| 52 |
-
|
| 53 |
-
def __init__(
|
| 54 |
-
self,
|
| 55 |
-
color_space='LAB',
|
| 56 |
-
std_hyper=-0.3,
|
| 57 |
-
distribution='normal',
|
| 58 |
-
probability=1.0,
|
| 59 |
-
):
|
| 60 |
-
super().__init__()
|
| 61 |
-
|
| 62 |
-
assert distribution in ['normal', 'laplace', 'uniform'], \
|
| 63 |
-
f"Unsupported distribution: {distribution}"
|
| 64 |
-
assert color_space in ['LAB', 'HSV', 'HED'], \
|
| 65 |
-
f"Unsupported color space: {color_space}"
|
| 66 |
-
|
| 67 |
-
self.color_space = color_space
|
| 68 |
-
self.std_hyper = std_hyper
|
| 69 |
-
self.distribution = distribution
|
| 70 |
-
self.probability = probability
|
| 71 |
-
|
| 72 |
-
if color_space == 'LAB':
|
| 73 |
-
stats = self.DEFAULT_LAB_STATS
|
| 74 |
-
self.channels = ['L', 'A', 'B']
|
| 75 |
-
elif color_space == 'HED':
|
| 76 |
-
stats = self.DEFAULT_HED_STATS
|
| 77 |
-
self.channels = ['H', 'E', 'D']
|
| 78 |
-
else:
|
| 79 |
-
stats = self.DEFAULT_LAB_STATS
|
| 80 |
-
self.channels = ['H', 'S', 'V']
|
| 81 |
-
|
| 82 |
-
self.channel_avgs_mean = [stats[c]['avg']['mean'] for c in self.channels]
|
| 83 |
-
self.channel_avgs_std = [stats[c]['avg']['std'] for c in self.channels]
|
| 84 |
-
self.channel_stds_mean = [stats[c]['std']['mean'] for c in self.channels]
|
| 85 |
-
self.channel_stds_std = [stats[c]['std']['std'] for c in self.channels]
|
| 86 |
-
|
| 87 |
-
def _getavgstd(self, image):
|
| 88 |
-
"""Get mean and std for each channel."""
|
| 89 |
-
avgs = []
|
| 90 |
-
stds = []
|
| 91 |
-
for idx in range(image.shape[2]):
|
| 92 |
-
avgs.append(np.mean(image[:, :, idx]))
|
| 93 |
-
stds.append(np.std(image[:, :, idx]))
|
| 94 |
-
return np.array(avgs), np.array(stds)
|
| 95 |
-
|
| 96 |
-
def _normalize(self, img, img_avgs, img_stds, tar_avgs, tar_stds):
|
| 97 |
-
"""Normalize image to target statistics."""
|
| 98 |
-
img_stds = np.clip(img_stds, 0.0001, 255)
|
| 99 |
-
img = (img - img_avgs) * (tar_stds / img_stds) + tar_avgs
|
| 100 |
-
|
| 101 |
-
if self.color_space in ['LAB', 'HSV']:
|
| 102 |
-
img = np.clip(img, 0, 255).astype(np.uint8)
|
| 103 |
-
|
| 104 |
-
return img
|
| 105 |
-
|
| 106 |
-
def _generate_virtual_template(self):
|
| 107 |
-
"""Generate virtual template statistics based on distribution."""
|
| 108 |
-
tar_avgs = []
|
| 109 |
-
tar_stds = []
|
| 110 |
-
|
| 111 |
-
if self.distribution == 'uniform':
|
| 112 |
-
for idx in range(3):
|
| 113 |
-
tar_avg = np.random.uniform(
|
| 114 |
-
low=self.channel_avgs_mean[idx] - 3 * self.channel_avgs_std[idx],
|
| 115 |
-
high=self.channel_avgs_mean[idx] + 3 * self.channel_avgs_std[idx],
|
| 116 |
-
)
|
| 117 |
-
tar_std = np.random.uniform(
|
| 118 |
-
low=self.channel_stds_mean[idx] - 3 * self.channel_stds_std[idx],
|
| 119 |
-
high=self.channel_stds_mean[idx] + 3 * self.channel_stds_std[idx],
|
| 120 |
-
)
|
| 121 |
-
tar_avgs.append(tar_avg)
|
| 122 |
-
tar_stds.append(tar_std)
|
| 123 |
-
else:
|
| 124 |
-
if self.distribution == 'normal':
|
| 125 |
-
np_distribution = np.random.normal
|
| 126 |
-
else:
|
| 127 |
-
np_distribution = np.random.laplace
|
| 128 |
-
|
| 129 |
-
for idx in range(3):
|
| 130 |
-
tar_avg = np_distribution(
|
| 131 |
-
loc=self.channel_avgs_mean[idx],
|
| 132 |
-
scale=self.channel_avgs_std[idx] * (1 + self.std_hyper),
|
| 133 |
-
)
|
| 134 |
-
tar_std = np_distribution(
|
| 135 |
-
loc=self.channel_stds_mean[idx],
|
| 136 |
-
scale=self.channel_stds_std[idx] * (1 + self.std_hyper),
|
| 137 |
-
)
|
| 138 |
-
tar_avgs.append(tar_avg)
|
| 139 |
-
tar_stds.append(tar_std)
|
| 140 |
-
|
| 141 |
-
return np.array(tar_avgs), np.array(tar_stds)
|
| 142 |
-
|
| 143 |
-
def augment(self, img):
|
| 144 |
-
"""Apply stain augmentation."""
|
| 145 |
-
if isinstance(img, Image.Image):
|
| 146 |
-
image = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
|
| 147 |
-
was_pil = True
|
| 148 |
-
else:
|
| 149 |
-
image = img
|
| 150 |
-
was_pil = False
|
| 151 |
-
|
| 152 |
-
# Color space conversion
|
| 153 |
-
if self.color_space == 'LAB':
|
| 154 |
-
image = cv2.cvtColor(image, cv2.COLOR_BGR2LAB)
|
| 155 |
-
elif self.color_space == 'HSV':
|
| 156 |
-
image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
|
| 157 |
-
elif self.color_space == 'HED':
|
| 158 |
-
image = skimage_color.rgb2hed(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
|
| 159 |
-
|
| 160 |
-
# Generate virtual template and normalize
|
| 161 |
-
tar_avgs, tar_stds = self._generate_virtual_template()
|
| 162 |
-
img_avgs, img_stds = self._getavgstd(image)
|
| 163 |
-
|
| 164 |
-
image = self._normalize(
|
| 165 |
-
img=image,
|
| 166 |
-
img_avgs=img_avgs,
|
| 167 |
-
img_stds=img_stds,
|
| 168 |
-
tar_avgs=tar_avgs,
|
| 169 |
-
tar_stds=tar_stds,
|
| 170 |
-
)
|
| 171 |
-
|
| 172 |
-
# Convert back to BGR/RGB
|
| 173 |
-
if self.color_space == 'LAB':
|
| 174 |
-
image = cv2.cvtColor(image, cv2.COLOR_LAB2BGR)
|
| 175 |
-
elif self.color_space == 'HSV':
|
| 176 |
-
image = cv2.cvtColor(image, cv2.COLOR_HSV2BGR)
|
| 177 |
-
elif self.color_space == 'HED':
|
| 178 |
-
nimg = skimage_color.hed2rgb(image)
|
| 179 |
-
imin = nimg.min()
|
| 180 |
-
imax = nimg.max()
|
| 181 |
-
rsimg = (255 * (nimg - imin) / (imax - imin + 1e-8)).astype('uint8')
|
| 182 |
-
image = cv2.cvtColor(rsimg, cv2.COLOR_RGB2BGR)
|
| 183 |
-
|
| 184 |
-
# Convert back to RGB for PIL
|
| 185 |
-
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
| 186 |
-
|
| 187 |
-
if was_pil:
|
| 188 |
-
return Image.fromarray(image)
|
| 189 |
-
return image
|
| 190 |
-
|
| 191 |
-
def forward(self, img):
|
| 192 |
-
if random.random() > self.probability:
|
| 193 |
-
return img
|
| 194 |
-
return self.augment(img)
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
class hed_mod(torch.nn.Module):
|
| 198 |
-
"""
|
| 199 |
-
HED color space augmentation for H&E stained histopathology images.
|
| 200 |
-
Randomly perturbs Hematoxylin, Eosin, and DAB channels.
|
| 201 |
-
"""
|
| 202 |
-
|
| 203 |
-
def __init__(self, probability=0.5, perturbation_range=0.05):
|
| 204 |
-
super().__init__()
|
| 205 |
-
self.probability = probability
|
| 206 |
-
self.mini = -perturbation_range
|
| 207 |
-
self.maxi = perturbation_range
|
| 208 |
-
|
| 209 |
-
def forward(self, img, label=None):
|
| 210 |
-
if random.random() > self.probability:
|
| 211 |
-
return img
|
| 212 |
-
|
| 213 |
-
if img is not None:
|
| 214 |
-
img = torchvision.transforms.functional.pil_to_tensor(img)
|
| 215 |
-
img = rearrange(img, 'c h w -> h w c')
|
| 216 |
-
hed_image = rgb2hed(img)
|
| 217 |
-
|
| 218 |
-
hed_image[..., 0] += random.uniform(self.mini, self.maxi) # H
|
| 219 |
-
hed_image[..., 1] += random.uniform(self.mini, self.maxi) # E
|
| 220 |
-
hed_image[..., 2] += random.uniform(self.mini, self.maxi) # D
|
| 221 |
-
|
| 222 |
-
hed_image = np.clip(hed_image, 0, 1)
|
| 223 |
-
img = hed2rgb(hed_image)
|
| 224 |
-
|
| 225 |
-
img = rearrange(img, 'h w c -> c h w')
|
| 226 |
-
img = torch.from_numpy(img)
|
| 227 |
-
img = torchvision.transforms.functional.to_pil_image(img)
|
| 228 |
-
|
| 229 |
-
if label is not None:
|
| 230 |
-
label = rearrange(label, 'c h w -> h w c')
|
| 231 |
-
hed_image = rgb2hed(label)
|
| 232 |
-
hed_image[..., 0] += random.uniform(self.mini, self.maxi)
|
| 233 |
-
hed_image[..., 1] += random.uniform(self.mini, self.maxi)
|
| 234 |
-
hed_image[..., 2] += random.uniform(self.mini, self.maxi)
|
| 235 |
-
label = rearrange(label, 'h w c -> c h w')
|
| 236 |
-
label = torch.from_numpy(label)
|
| 237 |
-
return img, label
|
| 238 |
-
|
| 239 |
-
return img
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
class RandomRotation90(torch.nn.Module):
|
| 243 |
-
"""
|
| 244 |
-
Random 90-degree rotation augmentation for histopathology images.
|
| 245 |
-
Pathology images are rotation-invariant, so we can apply 0, 90, 180, or 270 degree rotations.
|
| 246 |
-
"""
|
| 247 |
-
|
| 248 |
-
def __init__(self):
|
| 249 |
-
super().__init__()
|
| 250 |
-
self.angles = [0, 90, 180, 270]
|
| 251 |
-
|
| 252 |
-
def forward(self, img):
|
| 253 |
-
angle = random.choice(self.angles)
|
| 254 |
-
if angle == 0:
|
| 255 |
-
return img
|
| 256 |
-
return transforms.functional.rotate(img, angle)
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
class DataAugmentationDINO(object):
|
| 260 |
-
"""
|
| 261 |
-
Data augmentation pipeline for DINOv2 training on histopathology images.
|
| 262 |
-
|
| 263 |
-
Includes pathology-specific augmentations:
|
| 264 |
-
- RandStainNA: Stain normalization/augmentation in LAB color space
|
| 265 |
-
- HED augmentation: Color space perturbation
|
| 266 |
-
- 90-degree rotations: Rotation invariance
|
| 267 |
-
- Vertical and horizontal flips
|
| 268 |
-
- Gaussian blur
|
| 269 |
-
- Color jitter (no grayscale for H&E images)
|
| 270 |
-
"""
|
| 271 |
-
|
| 272 |
-
def __init__(
|
| 273 |
-
self,
|
| 274 |
-
global_crops_scale,
|
| 275 |
-
local_crops_scale,
|
| 276 |
-
local_crops_number,
|
| 277 |
-
global_crops_size=224,
|
| 278 |
-
local_crops_size=96,
|
| 279 |
-
):
|
| 280 |
-
self.global_crops_scale = global_crops_scale
|
| 281 |
-
self.local_crops_scale = local_crops_scale
|
| 282 |
-
self.local_crops_number = local_crops_number
|
| 283 |
-
self.global_crops_size = global_crops_size
|
| 284 |
-
self.local_crops_size = local_crops_size
|
| 285 |
-
|
| 286 |
-
logger.info("###################################")
|
| 287 |
-
logger.info("Using data augmentation parameters:")
|
| 288 |
-
logger.info(f"global_crops_scale: {global_crops_scale}")
|
| 289 |
-
logger.info(f"local_crops_scale: {local_crops_scale}")
|
| 290 |
-
logger.info(f"local_crops_number: {local_crops_number}")
|
| 291 |
-
logger.info(f"global_crops_size: {global_crops_size}")
|
| 292 |
-
logger.info(f"local_crops_size: {local_crops_size}")
|
| 293 |
-
logger.info("###################################")
|
| 294 |
-
|
| 295 |
-
# Geometric augmentations with rotation and both flips
|
| 296 |
-
self.geometric_augmentation_global = transforms.Compose(
|
| 297 |
-
[
|
| 298 |
-
# RandomRotation90(),
|
| 299 |
-
transforms.RandomResizedCrop(
|
| 300 |
-
global_crops_size, scale=global_crops_scale, interpolation=transforms.InterpolationMode.BICUBIC
|
| 301 |
-
),
|
| 302 |
-
transforms.RandomHorizontalFlip(p=0.5),
|
| 303 |
-
# transforms.RandomVerticalFlip(p=0.5),
|
| 304 |
-
]
|
| 305 |
-
)
|
| 306 |
-
|
| 307 |
-
self.geometric_augmentation_local = transforms.Compose(
|
| 308 |
-
[
|
| 309 |
-
# RandomRotation90(),
|
| 310 |
-
transforms.RandomResizedCrop(
|
| 311 |
-
local_crops_size, scale=local_crops_scale, interpolation=transforms.InterpolationMode.BICUBIC
|
| 312 |
-
),
|
| 313 |
-
transforms.RandomHorizontalFlip(p=0.5),
|
| 314 |
-
# transforms.RandomVerticalFlip(p=0.5),
|
| 315 |
-
]
|
| 316 |
-
)
|
| 317 |
-
|
| 318 |
-
# Normalization (ImageNet stats used by default)
|
| 319 |
-
self.normalize = transforms.Compose(
|
| 320 |
-
[
|
| 321 |
-
transforms.ToTensor(),
|
| 322 |
-
make_normalize_transform(),
|
| 323 |
-
]
|
| 324 |
-
)
|
| 325 |
-
|
| 326 |
-
# Pathology-specific stain augmentations
|
| 327 |
-
randstainna = RandStainNA(
|
| 328 |
-
color_space='LAB',
|
| 329 |
-
std_hyper=-0.3,
|
| 330 |
-
distribution='normal',
|
| 331 |
-
probability=0.5,
|
| 332 |
-
)
|
| 333 |
-
|
| 334 |
-
hed_aug = hed_mod(probability=0.5, perturbation_range=0.05)
|
| 335 |
-
|
| 336 |
-
self.global_transfo1 = transforms.Compose([
|
| 337 |
-
# randstainna,
|
| 338 |
-
hed_aug,
|
| 339 |
-
transforms.RandomApply([transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.1, hue=0.05)], p=0.8),
|
| 340 |
-
transforms.RandomGrayscale(p=0.2),
|
| 341 |
-
GaussianBlur(p=1.0),
|
| 342 |
-
self.normalize
|
| 343 |
-
])
|
| 344 |
-
|
| 345 |
-
self.global_transfo2 = transforms.Compose([
|
| 346 |
-
# randstainna,
|
| 347 |
-
hed_aug,
|
| 348 |
-
transforms.RandomApply([transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.1, hue=0.05)], p=0.8),
|
| 349 |
-
transforms.RandomGrayscale(p=0.2),
|
| 350 |
-
GaussianBlur(p=0.1),
|
| 351 |
-
self.normalize
|
| 352 |
-
])
|
| 353 |
-
|
| 354 |
-
self.local_transfo = transforms.Compose([
|
| 355 |
-
# randstainna,
|
| 356 |
-
hed_aug,
|
| 357 |
-
transforms.RandomApply([transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.1, hue=0.05)], p=0.8),
|
| 358 |
-
transforms.RandomGrayscale(p=0.2),
|
| 359 |
-
GaussianBlur(p=0.5),
|
| 360 |
-
self.normalize
|
| 361 |
-
])
|
| 362 |
-
|
| 363 |
-
def __call__(self, image):
|
| 364 |
-
output = {}
|
| 365 |
-
|
| 366 |
-
# Global crops
|
| 367 |
-
im1_base = self.geometric_augmentation_global(image)
|
| 368 |
-
global_crop_1 = self.global_transfo1(im1_base)
|
| 369 |
-
|
| 370 |
-
im2_base = self.geometric_augmentation_global(image)
|
| 371 |
-
global_crop_2 = self.global_transfo2(im2_base)
|
| 372 |
-
|
| 373 |
-
output["global_crops"] = [global_crop_1, global_crop_2]
|
| 374 |
-
output["global_crops_teacher"] = [global_crop_1, global_crop_2]
|
| 375 |
-
|
| 376 |
-
# Local crops
|
| 377 |
-
local_crops = [
|
| 378 |
-
self.local_transfo(self.geometric_augmentation_local(image)) for _ in range(self.local_crops_number)
|
| 379 |
-
]
|
| 380 |
-
output["local_crops"] = local_crops
|
| 381 |
-
output["offsets"] = ()
|
| 382 |
-
|
| 383 |
-
return output
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/data/collate.py
DELETED
|
@@ -1,62 +0,0 @@
|
|
| 1 |
-
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
-
#
|
| 3 |
-
# This source code is licensed under the Apache License, Version 2.0
|
| 4 |
-
# found in the LICENSE file in the root directory of this source tree.
|
| 5 |
-
|
| 6 |
-
import torch
|
| 7 |
-
import random
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
def collate_data_and_cast(samples_list, mask_ratio_tuple, mask_probability, dtype, n_tokens=None, mask_generator=None):
|
| 11 |
-
# dtype = torch.half # TODO: Remove
|
| 12 |
-
|
| 13 |
-
#So this is our batch basically, just in a list.
|
| 14 |
-
|
| 15 |
-
#print(samples_list[-1][-1])
|
| 16 |
-
|
| 17 |
-
images = []
|
| 18 |
-
indexes = []
|
| 19 |
-
for sample in samples_list:
|
| 20 |
-
images.append(sample[0])
|
| 21 |
-
indexes.append(sample[1])
|
| 22 |
-
|
| 23 |
-
samples_list = images
|
| 24 |
-
|
| 25 |
-
n_global_crops = len(samples_list[0][0]["global_crops"])
|
| 26 |
-
n_local_crops = len(samples_list[0][0]["local_crops"])
|
| 27 |
-
|
| 28 |
-
collated_global_crops = torch.stack([s[0]["global_crops"][i] for i in range(n_global_crops) for s in samples_list])
|
| 29 |
-
|
| 30 |
-
collated_local_crops = torch.stack([s[0]["local_crops"][i] for i in range(n_local_crops) for s in samples_list])
|
| 31 |
-
|
| 32 |
-
B = len(collated_global_crops)
|
| 33 |
-
N = n_tokens
|
| 34 |
-
n_samples_masked = int(B * mask_probability)
|
| 35 |
-
probs = torch.linspace(*mask_ratio_tuple, n_samples_masked + 1)
|
| 36 |
-
upperbound = 0
|
| 37 |
-
masks_list = []
|
| 38 |
-
for i in range(0, n_samples_masked):
|
| 39 |
-
prob_min = probs[i]
|
| 40 |
-
prob_max = probs[i + 1]
|
| 41 |
-
masks_list.append(torch.BoolTensor(mask_generator(int(N * random.uniform(prob_min, prob_max)))))
|
| 42 |
-
upperbound += int(N * prob_max)
|
| 43 |
-
for i in range(n_samples_masked, B):
|
| 44 |
-
masks_list.append(torch.BoolTensor(mask_generator(0)))
|
| 45 |
-
|
| 46 |
-
random.shuffle(masks_list)
|
| 47 |
-
|
| 48 |
-
collated_masks = torch.stack(masks_list).flatten(1)
|
| 49 |
-
mask_indices_list = collated_masks.flatten().nonzero().flatten()
|
| 50 |
-
|
| 51 |
-
masks_weight = (1 / collated_masks.sum(-1).clamp(min=1.0)).unsqueeze(-1).expand_as(collated_masks)[collated_masks]
|
| 52 |
-
|
| 53 |
-
return {
|
| 54 |
-
"collated_global_crops": collated_global_crops.to(dtype),
|
| 55 |
-
"collated_local_crops": collated_local_crops.to(dtype),
|
| 56 |
-
"collated_masks": collated_masks,
|
| 57 |
-
"mask_indices_list": mask_indices_list,
|
| 58 |
-
"masks_weight": masks_weight,
|
| 59 |
-
"upperbound": upperbound,
|
| 60 |
-
"n_masked_patches": torch.full((1,), fill_value=mask_indices_list.shape[0], dtype=torch.long),
|
| 61 |
-
"indexes": indexes
|
| 62 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/data/datasets/__init__.py
DELETED
|
@@ -1,9 +0,0 @@
|
|
| 1 |
-
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
-
#
|
| 3 |
-
# This source code is licensed under the Apache License, Version 2.0
|
| 4 |
-
# found in the LICENSE file in the root directory of this source tree.
|
| 5 |
-
|
| 6 |
-
from .image_net import ImageNet
|
| 7 |
-
from .image_net_22k import ImageNet22k
|
| 8 |
-
from .test_data import TestVisionDataset
|
| 9 |
-
from .slide_dataset import SlideDataset
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/data/datasets/decoders.py
DELETED
|
@@ -1,31 +0,0 @@
|
|
| 1 |
-
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
-
#
|
| 3 |
-
# This source code is licensed under the Apache License, Version 2.0
|
| 4 |
-
# found in the LICENSE file in the root directory of this source tree.
|
| 5 |
-
|
| 6 |
-
from io import BytesIO
|
| 7 |
-
from typing import Any
|
| 8 |
-
|
| 9 |
-
from PIL import Image
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
class Decoder:
|
| 13 |
-
def decode(self) -> Any:
|
| 14 |
-
raise NotImplementedError
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
class ImageDataDecoder(Decoder):
|
| 18 |
-
def __init__(self, image_data: bytes) -> None:
|
| 19 |
-
self._image_data = image_data
|
| 20 |
-
|
| 21 |
-
def decode(self) -> Image:
|
| 22 |
-
f = BytesIO(self._image_data)
|
| 23 |
-
return Image.open(f).convert(mode="RGB")
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
class TargetDecoder(Decoder):
|
| 27 |
-
def __init__(self, target: Any):
|
| 28 |
-
self._target = target
|
| 29 |
-
|
| 30 |
-
def decode(self) -> Any:
|
| 31 |
-
return self._target
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/data/datasets/extended.py
DELETED
|
@@ -1,38 +0,0 @@
|
|
| 1 |
-
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
-
#
|
| 3 |
-
# This source code is licensed under the Apache License, Version 2.0
|
| 4 |
-
# found in the LICENSE file in the root directory of this source tree.
|
| 5 |
-
|
| 6 |
-
from typing import Any, Tuple
|
| 7 |
-
|
| 8 |
-
from torchvision.datasets import VisionDataset
|
| 9 |
-
|
| 10 |
-
from .decoders import TargetDecoder, ImageDataDecoder
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
class ExtendedVisionDataset(VisionDataset):
|
| 14 |
-
def __init__(self, *args, **kwargs) -> None:
|
| 15 |
-
super().__init__(*args, **kwargs) # type: ignore
|
| 16 |
-
|
| 17 |
-
def get_image_data(self, index: int) -> bytes:
|
| 18 |
-
raise NotImplementedError
|
| 19 |
-
|
| 20 |
-
def get_target(self, index: int) -> Any:
|
| 21 |
-
raise NotImplementedError
|
| 22 |
-
|
| 23 |
-
def __getitem__(self, index: int) -> Tuple[Any, Any]:
|
| 24 |
-
try:
|
| 25 |
-
image_data = self.get_image_data(index)
|
| 26 |
-
image = ImageDataDecoder(image_data).decode()
|
| 27 |
-
except Exception as e:
|
| 28 |
-
raise RuntimeError(f"can not read image for sample {index}") from e
|
| 29 |
-
target = self.get_target(index)
|
| 30 |
-
target = TargetDecoder(target).decode()
|
| 31 |
-
|
| 32 |
-
if self.transforms is not None:
|
| 33 |
-
image, target = self.transforms(image, target)
|
| 34 |
-
|
| 35 |
-
return image, target
|
| 36 |
-
|
| 37 |
-
def __len__(self) -> int:
|
| 38 |
-
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/data/datasets/image_net.py
DELETED
|
@@ -1,290 +0,0 @@
|
|
| 1 |
-
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
-
#
|
| 3 |
-
# This source code is licensed under the Apache License, Version 2.0
|
| 4 |
-
# found in the LICENSE file in the root directory of this source tree.
|
| 5 |
-
|
| 6 |
-
import csv
|
| 7 |
-
from enum import Enum
|
| 8 |
-
import logging
|
| 9 |
-
import os
|
| 10 |
-
from typing import Callable, List, Optional, Tuple, Union
|
| 11 |
-
|
| 12 |
-
import numpy as np
|
| 13 |
-
|
| 14 |
-
from .extended import ExtendedVisionDataset
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
logger = logging.getLogger("dinov2")
|
| 18 |
-
_Target = int
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
class _Split(Enum):
|
| 22 |
-
TRAIN = "train"
|
| 23 |
-
VAL = "val"
|
| 24 |
-
TEST = "test" # NOTE: torchvision does not support the test split
|
| 25 |
-
|
| 26 |
-
@property
|
| 27 |
-
def length(self) -> int:
|
| 28 |
-
split_lengths = {
|
| 29 |
-
_Split.TRAIN: 1_281_167,
|
| 30 |
-
_Split.VAL: 50_000,
|
| 31 |
-
_Split.TEST: 100_000,
|
| 32 |
-
}
|
| 33 |
-
return split_lengths[self]
|
| 34 |
-
|
| 35 |
-
def get_dirname(self, class_id: Optional[str] = None) -> str:
|
| 36 |
-
return self.value if class_id is None else os.path.join(self.value, class_id)
|
| 37 |
-
|
| 38 |
-
def get_image_relpath(self, actual_index: int, class_id: Optional[str] = None) -> str:
|
| 39 |
-
dirname = self.get_dirname(class_id)
|
| 40 |
-
if self == _Split.TRAIN:
|
| 41 |
-
basename = f"{class_id}_{actual_index}"
|
| 42 |
-
else: # self in (_Split.VAL, _Split.TEST):
|
| 43 |
-
basename = f"ILSVRC2012_{self.value}_{actual_index:08d}"
|
| 44 |
-
return os.path.join(dirname, basename + ".JPEG")
|
| 45 |
-
|
| 46 |
-
def parse_image_relpath(self, image_relpath: str) -> Tuple[str, int]:
|
| 47 |
-
assert self != _Split.TEST
|
| 48 |
-
dirname, filename = os.path.split(image_relpath)
|
| 49 |
-
class_id = os.path.split(dirname)[-1]
|
| 50 |
-
basename, _ = os.path.splitext(filename)
|
| 51 |
-
actual_index = int(basename.split("_")[-1])
|
| 52 |
-
return class_id, actual_index
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
class ImageNet(ExtendedVisionDataset):
|
| 56 |
-
Target = Union[_Target]
|
| 57 |
-
Split = Union[_Split]
|
| 58 |
-
|
| 59 |
-
def __init__(
|
| 60 |
-
self,
|
| 61 |
-
*,
|
| 62 |
-
split: "ImageNet.Split",
|
| 63 |
-
root: str,
|
| 64 |
-
extra: str,
|
| 65 |
-
transforms: Optional[Callable] = None,
|
| 66 |
-
transform: Optional[Callable] = None,
|
| 67 |
-
target_transform: Optional[Callable] = None,
|
| 68 |
-
) -> None:
|
| 69 |
-
super().__init__(root, transforms, transform, target_transform)
|
| 70 |
-
self._extra_root = extra
|
| 71 |
-
self._split = split
|
| 72 |
-
|
| 73 |
-
self._entries = None
|
| 74 |
-
self._class_ids = None
|
| 75 |
-
self._class_names = None
|
| 76 |
-
|
| 77 |
-
@property
|
| 78 |
-
def split(self) -> "ImageNet.Split":
|
| 79 |
-
return self._split
|
| 80 |
-
|
| 81 |
-
def _get_extra_full_path(self, extra_path: str) -> str:
|
| 82 |
-
return os.path.join(self._extra_root, extra_path)
|
| 83 |
-
|
| 84 |
-
def _load_extra(self, extra_path: str) -> np.ndarray:
|
| 85 |
-
extra_full_path = self._get_extra_full_path(extra_path)
|
| 86 |
-
return np.load(extra_full_path, mmap_mode="r")
|
| 87 |
-
|
| 88 |
-
def _save_extra(self, extra_array: np.ndarray, extra_path: str) -> None:
|
| 89 |
-
extra_full_path = self._get_extra_full_path(extra_path)
|
| 90 |
-
os.makedirs(self._extra_root, exist_ok=True)
|
| 91 |
-
np.save(extra_full_path, extra_array)
|
| 92 |
-
|
| 93 |
-
@property
|
| 94 |
-
def _entries_path(self) -> str:
|
| 95 |
-
return f"entries-{self._split.value.upper()}.npy"
|
| 96 |
-
|
| 97 |
-
@property
|
| 98 |
-
def _class_ids_path(self) -> str:
|
| 99 |
-
return f"class-ids-{self._split.value.upper()}.npy"
|
| 100 |
-
|
| 101 |
-
@property
|
| 102 |
-
def _class_names_path(self) -> str:
|
| 103 |
-
return f"class-names-{self._split.value.upper()}.npy"
|
| 104 |
-
|
| 105 |
-
def _get_entries(self) -> np.ndarray:
|
| 106 |
-
if self._entries is None:
|
| 107 |
-
self._entries = self._load_extra(self._entries_path)
|
| 108 |
-
assert self._entries is not None
|
| 109 |
-
return self._entries
|
| 110 |
-
|
| 111 |
-
def _get_class_ids(self) -> np.ndarray:
|
| 112 |
-
if self._split == _Split.TEST:
|
| 113 |
-
assert False, "Class IDs are not available in TEST split"
|
| 114 |
-
if self._class_ids is None:
|
| 115 |
-
self._class_ids = self._load_extra(self._class_ids_path)
|
| 116 |
-
assert self._class_ids is not None
|
| 117 |
-
return self._class_ids
|
| 118 |
-
|
| 119 |
-
def _get_class_names(self) -> np.ndarray:
|
| 120 |
-
if self._split == _Split.TEST:
|
| 121 |
-
assert False, "Class names are not available in TEST split"
|
| 122 |
-
if self._class_names is None:
|
| 123 |
-
self._class_names = self._load_extra(self._class_names_path)
|
| 124 |
-
assert self._class_names is not None
|
| 125 |
-
return self._class_names
|
| 126 |
-
|
| 127 |
-
def find_class_id(self, class_index: int) -> str:
|
| 128 |
-
class_ids = self._get_class_ids()
|
| 129 |
-
return str(class_ids[class_index])
|
| 130 |
-
|
| 131 |
-
def find_class_name(self, class_index: int) -> str:
|
| 132 |
-
class_names = self._get_class_names()
|
| 133 |
-
return str(class_names[class_index])
|
| 134 |
-
|
| 135 |
-
def get_image_data(self, index: int) -> bytes:
|
| 136 |
-
entries = self._get_entries()
|
| 137 |
-
actual_index = entries[index]["actual_index"]
|
| 138 |
-
|
| 139 |
-
class_id = self.get_class_id(index)
|
| 140 |
-
|
| 141 |
-
image_relpath = self.split.get_image_relpath(actual_index, class_id)
|
| 142 |
-
image_full_path = os.path.join(self.root, image_relpath)
|
| 143 |
-
with open(image_full_path, mode="rb") as f:
|
| 144 |
-
image_data = f.read()
|
| 145 |
-
return image_data
|
| 146 |
-
|
| 147 |
-
def get_target(self, index: int) -> Optional[Target]:
|
| 148 |
-
entries = self._get_entries()
|
| 149 |
-
class_index = entries[index]["class_index"]
|
| 150 |
-
return None if self.split == _Split.TEST else int(class_index)
|
| 151 |
-
|
| 152 |
-
def get_targets(self) -> Optional[np.ndarray]:
|
| 153 |
-
entries = self._get_entries()
|
| 154 |
-
return None if self.split == _Split.TEST else entries["class_index"]
|
| 155 |
-
|
| 156 |
-
def get_class_id(self, index: int) -> Optional[str]:
|
| 157 |
-
entries = self._get_entries()
|
| 158 |
-
class_id = entries[index]["class_id"]
|
| 159 |
-
return None if self.split == _Split.TEST else str(class_id)
|
| 160 |
-
|
| 161 |
-
def get_class_name(self, index: int) -> Optional[str]:
|
| 162 |
-
entries = self._get_entries()
|
| 163 |
-
class_name = entries[index]["class_name"]
|
| 164 |
-
return None if self.split == _Split.TEST else str(class_name)
|
| 165 |
-
|
| 166 |
-
def __len__(self) -> int:
|
| 167 |
-
entries = self._get_entries()
|
| 168 |
-
assert len(entries) == self.split.length
|
| 169 |
-
return len(entries)
|
| 170 |
-
|
| 171 |
-
def _load_labels(self, labels_path: str) -> List[Tuple[str, str]]:
|
| 172 |
-
labels_full_path = os.path.join(self.root, labels_path)
|
| 173 |
-
labels = []
|
| 174 |
-
|
| 175 |
-
try:
|
| 176 |
-
with open(labels_full_path, "r") as f:
|
| 177 |
-
reader = csv.reader(f)
|
| 178 |
-
for row in reader:
|
| 179 |
-
class_id, class_name = row
|
| 180 |
-
labels.append((class_id, class_name))
|
| 181 |
-
except OSError as e:
|
| 182 |
-
raise RuntimeError(f'can not read labels file "{labels_full_path}"') from e
|
| 183 |
-
|
| 184 |
-
return labels
|
| 185 |
-
|
| 186 |
-
def _dump_entries(self) -> None:
|
| 187 |
-
split = self.split
|
| 188 |
-
if split == ImageNet.Split.TEST:
|
| 189 |
-
dataset = None
|
| 190 |
-
sample_count = split.length
|
| 191 |
-
max_class_id_length, max_class_name_length = 0, 0
|
| 192 |
-
else:
|
| 193 |
-
labels_path = "labels.txt"
|
| 194 |
-
logger.info(f'loading labels from "{labels_path}"')
|
| 195 |
-
labels = self._load_labels(labels_path)
|
| 196 |
-
|
| 197 |
-
# NOTE: Using torchvision ImageFolder for consistency
|
| 198 |
-
from torchvision.datasets import ImageFolder
|
| 199 |
-
|
| 200 |
-
dataset_root = os.path.join(self.root, split.get_dirname())
|
| 201 |
-
dataset = ImageFolder(dataset_root)
|
| 202 |
-
sample_count = len(dataset)
|
| 203 |
-
max_class_id_length, max_class_name_length = -1, -1
|
| 204 |
-
for sample in dataset.samples:
|
| 205 |
-
_, class_index = sample
|
| 206 |
-
class_id, class_name = labels[class_index]
|
| 207 |
-
max_class_id_length = max(len(class_id), max_class_id_length)
|
| 208 |
-
max_class_name_length = max(len(class_name), max_class_name_length)
|
| 209 |
-
|
| 210 |
-
dtype = np.dtype(
|
| 211 |
-
[
|
| 212 |
-
("actual_index", "<u4"),
|
| 213 |
-
("class_index", "<u4"),
|
| 214 |
-
("class_id", f"U{max_class_id_length}"),
|
| 215 |
-
("class_name", f"U{max_class_name_length}"),
|
| 216 |
-
]
|
| 217 |
-
)
|
| 218 |
-
entries_array = np.empty(sample_count, dtype=dtype)
|
| 219 |
-
|
| 220 |
-
if split == ImageNet.Split.TEST:
|
| 221 |
-
old_percent = -1
|
| 222 |
-
for index in range(sample_count):
|
| 223 |
-
percent = 100 * (index + 1) // sample_count
|
| 224 |
-
if percent > old_percent:
|
| 225 |
-
logger.info(f"creating entries: {percent}%")
|
| 226 |
-
old_percent = percent
|
| 227 |
-
|
| 228 |
-
actual_index = index + 1
|
| 229 |
-
class_index = np.uint32(-1)
|
| 230 |
-
class_id, class_name = "", ""
|
| 231 |
-
entries_array[index] = (actual_index, class_index, class_id, class_name)
|
| 232 |
-
else:
|
| 233 |
-
class_names = {class_id: class_name for class_id, class_name in labels}
|
| 234 |
-
|
| 235 |
-
assert dataset
|
| 236 |
-
old_percent = -1
|
| 237 |
-
for index in range(sample_count):
|
| 238 |
-
percent = 100 * (index + 1) // sample_count
|
| 239 |
-
if percent > old_percent:
|
| 240 |
-
logger.info(f"creating entries: {percent}%")
|
| 241 |
-
old_percent = percent
|
| 242 |
-
|
| 243 |
-
image_full_path, class_index = dataset.samples[index]
|
| 244 |
-
image_relpath = os.path.relpath(image_full_path, self.root)
|
| 245 |
-
class_id, actual_index = split.parse_image_relpath(image_relpath)
|
| 246 |
-
class_name = class_names[class_id]
|
| 247 |
-
entries_array[index] = (actual_index, class_index, class_id, class_name)
|
| 248 |
-
|
| 249 |
-
logger.info(f'saving entries to "{self._entries_path}"')
|
| 250 |
-
self._save_extra(entries_array, self._entries_path)
|
| 251 |
-
|
| 252 |
-
def _dump_class_ids_and_names(self) -> None:
|
| 253 |
-
split = self.split
|
| 254 |
-
if split == ImageNet.Split.TEST:
|
| 255 |
-
return
|
| 256 |
-
|
| 257 |
-
entries_array = self._load_extra(self._entries_path)
|
| 258 |
-
|
| 259 |
-
max_class_id_length, max_class_name_length, max_class_index = -1, -1, -1
|
| 260 |
-
for entry in entries_array:
|
| 261 |
-
class_index, class_id, class_name = (
|
| 262 |
-
entry["class_index"],
|
| 263 |
-
entry["class_id"],
|
| 264 |
-
entry["class_name"],
|
| 265 |
-
)
|
| 266 |
-
max_class_index = max(int(class_index), max_class_index)
|
| 267 |
-
max_class_id_length = max(len(str(class_id)), max_class_id_length)
|
| 268 |
-
max_class_name_length = max(len(str(class_name)), max_class_name_length)
|
| 269 |
-
|
| 270 |
-
class_count = max_class_index + 1
|
| 271 |
-
class_ids_array = np.empty(class_count, dtype=f"U{max_class_id_length}")
|
| 272 |
-
class_names_array = np.empty(class_count, dtype=f"U{max_class_name_length}")
|
| 273 |
-
for entry in entries_array:
|
| 274 |
-
class_index, class_id, class_name = (
|
| 275 |
-
entry["class_index"],
|
| 276 |
-
entry["class_id"],
|
| 277 |
-
entry["class_name"],
|
| 278 |
-
)
|
| 279 |
-
class_ids_array[class_index] = class_id
|
| 280 |
-
class_names_array[class_index] = class_name
|
| 281 |
-
|
| 282 |
-
logger.info(f'saving class IDs to "{self._class_ids_path}"')
|
| 283 |
-
self._save_extra(class_ids_array, self._class_ids_path)
|
| 284 |
-
|
| 285 |
-
logger.info(f'saving class names to "{self._class_names_path}"')
|
| 286 |
-
self._save_extra(class_names_array, self._class_names_path)
|
| 287 |
-
|
| 288 |
-
def dump_extra(self) -> None:
|
| 289 |
-
self._dump_entries()
|
| 290 |
-
self._dump_class_ids_and_names()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/data/datasets/image_net_22k.py
DELETED
|
@@ -1,302 +0,0 @@
|
|
| 1 |
-
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
-
#
|
| 3 |
-
# This source code is licensed under the Apache License, Version 2.0
|
| 4 |
-
# found in the LICENSE file in the root directory of this source tree.
|
| 5 |
-
|
| 6 |
-
from dataclasses import dataclass
|
| 7 |
-
from enum import Enum
|
| 8 |
-
from functools import lru_cache
|
| 9 |
-
from gzip import GzipFile
|
| 10 |
-
from io import BytesIO
|
| 11 |
-
from mmap import ACCESS_READ, mmap
|
| 12 |
-
import os
|
| 13 |
-
from typing import Any, Callable, List, Optional, Set, Tuple
|
| 14 |
-
import warnings
|
| 15 |
-
|
| 16 |
-
import numpy as np
|
| 17 |
-
|
| 18 |
-
from .extended import ExtendedVisionDataset
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
_Labels = int
|
| 22 |
-
|
| 23 |
-
_DEFAULT_MMAP_CACHE_SIZE = 16 # Warning: This can exhaust file descriptors
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
@dataclass
|
| 27 |
-
class _ClassEntry:
|
| 28 |
-
block_offset: int
|
| 29 |
-
maybe_filename: Optional[str] = None
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
@dataclass
|
| 33 |
-
class _Entry:
|
| 34 |
-
class_index: int # noqa: E701
|
| 35 |
-
start_offset: int
|
| 36 |
-
end_offset: int
|
| 37 |
-
filename: str
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
class _Split(Enum):
|
| 41 |
-
TRAIN = "train"
|
| 42 |
-
VAL = "val"
|
| 43 |
-
|
| 44 |
-
@property
|
| 45 |
-
def length(self) -> int:
|
| 46 |
-
return {
|
| 47 |
-
_Split.TRAIN: 11_797_647,
|
| 48 |
-
_Split.VAL: 561_050,
|
| 49 |
-
}[self]
|
| 50 |
-
|
| 51 |
-
def entries_path(self):
|
| 52 |
-
return f"imagenet21kp_{self.value}.txt"
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
def _get_tarball_path(class_id: str) -> str:
|
| 56 |
-
return f"{class_id}.tar"
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
def _make_mmap_tarball(tarballs_root: str, mmap_cache_size: int):
|
| 60 |
-
@lru_cache(maxsize=mmap_cache_size)
|
| 61 |
-
def _mmap_tarball(class_id: str) -> mmap:
|
| 62 |
-
tarball_path = _get_tarball_path(class_id)
|
| 63 |
-
tarball_full_path = os.path.join(tarballs_root, tarball_path)
|
| 64 |
-
with open(tarball_full_path) as f:
|
| 65 |
-
return mmap(fileno=f.fileno(), length=0, access=ACCESS_READ)
|
| 66 |
-
|
| 67 |
-
return _mmap_tarball
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
class ImageNet22k(ExtendedVisionDataset):
|
| 71 |
-
_GZIPPED_INDICES: Set[int] = {
|
| 72 |
-
841_545,
|
| 73 |
-
1_304_131,
|
| 74 |
-
2_437_921,
|
| 75 |
-
2_672_079,
|
| 76 |
-
2_795_676,
|
| 77 |
-
2_969_786,
|
| 78 |
-
6_902_965,
|
| 79 |
-
6_903_550,
|
| 80 |
-
6_903_628,
|
| 81 |
-
7_432_557,
|
| 82 |
-
7_432_589,
|
| 83 |
-
7_813_809,
|
| 84 |
-
8_329_633,
|
| 85 |
-
10_296_990,
|
| 86 |
-
10_417_652,
|
| 87 |
-
10_492_265,
|
| 88 |
-
10_598_078,
|
| 89 |
-
10_782_398,
|
| 90 |
-
10_902_612,
|
| 91 |
-
11_203_736,
|
| 92 |
-
11_342_890,
|
| 93 |
-
11_397_596,
|
| 94 |
-
11_589_762,
|
| 95 |
-
11_705_103,
|
| 96 |
-
12_936_875,
|
| 97 |
-
13_289_782,
|
| 98 |
-
}
|
| 99 |
-
Labels = _Labels
|
| 100 |
-
|
| 101 |
-
def __init__(
|
| 102 |
-
self,
|
| 103 |
-
*,
|
| 104 |
-
root: str,
|
| 105 |
-
extra: str,
|
| 106 |
-
transforms: Optional[Callable] = None,
|
| 107 |
-
transform: Optional[Callable] = None,
|
| 108 |
-
target_transform: Optional[Callable] = None,
|
| 109 |
-
mmap_cache_size: int = _DEFAULT_MMAP_CACHE_SIZE,
|
| 110 |
-
) -> None:
|
| 111 |
-
super().__init__(root, transforms, transform, target_transform)
|
| 112 |
-
self._extra_root = extra
|
| 113 |
-
|
| 114 |
-
entries_path = self._get_entries_path(root)
|
| 115 |
-
self._entries = self._load_extra(entries_path)
|
| 116 |
-
|
| 117 |
-
class_ids_path = self._get_class_ids_path(root)
|
| 118 |
-
self._class_ids = self._load_extra(class_ids_path)
|
| 119 |
-
|
| 120 |
-
self._gzipped_indices = ImageNet22k._GZIPPED_INDICES
|
| 121 |
-
self._mmap_tarball = _make_mmap_tarball(self._tarballs_root, mmap_cache_size)
|
| 122 |
-
|
| 123 |
-
def _get_entries_path(self, root: Optional[str] = None) -> str:
|
| 124 |
-
return "entries.npy"
|
| 125 |
-
|
| 126 |
-
def _get_class_ids_path(self, root: Optional[str] = None) -> str:
|
| 127 |
-
return "class-ids.npy"
|
| 128 |
-
|
| 129 |
-
def _find_class_ids(self, path: str) -> List[str]:
|
| 130 |
-
class_ids = []
|
| 131 |
-
|
| 132 |
-
with os.scandir(path) as entries:
|
| 133 |
-
for entry in entries:
|
| 134 |
-
root, ext = os.path.splitext(entry.name)
|
| 135 |
-
if ext != ".tar":
|
| 136 |
-
continue
|
| 137 |
-
class_ids.append(root)
|
| 138 |
-
|
| 139 |
-
return sorted(class_ids)
|
| 140 |
-
|
| 141 |
-
def _load_entries_class_ids(self, root: Optional[str] = None) -> Tuple[List[_Entry], List[str]]:
|
| 142 |
-
root = self.get_root(root)
|
| 143 |
-
entries: List[_Entry] = []
|
| 144 |
-
class_ids = self._find_class_ids(root)
|
| 145 |
-
|
| 146 |
-
for class_index, class_id in enumerate(class_ids):
|
| 147 |
-
path = os.path.join(root, "blocks", f"{class_id}.log")
|
| 148 |
-
class_entries = []
|
| 149 |
-
|
| 150 |
-
try:
|
| 151 |
-
with open(path) as f:
|
| 152 |
-
for line in f:
|
| 153 |
-
line = line.rstrip()
|
| 154 |
-
block, filename = line.split(":")
|
| 155 |
-
block_offset = int(block[6:])
|
| 156 |
-
filename = filename[1:]
|
| 157 |
-
|
| 158 |
-
maybe_filename = None
|
| 159 |
-
if filename != "** Block of NULs **":
|
| 160 |
-
maybe_filename = filename
|
| 161 |
-
_, ext = os.path.splitext(filename)
|
| 162 |
-
# assert ext == ".JPEG"
|
| 163 |
-
|
| 164 |
-
class_entry = _ClassEntry(block_offset, maybe_filename)
|
| 165 |
-
class_entries.append(class_entry)
|
| 166 |
-
except OSError as e:
|
| 167 |
-
raise RuntimeError(f'can not read blocks file "{path}"') from e
|
| 168 |
-
|
| 169 |
-
assert class_entries[-1].maybe_filename is None
|
| 170 |
-
|
| 171 |
-
for class_entry1, class_entry2 in zip(class_entries, class_entries[1:]):
|
| 172 |
-
assert class_entry1.block_offset <= class_entry2.block_offset
|
| 173 |
-
start_offset = 512 * class_entry1.block_offset
|
| 174 |
-
end_offset = 512 * class_entry2.block_offset
|
| 175 |
-
assert class_entry1.maybe_filename is not None
|
| 176 |
-
filename = class_entry1.maybe_filename
|
| 177 |
-
entry = _Entry(class_index, start_offset, end_offset, filename)
|
| 178 |
-
# Skip invalid image files (PIL throws UnidentifiedImageError)
|
| 179 |
-
if filename == "n06470073_47249.JPEG":
|
| 180 |
-
continue
|
| 181 |
-
entries.append(entry)
|
| 182 |
-
|
| 183 |
-
return entries, class_ids
|
| 184 |
-
|
| 185 |
-
def _load_extra(self, extra_path: str) -> np.ndarray:
|
| 186 |
-
extra_root = self._extra_root
|
| 187 |
-
extra_full_path = os.path.join(extra_root, extra_path)
|
| 188 |
-
return np.load(extra_full_path, mmap_mode="r")
|
| 189 |
-
|
| 190 |
-
def _save_extra(self, extra_array: np.ndarray, extra_path: str) -> None:
|
| 191 |
-
extra_root = self._extra_root
|
| 192 |
-
extra_full_path = os.path.join(extra_root, extra_path)
|
| 193 |
-
os.makedirs(extra_root, exist_ok=True)
|
| 194 |
-
np.save(extra_full_path, extra_array)
|
| 195 |
-
|
| 196 |
-
@property
|
| 197 |
-
def _tarballs_root(self) -> str:
|
| 198 |
-
return self.root
|
| 199 |
-
|
| 200 |
-
def find_class_id(self, class_index: int) -> str:
|
| 201 |
-
return str(self._class_ids[class_index])
|
| 202 |
-
|
| 203 |
-
def get_image_data(self, index: int) -> bytes:
|
| 204 |
-
entry = self._entries[index]
|
| 205 |
-
class_id = entry["class_id"]
|
| 206 |
-
class_mmap = self._mmap_tarball(class_id)
|
| 207 |
-
|
| 208 |
-
start_offset, end_offset = entry["start_offset"], entry["end_offset"]
|
| 209 |
-
try:
|
| 210 |
-
mapped_data = class_mmap[start_offset:end_offset]
|
| 211 |
-
data = mapped_data[512:] # Skip entry header block
|
| 212 |
-
|
| 213 |
-
if len(data) >= 2 and tuple(data[:2]) == (0x1F, 0x8B):
|
| 214 |
-
assert index in self._gzipped_indices, f"unexpected gzip header for sample {index}"
|
| 215 |
-
with GzipFile(fileobj=BytesIO(data)) as g:
|
| 216 |
-
data = g.read()
|
| 217 |
-
except Exception as e:
|
| 218 |
-
raise RuntimeError(f"can not retrieve image data for sample {index} " f'from "{class_id}" tarball') from e
|
| 219 |
-
|
| 220 |
-
return data
|
| 221 |
-
|
| 222 |
-
def get_target(self, index: int) -> Any:
|
| 223 |
-
return int(self._entries[index]["class_index"])
|
| 224 |
-
|
| 225 |
-
def get_targets(self) -> np.ndarray:
|
| 226 |
-
return self._entries["class_index"]
|
| 227 |
-
|
| 228 |
-
def get_class_id(self, index: int) -> str:
|
| 229 |
-
return str(self._entries[index]["class_id"])
|
| 230 |
-
|
| 231 |
-
def get_class_ids(self) -> np.ndarray:
|
| 232 |
-
return self._entries["class_id"]
|
| 233 |
-
|
| 234 |
-
def __getitem__(self, index: int) -> Tuple[Any, Any]:
|
| 235 |
-
with warnings.catch_warnings():
|
| 236 |
-
warnings.simplefilter("ignore")
|
| 237 |
-
return super().__getitem__(index)
|
| 238 |
-
|
| 239 |
-
def __len__(self) -> int:
|
| 240 |
-
return len(self._entries)
|
| 241 |
-
|
| 242 |
-
def _dump_entries(self, *args, **kwargs) -> None:
|
| 243 |
-
entries, class_ids = self._load_entries_class_ids(*args, **kwargs)
|
| 244 |
-
|
| 245 |
-
max_class_id_length, max_filename_length, max_class_index = -1, -1, -1
|
| 246 |
-
for entry in entries:
|
| 247 |
-
class_id = class_ids[entry.class_index]
|
| 248 |
-
max_class_index = max(entry.class_index, max_class_index)
|
| 249 |
-
max_class_id_length = max(len(class_id), max_class_id_length)
|
| 250 |
-
max_filename_length = max(len(entry.filename), max_filename_length)
|
| 251 |
-
|
| 252 |
-
dtype = np.dtype(
|
| 253 |
-
[
|
| 254 |
-
("class_index", "<u4"),
|
| 255 |
-
("class_id", f"U{max_class_id_length}"),
|
| 256 |
-
("start_offset", "<u4"),
|
| 257 |
-
("end_offset", "<u4"),
|
| 258 |
-
("filename", f"U{max_filename_length}"),
|
| 259 |
-
]
|
| 260 |
-
)
|
| 261 |
-
sample_count = len(entries)
|
| 262 |
-
entries_array = np.empty(sample_count, dtype=dtype)
|
| 263 |
-
for i, entry in enumerate(entries):
|
| 264 |
-
class_index = entry.class_index
|
| 265 |
-
class_id = class_ids[class_index]
|
| 266 |
-
start_offset = entry.start_offset
|
| 267 |
-
end_offset = entry.end_offset
|
| 268 |
-
filename = entry.filename
|
| 269 |
-
entries_array[i] = (
|
| 270 |
-
class_index,
|
| 271 |
-
class_id,
|
| 272 |
-
start_offset,
|
| 273 |
-
end_offset,
|
| 274 |
-
filename,
|
| 275 |
-
)
|
| 276 |
-
|
| 277 |
-
entries_path = self._get_entries_path(*args, **kwargs)
|
| 278 |
-
self._save_extra(entries_array, entries_path)
|
| 279 |
-
|
| 280 |
-
def _dump_class_ids(self, *args, **kwargs) -> None:
|
| 281 |
-
entries_path = self._get_entries_path(*args, **kwargs)
|
| 282 |
-
entries_array = self._load_extra(entries_path)
|
| 283 |
-
|
| 284 |
-
max_class_id_length, max_class_index = -1, -1
|
| 285 |
-
for entry in entries_array:
|
| 286 |
-
class_index, class_id = entry["class_index"], entry["class_id"]
|
| 287 |
-
max_class_index = max(int(class_index), max_class_index)
|
| 288 |
-
max_class_id_length = max(len(str(class_id)), max_class_id_length)
|
| 289 |
-
|
| 290 |
-
class_ids_array = np.empty(max_class_index + 1, dtype=f"U{max_class_id_length}")
|
| 291 |
-
for entry in entries_array:
|
| 292 |
-
class_index, class_id = entry["class_index"], entry["class_id"]
|
| 293 |
-
class_ids_array[class_index] = class_id
|
| 294 |
-
class_ids_path = self._get_class_ids_path(*args, **kwargs)
|
| 295 |
-
self._save_extra(class_ids_array, class_ids_path)
|
| 296 |
-
|
| 297 |
-
def _dump_extra(self, *args, **kwargs) -> None:
|
| 298 |
-
self._dump_entries(*args, *kwargs)
|
| 299 |
-
self._dump_class_ids(*args, *kwargs)
|
| 300 |
-
|
| 301 |
-
def dump_extra(self, root: Optional[str] = None) -> None:
|
| 302 |
-
return self._dump_extra(root)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/data/datasets/slide_dataset.py
DELETED
|
@@ -1,99 +0,0 @@
|
|
| 1 |
-
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
-
# This source code is licensed under the Apache License, Version 2.0
|
| 3 |
-
# found in the LICENSE file in the root directory of this source tree.
|
| 4 |
-
|
| 5 |
-
import atexit
|
| 6 |
-
from collections import OrderedDict
|
| 7 |
-
from typing import Any, Tuple
|
| 8 |
-
|
| 9 |
-
from .extended import ExtendedVisionDataset
|
| 10 |
-
from pathlib import Path
|
| 11 |
-
from openslide import OpenSlide
|
| 12 |
-
import numpy as np
|
| 13 |
-
import cv2
|
| 14 |
-
|
| 15 |
-
_SLIDE_CACHE: "OrderedDict[str, OpenSlide]" = OrderedDict()
|
| 16 |
-
_SLIDE_CACHE_LIMIT = 512
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
def _close_all_slides():
|
| 20 |
-
for slide in _SLIDE_CACHE.values():
|
| 21 |
-
slide.close()
|
| 22 |
-
_SLIDE_CACHE.clear()
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
atexit.register(_close_all_slides)
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
class SlideDataset(ExtendedVisionDataset):
|
| 29 |
-
def __init__(self, root, sample_list_path, *args, **kwargs) -> None:
|
| 30 |
-
super().__init__(root, *args, **kwargs)
|
| 31 |
-
self.sample_list_path = Path(sample_list_path)
|
| 32 |
-
if not self.sample_list_path.is_file():
|
| 33 |
-
raise FileNotFoundError(f"Sample list not found at {self.sample_list_path}")
|
| 34 |
-
|
| 35 |
-
with self.sample_list_path.open("r") as f:
|
| 36 |
-
self.image_files = [line.strip() for line in f if line.strip()]
|
| 37 |
-
|
| 38 |
-
print(f"This many resolved paths {len(self.image_files)} from {self.sample_list_path}")
|
| 39 |
-
|
| 40 |
-
def get_all(self, index):
|
| 41 |
-
parts = self.image_files[index].split(" ")
|
| 42 |
-
path = parts[0]
|
| 43 |
-
image = _SLIDE_CACHE.get(path)
|
| 44 |
-
if image is None:
|
| 45 |
-
image = OpenSlide(path)
|
| 46 |
-
_SLIDE_CACHE[path] = image
|
| 47 |
-
if len(_SLIDE_CACHE) > _SLIDE_CACHE_LIMIT:
|
| 48 |
-
_, old = _SLIDE_CACHE.popitem(last=False)
|
| 49 |
-
old.close()
|
| 50 |
-
else:
|
| 51 |
-
_SLIDE_CACHE.move_to_end(path)
|
| 52 |
-
return image, path
|
| 53 |
-
|
| 54 |
-
def __getitem__(self, index: int) -> Tuple[Any, Any]:
|
| 55 |
-
path = self.image_files[index]
|
| 56 |
-
parts = path.split(" ")
|
| 57 |
-
path, x, y, level = parts
|
| 58 |
-
x = int(x)
|
| 59 |
-
y = int(y)
|
| 60 |
-
level = int(level)
|
| 61 |
-
|
| 62 |
-
image = _SLIDE_CACHE.get(path)
|
| 63 |
-
if image is None:
|
| 64 |
-
image = OpenSlide(path)
|
| 65 |
-
_SLIDE_CACHE[path] = image
|
| 66 |
-
if len(_SLIDE_CACHE) > _SLIDE_CACHE_LIMIT:
|
| 67 |
-
_, old = _SLIDE_CACHE.popitem(last=False)
|
| 68 |
-
old.close()
|
| 69 |
-
else:
|
| 70 |
-
_SLIDE_CACHE.move_to_end(path)
|
| 71 |
-
|
| 72 |
-
patch_size = 224
|
| 73 |
-
|
| 74 |
-
patch = image.read_region((x, y), level=level, size=(patch_size, patch_size))
|
| 75 |
-
|
| 76 |
-
res = patch.convert("RGB")
|
| 77 |
-
if self.transforms is not None:
|
| 78 |
-
return self.transforms(res, None), index
|
| 79 |
-
|
| 80 |
-
return res, None, index
|
| 81 |
-
|
| 82 |
-
def hsv(self, tile_rgb, patch_size):
|
| 83 |
-
tile = np.array(tile_rgb)
|
| 84 |
-
tile = cv2.cvtColor(tile, cv2.COLOR_RGB2HSV)
|
| 85 |
-
min_ratio = .6
|
| 86 |
-
|
| 87 |
-
lower_bound = np.array([90, 8, 103])
|
| 88 |
-
upper_bound = np.array([180, 255, 255])
|
| 89 |
-
|
| 90 |
-
mask = cv2.inRange(tile, lower_bound, upper_bound)
|
| 91 |
-
|
| 92 |
-
ratio = np.count_nonzero(mask) / mask.size
|
| 93 |
-
if ratio > min_ratio:
|
| 94 |
-
return tile_rgb
|
| 95 |
-
else: # ratio failed, reject
|
| 96 |
-
return None
|
| 97 |
-
|
| 98 |
-
def __len__(self) -> int:
|
| 99 |
-
return len(self.image_files)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/data/loaders.py
DELETED
|
@@ -1,229 +0,0 @@
|
|
| 1 |
-
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
-
#
|
| 3 |
-
# This source code is licensed under the Apache License, Version 2.0
|
| 4 |
-
# found in the LICENSE file in the root directory of this source tree.
|
| 5 |
-
|
| 6 |
-
import logging
|
| 7 |
-
from enum import Enum
|
| 8 |
-
from typing import Any, Callable, List, Optional, TypeVar
|
| 9 |
-
|
| 10 |
-
import torch
|
| 11 |
-
from torch.utils.data import Sampler
|
| 12 |
-
|
| 13 |
-
from .datasets import ImageNet, ImageNet22k, TestVisionDataset, SlideDataset
|
| 14 |
-
from .samplers import EpochSampler, InfiniteSampler, ShardedInfiniteSampler
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
logger = logging.getLogger("dinov2")
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
class SamplerType(Enum):
|
| 21 |
-
DISTRIBUTED = 0
|
| 22 |
-
EPOCH = 1
|
| 23 |
-
INFINITE = 2
|
| 24 |
-
SHARDED_INFINITE = 3
|
| 25 |
-
SHARDED_INFINITE_NEW = 4
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
def _make_bool_str(b: bool) -> str:
|
| 29 |
-
return "yes" if b else "no"
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
def _make_sample_transform(image_transform: Optional[Callable] = None, target_transform: Optional[Callable] = None):
|
| 33 |
-
def transform(sample):
|
| 34 |
-
image, target = sample
|
| 35 |
-
if image_transform is not None:
|
| 36 |
-
image = image_transform(image)
|
| 37 |
-
if target_transform is not None:
|
| 38 |
-
target = target_transform(target)
|
| 39 |
-
return image, target
|
| 40 |
-
|
| 41 |
-
return transform
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
def _parse_dataset_str(dataset_str: str):
|
| 45 |
-
|
| 46 |
-
tokens = dataset_str.split(":")
|
| 47 |
-
|
| 48 |
-
name = tokens[0]
|
| 49 |
-
kwargs = {}
|
| 50 |
-
|
| 51 |
-
for token in tokens[1:]:
|
| 52 |
-
key, value = token.split("=")
|
| 53 |
-
assert key in ("root", "extra", "split", "sample_list_path")
|
| 54 |
-
kwargs[key] = value
|
| 55 |
-
|
| 56 |
-
if name == "ImageNet":
|
| 57 |
-
class_ = ImageNet
|
| 58 |
-
if "split" in kwargs:
|
| 59 |
-
kwargs["split"] = ImageNet.Split[kwargs["split"]]
|
| 60 |
-
elif name == "ImageNet22k":
|
| 61 |
-
class_ = ImageNet22k
|
| 62 |
-
elif name.lower() == "pathology":
|
| 63 |
-
class_ = SlideDataset
|
| 64 |
-
print("kwargs", kwargs)
|
| 65 |
-
else:
|
| 66 |
-
raise ValueError(f'Unsupported dataset "{name}"')
|
| 67 |
-
|
| 68 |
-
return class_, kwargs
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
def make_dataset(
|
| 72 |
-
*,
|
| 73 |
-
dataset_str: str,
|
| 74 |
-
transform: Optional[Callable] = None,
|
| 75 |
-
target_transform: Optional[Callable] = None,
|
| 76 |
-
):
|
| 77 |
-
"""
|
| 78 |
-
Creates a dataset with the specified parameters.
|
| 79 |
-
|
| 80 |
-
Args:
|
| 81 |
-
dataset_str: A dataset string description (e.g. ImageNet:split=TRAIN).
|
| 82 |
-
transform: A transform to apply to images.
|
| 83 |
-
target_transform: A transform to apply to targets.
|
| 84 |
-
|
| 85 |
-
Returns:
|
| 86 |
-
The created dataset.
|
| 87 |
-
"""
|
| 88 |
-
logger.info(f'using dataset: "{dataset_str}"')
|
| 89 |
-
|
| 90 |
-
class_, kwargs = _parse_dataset_str(dataset_str)
|
| 91 |
-
dataset = class_(transform=transform, target_transform=target_transform, **kwargs)
|
| 92 |
-
|
| 93 |
-
logger.info(f"# of dataset samples: {len(dataset):,d}")
|
| 94 |
-
|
| 95 |
-
# Aggregated datasets do not expose (yet) these attributes, so add them.
|
| 96 |
-
if not hasattr(dataset, "transform"):
|
| 97 |
-
setattr(dataset, "transform", transform)
|
| 98 |
-
if not hasattr(dataset, "target_transform"):
|
| 99 |
-
setattr(dataset, "target_transform", target_transform)
|
| 100 |
-
|
| 101 |
-
return dataset
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
def _make_sampler(
|
| 105 |
-
*,
|
| 106 |
-
dataset,
|
| 107 |
-
type: Optional[SamplerType] = None,
|
| 108 |
-
shuffle: bool = False,
|
| 109 |
-
seed: int = 0,
|
| 110 |
-
size: int = -1,
|
| 111 |
-
advance: int = 0,
|
| 112 |
-
) -> Optional[Sampler]:
|
| 113 |
-
sample_count = len(dataset)
|
| 114 |
-
|
| 115 |
-
if type == SamplerType.INFINITE:
|
| 116 |
-
logger.info("sampler: infinite")
|
| 117 |
-
if size > 0:
|
| 118 |
-
raise ValueError("sampler size > 0 is invalid")
|
| 119 |
-
return InfiniteSampler(
|
| 120 |
-
sample_count=sample_count,
|
| 121 |
-
shuffle=shuffle,
|
| 122 |
-
seed=seed,
|
| 123 |
-
advance=advance,
|
| 124 |
-
)
|
| 125 |
-
elif type in (SamplerType.SHARDED_INFINITE, SamplerType.SHARDED_INFINITE_NEW):
|
| 126 |
-
logger.info("sampler: sharded infinite")
|
| 127 |
-
if size > 0:
|
| 128 |
-
raise ValueError("sampler size > 0 is invalid")
|
| 129 |
-
# TODO: Remove support for old shuffling
|
| 130 |
-
use_new_shuffle_tensor_slice = type == SamplerType.SHARDED_INFINITE_NEW
|
| 131 |
-
return ShardedInfiniteSampler(
|
| 132 |
-
sample_count=sample_count,
|
| 133 |
-
shuffle=shuffle,
|
| 134 |
-
seed=seed,
|
| 135 |
-
advance=advance,
|
| 136 |
-
use_new_shuffle_tensor_slice=use_new_shuffle_tensor_slice,
|
| 137 |
-
)
|
| 138 |
-
elif type == SamplerType.EPOCH:
|
| 139 |
-
logger.info("sampler: epoch")
|
| 140 |
-
if advance > 0:
|
| 141 |
-
raise NotImplementedError("sampler advance > 0 is not supported")
|
| 142 |
-
size = size if size > 0 else sample_count
|
| 143 |
-
logger.info(f"# of samples / epoch: {size:,d}")
|
| 144 |
-
return EpochSampler(
|
| 145 |
-
size=size,
|
| 146 |
-
sample_count=sample_count,
|
| 147 |
-
shuffle=shuffle,
|
| 148 |
-
seed=seed,
|
| 149 |
-
)
|
| 150 |
-
elif type == SamplerType.DISTRIBUTED:
|
| 151 |
-
logger.info("sampler: distributed")
|
| 152 |
-
if size > 0:
|
| 153 |
-
raise ValueError("sampler size > 0 is invalid")
|
| 154 |
-
if advance > 0:
|
| 155 |
-
raise ValueError("sampler advance > 0 is invalid")
|
| 156 |
-
return torch.utils.data.DistributedSampler(
|
| 157 |
-
dataset=dataset,
|
| 158 |
-
shuffle=shuffle,
|
| 159 |
-
seed=seed,
|
| 160 |
-
drop_last=False,
|
| 161 |
-
)
|
| 162 |
-
|
| 163 |
-
logger.info("sampler: none")
|
| 164 |
-
return None
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
T = TypeVar("T")
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
def make_data_loader(
|
| 171 |
-
*,
|
| 172 |
-
dataset,
|
| 173 |
-
batch_size: int,
|
| 174 |
-
num_workers: int,
|
| 175 |
-
shuffle: bool = True,
|
| 176 |
-
seed: int = 0,
|
| 177 |
-
sampler_type: Optional[SamplerType] = SamplerType.INFINITE,
|
| 178 |
-
sampler_size: int = -1,
|
| 179 |
-
sampler_advance: int = 0,
|
| 180 |
-
drop_last: bool = True,
|
| 181 |
-
persistent_workers: bool = False,
|
| 182 |
-
collate_fn: Optional[Callable[[List[T]], Any]] = None,
|
| 183 |
-
prefetch_factor: int=2,
|
| 184 |
-
):
|
| 185 |
-
"""
|
| 186 |
-
Creates a data loader with the specified parameters.
|
| 187 |
-
|
| 188 |
-
Args:
|
| 189 |
-
dataset: A dataset (third party, LaViDa or WebDataset).
|
| 190 |
-
batch_size: The size of batches to generate.
|
| 191 |
-
num_workers: The number of workers to use.
|
| 192 |
-
shuffle: Whether to shuffle samples.
|
| 193 |
-
seed: The random seed to use.
|
| 194 |
-
sampler_type: Which sampler to use: EPOCH, INFINITE, SHARDED_INFINITE, SHARDED_INFINITE_NEW, DISTRIBUTED or None.
|
| 195 |
-
sampler_size: The number of images per epoch (when applicable) or -1 for the entire dataset.
|
| 196 |
-
sampler_advance: How many samples to skip (when applicable).
|
| 197 |
-
drop_last: Whether the last non-full batch of data should be dropped.
|
| 198 |
-
persistent_workers: maintain the workers Dataset instances alive after a dataset has been consumed once.
|
| 199 |
-
collate_fn: Function that performs batch collation
|
| 200 |
-
"""
|
| 201 |
-
|
| 202 |
-
sampler = _make_sampler(
|
| 203 |
-
dataset=dataset,
|
| 204 |
-
type=sampler_type,
|
| 205 |
-
shuffle=shuffle,
|
| 206 |
-
seed=seed,
|
| 207 |
-
size=sampler_size,
|
| 208 |
-
advance=sampler_advance,
|
| 209 |
-
)
|
| 210 |
-
|
| 211 |
-
logger.info("using PyTorch data loader")
|
| 212 |
-
data_loader = torch.utils.data.DataLoader(
|
| 213 |
-
dataset,
|
| 214 |
-
sampler=sampler,
|
| 215 |
-
batch_size=batch_size,
|
| 216 |
-
num_workers=num_workers,
|
| 217 |
-
pin_memory=True,
|
| 218 |
-
drop_last=drop_last,
|
| 219 |
-
persistent_workers=persistent_workers,
|
| 220 |
-
collate_fn=collate_fn,
|
| 221 |
-
timeout=600,
|
| 222 |
-
prefetch_factor=prefetch_factor,
|
| 223 |
-
)
|
| 224 |
-
|
| 225 |
-
try:
|
| 226 |
-
logger.info(f"# of batches: {len(data_loader):,d}")
|
| 227 |
-
except TypeError: # data loader has no length
|
| 228 |
-
logger.info("infinite data loader")
|
| 229 |
-
return data_loader
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/data/masking.py
DELETED
|
@@ -1,86 +0,0 @@
|
|
| 1 |
-
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
-
#
|
| 3 |
-
# This source code is licensed under the Apache License, Version 2.0
|
| 4 |
-
# found in the LICENSE file in the root directory of this source tree.
|
| 5 |
-
|
| 6 |
-
import random
|
| 7 |
-
import math
|
| 8 |
-
import numpy as np
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
class MaskingGenerator:
|
| 12 |
-
def __init__(
|
| 13 |
-
self,
|
| 14 |
-
input_size,
|
| 15 |
-
num_masking_patches=None,
|
| 16 |
-
min_num_patches=4,
|
| 17 |
-
max_num_patches=None,
|
| 18 |
-
min_aspect=0.3,
|
| 19 |
-
max_aspect=None,
|
| 20 |
-
):
|
| 21 |
-
if not isinstance(input_size, tuple):
|
| 22 |
-
input_size = (input_size,) * 2
|
| 23 |
-
self.height, self.width = input_size
|
| 24 |
-
|
| 25 |
-
self.num_patches = self.height * self.width
|
| 26 |
-
self.num_masking_patches = num_masking_patches
|
| 27 |
-
|
| 28 |
-
self.min_num_patches = min_num_patches
|
| 29 |
-
self.max_num_patches = num_masking_patches if max_num_patches is None else max_num_patches
|
| 30 |
-
|
| 31 |
-
max_aspect = max_aspect or 1 / min_aspect
|
| 32 |
-
self.log_aspect_ratio = (math.log(min_aspect), math.log(max_aspect))
|
| 33 |
-
|
| 34 |
-
def __repr__(self):
|
| 35 |
-
repr_str = "Generator(%d, %d -> [%d ~ %d], max = %d, %.3f ~ %.3f)" % (
|
| 36 |
-
self.height,
|
| 37 |
-
self.width,
|
| 38 |
-
self.min_num_patches,
|
| 39 |
-
self.max_num_patches,
|
| 40 |
-
self.num_masking_patches,
|
| 41 |
-
self.log_aspect_ratio[0],
|
| 42 |
-
self.log_aspect_ratio[1],
|
| 43 |
-
)
|
| 44 |
-
return repr_str
|
| 45 |
-
|
| 46 |
-
def get_shape(self):
|
| 47 |
-
return self.height, self.width
|
| 48 |
-
|
| 49 |
-
def _mask(self, mask, max_mask_patches):
|
| 50 |
-
delta = 0
|
| 51 |
-
for _ in range(10):
|
| 52 |
-
target_area = random.uniform(self.min_num_patches, max_mask_patches)
|
| 53 |
-
aspect_ratio = math.exp(random.uniform(*self.log_aspect_ratio))
|
| 54 |
-
h = int(round(math.sqrt(target_area * aspect_ratio)))
|
| 55 |
-
w = int(round(math.sqrt(target_area / aspect_ratio)))
|
| 56 |
-
if w < self.width and h < self.height:
|
| 57 |
-
top = random.randint(0, self.height - h)
|
| 58 |
-
left = random.randint(0, self.width - w)
|
| 59 |
-
|
| 60 |
-
num_masked = mask[top : top + h, left : left + w].sum()
|
| 61 |
-
# Overlap
|
| 62 |
-
if 0 < h * w - num_masked <= max_mask_patches:
|
| 63 |
-
for i in range(top, top + h):
|
| 64 |
-
for j in range(left, left + w):
|
| 65 |
-
if mask[i, j] == 0:
|
| 66 |
-
mask[i, j] = 1
|
| 67 |
-
delta += 1
|
| 68 |
-
|
| 69 |
-
if delta > 0:
|
| 70 |
-
break
|
| 71 |
-
return delta
|
| 72 |
-
|
| 73 |
-
def __call__(self, num_masking_patches=0):
|
| 74 |
-
mask = np.zeros(shape=self.get_shape(), dtype=bool)
|
| 75 |
-
mask_count = 0
|
| 76 |
-
while mask_count < num_masking_patches:
|
| 77 |
-
max_mask_patches = num_masking_patches - mask_count
|
| 78 |
-
max_mask_patches = min(max_mask_patches, self.max_num_patches)
|
| 79 |
-
|
| 80 |
-
delta = self._mask(mask, max_mask_patches)
|
| 81 |
-
if delta == 0:
|
| 82 |
-
break
|
| 83 |
-
else:
|
| 84 |
-
mask_count += delta
|
| 85 |
-
|
| 86 |
-
return mask
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/data/openpath_wds.py
DELETED
|
@@ -1,229 +0,0 @@
|
|
| 1 |
-
# OpenPath WebDataset loader for OpenMidnight (DINOv2 fork).
|
| 2 |
-
#
|
| 3 |
-
# OpenMidnight ships two data paths: HF-parquet streaming and on-the-fly SVS
|
| 4 |
-
# patching. OpenPath data is ALREADY pre-patched into WebDataset tar shards
|
| 5 |
-
# (`data/tiles/shards/w*/*.tar`, each sample = key.jpg + key.json). This module
|
| 6 |
-
# adds a third path that streams those shards and yields samples shaped exactly
|
| 7 |
-
# like OpenMidnight's collate expects: `((transform(pil), None), meta)`.
|
| 8 |
-
#
|
| 9 |
-
# It is self-contained (pure tarfile — no `webdataset` dependency) and replicates
|
| 10 |
-
# the proven default behavior of our original loader: resampled random shard
|
| 11 |
-
# sampling (with replacement) + a sample-level shuffle buffer + split filtering.
|
| 12 |
-
# (The InterleavedShards round-robin variant was falsified downstream, so it is
|
| 13 |
-
# intentionally not ported.)
|
| 14 |
-
#
|
| 15 |
-
# dataset_str format (reuses `cfg.train.sample_list_path`):
|
| 16 |
-
# openpath:glob=/abs/shards/w*/*.tar:split=/abs/pretrain_train.txt[:mag=20]
|
| 17 |
-
import glob as _glob
|
| 18 |
-
import io as _io
|
| 19 |
-
import json as _json
|
| 20 |
-
import os as _os
|
| 21 |
-
import random as _random
|
| 22 |
-
import tarfile as _tarfile
|
| 23 |
-
|
| 24 |
-
import torch
|
| 25 |
-
from PIL import Image
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
def parse_openpath_path(dataset_str):
|
| 29 |
-
assert dataset_str.startswith("openpath:"), dataset_str
|
| 30 |
-
out = {}
|
| 31 |
-
for kv in dataset_str[len("openpath:"):].split(":"):
|
| 32 |
-
if not kv:
|
| 33 |
-
continue
|
| 34 |
-
k, _, v = kv.partition("=")
|
| 35 |
-
out[k] = v
|
| 36 |
-
assert "glob" in out, "openpath dataset_path requires glob=..."
|
| 37 |
-
mag = float(out["mag"]) if out.get("mag") else None
|
| 38 |
-
return out["glob"], out.get("split") or None, mag
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
def _iter_shard(path):
|
| 42 |
-
"""Yield {'__key__','jpg','json'} dicts from a tar shard. A sample's members
|
| 43 |
-
(key.jpg, key.json) are contiguous in-tar, so group by key."""
|
| 44 |
-
grp, cur = {}, None
|
| 45 |
-
try:
|
| 46 |
-
with _tarfile.open(path) as tar:
|
| 47 |
-
for m in tar:
|
| 48 |
-
if not m.isfile():
|
| 49 |
-
continue
|
| 50 |
-
key, _, ext = m.name.rpartition(".")
|
| 51 |
-
if cur is not None and key != cur:
|
| 52 |
-
if "jpg" in grp and "json" in grp:
|
| 53 |
-
yield grp
|
| 54 |
-
grp = {}
|
| 55 |
-
cur = key
|
| 56 |
-
grp["__key__"] = key
|
| 57 |
-
f = tar.extractfile(m)
|
| 58 |
-
if f is not None:
|
| 59 |
-
grp[ext] = f.read()
|
| 60 |
-
if "jpg" in grp and "json" in grp:
|
| 61 |
-
yield grp
|
| 62 |
-
except Exception:
|
| 63 |
-
return
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
class OpenPathWds(torch.utils.data.IterableDataset):
|
| 67 |
-
"""Infinite, rank/worker-sharded stream of transformed OpenPath tiles.
|
| 68 |
-
|
| 69 |
-
Yields `((transform(pil_rgb), None), key)` to match OpenMidnight's
|
| 70 |
-
`collate_data_and_cast` (reads sample[0]=(crops_dict,None), sample[1]=meta)."""
|
| 71 |
-
|
| 72 |
-
def __init__(self, shards, transform, keep_ids=None, mag=None, shuffle=2000, base_seed=0, interleave=24):
|
| 73 |
-
super().__init__()
|
| 74 |
-
self.shards = shards
|
| 75 |
-
self.transform = transform
|
| 76 |
-
self.keep_ids = keep_ids
|
| 77 |
-
self.mag = mag
|
| 78 |
-
self.shuffle = shuffle
|
| 79 |
-
self.base_seed = base_seed
|
| 80 |
-
# ★ 각 shard=4-5 WSI 연속타일. K개 shard 동시 round-robin → ~K×4.5 WSI/배치 다양성.
|
| 81 |
-
# 필수 — 단일/소수 WSI 배치는 DINO/iBOT centering·sharpening 통계 붕괴(검증됨).
|
| 82 |
-
self.interleave = max(1, interleave)
|
| 83 |
-
|
| 84 |
-
def _keep(self, raw_json):
|
| 85 |
-
if self.keep_ids is None and self.mag is None:
|
| 86 |
-
return True
|
| 87 |
-
try:
|
| 88 |
-
j = _json.loads(raw_json)
|
| 89 |
-
except Exception:
|
| 90 |
-
return False
|
| 91 |
-
if self.keep_ids is not None and j.get("wsi_id") not in self.keep_ids:
|
| 92 |
-
return False
|
| 93 |
-
if self.mag is not None and j.get("mag") != self.mag:
|
| 94 |
-
return False
|
| 95 |
-
return True
|
| 96 |
-
|
| 97 |
-
def __iter__(self):
|
| 98 |
-
wi = torch.utils.data.get_worker_info()
|
| 99 |
-
rank = int(_os.environ.get("RANK", 0))
|
| 100 |
-
world = int(_os.environ.get("WORLD_SIZE", 1))
|
| 101 |
-
wid = wi.id if wi else 0
|
| 102 |
-
nw = wi.num_workers if wi else 1
|
| 103 |
-
# Per-(rank,worker) RNG so every reader draws an independent shard stream
|
| 104 |
-
# (resampled-with-replacement, like wds.WebDataset(resampled=True)).
|
| 105 |
-
rng = _random.Random(self.base_seed + rank * 1_000_003 + wid * 9176 + 17)
|
| 106 |
-
|
| 107 |
-
buf = []
|
| 108 |
-
S = max(self.shuffle, 1)
|
| 109 |
-
|
| 110 |
-
def _one_shard_stream():
|
| 111 |
-
# 한 shard를 끝까지 흘리고, 소진되면 새 무작위 shard로 교체(무한).
|
| 112 |
-
while True:
|
| 113 |
-
shard = rng.choice(self.shards)
|
| 114 |
-
for s in _iter_shard(shard):
|
| 115 |
-
if self._keep(s.get("json", b"")):
|
| 116 |
-
yield s
|
| 117 |
-
|
| 118 |
-
def gen():
|
| 119 |
-
# ★ K개 shard 스트림을 동시에 열어 round-robin → 연속샘플이 서로 다른 shard/WSI에서.
|
| 120 |
-
K = min(self.interleave, len(self.shards))
|
| 121 |
-
streams = [_one_shard_stream() for _ in range(K)]
|
| 122 |
-
while True:
|
| 123 |
-
for st in streams:
|
| 124 |
-
yield next(st)
|
| 125 |
-
|
| 126 |
-
src = gen()
|
| 127 |
-
# prime shuffle buffer
|
| 128 |
-
for _ in range(S):
|
| 129 |
-
buf.append(next(src))
|
| 130 |
-
while True:
|
| 131 |
-
i = rng.randrange(len(buf))
|
| 132 |
-
s = buf[i]
|
| 133 |
-
buf[i] = next(src)
|
| 134 |
-
try:
|
| 135 |
-
img = Image.open(_io.BytesIO(s["jpg"])).convert("RGB")
|
| 136 |
-
except Exception:
|
| 137 |
-
continue
|
| 138 |
-
yield (self.transform(img), None), s["__key__"]
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
def _iter_parquet(path):
|
| 142 |
-
"""parquet 파일에서 {'jpg','__key__'} 샘플을 yield (image_bytes 컬럼=jpg/png 바이트)."""
|
| 143 |
-
import pyarrow.parquet as _pq
|
| 144 |
-
try:
|
| 145 |
-
t = _pq.read_table(path, columns=["image_bytes", "slide_path", "x", "y"])
|
| 146 |
-
cols = t.to_pydict()
|
| 147 |
-
ib = cols["image_bytes"]; sp = cols["slide_path"]; xs = cols["x"]; ys = cols["y"]
|
| 148 |
-
for i in range(len(ib)):
|
| 149 |
-
yield {"jpg": ib[i], "__key__": f"{sp[i]}_{xs[i]}_{ys[i]}"}
|
| 150 |
-
except Exception:
|
| 151 |
-
return
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
class ParquetTiles(torch.utils.data.IterableDataset):
|
| 155 |
-
"""parquet 파일 리스트를 resampled-with-replacement로 스트리밍(tar 로더와 동일 패턴)."""
|
| 156 |
-
def __init__(self, files, transform, shuffle=1000, base_seed=0):
|
| 157 |
-
super().__init__()
|
| 158 |
-
self.files = files; self.transform = transform
|
| 159 |
-
self.shuffle = shuffle; self.base_seed = base_seed
|
| 160 |
-
|
| 161 |
-
def __iter__(self):
|
| 162 |
-
wi = torch.utils.data.get_worker_info()
|
| 163 |
-
rank = int(_os.environ.get("RANK", 0)); wid = wi.id if wi else 0
|
| 164 |
-
rng = _random.Random(self.base_seed + rank * 1_000_003 + wid * 9176 + 17)
|
| 165 |
-
S = max(self.shuffle, 1)
|
| 166 |
-
|
| 167 |
-
def gen():
|
| 168 |
-
while True:
|
| 169 |
-
for s in _iter_parquet(rng.choice(self.files)):
|
| 170 |
-
yield s
|
| 171 |
-
src = gen()
|
| 172 |
-
buf = [next(src) for _ in range(S)]
|
| 173 |
-
while True:
|
| 174 |
-
i = rng.randrange(len(buf)); s = buf[i]; buf[i] = next(src)
|
| 175 |
-
try:
|
| 176 |
-
img = Image.open(_io.BytesIO(s["jpg"])).convert("RGB")
|
| 177 |
-
except Exception:
|
| 178 |
-
continue
|
| 179 |
-
yield (self.transform(img), None), s["__key__"]
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
def make_openpath_parquet_loader(dataset_str, batch_size, num_workers, data_transform,
|
| 183 |
-
collate_fn, shuffle=50000, prefetch_factor=4):
|
| 184 |
-
# ★ shuffle=50000(OpenMidnight 동일): 각 parquet=1슬라이드라 큰 버퍼로 ~22슬라이드 혼합
|
| 185 |
-
# 필수 — 작은 버퍼는 단일슬라이드 배치 → DINO/iBOT 통계 붕괴.
|
| 186 |
-
# dataset_str: "parquet:glob=/abs/**/*.parquet"
|
| 187 |
-
glob_pat = dataset_str[len("parquet:"):]
|
| 188 |
-
if glob_pat.startswith("glob="):
|
| 189 |
-
glob_pat = glob_pat[len("glob="):]
|
| 190 |
-
files = sorted(_glob.glob(glob_pat))
|
| 191 |
-
if not files:
|
| 192 |
-
raise FileNotFoundError(f"no parquet match {glob_pat}")
|
| 193 |
-
print(f"[openpath_parquet] files={len(files)}", flush=True)
|
| 194 |
-
ds = ParquetTiles(files, data_transform, shuffle=shuffle)
|
| 195 |
-
return torch.utils.data.DataLoader(
|
| 196 |
-
ds, batch_size=batch_size, num_workers=num_workers, drop_last=True,
|
| 197 |
-
pin_memory=True, persistent_workers=num_workers > 0, collate_fn=collate_fn,
|
| 198 |
-
prefetch_factor=prefetch_factor if num_workers > 0 else None,
|
| 199 |
-
)
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
def make_openpath_loader(dataset_str, batch_size, num_workers, data_transform,
|
| 203 |
-
collate_fn, shuffle=1000, prefetch_factor=4):
|
| 204 |
-
shard_glob, split_path, mag = parse_openpath_path(dataset_str)
|
| 205 |
-
# glob may be a single pattern or several comma-separated ones (e.g. to
|
| 206 |
-
# union the base corpus with an extra source like CPTAC living under a
|
| 207 |
-
# different data root). dedup in case patterns overlap.
|
| 208 |
-
shards = sorted({s for pat in shard_glob.split(",") if pat
|
| 209 |
-
for s in _glob.glob(pat)})
|
| 210 |
-
if not shards:
|
| 211 |
-
raise FileNotFoundError(f"no shards match {shard_glob}")
|
| 212 |
-
keep_ids = None
|
| 213 |
-
if split_path:
|
| 214 |
-
with open(split_path) as f:
|
| 215 |
-
keep_ids = set(f.read().split())
|
| 216 |
-
print(f"[openpath_wds] shards={len(shards)} split={'Y' if keep_ids else 'N'} mag={mag}",
|
| 217 |
-
flush=True)
|
| 218 |
-
|
| 219 |
-
ds = OpenPathWds(shards, data_transform, keep_ids=keep_ids, mag=mag, shuffle=shuffle)
|
| 220 |
-
return torch.utils.data.DataLoader(
|
| 221 |
-
ds,
|
| 222 |
-
batch_size=batch_size,
|
| 223 |
-
num_workers=num_workers,
|
| 224 |
-
drop_last=True,
|
| 225 |
-
pin_memory=True,
|
| 226 |
-
persistent_workers=num_workers > 0,
|
| 227 |
-
collate_fn=collate_fn,
|
| 228 |
-
prefetch_factor=prefetch_factor if num_workers > 0 else None,
|
| 229 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpenMidnight/dinov2/data/samplers.py
DELETED
|
@@ -1,229 +0,0 @@
|
|
| 1 |
-
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
-
#
|
| 3 |
-
# This source code is licensed under the Apache License, Version 2.0
|
| 4 |
-
# found in the LICENSE file in the root directory of this source tree.
|
| 5 |
-
|
| 6 |
-
import itertools
|
| 7 |
-
from typing import Any, Optional
|
| 8 |
-
import warnings
|
| 9 |
-
|
| 10 |
-
import numpy as np
|
| 11 |
-
import torch
|
| 12 |
-
from torch.utils.data.sampler import Sampler
|
| 13 |
-
|
| 14 |
-
import dinov2.distributed as distributed
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
class EpochSampler(Sampler):
|
| 18 |
-
def __init__(
|
| 19 |
-
self,
|
| 20 |
-
*,
|
| 21 |
-
size: int,
|
| 22 |
-
sample_count: int,
|
| 23 |
-
shuffle: bool = False,
|
| 24 |
-
seed: int = 0,
|
| 25 |
-
start: Optional[int] = None,
|
| 26 |
-
step: Optional[int] = None,
|
| 27 |
-
):
|
| 28 |
-
self._size = size
|
| 29 |
-
self._sample_count = sample_count
|
| 30 |
-
self._shuffle = shuffle
|
| 31 |
-
self._seed = seed
|
| 32 |
-
self._start = distributed.get_global_rank() if start is None else start
|
| 33 |
-
self._step = distributed.get_global_size() if step is None else step
|
| 34 |
-
self._epoch = 0
|
| 35 |
-
|
| 36 |
-
def __iter__(self):
|
| 37 |
-
count = (self._size + self._sample_count - 1) // self._sample_count
|
| 38 |
-
tiled_indices = np.tile(np.arange(self._sample_count), count)
|
| 39 |
-
if self._shuffle:
|
| 40 |
-
seed = self._seed * self._epoch if self._seed != 0 else self._epoch
|
| 41 |
-
rng = np.random.default_rng(seed)
|
| 42 |
-
iterable = rng.choice(tiled_indices, self._size, replace=False)
|
| 43 |
-
else:
|
| 44 |
-
iterable = tiled_indices[: self._size]
|
| 45 |
-
|
| 46 |
-
yield from itertools.islice(iterable, self._start, None, self._step)
|
| 47 |
-
|
| 48 |
-
def __len__(self):
|
| 49 |
-
return (self._size - self._start + self._step - 1) // self._step
|
| 50 |
-
|
| 51 |
-
def set_epoch(self, epoch):
|
| 52 |
-
self._epoch = epoch
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
def _get_numpy_dtype(size: int) -> Any:
|
| 56 |
-
return np.int32 if size <= 2**31 else np.int64
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
def _get_torch_dtype(size: int) -> Any:
|
| 60 |
-
return torch.int32 if size <= 2**31 else torch.int64
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
def _generate_randperm_indices(*, size: int, generator: torch.Generator):
|
| 64 |
-
"""Generate the indices of a random permutation."""
|
| 65 |
-
dtype = _get_torch_dtype(size)
|
| 66 |
-
# This is actually matching PyTorch's CPU implementation, see: https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/native/TensorFactories.cpp#L900-L921
|
| 67 |
-
perm = torch.arange(size, dtype=dtype)
|
| 68 |
-
for i in range(size):
|
| 69 |
-
j = torch.randint(i, size, size=(1,), generator=generator).item()
|
| 70 |
-
|
| 71 |
-
# Always swap even if no-op
|
| 72 |
-
value = perm[j].item()
|
| 73 |
-
perm[j] = perm[i].item()
|
| 74 |
-
perm[i] = value
|
| 75 |
-
yield value
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
class InfiniteSampler(Sampler):
|
| 79 |
-
def __init__(
|
| 80 |
-
self,
|
| 81 |
-
*,
|
| 82 |
-
sample_count: int,
|
| 83 |
-
shuffle: bool = False,
|
| 84 |
-
seed: int = 0,
|
| 85 |
-
start: Optional[int] = None,
|
| 86 |
-
step: Optional[int] = None,
|
| 87 |
-
advance: int = 0,
|
| 88 |
-
):
|
| 89 |
-
self._sample_count = sample_count
|
| 90 |
-
self._seed = seed
|
| 91 |
-
self._shuffle = shuffle
|
| 92 |
-
self._start = distributed.get_global_rank() if start is None else start
|
| 93 |
-
self._step = distributed.get_global_size() if step is None else step
|
| 94 |
-
self._advance = advance
|
| 95 |
-
|
| 96 |
-
def __iter__(self):
|
| 97 |
-
if self._shuffle:
|
| 98 |
-
iterator = self._shuffled_iterator()
|
| 99 |
-
else:
|
| 100 |
-
iterator = self._iterator()
|
| 101 |
-
|
| 102 |
-
yield from itertools.islice(iterator, self._advance, None)
|
| 103 |
-
|
| 104 |
-
def _iterator(self):
|
| 105 |
-
assert not self._shuffle
|
| 106 |
-
|
| 107 |
-
while True:
|
| 108 |
-
iterable = range(self._sample_count)
|
| 109 |
-
yield from itertools.islice(iterable, self._start, None, self._step)
|
| 110 |
-
|
| 111 |
-
def _shuffled_iterator(self):
|
| 112 |
-
assert self._shuffle
|
| 113 |
-
|
| 114 |
-
# Instantiate a generator here (rather than in the ctor) to keep the class
|
| 115 |
-
# picklable (requirement of mp.spawn)
|
| 116 |
-
generator = torch.Generator().manual_seed(self._seed)
|
| 117 |
-
|
| 118 |
-
while True:
|
| 119 |
-
iterable = _generate_randperm_indices(size=self._sample_count, generator=generator)
|
| 120 |
-
yield from itertools.islice(iterable, self._start, None, self._step)
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
# The following function is somewhat equivalent to _new_shuffle_tensor_slice below,
|
| 124 |
-
# but avoids a full in-place random permutation generation.
|
| 125 |
-
def _shuffle_tensor_slice(
|
| 126 |
-
*, tensor: torch.Tensor, start: int = 0, step: int = 1, generator: torch.Generator
|
| 127 |
-
) -> np.ndarray:
|
| 128 |
-
stop = len(tensor)
|
| 129 |
-
count = stop // step
|
| 130 |
-
drop_count = stop - step * count
|
| 131 |
-
if drop_count:
|
| 132 |
-
warnings.warn(f"# of dropped samples: {drop_count}")
|
| 133 |
-
|
| 134 |
-
dtype = _get_numpy_dtype(stop)
|
| 135 |
-
result = np.empty(count, dtype=dtype)
|
| 136 |
-
|
| 137 |
-
for i in range(count):
|
| 138 |
-
j = torch.randint(0, i + 1, size=(1,), generator=generator).item() if i > 0 else 0
|
| 139 |
-
|
| 140 |
-
result[i] = result[j]
|
| 141 |
-
result[j] = tensor[start + i * step].item()
|
| 142 |
-
|
| 143 |
-
return result
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
def _new_shuffle_tensor_slice(
|
| 147 |
-
*, tensor: torch.Tensor, start: int = 0, step: int = 1, generator: torch.Generator
|
| 148 |
-
) -> np.ndarray:
|
| 149 |
-
stop = len(tensor)
|
| 150 |
-
count = stop // step
|
| 151 |
-
dtype = torch.int64 # Needed for using randperm result as indices
|
| 152 |
-
count = stop // step
|
| 153 |
-
drop_count = stop - step * count
|
| 154 |
-
if drop_count:
|
| 155 |
-
warnings.warn(f"# of dropped samples: {drop_count}")
|
| 156 |
-
indices = torch.randperm(count, dtype=dtype, generator=generator)
|
| 157 |
-
return tensor[start::step][indices].numpy()
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
def _make_seed(seed: int, start: int, iter_count: int) -> int:
|
| 161 |
-
# NOTE: Tried a few variants (including iter_count << 32), this one worked best.
|
| 162 |
-
return seed + start + (iter_count << 24)
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
class ShardedInfiniteSampler(Sampler):
|
| 166 |
-
def __init__(
|
| 167 |
-
self,
|
| 168 |
-
*,
|
| 169 |
-
sample_count: int,
|
| 170 |
-
shuffle: bool = False,
|
| 171 |
-
seed: int = 0,
|
| 172 |
-
start: Optional[int] = None,
|
| 173 |
-
step: Optional[int] = None,
|
| 174 |
-
advance: int = 0,
|
| 175 |
-
use_new_shuffle_tensor_slice: bool = False,
|
| 176 |
-
):
|
| 177 |
-
self._sample_count = sample_count
|
| 178 |
-
self._seed = seed
|
| 179 |
-
self._shuffle = shuffle
|
| 180 |
-
self._start = distributed.get_global_rank() if start is None else start
|
| 181 |
-
self._step = distributed.get_global_size() if step is None else step
|
| 182 |
-
self._advance = advance
|
| 183 |
-
self._iter_count = 0
|
| 184 |
-
self._shuffle_tensor_slice_fn = (
|
| 185 |
-
_new_shuffle_tensor_slice if use_new_shuffle_tensor_slice else _shuffle_tensor_slice
|
| 186 |
-
)
|
| 187 |
-
|
| 188 |
-
def __iter__(self):
|
| 189 |
-
iter_count = self._advance // self._sample_count
|
| 190 |
-
if iter_count > 0:
|
| 191 |
-
self._advance -= iter_count * self._sample_count
|
| 192 |
-
self._iter_count += iter_count
|
| 193 |
-
|
| 194 |
-
if self._shuffle:
|
| 195 |
-
iterator = self._shuffled_iterator()
|
| 196 |
-
else:
|
| 197 |
-
iterator = self._iterator()
|
| 198 |
-
|
| 199 |
-
yield from itertools.islice(iterator, self._advance, None)
|
| 200 |
-
|
| 201 |
-
def _iterator(self):
|
| 202 |
-
assert not self._shuffle
|
| 203 |
-
|
| 204 |
-
while True:
|
| 205 |
-
iterable = range(self._sample_count)
|
| 206 |
-
yield from itertools.islice(iterable, self._start, None, self._step)
|
| 207 |
-
|
| 208 |
-
def _shuffled_iterator(self):
|
| 209 |
-
assert self._shuffle
|
| 210 |
-
|
| 211 |
-
# Instantiate a generator here (rather than in the ctor) to be keep the class
|
| 212 |
-
# picklable (requirement of mp.spawn)
|
| 213 |
-
generator = torch.Generator()
|
| 214 |
-
|
| 215 |
-
# Always shuffle everything first
|
| 216 |
-
generator.manual_seed(self._seed)
|
| 217 |
-
dtype = _get_torch_dtype(self._sample_count)
|
| 218 |
-
perm = torch.randperm(self._sample_count, dtype=dtype, generator=generator)
|
| 219 |
-
|
| 220 |
-
while True:
|
| 221 |
-
# Re-seed on each iteration to allow skipping whole permutations
|
| 222 |
-
seed = _make_seed(self._seed, self._start, self._iter_count)
|
| 223 |
-
generator.manual_seed(seed)
|
| 224 |
-
|
| 225 |
-
iterable = self._shuffle_tensor_slice_fn(
|
| 226 |
-
tensor=perm, start=self._start, step=self._step, generator=generator
|
| 227 |
-
)
|
| 228 |
-
yield from iterable
|
| 229 |
-
self._iter_count += 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|