scikit-ops 0.1.0.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- scikit_ops-0.1.0.dev0.dist-info/METADATA +372 -0
- scikit_ops-0.1.0.dev0.dist-info/RECORD +66 -0
- scikit_ops-0.1.0.dev0.dist-info/WHEEL +4 -0
- scikit_ops-0.1.0.dev0.dist-info/entry_points.txt +2 -0
- skop/__init__.py +14 -0
- skop/envs/cellpose3/pixi.toml +46 -0
- skop/envs/cupy/pixi.toml +35 -0
- skop/envs/minimal/pixi.toml +25 -0
- skop/envs/pytorch/pixi.toml +88 -0
- skop/envs/sdeconv/pixi.toml +85 -0
- skop/envs/segment-everything/pixi.toml +96 -0
- skop/envs/skimage/pixi.toml +28 -0
- skop/envs/stardist-tf/pixi.toml +91 -0
- skop/envs/unseg-cv/pixi.toml +27 -0
- skop/models.py +145 -0
- skop/ops/__init__.py +19 -0
- skop/ops/_util.py +115 -0
- skop/ops/deconvolve/__init__.py +18 -0
- skop/ops/deconvolve/_edges.py +92 -0
- skop/ops/deconvolve/_pad.py +117 -0
- skop/ops/deconvolve/richardson_lucy.py +100 -0
- skop/ops/deconvolve/richardson_lucy_cupy.py +116 -0
- skop/ops/detect/__init__.py +28 -0
- skop/ops/detect/_result.py +45 -0
- skop/ops/detect/fastsam.py +103 -0
- skop/ops/detect/jdll_yolo.py +115 -0
- skop/ops/detect/object_aware_yolo.py +112 -0
- skop/ops/detect/yolo.py +441 -0
- skop/ops/edges.py +382 -0
- skop/ops/generate.py +77 -0
- skop/ops/kernels/__init__.py +39 -0
- skop/ops/kernels/_fluorophore.py +42 -0
- skop/ops/kernels/_recenter.py +60 -0
- skop/ops/kernels/gibson_lanni.py +147 -0
- skop/ops/kernels/paraxial.py +128 -0
- skop/ops/kernels/psf.py +59 -0
- skop/ops/labels.py +144 -0
- skop/ops/mask/__init__.py +29 -0
- skop/ops/mask/_result.py +40 -0
- skop/ops/mask/microsam.py +114 -0
- skop/ops/mask/mobilesam.py +176 -0
- skop/ops/morphology.py +214 -0
- skop/ops/normalize.py +85 -0
- skop/ops/segment/__init__.py +24 -0
- skop/ops/segment/cellpose3.py +245 -0
- skop/ops/segment/cellpose4.py +127 -0
- skop/ops/segment/instanseg.py +162 -0
- skop/ops/segment/stardist2d.py +192 -0
- skop/ops/segment/starfun3d.py +104 -0
- skop/ops/segment/unseg/__init__.py +138 -0
- skop/ops/segment/unseg/_algorithm.py +1618 -0
- skop/ops/smooth.py +452 -0
- skop/ops/threshold.py +278 -0
- skop/ops/toy.py +114 -0
- skop/ops/train/__init__.py +81 -0
- skop/ops/train/_util.py +152 -0
- skop/ops/train/cellpose3.py +189 -0
- skop/ops/train/cellpose4.py +164 -0
- skop/ops/train/stardist2d.py +439 -0
- skop/ops/workflows/__init__.py +43 -0
- skop/ops/workflows/deconvolve/__init__.py +7 -0
- skop/ops/workflows/deconvolve/with_psf.py +103 -0
- skop/ops/workflows/mask/__init__.py +7 -0
- skop/ops/workflows/mask/detect_then_mask.py +97 -0
- skop/ops/workflows/segment/__init__.py +7 -0
- skop/ops/workflows/segment/connect_2d_in_3d.py +145 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"""Finetune one of Cellpose 3's models on your own patches.
|
|
2
|
+
|
|
3
|
+
Runs in the 'cellpose3' environment beside ``skop.ops.segment.cellpose3``.
|
|
4
|
+
Separate from ``train_cellpose`` for the same reason the inference ops are
|
|
5
|
+
separate: version 4 replaced the model zoo and moved the API, so no pin makes
|
|
6
|
+
the two coexist.
|
|
7
|
+
|
|
8
|
+
The channel settings are the reason to read this rather than assume it
|
|
9
|
+
matches CPSAM. Cellpose 3 takes two input planes, cytoplasm and optional
|
|
10
|
+
nuclei, and it takes them at training time as well as at inference. The two
|
|
11
|
+
must agree -- see ``cytoplasm_channel`` below.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from typing import Annotated
|
|
17
|
+
|
|
18
|
+
from opspec.op import op
|
|
19
|
+
from opspec.runtime import progress
|
|
20
|
+
from skop.ops.segment.cellpose3 import (
|
|
21
|
+
_CHANNEL_NUMBER,
|
|
22
|
+
CytoplasmChannel,
|
|
23
|
+
NucleusChannel,
|
|
24
|
+
PretrainedModel,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
from ._util import read_pairs, relay_epochs, write_history
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@op(env="cellpose3")
|
|
31
|
+
def train_cellpose3(
|
|
32
|
+
images: list[str],
|
|
33
|
+
labels: list[str],
|
|
34
|
+
model_dir: str,
|
|
35
|
+
name: str = "cellpose3_model",
|
|
36
|
+
model: PretrainedModel = PretrainedModel.cyto3,
|
|
37
|
+
initial_model: str = "",
|
|
38
|
+
cytoplasm_channel: CytoplasmChannel = CytoplasmChannel.grayscale,
|
|
39
|
+
nucleus_channel: NucleusChannel = NucleusChannel.none,
|
|
40
|
+
epochs: Annotated[int, {"widget_type": "SpinBox", "min": 1, "max": 5000}] = 100,
|
|
41
|
+
learning_rate: float = 0.005,
|
|
42
|
+
weight_decay: float = 1e-5,
|
|
43
|
+
batch_size: Annotated[int, {"widget_type": "SpinBox", "min": 1, "max": 64}] = 8,
|
|
44
|
+
patch_size: Annotated[
|
|
45
|
+
int, {"widget_type": "SpinBox", "min": 64, "max": 1024, "step": 32}
|
|
46
|
+
] = 224,
|
|
47
|
+
min_train_masks: Annotated[int, {"widget_type": "SpinBox", "min": 0, "max": 100}] = 5,
|
|
48
|
+
normalize: bool = True,
|
|
49
|
+
rescale: bool = True,
|
|
50
|
+
nimg_per_epoch: Annotated[int, {"widget_type": "SpinBox", "min": 0, "max": 100000}] = 0,
|
|
51
|
+
val_size: Annotated[int, {"widget_type": "SpinBox", "min": 1, "max": 100}] = 2,
|
|
52
|
+
dataset_id: str = "",
|
|
53
|
+
) -> str:
|
|
54
|
+
"""Finetune a Cellpose 3 model and write it to disk.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
images: Paths to the input patches, in order.
|
|
58
|
+
labels: Paths to the label patches. ``labels[k]`` is the truth for
|
|
59
|
+
``images[k]`` -- the pairing is positional, and the caller owns it.
|
|
60
|
+
model_dir: Directory to train into. Cellpose puts the model in a
|
|
61
|
+
``models/`` subdirectory of it, which is its convention and is
|
|
62
|
+
left alone here.
|
|
63
|
+
name: Filename for the trained model.
|
|
64
|
+
model: Which built-in to start from. cyto3 is the generalist; a
|
|
65
|
+
specialist trained on your imaging domain starts closer and needs
|
|
66
|
+
fewer patches. Ignored when ``initial_model`` is set.
|
|
67
|
+
initial_model: A model of your own to continue from, which takes
|
|
68
|
+
precedence over ``model``, and what decides whether the history
|
|
69
|
+
continues or starts over: pass the path this op returned to carry
|
|
70
|
+
on, leave it empty to start again. It must be a Cellpose 1-3 model, not a
|
|
71
|
+
CPSAM one; ``skop.models.cellpose_flavor`` tells them apart from
|
|
72
|
+
the file.
|
|
73
|
+
cytoplasm_channel: Which colour the cells are in. Left at
|
|
74
|
+
``grayscale``, the colours are averaged into one plane before
|
|
75
|
+
Cellpose sees them. **Whatever you choose here you must pass
|
|
76
|
+
again to** ``skop.ops.segment.cellpose3``: the two input planes
|
|
77
|
+
are what the network learned, so a model trained on green
|
|
78
|
+
cytoplasm and run on a grey average is being shown something it
|
|
79
|
+
never saw.
|
|
80
|
+
nucleus_channel: Which colour the nuclei are in. Ignored unless
|
|
81
|
+
``cytoplasm_channel`` names a colour too -- Cellpose reads the
|
|
82
|
+
pair, and a grey first slot drops the second.
|
|
83
|
+
epochs: Passes over the training set. Cellpose's own default is
|
|
84
|
+
2000, which is a from-scratch number; finetuning a zoo model
|
|
85
|
+
wants far fewer.
|
|
86
|
+
learning_rate: Cellpose 3's default. Three orders larger than
|
|
87
|
+
CPSAM's, because this is a far smaller network.
|
|
88
|
+
weight_decay: L2 penalty on the weights.
|
|
89
|
+
batch_size: Patches per batch.
|
|
90
|
+
patch_size: Size of the crops training samples from the patches.
|
|
91
|
+
min_train_masks: Skip patches with fewer masks than this. 0 keeps
|
|
92
|
+
everything -- which is what you want when empty patches are
|
|
93
|
+
themselves the lesson.
|
|
94
|
+
normalize: Whether to percentile-normalize each patch first.
|
|
95
|
+
rescale: Resize each patch so its objects match the diameter the
|
|
96
|
+
network expects. On by default, and worth keeping: it is what
|
|
97
|
+
lets a model trained at one magnification work at another. Turn
|
|
98
|
+
it off when your objects are all one size and you would rather
|
|
99
|
+
the network learn that size.
|
|
100
|
+
nimg_per_epoch: How many patches make up one epoch. 0 means all of
|
|
101
|
+
them, which is Cellpose's own behaviour. Set it lower when the
|
|
102
|
+
training set is large enough that a pass over all of it is too
|
|
103
|
+
coarse a unit to watch -- 10000 patches is one very long epoch,
|
|
104
|
+
where 50 an epoch gives a loss curve that moves.
|
|
105
|
+
val_size: How many pairs, taken from the end, to hold out for
|
|
106
|
+
validation.
|
|
107
|
+
dataset_id: Opaque label for the training data, recorded in the
|
|
108
|
+
history file so a plot can mark where it changed.
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
The path of the model file. A ``<model file>_history.csv`` is written
|
|
112
|
+
beside it -- epoch, loss, val_loss, run, model, dataset -- appended to
|
|
113
|
+
when ``initial_model`` continues this same model, so the curve spans
|
|
114
|
+
every run the weights have had. Any other run starts the file over,
|
|
115
|
+
because Cellpose has just overwritten the model the old curve
|
|
116
|
+
described. To keep both, train the second under a different ``name``.
|
|
117
|
+
|
|
118
|
+
The mean diameter of the training labels is saved into the model as
|
|
119
|
+
``diam_labels``. That is what ``skop.ops.segment.cellpose3`` falls
|
|
120
|
+
back to when it is given ``diameter=0`` and a model of yours: a
|
|
121
|
+
finetuned checkpoint carries no size model, so the size it was
|
|
122
|
+
trained at is the best answer available.
|
|
123
|
+
"""
|
|
124
|
+
from pathlib import Path
|
|
125
|
+
|
|
126
|
+
import numpy as np
|
|
127
|
+
from cellpose import models, train
|
|
128
|
+
|
|
129
|
+
# Cellpose makes `<model_dir>/models` without parents.
|
|
130
|
+
Path(model_dir).mkdir(parents=True, exist_ok=True)
|
|
131
|
+
|
|
132
|
+
X_train, Y_train, X_val, Y_val = read_pairs(images, labels, val_size)
|
|
133
|
+
|
|
134
|
+
# Always passed: without `channels` the training reshape is skipped and
|
|
135
|
+
# a 2-D image reaches normalize_img, which wants a plane axis.
|
|
136
|
+
select = {
|
|
137
|
+
"channels": [
|
|
138
|
+
_CHANNEL_NUMBER[cytoplasm_channel.value],
|
|
139
|
+
_CHANNEL_NUMBER[nucleus_channel.value],
|
|
140
|
+
]
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
# int32 arrives from label arithmetic upstream; normalization chokes on it.
|
|
144
|
+
X_train = [np.asarray(x).astype(np.float32) for x in X_train]
|
|
145
|
+
X_val = [np.asarray(x).astype(np.float32) for x in X_val]
|
|
146
|
+
Y_train = [np.asarray(y).astype(np.uint16) for y in Y_train]
|
|
147
|
+
Y_val = [np.asarray(y).astype(np.uint16) for y in Y_val]
|
|
148
|
+
|
|
149
|
+
# CellposeModel, not Cellpose: the SizeModel it bundles trains separately.
|
|
150
|
+
if initial_model:
|
|
151
|
+
source = initial_model
|
|
152
|
+
build = dict(pretrained_model=initial_model)
|
|
153
|
+
else:
|
|
154
|
+
source = model.value
|
|
155
|
+
build = dict(model_type=model.value)
|
|
156
|
+
|
|
157
|
+
progress(f"Loading Cellpose 3 model {source}")
|
|
158
|
+
net = models.CellposeModel(gpu=True, **build).net
|
|
159
|
+
|
|
160
|
+
progress(
|
|
161
|
+
f"Training {name}: {len(X_train)} train, {len(X_val)} val, "
|
|
162
|
+
f"{epochs} epochs",
|
|
163
|
+
0,
|
|
164
|
+
epochs,
|
|
165
|
+
)
|
|
166
|
+
with relay_epochs(epochs) as relay:
|
|
167
|
+
model_path, _, _ = train.train_seg(
|
|
168
|
+
net,
|
|
169
|
+
train_data=X_train,
|
|
170
|
+
train_labels=Y_train,
|
|
171
|
+
test_data=X_val,
|
|
172
|
+
test_labels=Y_val,
|
|
173
|
+
n_epochs=epochs,
|
|
174
|
+
learning_rate=learning_rate,
|
|
175
|
+
weight_decay=weight_decay,
|
|
176
|
+
batch_size=batch_size,
|
|
177
|
+
bsize=patch_size,
|
|
178
|
+
min_train_masks=min_train_masks,
|
|
179
|
+
normalize=normalize,
|
|
180
|
+
rescale=rescale,
|
|
181
|
+
# None, not 0: Cellpose reads None as "every image".
|
|
182
|
+
nimg_per_epoch=nimg_per_epoch or None,
|
|
183
|
+
save_path=model_dir,
|
|
184
|
+
model_name=name,
|
|
185
|
+
**select,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
write_history(model_path, relay.history, name, dataset_id, initial_model)
|
|
189
|
+
return str(model_path)
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""Finetune CellposeSAM on your own patches.
|
|
2
|
+
|
|
3
|
+
Runs in the shared 'pytorch' environment, beside
|
|
4
|
+
``skop.ops.segment.cellpose4`` -- so a caller that trains and then segments
|
|
5
|
+
builds one environment, not two.
|
|
6
|
+
|
|
7
|
+
Cellpose 3's trainer is the exception, in ``cellpose3.py`` and its own
|
|
8
|
+
environment, for the same reason the inference ops are split.
|
|
9
|
+
|
|
10
|
+
Takes paths and returns a path, like every training op (design 0011). What
|
|
11
|
+
comes back is a **file**, not a directory: that is how Cellpose saves a
|
|
12
|
+
model, and it is why a caller listing trained models lists files here where
|
|
13
|
+
StarDist would have it list directories.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from typing import Annotated
|
|
19
|
+
|
|
20
|
+
from opspec.op import op
|
|
21
|
+
from opspec.runtime import progress
|
|
22
|
+
from skop.ops.segment.cellpose4 import PretrainedModel
|
|
23
|
+
|
|
24
|
+
from .._util import channel_axis, to_gray
|
|
25
|
+
from ._util import read_pairs, relay_epochs, write_history
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@op(env="pytorch")
|
|
29
|
+
def train_cellpose4(
|
|
30
|
+
images: list[str],
|
|
31
|
+
labels: list[str],
|
|
32
|
+
model_dir: str,
|
|
33
|
+
name: str = "cellpose4_model",
|
|
34
|
+
model: PretrainedModel = PretrainedModel.cpsam_v2,
|
|
35
|
+
initial_model: str = "",
|
|
36
|
+
epochs: Annotated[int, {"widget_type": "SpinBox", "min": 1, "max": 5000}] = 100,
|
|
37
|
+
learning_rate: float = 1e-5,
|
|
38
|
+
weight_decay: float = 0.1,
|
|
39
|
+
batch_size: Annotated[int, {"widget_type": "SpinBox", "min": 1, "max": 64}] = 1,
|
|
40
|
+
patch_size: Annotated[
|
|
41
|
+
int, {"widget_type": "SpinBox", "min": 64, "max": 1024, "step": 32}
|
|
42
|
+
] = 256,
|
|
43
|
+
min_train_masks: Annotated[int, {"widget_type": "SpinBox", "min": 0, "max": 100}] = 5,
|
|
44
|
+
normalize: bool = True,
|
|
45
|
+
rescale: bool = False,
|
|
46
|
+
nimg_per_epoch: Annotated[int, {"widget_type": "SpinBox", "min": 0, "max": 100000}] = 0,
|
|
47
|
+
collapse_channels: bool = False,
|
|
48
|
+
val_size: Annotated[int, {"widget_type": "SpinBox", "min": 1, "max": 100}] = 2,
|
|
49
|
+
dataset_id: str = "",
|
|
50
|
+
) -> str:
|
|
51
|
+
"""Finetune CPSAM and write the model to disk.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
images: Paths to the input patches, in order.
|
|
55
|
+
labels: Paths to the label patches. ``labels[k]`` is the truth for
|
|
56
|
+
``images[k]`` -- the pairing is positional, and the caller owns it.
|
|
57
|
+
model_dir: Directory to train into. Cellpose puts the model in a
|
|
58
|
+
``models/`` subdirectory of it, which is its convention and is
|
|
59
|
+
left alone here.
|
|
60
|
+
name: Filename for the trained model.
|
|
61
|
+
model: Which built-in to finetune. ``cpdino_vitb`` is the small
|
|
62
|
+
backbone and the one to reach for when the others run the GPU
|
|
63
|
+
out of memory. Ignored when ``initial_model`` is set.
|
|
64
|
+
initial_model: A model of your own to continue from, and what
|
|
65
|
+
decides whether the history continues or starts over: pass the
|
|
66
|
+
path this op returned to carry on, leave it empty to start again.
|
|
67
|
+
Empty starts
|
|
68
|
+
from the built-in named by ``model``, which is the usual thing
|
|
69
|
+
to do -- these are large models and a handful of patches
|
|
70
|
+
finetunes one, where they would not train it from nothing. It
|
|
71
|
+
must be a Cellpose 4 model, not a Cellpose 3 one, and it brings
|
|
72
|
+
its own backbone.
|
|
73
|
+
epochs: Passes over the training set.
|
|
74
|
+
learning_rate: Cellpose's default for finetuning CPSAM, and low on
|
|
75
|
+
purpose: the weights being adjusted are already good.
|
|
76
|
+
weight_decay: L2 penalty on the weights.
|
|
77
|
+
batch_size: Patches per batch. 1 by default because CPSAM is large
|
|
78
|
+
and the memory is the constraint; raise it if the card allows.
|
|
79
|
+
patch_size: Size of the crops training samples from the patches.
|
|
80
|
+
min_train_masks: Skip patches with fewer masks than this. 0 keeps
|
|
81
|
+
everything -- which is what you want when empty patches are
|
|
82
|
+
themselves the lesson.
|
|
83
|
+
normalize: Whether to percentile-normalize each patch first.
|
|
84
|
+
rescale: Resize each patch so its objects match the diameter the
|
|
85
|
+
network expects. Off by default here, unlike Cellpose 3: CPSAM
|
|
86
|
+
was trained across scales and does not need it.
|
|
87
|
+
nimg_per_epoch: How many patches make up one epoch. 0 means all of
|
|
88
|
+
them, which is Cellpose's own behaviour. Set it lower when the
|
|
89
|
+
training set is large enough that a pass over all of it is too
|
|
90
|
+
coarse a unit to watch.
|
|
91
|
+
collapse_channels: Average a trailing RGB(A) axis to grey before
|
|
92
|
+
training. Must match what you pass ``skop.ops.segment.cellpose4``
|
|
93
|
+
afterwards: a model finetuned on grey and then run on colour is
|
|
94
|
+
being shown something it never saw.
|
|
95
|
+
val_size: How many pairs, taken from the end, to hold out for
|
|
96
|
+
validation.
|
|
97
|
+
dataset_id: Opaque label for the training data, recorded in the
|
|
98
|
+
history file so a plot can mark where it changed.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
The path of the model file. A ``<model file>_history.csv`` is written
|
|
102
|
+
beside it -- epoch, loss, val_loss, run, model, dataset -- appended to
|
|
103
|
+
when ``initial_model`` continues this same model, so the curve spans
|
|
104
|
+
every run the weights have had. Any other run starts the file over,
|
|
105
|
+
because Cellpose has just overwritten the model the old curve
|
|
106
|
+
described. To keep both, train the second under a different ``name``.
|
|
107
|
+
"""
|
|
108
|
+
from pathlib import Path
|
|
109
|
+
|
|
110
|
+
import numpy as np
|
|
111
|
+
from cellpose import models, train
|
|
112
|
+
|
|
113
|
+
# Cellpose makes `<model_dir>/models` without parents.
|
|
114
|
+
Path(model_dir).mkdir(parents=True, exist_ok=True)
|
|
115
|
+
|
|
116
|
+
X_train, Y_train, X_val, Y_val = read_pairs(images, labels, val_size)
|
|
117
|
+
|
|
118
|
+
if collapse_channels:
|
|
119
|
+
X_train = [to_gray(x) for x in X_train]
|
|
120
|
+
X_val = [to_gray(x) for x in X_val]
|
|
121
|
+
axis = None
|
|
122
|
+
else:
|
|
123
|
+
axis = channel_axis(X_train[0])
|
|
124
|
+
|
|
125
|
+
# Labels reach here as whatever the patch writer used; the flow
|
|
126
|
+
# computation wants a plain integer image.
|
|
127
|
+
Y_train = [np.asarray(y).astype(np.uint16) for y in Y_train]
|
|
128
|
+
Y_val = [np.asarray(y).astype(np.uint16) for y in Y_val]
|
|
129
|
+
|
|
130
|
+
# A finetuned checkpoint carries its own backbone.
|
|
131
|
+
source = initial_model or model.value
|
|
132
|
+
progress(f"Loading Cellpose model {source}")
|
|
133
|
+
net = models.CellposeModel(gpu=True, pretrained_model=source).net
|
|
134
|
+
|
|
135
|
+
progress(
|
|
136
|
+
f"Training {name}: {len(X_train)} train, {len(X_val)} val, "
|
|
137
|
+
f"{epochs} epochs",
|
|
138
|
+
0,
|
|
139
|
+
epochs,
|
|
140
|
+
)
|
|
141
|
+
with relay_epochs(epochs) as relay:
|
|
142
|
+
model_path, _, _ = train.train_seg(
|
|
143
|
+
net,
|
|
144
|
+
train_data=X_train,
|
|
145
|
+
train_labels=Y_train,
|
|
146
|
+
test_data=X_val,
|
|
147
|
+
test_labels=Y_val,
|
|
148
|
+
channel_axis=axis,
|
|
149
|
+
n_epochs=epochs,
|
|
150
|
+
learning_rate=learning_rate,
|
|
151
|
+
weight_decay=weight_decay,
|
|
152
|
+
batch_size=batch_size,
|
|
153
|
+
bsize=patch_size,
|
|
154
|
+
min_train_masks=min_train_masks,
|
|
155
|
+
normalize=normalize,
|
|
156
|
+
rescale=rescale,
|
|
157
|
+
# None, not 0: Cellpose reads None as "every image".
|
|
158
|
+
nimg_per_epoch=nimg_per_epoch or None,
|
|
159
|
+
save_path=model_dir,
|
|
160
|
+
model_name=name,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
write_history(model_path, relay.history, name, dataset_id, initial_model)
|
|
164
|
+
return str(model_path)
|