CertSafari
    Snowflake SnowPro Advanced: Data Scientist (DSA-C03)· Lessons

    Domain 3 · Lesson 11/16

    Training Models in Snowflake: Stored Procedures, Tuning, Sampling, UDTFs and External Functions

    Train a data science model.

    15 min read
    6.2% of exam
    6 sources
    Published 5 Oct 2026
    Docs as of 4 Oct 2026

    What you will be able to do

    • Train a model in a Python stored procedure on a Snowpark-optimized warehouse
    • Keep a hold-out test split separate from cross validation and tuning
    • Configure a hyperparameter search with a metric and a maximize/minimize mode
    • Down-sample a table with SAMPLE, and say what the sources leave out about up-sampling
    • Choose between per-partition training in a UDTF, Many Model Training, and an external function

    1.Training with Python stored procedures

    The most direct way to train a model in Snowflake is a Python stored procedure. Inside it, nested Snowpark queries load and transform the dataset. The data is then pulled into the procedure's memory for preprocessing and fitting, and the fitted model is written to a stage. Training can need a lot of memory, so Snowflake provides Snowpark-optimized warehouses for running single-node training with custom code.

    Checkpoint 1 of 9· Fill the gap

    Which warehouse type makes this the right home for a memory-heavy training procedure?

    CREATE OR REPLACE WAREHOUSE snowpark_opt_wh WITH
      WAREHOUSE_SIZE = 'MEDIUM'
      WAREHOUSE_TYPE = ' ? '
      MAX_CONCURRENCY_LEVEL = 1;

    The guidelines are specific. Set WAREHOUSE_SIZE = MEDIUM so the warehouse has one Snowpark-optimized node. Make it multi-cluster if you need concurrency. Keep other workloads off it. The procedure's nested queries can run on a separate warehouse, chosen with session.use_warehouse(), which you can size independently to match the data. The procedure itself declares its libraries in PACKAGES:

    Header of a training procedure that brings in scikit-learn and joblibsql
    CREATE OR REPLACE PROCEDURE train()
      RETURNS VARIANT
      LANGUAGE PYTHON
      RUNTIME_VERSION = 3.12
      PACKAGES = ('snowflake-snowpark-python', 'scikit-learn', 'joblib')
      HANDLER = 'main'
    AS $$

    Sources1

    2.Partitioning: train/validation hold-out and cross validation

    The body of train() shows how to partition the data. It holds out a test split first, and then runs cross validation only on the rows that remain:

    Hold out 20% for testing, then cross-validate (cv=10) on the training portion onlypython
    def main(session):
     # Load features
     df = session.table('MARKETING_BUDGETS_FEATURES').to_pandas()
     X = df.drop('REVENUE', axis = 1)
     y = df['REVENUE']
    
     # Split dataset into training and test
     X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state = 42)
    
     # Preprocess numeric columns
     numeric_features = ['SEARCH_ENGINE','SOCIAL_MEDIA','VIDEO','EMAIL']
     numeric_transformer = Pipeline(steps=[('poly',PolynomialFeatures(degree = 2)),('scaler', StandardScaler())])
     preprocessor = ColumnTransformer(transformers=[('num', numeric_transformer, numeric_features)])
    
     # Create pipeline and train
     pipeline = Pipeline(steps=[('preprocessor', preprocessor),('classifier', LinearRegression(n_jobs=-1))])
     model = GridSearchCV(pipeline, param_grid={}, n_jobs=-1, cv=10)
     model.fit(X_train, y_train)

    Three details in this code matter. First, train_test_split runs before any preprocessing, and random_state = 42 makes the split repeatable. Second, the StandardScaler and polynomial features sit inside the Pipeline, and the whole pipeline is fitted with model.fit(X_train, y_train). The 20% test rows therefore never influence the scaler or the 10-fold cross validation. Third, the procedure saves the model to @ml_models with session.file.put and then returns an R² score for both train and test. The test score is the hold-out estimate. The same separation appears in the HPO API on the container runtime, where dataset_map pairs a "train" and a "test" DataConnector and passes both to the training function.

    Checkpoint 2 of 9· Put it in order

    Put the steps of the train() procedure in the order its code runs them

    1. 1.Return R2 on the train and test data
    2. 2.Split into X_train/X_test with test_size=0.2
    3. 3.Dump the model and put it on @ml_models
    4. 4.Build the preprocessing + LinearRegression Pipeline
    5. 5.Load MARKETING_BUDGETS_FEATURES into pandas
    6. 6.Fit GridSearchCV (cv=10) on X_train, y_train

    Checkpoint 3 of 9· Exam question

    A data scientist maintains a churn feature pipeline that joins raw event tables and aggregates them hourly. They want Snowflake to keep the feature table fresh without hand-written scheduling code. Select TWO statements that correctly describe using dynamic tables for this pipeline.(Select 2)

    Sources12

    3.Hyperparameter tuning and optimization metric selection

    In train(), GridSearchCV is given an empty param_grid={}. It cross-validates one configuration and searches nothing. Real tuning needs a search space. For that, Snowflake ML's HPO API on the container runtime runs trials in parallel and can scale across nodes of a compute pool. A *grid search* evaluates every combination of values you define. Sampling functions draw values instead: uniform, loguniform (suited to learning rates, which span orders of magnitude), randint and choice.

    A search space over XGBoost-style hyperparameterspython
    search_space = {
        "n_estimators": tune.uniform(50, 200),
        "max_depth": tune.uniform(3, 10),
        "learning_rate": tune.uniform(0.01, 0.3),
    }

    A TunerConfig then states what counts as "best": the metric being optimized, the mode (maximize or minimize), the search algorithm, the number of trials, and the concurrency. Choosing the metric means matching it to the task and to the direction of improvement. Log loss is a loss, so you minimize it. AUC, precision, recall and F1 are scores, so you maximize them. F1 is the documented balance between precision and recall when classes are uneven. For RMSE, the regression metric named in the exam guide, the sources here have no Snowflake-specific guidance. Treat it by the same rule: an error you minimize.

    Checkpoint 4 of 9· Check yourself

    A tuner should pick the classifier with the best log loss. How should TunerConfig be set?

    Sources23

    4.Down-sampling and up-sampling

    Class imbalance is usually handled by changing the training data. Snowflake's built-in tool for down-sampling is the SAMPLE (or TABLESAMPLE) clause. It can sample a fraction of a table or a fixed number of rows:

    SAMPLE methods
    MethodUnit sampledFraction (probability)Fixed number of rows
    BERNOULLI / ROW (default)Each row, with probability p/100YesYes, returns exactly that many unless the table has fewer
    SYSTEM / BLOCKEach block of rows, with probability p/100YesNo

    With a fraction, the number of rows you get back depends on table size and probability. A seed makes the sample deterministic, which matters if you want to reproduce a training run. Apply the hold-out lesson from train() here as well. Resampling changes the training data, so it belongs after the split and only on the training portion. The test split should keep the real class balance. Up-sampling (repeating or synthesizing minority rows) is not covered by the sources for this lesson. That gap is real, and this lesson does not fill it from memory.

    Checkpoint 5 of 9· Exam question

    A scalar Python UDF applies a log transform and z-score to 80 million rows and runs slowly because Snowflake invokes it once for every row. Which change most directly reduces this per-row overhead?

    Checkpoint 6 of 9· Check yourself

    You need exactly 40,000 rows sampled from the majority class. Which sampling method can do that?

    Sources4

    5.Training with Python UDTFs and per-partition models

    Sometimes the right design is one model per segment, such as per region or per customer group. A Python UDTF fits that shape. The query's OVER (PARTITION BY ...) splits the input, process sees each row of a partition, and end_partition runs after the last row and can yield the partition's result. Snowpark-optimized warehouses can help some UDTF workloads as well as UDFs. The provided UDTF documentation demonstrates the partition mechanics, not a training handler. Treat a UDTF-trains-a-model design as these mechanics applied to fitting, not as a documented recipe.

    The documented tool for per-partition training is Many Model Training (MMT). It requires a Snowflake ML container runtime. It partitions a Snowpark DataFrame by a column and trains a separate model on each partition in parallel. Your function receives (data_connector, context), where context.partition_id identifies the partition. MMT serializes the models to a stage for you. XGBoost and scikit-learn are supported out of the box, TorchSerde and TensorFlowSerde cover the deep learning frameworks, and a custom ModelSerde handles anything else.

    One model per region, trained in parallel and stored on a stagepython
    from snowflake.ml.modeling.distributors.many_model import ManyModelTraining
    
    trainer = ManyModelTraining(train_xgboost_model, "model_stage") # Specify the stage to store the models
    training_run = trainer.run(
        partition_by="region",  # Train separate models for each region
        snowpark_dataframe=sales_data,
        run_id="regional_models_v1" # Specify a unique ID for the training run
    )
    Per-partition training: Python UDTF vs Many Model Training
    AspectPython UDTFMany Model Training
    How partitions are setOVER (PARTITION BY symbol) in the calling querypartition_by="region" in trainer.run
    Per-partition hook__init__ / process / end_partitionTraining function (data_connector, context)
    OutputTuples yielded to the query resultModels on the stage at run_id/{partition_id}
    ComputeWarehouse (Snowpark-optimized can help)Snowflake ML container runtime

    Checkpoint 7 of 9· Check yourself

    After the run above, where is the model for the North region stored?

    Sources15

    6.Training outside Snowflake through external functions

    If the compute lives outside Snowflake, for example a GPU service, an external function connects it. An external function is a UDF that holds no code of its own. It is a database object that records how to reach a remote service, and it is called with ordinary dot notation in SQL. The remote service must behave like a scalar function. It accepts JSON input, returns JSON output, exposes an HTTPS endpoint, and returns exactly one row for each row received. Examples are an AWS Lambda function, an Azure Function, or an HTTPS server on EC2.

    Snowflake never calls the remote service directly. A proxy service such as Amazon API Gateway or Azure API Management relays the requests, and can add authentication. The security details live in an API integration object. These constraints shape any outside-training design: the data arrives row by row as JSON and each row returns one result. Dynamic tables cannot include external functions, so calls to the remote service belong in queries or procedures.

    Checkpoint 8 of 9· Exam question

    A retailer has one table holding sales history for 1,200 stores and wants a separate demand model per store, trained in parallel inside Snowflake, with each fitted model serialized for later use. Which approach fits best?

    Checkpoint 9 of 9· Check yourself

    Which statement about calling an external training service is true?

    Sources6

    Exam traps

    Each one states something that sounds right. Open it to see what is actually true.

    1. 1.A bigger Snowpark-optimized warehouse spreads one stored-procedure training job across more nodes, so you should size it as large as possible.Why is that wrong?

      Stored-procedure training is single-node. The guideline is MEDIUM, which gives exactly one Snowpark-optimized node, with multi-cluster only for concurrency.

      Covered in Training with Python stored procedures

    2. 2.SAMPLE SYSTEM (n ROWS) is a faster way to get an exact-sized down-sample.Why is that wrong?

      SYSTEM/BLOCK samples whole blocks by probability and accepts no fixed row count. Only BERNOULLI/ROW returns an exact number of rows.

      Covered in Down-sampling and up-sampling

    3. 3.An external function can send a batch to an outside trainer and receive any number of rows back, such as a table of fitted parameters.Why is that wrong?

      External functions are scalar. The remote service must return exactly one row for each row it receives, as JSON.

      Covered in Training outside Snowflake through external functions

    Practise it for real

    Train and persist a regression model inside Snowflake with a Python stored procedure on a Snowpark-optimized warehouse

    1. 1.With ORGADMIN, go to Admin » Terms in Snowsight and enable Anaconda packages, if no one has done so for this account yet.

      Why: The procedure uses scikit-learn from Anaconda and calls to_pandas, and neither works until the External Offerings Terms are accepted.

      You should see: The Anaconda section shows the terms as acknowledged.

    2. 2.Run CREATE OR REPLACE WAREHOUSE snowpark_opt_wh WITH WAREHOUSE_SIZE = 'MEDIUM' WAREHOUSE_TYPE = 'SNOWPARK-OPTIMIZED' MAX_CONCURRENCY_LEVEL = 1; and use it.

      Why: MEDIUM gives exactly one Snowpark-optimized node, the documented setup for single-node training.

      You should see: The warehouse is created and set as current.

    3. 3.Create the train() procedure from the documented example, with PACKAGES snowflake-snowpark-python, scikit-learn and joblib and HANDLER 'main'. You need a MARKETING_BUDGETS_FEATURES table and an @ml_models stage.

      Why: The handler splits 80/20, cross-validates on the training rows, and saves the model to the stage.

      You should see: The procedure TRAIN is created successfully.

    4. 4.Run CALL train();

      Why: Runs the training and the hold-out evaluation in one call.

      You should see: A VARIANT with keys 'R2 score on Train' and 'R2 score on Test', and model.joblib uploaded to @ml_models.

    Stuck? Get a nudge

    If the test R2 is well below the train R2, the model is overfitting. Only the 20% hold-out shows you that.

    Sources

    Every claim above is drawn from one of these pages, quoted as it was written on the date shown.

    1. 1.
      “Snowpark-optimized warehouses make it possible to use Snowpark stored procedures to run single-node ML training workloads directly in Snowflake.”
      ↩︎ Training with Python stored procedures
      “Use the session.use_warehouse() API to select the warehouse for the query inside the stored procedure.”
      ↩︎ Training with Python stored procedures
      “model = GridSearchCV(pipeline, param_grid={}, n_jobs=-1, cv=10)”
      ↩︎ Partitioning: train/validation hold-out and cross validation
      “These optimized warehouses can also benefit some UDF and UDTF scenarios.”
      ↩︎ Training with Python UDTFs and per-partition models
      “Set WAREHOUSE_SIZE = MEDIUM to ensure that the Snowpark-optimized warehouse consists of 1 Snowpark-optimized node.”
      ↩︎ Exam trap 1
      “to load and transform the dataset, which is then loaded into the stored procedure memory to perform pre-processing and ML training”
      ↩︎ Checkpoint
    2. 2.
      “The dataset_map object is a dictionary that pairs the training or test dataset with its corresponding Snowflake DataConnector object.”
      ↩︎ Partitioning: train/validation hold-out and cross validation
      “Within the object, you specify the metric being optimized, the optimization mode, and the other execution parameters.”
      ↩︎ Hyperparameter tuning and optimization metric selection
      “Mode Determines whether the objective is to maximize or minimize the metric”
      ↩︎ Hyperparameter tuning and optimization metric selection
      “Grid search Explores a grid for hyperparameter values that you define.”
      ↩︎ Hyperparameter tuning and optimization metric selection
    3. 3.
      “It provides a balance between precision and recall, especially when there is an uneven class distribution.”
      ↩︎ Hyperparameter tuning and optimization metric selection
      “Logistic Loss (LogLoss) is calculated for the model as a whole. The objective of prediction is to minimize the loss function.”
      ↩︎ Checkpoint
    4. 4.
      “When you sample a fixed, specified number of rows, the query returns the exact number of specified rows unless the table contains fewer rows.”
      ↩︎ Down-sampling and up-sampling
      “You can specify a seed to make the sampling deterministic.”
      ↩︎ Down-sampling and up-sampling
      “BERNOULLI (or ROW): Includes each row with a probability of p/100.”
      ↩︎ Exam trap 2
      “{ SYSTEM | BLOCK } ( <probability> )”
      ↩︎ Checkpoint
    5. 5.
      “MMT partitions your Snowpark DataFrame by a specified column and trains separate models on each partition in parallel.”
      ↩︎ Training with Python UDTFs and per-partition models
      “MMT requires a Snowflake ML container runtime environment.”
      ↩︎ Training with Python UDTFs and per-partition models
      “Models are stored in the stage at run_id/{partition_id} where partition_id is the partition column value.”
      ↩︎ Checkpoint
    6. 6.
      “Accept JSON inputs and return JSON outputs.”
      ↩︎ Training outside Snowflake through external functions
      “Snowflake stores security-related external function information in an API integration.”
      ↩︎ Training outside Snowflake through external functions
      “Snowflake supports scalar external functions; the remote service must return exactly one row for each row received.”
      ↩︎ Exam trap 3
      “Snowflake supports scalar external functions; the remote service must return exactly one row for each row received.”
      ↩︎ Prediction
      “Snowflake does not call a remote service directly. Instead, Snowflake calls a proxy service, which relays the data to the remote service.”
      ↩︎ Checkpoint

    Ready to test yourself?

    Practise the 22 questions on this subdomain.

    Spotted a mistake, or was something unclear? Tell us.