Skip to content

Commit 69c6823

Browse files
authored
Merge pull request #3097 from bghira/agent/dataset-batch-mps-limit
Validate per-dataset batch sizes on MPS
2 parents 17c0e31 + ab63f62 commit 69c6823

4 files changed

Lines changed: 86 additions & 8 deletions

File tree

simpletuner/helpers/configuration/cmd_args.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from accelerate.utils import ProjectConfiguration
1818

1919
from simpletuner.helpers.configuration.cli_utils import mapping_to_cli_args, normalize_lr_scheduler_value
20+
from simpletuner.helpers.configuration.platform_validation import validate_mps_train_batch_size
2021
from simpletuner.helpers.configuration.template_vars import render_modelspec_comment
2122
from simpletuner.helpers.distillation.common import validate_distillation_text_encoder_training
2223
from simpletuner.helpers.logging import get_logger
@@ -963,12 +964,7 @@ def _normalize_input_args(raw_args):
963964
if torch.backends.mps.is_available():
964965
if args.model_family.lower() not in ["sd3", "flux", "legacy"] and not args.unet_attention_slice:
965966
warning_log("MPS may benefit from the use of --unet_attention_slice for memory savings at the cost of speed.")
966-
if args.train_batch_size > 16:
967-
raise ValueError(
968-
"An M3 Max 128G will use 12 seconds per step at a batch size of 1 and 65 seconds per step at a batch size of 12."
969-
" Any higher values will result in NDArray size errors or other unstable training results and crashes."
970-
"\nPlease reduce the batch size to 12 or lower."
971-
)
967+
validate_mps_train_batch_size(args.train_batch_size)
972968

973969
if args.quantize_via == "accelerator":
974970
args.quantize_via = "cpu"
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import torch
2+
3+
4+
def validate_mps_train_batch_size(train_batch_size: int) -> None:
5+
"""Reject training batch sizes that exceed the existing MPS safety limit."""
6+
if torch.backends.mps.is_available() and train_batch_size > 16:
7+
raise ValueError(
8+
"An M3 Max 128G will use 12 seconds per step at a batch size of 1 and 65 seconds per step at a batch size of 12."
9+
" Any higher values will result in NDArray size errors or other unstable training results and crashes."
10+
"\nPlease reduce the batch size to 12 or lower."
11+
)

simpletuner/helpers/data_backend/factory.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ def _coerce_bucket_keys(indices: Dict[Any, Iterable]) -> Dict[Any, list]:
9696
from simpletuner.helpers.caching.image_embed import ImageEmbedCache
9797
from simpletuner.helpers.caching.text_embeds import TextEmbeddingCache
9898
from simpletuner.helpers.caching.vae import VAECache
99+
from simpletuner.helpers.configuration.platform_validation import validate_mps_train_batch_size
99100
from simpletuner.helpers.configuration.template_vars import resolve_value_placeholders
100101
from simpletuner.helpers.data_backend.aws import S3DataBackend
101102
from simpletuner.helpers.data_backend.base import BaseDataBackend
@@ -312,6 +313,8 @@ def init_backend_config(backend: dict, args: dict, accelerator) -> dict:
312313
dataset_train_batch_size = (
313314
resolve_dataset_train_batch_size(backend, args, dataset_type) if has_training_batch_size else None
314315
)
316+
if dataset_train_batch_size is not None:
317+
validate_mps_train_batch_size(dataset_train_batch_size)
315318

316319
start_epoch = normalize_start_epoch(backend.get("start_epoch", 1))
317320
start_step = normalize_start_step(backend.get("start_step", 0))

tests/test_factory_edge_cases.py

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,71 @@ def test_init_backend_config_uses_dataset_train_batch_size(self):
453453
self.assertEqual(result["config"]["train_batch_size"], 2)
454454
self.assertEqual(result["bucket_report"].constraints["train_batch_size"], 2)
455455

456+
def test_init_backend_config_rejects_unsafe_dataset_train_batch_size_on_mps(self):
457+
from simpletuner.helpers.data_backend.factory import init_backend_config
458+
459+
self.args.train_batch_size = 1
460+
training_dataset_types = (
461+
DatasetType.IMAGE,
462+
DatasetType.VIDEO,
463+
DatasetType.AUDIO,
464+
DatasetType.CONDITIONING,
465+
DatasetType.CAPTION,
466+
DatasetType.GROUNDING,
467+
)
468+
469+
with patch(
470+
"simpletuner.helpers.configuration.platform_validation.torch.backends.mps.is_available", return_value=True
471+
):
472+
for dataset_type in training_dataset_types:
473+
with self.subTest(dataset_type=dataset_type.value):
474+
backend = {
475+
"id": f"{dataset_type.value}-unsafe-batch",
476+
"type": "local",
477+
"dataset_type": dataset_type.value,
478+
"train_batch_size": 17,
479+
"instance_data_dir": self.temp_dir,
480+
}
481+
482+
with self.assertRaisesRegex(ValueError, "Please reduce the batch size to 12 or lower"):
483+
init_backend_config(backend, self.args, self.accelerator)
484+
485+
def test_init_backend_config_allows_train_batch_size_at_mps_limit(self):
486+
from simpletuner.helpers.data_backend.factory import init_backend_config
487+
488+
backend = {
489+
"id": "image-safe-mps-batch",
490+
"type": "local",
491+
"dataset_type": "image",
492+
"train_batch_size": 16,
493+
"instance_data_dir": self.temp_dir,
494+
}
495+
496+
with patch(
497+
"simpletuner.helpers.configuration.platform_validation.torch.backends.mps.is_available", return_value=True
498+
):
499+
result = init_backend_config(backend, self.args, self.accelerator)
500+
501+
self.assertEqual(result["config"]["train_batch_size"], 16)
502+
503+
def test_init_backend_config_allows_large_train_batch_size_without_mps(self):
504+
from simpletuner.helpers.data_backend.factory import init_backend_config
505+
506+
backend = {
507+
"id": "image-non-mps-batch",
508+
"type": "local",
509+
"dataset_type": "image",
510+
"train_batch_size": 17,
511+
"instance_data_dir": self.temp_dir,
512+
}
513+
514+
with patch(
515+
"simpletuner.helpers.configuration.platform_validation.torch.backends.mps.is_available", return_value=False
516+
):
517+
result = init_backend_config(backend, self.args, self.accelerator)
518+
519+
self.assertEqual(result["config"]["train_batch_size"], 17)
520+
456521
def test_eval_backend_config_forces_train_batch_size_one(self):
457522
from simpletuner.helpers.data_backend.factory import init_backend_config
458523

@@ -461,11 +526,14 @@ def test_eval_backend_config_forces_train_batch_size_one(self):
461526
"id": "eval-custom-batch",
462527
"type": "local",
463528
"dataset_type": "eval",
464-
"train_batch_size": 3,
529+
"train_batch_size": 17,
465530
"instance_data_dir": self.temp_dir,
466531
}
467532

468-
result = init_backend_config(backend, self.args, self.accelerator)
533+
with patch(
534+
"simpletuner.helpers.configuration.platform_validation.torch.backends.mps.is_available", return_value=True
535+
):
536+
result = init_backend_config(backend, self.args, self.accelerator)
469537

470538
self.assertEqual(result["config"]["train_batch_size"], 1)
471539

0 commit comments

Comments
 (0)