v3 is a rewrite, not an upgrade
Check sagemaker.__version__ first. Anything below 3.0 is the old Estimator/Model/Predictor SDK — a different API with a different import layout.
ModelTrainer replaces every *Estimator
One class for PyTorch, TensorFlow, HuggingFace, SKLearn, XGBoost and custom containers. The framework is decided by the image, not the class you import.
ModelBuilder replaces every *Model
And build() is now a separate step before deploy(). Pass model=trainer to go from training to endpoint with no glue code.
.train() not .fit(), .invoke() not .predict()
train(input_data_config=[InputData(...)]) takes a list of channels; endpoint.invoke(body=..., content_type=...) takes bytes and returns bytes.
config objects, not loose kwargs
Compute, SourceCode, InputData, StoppingCondition replace instance_type=, entry_point=, TrainingInput, max_run=.
everything lives under sagemaker.core
Session, get_execution_role, image_uris, Transformer, parameter, clarify, lineage, workflow — all moved there, and core.resources.* replaces raw boto3.
serializers / deserializers are gone
Handle encoding yourself with json.dumps / json.loads. You pass a content_type to invoke and decode the response bytes.
the four packages, by verb
train to train, serve to deploy, mlops to automate, core underneath them all. Import explicitly from each; the bare import sagemaker style is gone.
fine-tuning is v3-only
SFTTrainer, DPOTrainer, RLVRTrainer, RLAIFTrainer — LoRA, preference optimisation and RLHF, serverless, with no v2 equivalent at all.
save to /opt/ml/model
Only what the script writes there is tarred into model.tar.gz. A job can succeed and still hand you an empty artifact if you saved elsewhere.
a real-time endpoint bills per hour
Traffic or not, it charges while it exists. Endpoint.get(name).delete() is the single biggest way to avoid a surprise bill; serverless and batch scale to zero.
get_execution_role() only works on SageMaker
Inside Studio and notebooks it resolves the attached role; run it locally and it fails — pass a role ARN string instead.
under a PipelineSession, jobs don't launch
Calling trainer.train() or processor.run() returns step_args for a pipeline step instead of running immediately. That deferral is the whole pipeline trick.
match image_scope to the task
image_uris.retrieve(..., image_scope="training") vs "inference" — the serving image differs, and the wrong one fails at deploy time.