dsa-helpers 1.1.18.dev0__tar.gz → 1.1.19.dev2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dsa_helpers-1.1.18.dev0/dsa_helpers.egg-info → dsa_helpers-1.1.19.dev2}/PKG-INFO +1 -1
- dsa_helpers-1.1.19.dev2/docker/Dockerfile +12 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/__init__.py +1 -1
- dsa_helpers-1.1.19.dev2/dsa_helpers/ml/callbacks.py +134 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/ml/segformer_semantic_segmentation/train.py +75 -58
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2/dsa_helpers.egg-info}/PKG-INFO +1 -1
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/pyproject.toml +1 -1
- dsa_helpers-1.1.18.dev0/docker/Dockerfile +0 -14
- dsa_helpers-1.1.18.dev0/dsa_helpers/ml/callbacks.py +0 -55
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/.gitignore +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/.readthedocs.yaml +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/LICENSE +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/README.md +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/app.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dev.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/docker/README.md +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/docs/requirements.txt +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/docs/source/conf.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/docs/source/index.rst +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/cli_utils.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/dash/__init__.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/dash/dash_paperdragon_utils.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/dash/header.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/dash/header_callbacks.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/girder_utils.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/gpd_utils.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/image_utils.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/imread.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/imwrite.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/ml/__init__.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/ml/datasets/SegFormerSegmentationDataset.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/ml/datasets/__init__.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/ml/datasets/utils.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/ml/evaluate.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/ml/metrics.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/ml/object_detection/__init__.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/ml/object_detection/get_pearce_roi_images.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/ml/object_detection/tile_image.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/ml/object_detection/utils.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/ml/segformer_semantic_segmentation/__init__.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/ml/segformer_semantic_segmentation/datasets.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/ml/segformer_semantic_segmentation/evaluate.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/ml/segformer_semantic_segmentation/inference.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/ml/segformer_semantic_segmentation/transforms.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/ml/segformer_semantic_segmentation/utils.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/ml/transforms/__init__.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/ml/transforms/segformer_transforms.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/mongo_utils.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/tile_utils.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/tiling.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/utils.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers.egg-info/SOURCES.txt +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers.egg-info/dependency_links.txt +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers.egg-info/requires.txt +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers.egg-info/top_level.txt +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/requirements.txt +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dsa_helpers
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.19.dev2
|
|
4
4
|
Summary: Utility functions for working with the DSA girder client.
|
|
5
5
|
Author-email: "Juan C. Vizcarra" <jvizcar@emory.edu>
|
|
6
6
|
Maintainer-email: "Juan C. Vizcarra" <jvizcar@emory.edu>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
FROM pytorch/pytorch:2.8.0-cuda12.6-cudnn9-runtime
|
|
2
|
+
|
|
3
|
+
RUN pip install --no-cache-dir dsa-helpers && \
|
|
4
|
+
pip install --no-cache-dir large-image[all] --find-links https://girder.github.io/large_image_wheels && \
|
|
5
|
+
pip install --no-cache-dir histomicstk
|
|
6
|
+
|
|
7
|
+
# # Dependencies for OpenCV (not needed anymore, but keeping for reference)
|
|
8
|
+
# RUN apt-get update && apt-get install ffmpeg libsm6 libxext6 -y
|
|
9
|
+
|
|
10
|
+
RUN mkdir /app && chmod 777 /app && mkdir /data && chmod 777 /data
|
|
11
|
+
|
|
12
|
+
WORKDIR /app
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""Provides custom callbacks for the Hugging Face Trainer class."""
|
|
2
|
+
|
|
3
|
+
from transformers import TrainerCallback
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
import pandas as pd
|
|
6
|
+
from copy import deepcopy
|
|
7
|
+
import os, shutil
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class MetricsLoggerCallback(TrainerCallback):
|
|
11
|
+
def _load_current_file(self, output_dir):
|
|
12
|
+
output_dir = Path(output_dir)
|
|
13
|
+
metrics_file = output_dir / "metrics.csv"
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
pd.read_csv(metrics_file)
|
|
17
|
+
if metrics_file.exists()
|
|
18
|
+
else pd.DataFrame()
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
def on_log(self, args, state, control, logs=None, **kwargs):
|
|
22
|
+
"""
|
|
23
|
+
Triggered during training logging (e.g., for training loss).
|
|
24
|
+
"""
|
|
25
|
+
if "loss" in logs: # Ensure training loss is available
|
|
26
|
+
metrics_df = self._load_current_file(args.output_dir)
|
|
27
|
+
|
|
28
|
+
# Append the epoch and the training loss.
|
|
29
|
+
loss = logs["loss"]
|
|
30
|
+
epoch = state.epoch
|
|
31
|
+
|
|
32
|
+
metrics_df = pd.concat(
|
|
33
|
+
[
|
|
34
|
+
metrics_df,
|
|
35
|
+
pd.DataFrame([{"epoch": epoch, "train_loss": loss}]),
|
|
36
|
+
],
|
|
37
|
+
ignore_index=True,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# Save it back to file.
|
|
41
|
+
metrics_df.to_csv(
|
|
42
|
+
Path(args.output_dir) / "metrics.csv", index=False
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
def on_evaluate(self, args, state, control, metrics, **kwargs):
|
|
46
|
+
"""
|
|
47
|
+
This is triggered at the end of evaluation (including at the end of each epoch).
|
|
48
|
+
"""
|
|
49
|
+
# Get the current metrics file.
|
|
50
|
+
metrics_df = self._load_current_file(args.output_dir)
|
|
51
|
+
|
|
52
|
+
# Add metrics to the last row of the current metrics file.
|
|
53
|
+
for k, v in metrics.items():
|
|
54
|
+
metrics_df.at[metrics_df.index[-1], k] = v
|
|
55
|
+
|
|
56
|
+
# Save the updated metrics file.
|
|
57
|
+
metrics_df.to_csv(Path(args.output_dir) / "metrics.csv", index=False)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class BestModelCallback(TrainerCallback):
|
|
61
|
+
"""Callback for tracking and saving the best model checkpoint.
|
|
62
|
+
|
|
63
|
+
This class was developed with the help of ChatGPT."""
|
|
64
|
+
|
|
65
|
+
def __init__(self, save_dir: str):
|
|
66
|
+
"""
|
|
67
|
+
Initialize the callback.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
save_dir (str): Directory where model is saving checkpoints.
|
|
71
|
+
The best checkpoint will be saved in a subdirectory
|
|
72
|
+
called "best-checkpoint".
|
|
73
|
+
|
|
74
|
+
"""
|
|
75
|
+
self.best_loss = float("inf")
|
|
76
|
+
self.best_model_state = None
|
|
77
|
+
self.save_dir = save_dir
|
|
78
|
+
|
|
79
|
+
def on_evaluate(self, args, state, control, metrics, model, **kwargs):
|
|
80
|
+
"""Called after evaluation. Track the best model state."""
|
|
81
|
+
# Get the mean_dice metric
|
|
82
|
+
current_metric = metrics.get("eval_loss")
|
|
83
|
+
|
|
84
|
+
if current_metric is not None and current_metric < self.best_loss:
|
|
85
|
+
self.best_loss = current_metric
|
|
86
|
+
print("New best loss.")
|
|
87
|
+
|
|
88
|
+
# Store the model state.
|
|
89
|
+
self.best_model_state = deepcopy(model.state_dict())
|
|
90
|
+
|
|
91
|
+
def on_train_end(self, args, state, control, model, **kwargs):
|
|
92
|
+
"""Logic:
|
|
93
|
+
* Create a best-checkpoint directory
|
|
94
|
+
* Save the best model state into the best-checkpoint directory
|
|
95
|
+
* Copy all the other model files (from the normal checkpoint
|
|
96
|
+
directory) into the best-checkpoint directory
|
|
97
|
+
|
|
98
|
+
"""
|
|
99
|
+
if self.best_model_state is not None:
|
|
100
|
+
# Create best-checkpoint directory
|
|
101
|
+
best_checkpoint_dir = os.path.join(
|
|
102
|
+
self.save_dir, "best-checkpoint"
|
|
103
|
+
)
|
|
104
|
+
os.makedirs(best_checkpoint_dir, exist_ok=True)
|
|
105
|
+
|
|
106
|
+
# Load the best model state
|
|
107
|
+
model.load_state_dict(self.best_model_state)
|
|
108
|
+
|
|
109
|
+
# Save the model (this creates model.safetensors or pytorch_model.bin)
|
|
110
|
+
model.save_pretrained(best_checkpoint_dir)
|
|
111
|
+
|
|
112
|
+
# Copy other checkpoint files from the latest checkpoint
|
|
113
|
+
# Find the latest checkpoint directory (checkpoint-{step_number})
|
|
114
|
+
save_path = Path(self.save_dir)
|
|
115
|
+
checkpoint_dirs = [
|
|
116
|
+
d
|
|
117
|
+
for d in save_path.iterdir()
|
|
118
|
+
if d.is_dir() and d.name.startswith("checkpoint-")
|
|
119
|
+
]
|
|
120
|
+
if checkpoint_dirs:
|
|
121
|
+
# Sort by step number to get the latest
|
|
122
|
+
latest_checkpoint = max(
|
|
123
|
+
checkpoint_dirs, key=lambda x: int(x.name.split("-")[1])
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
# Copy all files from the latest checkpoint, except the model files we just created
|
|
127
|
+
for file_path in latest_checkpoint.iterdir():
|
|
128
|
+
if file_path.is_file():
|
|
129
|
+
# Skip model files since we already saved the best model state
|
|
130
|
+
if not file_path.name.startswith(
|
|
131
|
+
("model.", "pytorch_model.")
|
|
132
|
+
):
|
|
133
|
+
dst = Path(best_checkpoint_dir) / file_path.name
|
|
134
|
+
shutil.copy2(file_path, dst)
|
|
@@ -6,6 +6,7 @@ from transformers import (
|
|
|
6
6
|
)
|
|
7
7
|
import numpy as np
|
|
8
8
|
from pathlib import Path
|
|
9
|
+
import tempfile, shutil
|
|
9
10
|
|
|
10
11
|
import torch
|
|
11
12
|
from torch import nn
|
|
@@ -13,7 +14,7 @@ from torch import nn
|
|
|
13
14
|
from .utils import create_segformer_segmentation_dataset
|
|
14
15
|
from .transforms import get_train_transforms, val_transforms
|
|
15
16
|
from .evaluate import per_class_dice_on_dataset
|
|
16
|
-
from ..callbacks import MetricsLoggerCallback
|
|
17
|
+
from ..callbacks import MetricsLoggerCallback, BestModelCallback
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
def train(
|
|
@@ -41,6 +42,7 @@ def train(
|
|
|
41
42
|
contrast: float = 0.25,
|
|
42
43
|
saturation: float = 0.25,
|
|
43
44
|
hue: float = 0.1,
|
|
45
|
+
rank: int = 0,
|
|
44
46
|
):
|
|
45
47
|
"""Workflow for training a SegFormer semantic segmentation model.
|
|
46
48
|
|
|
@@ -101,6 +103,7 @@ def train(
|
|
|
101
103
|
images. Defaults to 0.25.
|
|
102
104
|
hue (float, optional): Hue factor to apply to the images.
|
|
103
105
|
Defaults to 0.1.
|
|
106
|
+
rank (int, optional): Rank of the process. Defaults to 0.
|
|
104
107
|
|
|
105
108
|
Returns:
|
|
106
109
|
tuple: A tuple containing the trainer and the results dictionary.
|
|
@@ -108,8 +111,11 @@ def train(
|
|
|
108
111
|
"""
|
|
109
112
|
save_dir_path = Path(save_dir)
|
|
110
113
|
|
|
111
|
-
if
|
|
112
|
-
|
|
114
|
+
if rank == 0:
|
|
115
|
+
if save_dir_path.is_dir():
|
|
116
|
+
raise FileExistsError(f"Save directory {save_dir} already exists.")
|
|
117
|
+
else:
|
|
118
|
+
save_dir_path.mkdir(parents=True, exist_ok=True)
|
|
113
119
|
|
|
114
120
|
# Read the dataframes if they are strings.
|
|
115
121
|
if isinstance(train_data, str):
|
|
@@ -139,6 +145,10 @@ def train(
|
|
|
139
145
|
ignore_mismatched_sizes=True,
|
|
140
146
|
).to(device)
|
|
141
147
|
|
|
148
|
+
if rank != 0:
|
|
149
|
+
# Create temp directory for the model.
|
|
150
|
+
save_dir = tempfile.mkdtemp()
|
|
151
|
+
|
|
142
152
|
# Training arguments.
|
|
143
153
|
training_args = TrainingArguments(
|
|
144
154
|
str(save_dir),
|
|
@@ -150,7 +160,6 @@ def train(
|
|
|
150
160
|
eval_strategy="epoch",
|
|
151
161
|
save_strategy="epoch",
|
|
152
162
|
logging_strategy="epoch",
|
|
153
|
-
load_best_model_at_end=True,
|
|
154
163
|
save_total_limit=1,
|
|
155
164
|
)
|
|
156
165
|
|
|
@@ -236,6 +245,11 @@ def train(
|
|
|
236
245
|
metrics["mean_dice"] = float(np.mean(list(metrics.values())))
|
|
237
246
|
return metrics
|
|
238
247
|
|
|
248
|
+
if rank == 0:
|
|
249
|
+
callbacks = [MetricsLoggerCallback, BestModelCallback(str(save_dir))]
|
|
250
|
+
else:
|
|
251
|
+
callbacks = [MetricsLoggerCallback]
|
|
252
|
+
|
|
239
253
|
# Trainer
|
|
240
254
|
trainer = Trainer(
|
|
241
255
|
model=model,
|
|
@@ -243,73 +257,62 @@ def train(
|
|
|
243
257
|
train_dataset=train_dataset,
|
|
244
258
|
eval_dataset=val_dataset,
|
|
245
259
|
compute_metrics=compute_metrics,
|
|
246
|
-
callbacks=
|
|
260
|
+
callbacks=callbacks,
|
|
247
261
|
)
|
|
248
262
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
# Rename the checkpoint to not include a step number.
|
|
253
|
-
save_dir = Path(save_dir)
|
|
254
|
-
for p in save_dir.iterdir():
|
|
255
|
-
if p.is_dir() and p.name.startswith("checkpoint-"):
|
|
256
|
-
p.rename(save_dir / "checkpoint")
|
|
263
|
+
if rank != 0:
|
|
264
|
+
# Train the model.
|
|
265
|
+
_ = trainer.train()
|
|
257
266
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
267
|
+
# Delete the temp directory.
|
|
268
|
+
shutil.rmtree(save_dir)
|
|
269
|
+
return None
|
|
270
|
+
else:
|
|
271
|
+
print("Starting training...")
|
|
272
|
+
_ = trainer.train()
|
|
273
|
+
print("Training completed successfully!")
|
|
262
274
|
|
|
263
|
-
|
|
264
|
-
continue # only track end of each epoch
|
|
275
|
+
results = {"train": {}, "val": {}}
|
|
265
276
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
data = results["val"]
|
|
269
|
-
else:
|
|
270
|
-
data = results["train"]
|
|
277
|
+
for log in trainer.state.log_history[:-1]:
|
|
278
|
+
epoch = log["epoch"]
|
|
271
279
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
k = k[5:]
|
|
280
|
+
if epoch % 1:
|
|
281
|
+
continue # only track end of each epoch
|
|
275
282
|
|
|
276
|
-
if
|
|
277
|
-
|
|
283
|
+
# Check if this is tracking train or validation.
|
|
284
|
+
if "eval_loss" in log:
|
|
285
|
+
data = results["val"]
|
|
286
|
+
else:
|
|
287
|
+
data = results["train"]
|
|
278
288
|
|
|
279
|
-
|
|
289
|
+
for k, v in log.items():
|
|
290
|
+
if k.startswith("eval_"):
|
|
291
|
+
k = k[5:]
|
|
280
292
|
|
|
281
|
-
|
|
282
|
-
|
|
293
|
+
if k not in data:
|
|
294
|
+
data[k] = []
|
|
283
295
|
|
|
284
|
-
|
|
285
|
-
results["train"]["metrics"] = per_class_dice_on_dataset(
|
|
286
|
-
model,
|
|
287
|
-
train_data,
|
|
288
|
-
label2id,
|
|
289
|
-
batch_size=batch_size,
|
|
290
|
-
device=device,
|
|
291
|
-
tile_size=tile_size,
|
|
292
|
-
tqdm_notebook=tqdm_notebook,
|
|
293
|
-
)
|
|
296
|
+
data[k].append(v)
|
|
294
297
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
model,
|
|
298
|
-
val_data,
|
|
299
|
-
label2id,
|
|
300
|
-
batch_size=batch_size,
|
|
301
|
-
device=device,
|
|
302
|
-
tile_size=tile_size,
|
|
303
|
-
tqdm_notebook=tqdm_notebook,
|
|
304
|
-
)
|
|
298
|
+
# For each of the datasets provided, get the metrics.
|
|
299
|
+
model = trainer.model
|
|
305
300
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
301
|
+
print("Calculating train metrics...")
|
|
302
|
+
results["train"]["metrics"] = per_class_dice_on_dataset(
|
|
303
|
+
model,
|
|
304
|
+
train_data,
|
|
305
|
+
label2id,
|
|
306
|
+
batch_size=batch_size,
|
|
307
|
+
device=device,
|
|
308
|
+
tile_size=tile_size,
|
|
309
|
+
tqdm_notebook=tqdm_notebook,
|
|
310
|
+
)
|
|
309
311
|
|
|
310
|
-
|
|
312
|
+
print("Calculating val metrics...")
|
|
313
|
+
results["val"]["metrics"] = per_class_dice_on_dataset(
|
|
311
314
|
model,
|
|
312
|
-
|
|
315
|
+
val_data,
|
|
313
316
|
label2id,
|
|
314
317
|
batch_size=batch_size,
|
|
315
318
|
device=device,
|
|
@@ -317,4 +320,18 @@ def train(
|
|
|
317
320
|
tqdm_notebook=tqdm_notebook,
|
|
318
321
|
)
|
|
319
322
|
|
|
320
|
-
|
|
323
|
+
if test_data is not None:
|
|
324
|
+
print("Calculating test metrics...")
|
|
325
|
+
results["test"] = {}
|
|
326
|
+
|
|
327
|
+
results["test"]["metrics"] = per_class_dice_on_dataset(
|
|
328
|
+
model,
|
|
329
|
+
test_data,
|
|
330
|
+
label2id,
|
|
331
|
+
batch_size=batch_size,
|
|
332
|
+
device=device,
|
|
333
|
+
tile_size=tile_size,
|
|
334
|
+
tqdm_notebook=tqdm_notebook,
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
return trainer, results
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dsa_helpers
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.19.dev2
|
|
4
4
|
Summary: Utility functions for working with the DSA girder client.
|
|
5
5
|
Author-email: "Juan C. Vizcarra" <jvizcar@emory.edu>
|
|
6
6
|
Maintainer-email: "Juan C. Vizcarra" <jvizcar@emory.edu>
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
FROM pytorch/pytorch:2.8.0-cuda12.9-cudnn9-runtime
|
|
2
|
-
|
|
3
|
-
RUN pip install --no-cache-dir dsa-helpers
|
|
4
|
-
RUN pip install --no-cache-dir large-image[all] --find-links https://girder.github.io/large_image_wheels
|
|
5
|
-
RUN python -m pip install --no-cache-dir histomicstk --find-links https://girder.github.io/large_image_wheels
|
|
6
|
-
|
|
7
|
-
# Dependencies for OpenCV.
|
|
8
|
-
RUN apt-get update && apt-get install ffmpeg libsm6 libxext6 -y
|
|
9
|
-
|
|
10
|
-
RUN mkdir /app
|
|
11
|
-
RUN chmod 777 /app
|
|
12
|
-
|
|
13
|
-
RUN mkdir /data
|
|
14
|
-
RUN chmod 777 /data
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
"""Provides custom callbacks for the Hugging Face Trainer class."""
|
|
2
|
-
|
|
3
|
-
from transformers import TrainerCallback
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
import pandas as pd
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class MetricsLoggerCallback(TrainerCallback):
|
|
9
|
-
def _load_current_file(self, output_dir):
|
|
10
|
-
output_dir = Path(output_dir)
|
|
11
|
-
metrics_file = output_dir / "metrics.csv"
|
|
12
|
-
|
|
13
|
-
return (
|
|
14
|
-
pd.read_csv(metrics_file)
|
|
15
|
-
if metrics_file.exists()
|
|
16
|
-
else pd.DataFrame()
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
def on_log(self, args, state, control, logs=None, **kwargs):
|
|
20
|
-
"""
|
|
21
|
-
Triggered during training logging (e.g., for training loss).
|
|
22
|
-
"""
|
|
23
|
-
if "loss" in logs: # Ensure training loss is available
|
|
24
|
-
metrics_df = self._load_current_file(args.output_dir)
|
|
25
|
-
|
|
26
|
-
# Append the epoch and the training loss.
|
|
27
|
-
loss = logs["loss"]
|
|
28
|
-
epoch = state.epoch
|
|
29
|
-
|
|
30
|
-
metrics_df = pd.concat(
|
|
31
|
-
[
|
|
32
|
-
metrics_df,
|
|
33
|
-
pd.DataFrame([{"epoch": epoch, "train_loss": loss}]),
|
|
34
|
-
],
|
|
35
|
-
ignore_index=True,
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
# Save it back to file.
|
|
39
|
-
metrics_df.to_csv(
|
|
40
|
-
Path(args.output_dir) / "metrics.csv", index=False
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
def on_evaluate(self, args, state, control, metrics, **kwargs):
|
|
44
|
-
"""
|
|
45
|
-
This is triggered at the end of evaluation (including at the end of each epoch).
|
|
46
|
-
"""
|
|
47
|
-
# Get the current metrics file.
|
|
48
|
-
metrics_df = self._load_current_file(args.output_dir)
|
|
49
|
-
|
|
50
|
-
# Add metrics to the last row of the current metrics file.
|
|
51
|
-
for k, v in metrics.items():
|
|
52
|
-
metrics_df.at[metrics_df.index[-1], k] = v
|
|
53
|
-
|
|
54
|
-
# Save the updated metrics file.
|
|
55
|
-
metrics_df.to_csv(Path(args.output_dir) / "metrics.csv", index=False)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/dash/dash_paperdragon_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/ml/object_detection/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/ml/object_detection/tile_image.py
RENAMED
|
File without changes
|
{dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers/ml/object_detection/utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev2}/dsa_helpers.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|