dsa-helpers 1.1.18.dev0__tar.gz → 1.1.19.dev1__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.dev1}/PKG-INFO +1 -1
- dsa_helpers-1.1.19.dev1/docker/Dockerfile +12 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/__init__.py +1 -1
- dsa_helpers-1.1.19.dev1/dsa_helpers/ml/callbacks.py +134 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/ml/segformer_semantic_segmentation/train.py +68 -59
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1/dsa_helpers.egg-info}/PKG-INFO +1 -1
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/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.dev1}/.gitignore +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/.readthedocs.yaml +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/LICENSE +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/README.md +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/app.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dev.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/docker/README.md +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/docs/requirements.txt +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/docs/source/conf.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/docs/source/index.rst +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/cli_utils.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/dash/__init__.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/dash/dash_paperdragon_utils.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/dash/header.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/dash/header_callbacks.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/girder_utils.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/gpd_utils.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/image_utils.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/imread.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/imwrite.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/ml/__init__.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/ml/datasets/SegFormerSegmentationDataset.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/ml/datasets/__init__.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/ml/datasets/utils.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/ml/evaluate.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/ml/metrics.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/ml/object_detection/__init__.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/ml/object_detection/get_pearce_roi_images.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/ml/object_detection/tile_image.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/ml/object_detection/utils.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/ml/segformer_semantic_segmentation/__init__.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/ml/segformer_semantic_segmentation/datasets.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/ml/segformer_semantic_segmentation/evaluate.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/ml/segformer_semantic_segmentation/inference.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/ml/segformer_semantic_segmentation/transforms.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/ml/segformer_semantic_segmentation/utils.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/ml/transforms/__init__.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/ml/transforms/segformer_transforms.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/mongo_utils.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/tile_utils.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/tiling.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers/utils.py +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers.egg-info/SOURCES.txt +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers.egg-info/dependency_links.txt +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers.egg-info/requires.txt +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/dsa_helpers.egg-info/top_level.txt +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/requirements.txt +0 -0
- {dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/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.dev1
|
|
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)
|
|
@@ -13,7 +13,7 @@ from torch import nn
|
|
|
13
13
|
from .utils import create_segformer_segmentation_dataset
|
|
14
14
|
from .transforms import get_train_transforms, val_transforms
|
|
15
15
|
from .evaluate import per_class_dice_on_dataset
|
|
16
|
-
from ..callbacks import MetricsLoggerCallback
|
|
16
|
+
from ..callbacks import MetricsLoggerCallback, BestModelCallback
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def train(
|
|
@@ -41,6 +41,7 @@ def train(
|
|
|
41
41
|
contrast: float = 0.25,
|
|
42
42
|
saturation: float = 0.25,
|
|
43
43
|
hue: float = 0.1,
|
|
44
|
+
rank: int = 0,
|
|
44
45
|
):
|
|
45
46
|
"""Workflow for training a SegFormer semantic segmentation model.
|
|
46
47
|
|
|
@@ -101,6 +102,7 @@ def train(
|
|
|
101
102
|
images. Defaults to 0.25.
|
|
102
103
|
hue (float, optional): Hue factor to apply to the images.
|
|
103
104
|
Defaults to 0.1.
|
|
105
|
+
rank (int, optional): Rank of the process. Defaults to 0.
|
|
104
106
|
|
|
105
107
|
Returns:
|
|
106
108
|
tuple: A tuple containing the trainer and the results dictionary.
|
|
@@ -108,8 +110,11 @@ def train(
|
|
|
108
110
|
"""
|
|
109
111
|
save_dir_path = Path(save_dir)
|
|
110
112
|
|
|
111
|
-
if
|
|
112
|
-
|
|
113
|
+
if rank == 0:
|
|
114
|
+
if save_dir_path.is_dir():
|
|
115
|
+
raise FileExistsError(f"Save directory {save_dir} already exists.")
|
|
116
|
+
else:
|
|
117
|
+
save_dir_path.mkdir(parents=True, exist_ok=True)
|
|
113
118
|
|
|
114
119
|
# Read the dataframes if they are strings.
|
|
115
120
|
if isinstance(train_data, str):
|
|
@@ -150,7 +155,6 @@ def train(
|
|
|
150
155
|
eval_strategy="epoch",
|
|
151
156
|
save_strategy="epoch",
|
|
152
157
|
logging_strategy="epoch",
|
|
153
|
-
load_best_model_at_end=True,
|
|
154
158
|
save_total_limit=1,
|
|
155
159
|
)
|
|
156
160
|
|
|
@@ -236,6 +240,11 @@ def train(
|
|
|
236
240
|
metrics["mean_dice"] = float(np.mean(list(metrics.values())))
|
|
237
241
|
return metrics
|
|
238
242
|
|
|
243
|
+
if rank == 0:
|
|
244
|
+
callbacks = [MetricsLoggerCallback, BestModelCallback(str(save_dir))]
|
|
245
|
+
else:
|
|
246
|
+
callbacks = [MetricsLoggerCallback]
|
|
247
|
+
|
|
239
248
|
# Trainer
|
|
240
249
|
trainer = Trainer(
|
|
241
250
|
model=model,
|
|
@@ -243,73 +252,59 @@ def train(
|
|
|
243
252
|
train_dataset=train_dataset,
|
|
244
253
|
eval_dataset=val_dataset,
|
|
245
254
|
compute_metrics=compute_metrics,
|
|
246
|
-
callbacks=
|
|
255
|
+
callbacks=callbacks,
|
|
247
256
|
)
|
|
248
257
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
results = {"train": {}, "val": {}}
|
|
259
|
-
|
|
260
|
-
for log in trainer.state.log_history[:-1]:
|
|
261
|
-
epoch = log["epoch"]
|
|
258
|
+
if rank != 0:
|
|
259
|
+
# Train the model.
|
|
260
|
+
_ = trainer.train()
|
|
261
|
+
return None
|
|
262
|
+
else:
|
|
263
|
+
print("Starting training...")
|
|
264
|
+
_ = trainer.train()
|
|
265
|
+
print("Training completed successfully!")
|
|
262
266
|
|
|
263
|
-
|
|
264
|
-
continue # only track end of each epoch
|
|
267
|
+
results = {"train": {}, "val": {}}
|
|
265
268
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
data = results["val"]
|
|
269
|
-
else:
|
|
270
|
-
data = results["train"]
|
|
269
|
+
for log in trainer.state.log_history[:-1]:
|
|
270
|
+
epoch = log["epoch"]
|
|
271
271
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
k = k[5:]
|
|
272
|
+
if epoch % 1:
|
|
273
|
+
continue # only track end of each epoch
|
|
275
274
|
|
|
276
|
-
if
|
|
277
|
-
|
|
275
|
+
# Check if this is tracking train or validation.
|
|
276
|
+
if "eval_loss" in log:
|
|
277
|
+
data = results["val"]
|
|
278
|
+
else:
|
|
279
|
+
data = results["train"]
|
|
278
280
|
|
|
279
|
-
|
|
281
|
+
for k, v in log.items():
|
|
282
|
+
if k.startswith("eval_"):
|
|
283
|
+
k = k[5:]
|
|
280
284
|
|
|
281
|
-
|
|
282
|
-
|
|
285
|
+
if k not in data:
|
|
286
|
+
data[k] = []
|
|
283
287
|
|
|
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
|
-
)
|
|
288
|
+
data[k].append(v)
|
|
294
289
|
|
|
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
|
-
)
|
|
290
|
+
# For each of the datasets provided, get the metrics.
|
|
291
|
+
model = trainer.model
|
|
305
292
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
293
|
+
print("Calculating train metrics...")
|
|
294
|
+
results["train"]["metrics"] = per_class_dice_on_dataset(
|
|
295
|
+
model,
|
|
296
|
+
train_data,
|
|
297
|
+
label2id,
|
|
298
|
+
batch_size=batch_size,
|
|
299
|
+
device=device,
|
|
300
|
+
tile_size=tile_size,
|
|
301
|
+
tqdm_notebook=tqdm_notebook,
|
|
302
|
+
)
|
|
309
303
|
|
|
310
|
-
|
|
304
|
+
print("Calculating val metrics...")
|
|
305
|
+
results["val"]["metrics"] = per_class_dice_on_dataset(
|
|
311
306
|
model,
|
|
312
|
-
|
|
307
|
+
val_data,
|
|
313
308
|
label2id,
|
|
314
309
|
batch_size=batch_size,
|
|
315
310
|
device=device,
|
|
@@ -317,4 +312,18 @@ def train(
|
|
|
317
312
|
tqdm_notebook=tqdm_notebook,
|
|
318
313
|
)
|
|
319
314
|
|
|
320
|
-
|
|
315
|
+
if test_data is not None:
|
|
316
|
+
print("Calculating test metrics...")
|
|
317
|
+
results["test"] = {}
|
|
318
|
+
|
|
319
|
+
results["test"]["metrics"] = per_class_dice_on_dataset(
|
|
320
|
+
model,
|
|
321
|
+
test_data,
|
|
322
|
+
label2id,
|
|
323
|
+
batch_size=batch_size,
|
|
324
|
+
device=device,
|
|
325
|
+
tile_size=tile_size,
|
|
326
|
+
tqdm_notebook=tqdm_notebook,
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
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.dev1
|
|
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.dev1}/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.dev1}/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.dev1}/dsa_helpers/ml/object_detection/tile_image.py
RENAMED
|
File without changes
|
{dsa_helpers-1.1.18.dev0 → dsa_helpers-1.1.19.dev1}/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.dev1}/dsa_helpers.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|