Skip to content

feat(train): add dry_run=True to train()#6027

Open
amazeAmazing wants to merge 2 commits into
aws:master-nova-follow-upsfrom
amazeAmazing:feature/dry-run-validation
Open

feat(train): add dry_run=True to train()#6027
amazeAmazing wants to merge 2 commits into
aws:master-nova-follow-upsfrom
amazeAmazing:feature/dry-run-validation

Conversation

@amazeAmazing

@amazeAmazing amazeAmazing commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Summary

Add dry_run=True parameter to all trainers (SFT, DPO, RLVR, RLAIF) and evaluators (BenchMark, CustomScorer, LLMAsJudge). When dry_run=True, all validation runs but no job is submitted and no compute is consumed.

What it does

  • All existing inline validation runs as normal (IAM role, hyperparameters, recipe constraints, model resolution)
  • validate_data_path_exists() validates S3 URIs and DataSet ARNs unconditionally before submission
  • dry_run=True short-circuits before the API call, returning None
  • Raises with a clear error message on validation failure

Changes

Commit 1: feat(train): add dry_run=True to train()

  • data_utils.py — new validate_data_path_exists() (S3 via list_objects_v2, DataSet ARN via describe_hub_content)
  • base_trainer.py — add dry_run to train(), _train_serverful_smtj(), _train_hyperpod()
  • sft/dpo/rlvr/rlaif_trainer.py — add dry_run, validate data paths, short-circuit
  • Trainer notebook examples (SFT, DPO, RLVR, RLAIF)
  • Unit tests in existing test files
  • Integration test (test_dry_run_integration.py)

Commit 2: feat(evaluate): add dry_run=True to evaluate()

  • base_evaluator.py — add dry_run to evaluate() signature
  • benchmark/custom_scorer/llm_as_judge_evaluator.py — add dry_run, validate dataset paths (custom scorer + LLM-as-judge), short-circuit before _start_execution()
  • Evaluator notebook examples (benchmark, custom_scorer, llm_as_judge)

Testing

  • Integration test uploads sample data to default bucket, validates S3 path failures, confirms result is None, verifies no job created via SageMaker API

Issue #, if available:

Description of changes:

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@amazeAmazing
amazeAmazing force-pushed the feature/dry-run-validation branch 2 times, most recently from 164a14c to 7b06162 Compare July 15, 2026 17:55
@amazeAmazing amazeAmazing reopened this Jul 15, 2026
@amazeAmazing
amazeAmazing force-pushed the feature/dry-run-validation branch 9 times, most recently from 008794d to 16c783a Compare July 15, 2026 22:18
@amazeAmazing amazeAmazing changed the title feat(train): add dry_run=True to train() and deploy() feat(train): add dry_run=True to train() Jul 15, 2026
@amazeAmazing
amazeAmazing marked this pull request as ready for review July 15, 2026 22:30
Add dry_run parameter to all trainers (SFT, DPO, RLVR, RLAIF).

When dry_run=True:
- All existing validation runs inline (IAM role, hyperparameters,
  recipe constraints, infrastructure availability)
- Returns None without submitting a job or consuming compute
- Raises with clear error message on validation failure

Additionally, validate_data_path_exists() is called unconditionally
(regardless of dry_run) before job submission to catch non-existent
S3 paths or dataset ARNs early.

Design follows nova-forge-sdk pattern: validation always runs as part
of the normal code path, dry_run short-circuits before the actual
TrainingJob.create API call.

Changes:
- data_utils.py: add validate_data_path_exists() utility (S3 + DataSet ARN)
- base_trainer.py: add dry_run to abstract train(), _train_serverful_smtj(),
  and _train_hyperpod()
- sft/dpo/rlvr/rlaif_trainer.py: add dry_run param, pass through to
  shared methods, short-circuit serverless path
- Notebook examples added to SFT, DPO, RLVR, RLAIF notebooks
- Unit tests added to existing test files
- Integration test added
@amazeAmazing
amazeAmazing force-pushed the feature/dry-run-validation branch 6 times, most recently from f6bd43d to 8a3936b Compare July 15, 2026 23:39
Comment thread sagemaker-train/src/sagemaker/train/common_utils/data_utils.py Outdated
Add dry_run parameter to BaseEvaluator.evaluate() and all subclasses
(BenchMarkEvaluator, CustomScorerEvaluator, LLMAsJudgeEvaluator).

When dry_run=True:
- All existing validation runs (IAM role, model resolution, recipe,
  pipeline rendering)
- Dataset S3 path / DataSet ARN validated via validate_data_path_exists()
- Returns None without submitting a pipeline execution
- Raises on validation failure

Dataset validation runs unconditionally (not just during dry_run) for
CustomScorerEvaluator and LLMAsJudgeEvaluator which accept user datasets.

Changes:
- base_evaluator.py: add dry_run to evaluate() signature
- benchmark_evaluator.py: add dry_run, short-circuit before _start_execution()
- custom_scorer_evaluator.py: add dry_run, validate dataset, short-circuit
- llm_as_judge_evaluator.py: add dry_run, validate dataset, short-circuit
- Notebook examples added to benchmark, custom_scorer, llm_as_judge notebooks
@amazeAmazing
amazeAmazing force-pushed the feature/dry-run-validation branch from 8a3936b to 6375702 Compare July 16, 2026 18:43
raise FileLoadError(f"Failed to read file {file_path}: {e}")


def validate_data_path_exists(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will have to check this but some trainers may also support a Dataset object. In that case the arn is stored as dataset.arn.

ValueError: If the path does not exist or is inaccessible.
"""
# Handle SageMaker hub-content DataSet ARNs
if data_path.startswith("arn:aws:sagemaker:") and "/DataSet/" in data_path:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just call _validate_dataset_arn_exists that's defined below to avoid duplicate code?



# Validate dataset path exists
if hasattr(self, 'dataset') and self.dataset and isinstance(self.dataset, str):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will have to check if Dataset object is also supported by Evaluator.

@_telemetry_emitter(feature=Feature.MODEL_CUSTOMIZATION, func_name="RLVRTrainer.train")
def train(self, training_dataset: Optional[Union[str, DataSet]] = None,
validation_dataset: Optional[Union[str, DataSet]] = None, wait: bool = True, wait_timeout: Optional[int] = None, poll: int = 5):
validation_dataset: Optional[Union[str, DataSet]] = None, wait: bool = True, wait_timeout: Optional[int] = None, poll: int = 5, dry_run: bool = False):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does CPTTrainer also require additional dry_run: bool parameter?

]},
]
body = "\n".join(json.dumps(s) for s in samples)
s3.put_object(Bucket=bucket, Key=DATASET_KEY, Body=body.encode("utf-8"))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Why create and delete on every test run. Just check if the object exists at that path. If it doesn't, create it.

The MLflow experiment name for organizing runs.
mlflow_run_name (Optional[str]):
The MLflow run name for this training job.
training_dataset (Optional[Union[str, DataSet]]):

@mujtaba1747 mujtaba1747 Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we support Dataset object as well. So dry_run needs to support Dataset object too. Should be an easy change IMO.

trainer.train(dry_run=True)


class TestDryRunPassesWithValidInputs:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would consider adding a test for serverful and hyperpod too. To make sure dry_runs in def train_hyperpod and train_serverful are also covered.

return None

# Execute training
model_trainer.train(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why didn't we add dry_run feature to Model trainer class. Customers can submit training jobs using model_trainer directly as well? I'm ok with not adding it if there's a good reason.



@pytest.fixture(scope="module")
def nonexistent_dataset(sagemaker_session):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should run the test with nonexistent_dataset_s3 and nonexistent_dataset_obj (ie Dataset object or Dataset arn)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants