There is a scene that repeats itself in almost every company starting out with machine learning, and AlpinaShop is about to live through it.

Lucía opens a notebook on her laptop. She downloads a CSV with the last year's orders, tries three models, and the third one gets it right 84 % of the time about which customers are going to buy. She shows the chart at Monday's meeting and everybody gets excited. Management asks when it will be on the website.

And then the uncomfortable questions start. Where exactly did that CSV come from? What filters did she apply? Which version of the library did she train it with? Where is the model? In her Downloads folder? Who is going to run it when a request comes in from the shop, and how fast does it have to answer? What happens when the data changes in six months' time? And what if Lucía leaves?

None of those questions is about the model. They are all about the system around the model, and they are precisely the ones that separate an interesting experiment from a product that works. This lesson is about that: what Vertex AI is, which component answers each question, and how it connects to the data platform you built in module 4.

And it is also about something a machine learning course almost never says: that the first honest decision in an ML project is deciding whether ML is needed at all.

Contents

  1. Why a notebook and a laptop are not enough
  2. The life cycle of a model
  3. Which Vertex AI component covers each stage
  4. Historical note: from AI Platform to Vertex AI
  5. Workbench: managed notebooks connected to the data
  6. Managed datasets and pedidos-historico
  7. Feature Store and training/serving skew
  8. Training: custom jobs and hyperparameters
  9. Model Registry, versions and evaluation
  10. Serving the model: online endpoints and batch prediction
  11. Model Monitoring: when the world changes and the model does not
  12. BigQuery ML: training with SQL without leaving the warehouse
  13. When BigQuery ML and when Vertex AI
  14. Platform cost and the two switches you have to turn off
  15. AlpinaShop's problem: recommending without training anything

  1. Why a notebook and a laptop are not enough

Lucía's notebook is not a bad thing. It is the right place to explore, and every ML project starts there. The problem appears when the result has to survive outside that session.

The five failures that come up every single time:

Laptop problem What happens in practice
It is not reproducible Nobody can obtain the same model again. The CSV was filtered by hand, the library was updated, the random seed was not fixed.
It does not scale Five years of orders and 60 GB of images do not fit in 16 GB of RAM. And training on CPU what needs a GPU takes days.
It cannot be served A model in a .pkl in Downloads does not answer HTTP requests with bounded latency, nor does it survive a reboot.
There is no traceability In an audit you cannot answer "which data trained the model that made this decision on 14 March".
It is not watched The model degrades in silence. Nobody notices until somebody on the business side says "this isn't getting it right any more".

An ML platform is the set of services that solves those five problems without you having to build them. Vertex AI is Google Cloud's: an umbrella covering feature storage, managed training, a model registry, prediction serving, monitoring and orchestration, all under the same IAM, the same billing and the same network as the rest of your project.

The idea worth fixing before moving on: the model is the small part of the system. It is a minimal percentage of the code and of the real effort. Everything else — data collection, verification, feature extraction, serving, monitoring, configuration management — is the system, and it is where projects fail.

  1. The life cycle of a model

A model is not built: it is cultivated. And it is not a straight line, it is a loop.

flowchart LR
    A[Business problem] --> B[Data]
    B --> C[Features]
    C --> D[Training]
    D --> E[Evaluation]
    E -->|no improvement| C
    E -->|improvement| F[Deployment]
    F --> G[Monitoring]
    G -->|drift detected| H[Retraining]
    H --> D
    G -->|the problem changed| A

The seven stages, with the question each one answers:

  1. Business problem. What decision is going to change because of this model? If the answer is "none, but it's interesting", the project has already failed.
  2. Data. What information exists, how much, of what quality and with what legal basis to use it? This is where module 4 pays off: alpinashop_analitica is governed, catalogued and de-identified.
  3. Features. Turning raw data into the numerical signals the model consumes. It is the stage that most determines the final result, far more than the choice of algorithm.
  4. Training. Fitting the model's parameters with historical data.
  5. Evaluation. Is it better than what was there before? Better than a simple heuristic? Better for the business, not just on a metric?
  6. Deployment. Making the model answer real requests, online or in batch.
  7. Monitoring. Watching that the incoming data still looks like the training data, and that the predictions are still useful.

The loop is what matters. A model deployed and forgotten is a liability: it carries on deciding with a view of the world that has already expired.

  1. Which Vertex AI component covers each stage

Stage Vertex AI component What it is for exactly
Exploration Workbench Managed notebooks with direct access to BigQuery and Cloud Storage
Data Managed Datasets Versioned datasets (tabular, image, text, video) with splits and labelling
Features Feature Store Central feature store, with online and batch serving
Training without code AutoML Trains by searching the architecture for you (lesson 05-02)
Training with code Custom Training Your code in a container, on the machine you choose
Tuning Hyperparameter Tuning Searches for the best combination of hyperparameters
Tracking Experiments + TensorBoard Compares runs, metrics and curves
Model catalogue Model Registry Versions models, stores evaluations and lineage
Evaluation Model Evaluation Metrics that are comparable across versions
Online serving Endpoints HTTPS API with autoscaling and traffic splitting
Batch serving Batch Prediction Millions of rows at once, with no live infrastructure
Watching Model Monitoring Data and prediction drift, with alerts
Automation Pipelines The full cycle as a reproducible graph (lesson 05-07)
Generative models Model Garden + Gemini Pre-trained and generative models (lesson 05-06)

You do not need to use them all. AlpinaShop, with 40 people, is going to use five or six. But it is worth knowing they are there, because the alternative to each one is building it yourself.

  1. Historical note: from AI Platform to Vertex AI

If you look for old documentation or tutorials, you are going to run into names that no longer exist, and it helps to know how to translate them.

Until 2021, Google Cloud had two separate ML offerings that talked to each other badly:

  • AI Platform (formerly Cloud ML Engine): training and prediction for people who wrote their own code.
  • AutoML (Vision, Natural Language, Tables…): independent products, each with its own console and its own API, for people who did not write code.

They were different worlds. An AutoML model and an AI Platform model shared no registry, no monitoring, no way of being deployed. Vertex AI unified them under a single API, a single model registry and a single console. Today AutoML is not a separate product: it is a way of training inside Vertex AI.

AI Platform is retired. If you find a tutorial that uses gcloud ai-platform jobs submit or the ml.googleapis.com endpoint, it is obsolete material. The current equivalent is gcloud ai custom-jobs create against aiplatform.googleapis.com. The confusion is common because the API name kept the old initials.

It is a good reminder of a general rule of this course: in the cloud, always verify against the current official documentation. Names, limits and prices change, and a three-year-old tutorial can lead you to a product that no longer exists.

  1. Workbench: managed notebooks connected to the data

Vertex AI Workbench is a managed JupyterLab running on a VM in your project. Compared with Lucía's laptop it has three advantages that matter:

  • It is where the data is. In europe-west1, inside the VPC, without taking anything outside.
  • It uses a service account, not personal credentials. Permissions are auditable and revocable.
  • It scales. If an experiment needs more memory or a GPU, you change the machine type and that is that.

Creating Lucía's instance:

gcloud workbench instances create wb-lucia-analitica \
  --project=alpinashop-datos \
  --location=europe-west1-b \
  --machine-type=e2-standard-4 \
  --service-account=sa-workbench-datos@alpinashop-datos.iam.gserviceaccount.com \
  --disable-public-ip \
  --metadata=idle-timeout-seconds=3600

Three options deserve explanation:

  • --service-account: the instance acts as that service account, which will hold roles/bigquery.dataViewer over the de-identified views and not over the raw tables. What you learned in 03-04 applies here unchanged.
  • --disable-public-ip: no public IP. Access goes through IAP and outbound internet through Cloud NAT, as in 03-01.
  • idle-timeout-seconds=3600: it shuts itself down after an hour of inactivity. This parameter is the difference between a €40 and a €400 invoice at the end of the month.

Inside the notebook, connecting to module 4's governed data is direct:

from google.cloud import bigquery
import pandas as pd

client = bigquery.Client(project="alpinashop-datos")

query = """
SELECT
  sku,
  categoria,
  COUNT(DISTINCT pedido_id)            AS pedidos,
  SUM(unidades)                        AS unidades,
  ROUND(SUM(importe_linea), 2)         AS importe
FROM `alpinashop-datos.alpinashop_analitica.lineas_pedido`
WHERE fecha_pedido >= DATE_SUB(CURRENT_DATE(), INTERVAL 365 DAY)
GROUP BY sku, categoria
ORDER BY importe DESC
"""

df = client.query(query).to_dataframe()
print(f"{len(df)} SKUs with sales in the last year")
print(df.head())

What it does and why like this. bigquery.Client authenticates itself with the instance's service account: there are no keys and no JSON files, which is exactly what you wanted to avoid after 03-06. The query aggregates in BigQuery and pulls down an already reduced result: around 2,400 rows instead of millions. This is the correct pattern, and the most expensive mistake is the opposite one — SELECT * over lineas_pedido and aggregating in pandas — which downloads gigabytes, bills bytes read and will probably exhaust the notebook's memory.

When the volume no longer fits in memory, there is bigframes, a pandas-like API that executes operations inside BigQuery instead of bringing the data over. It is the natural way out when to_dataframe() starts to hurt.

  1. Managed datasets and pedidos-historico

A managed dataset is a Vertex AI resource that points at your data and adds what a loose CSV does not have: identity, version, schema, train/validation/test splits and, for image and text, a labelling workflow.

Type Usual source Typical use at AlpinaShop
Tabular BigQuery or CSV in Cloud Storage Predicting purchase, forecasting demand
Image Cloud Storage + index file Classifying catalogue photos
Text Cloud Storage or BigQuery Classifying reviews
Video Cloud Storage Not applicable today

AlpinaShop creates its own straight from the governed dataset:

gcloud ai datasets create \
  --project=alpinashop-datos \
  --region=europe-west1 \
  --display-name=pedidos-historico \
  --metadata-schema-uri="gs://google-cloud-aiplatform/schema/dataset/metadata/tabular_1.0.0.yaml" \
  --metadata="{\"inputConfig\": {\"bigquerySource\": {\"uri\": \"bq://alpinashop-datos.alpinashop_analitica.v_pedidos_analitica\"}}}"

Notice a detail that is anything but accidental: the source is v_pedidos_analitica, the pseudonymised view you created in 04-07, not the pedidos table. The model never sees an email. Not because it is needed for training — it contributes nothing — but because every copy of a piece of personal data is one more legal obligation and one more risk.

Is using a managed dataset compulsory? No. Custom training can read directly from BigQuery or from Cloud Storage. Managed datasets are compulsory for AutoML and highly advisable when you have to label by hand or when you want it recorded exactly which data trained each version. For a custom job that already reads from a versioned view, they contribute less.

  1. Feature Store and training/serving skew

This section explains the most expensive and most silent mistake in applied machine learning. It is worth reading slowly.

A feature is a numerical or categorical signal that describes an entity and that the model uses to decide. For AlpinaShop:

Entity Example features
Customer Orders in 90 days, average amount, days since last order, favourite category
Product Price, category, sales in 30 days, average rating, times returned
Session Products viewed, minutes on the site, device, entry channel

The problem appears like this. Lucía trains in the notebook and computes "customer's orders in the last 90 days" with a SQL query over pedidos. It works: the model gets it right. Months later, Dani has to serve the model on the website, and he needs that same number in real time, in milliseconds, for the customer who is looking at the screen. He cannot fire a BigQuery query inside the HTTP request. So he reimplements it in Python against Cloud SQL.

And there, without anyone noticing, the difference appears. Lucía's query counted orders with status entregado. Dani's counts all of them, cancelled ones included. In production the model receives a number that means something different from what it learned. Nobody sees an error: there is no exception, there is no red log line. The model simply gets it right less often than it promised, and the team takes months to work out why.

That is training/serving skew: the feature is computed in two different ways in two different places.

Feature Store solves it with a simple idea: the feature is defined and computed once only, stored, and served both to training (in batch, with correct historical values) and to online serving (with low latency). One piece of code, one definition.

flowchart LR
    A[BigQuery: alpinashop_analitica] --> B[Feature Store<br/>cliente_pedidos_90d<br/>cliente_importe_medio]
    B --> C[Training<br/>historical read]
    B --> D[Online serving<br/>low latency]
    C --> E[Model]
    E --> D

In Vertex AI, the current generation of Feature Store leans on BigQuery as the source of truth: you define a feature view over a table or view, and an online store synchronises it to serve it fast.

# 1) Online store
gcloud ai feature-online-stores create alpinashop_fos \
  --project=alpinashop-datos --region=europe-west1 \
  --bigtable-min-node-count=1 --bigtable-max-node-count=2

# 2) Feature view over the table already computed in BigQuery
gcloud ai feature-views create cliente_features \
  --project=alpinashop-datos --region=europe-west1 \
  --feature-online-store=alpinashop_fos \
  --bigquery-source-uri="bq://alpinashop-datos.alpinashop_analitica.features_cliente" \
  --entity-id-columns=cliente_hash \
  --cron="0 3 * * *"

The key lies in --entity-id-columns=cliente_hash and in --cron. The entity is identified by the customer's hash, the same one you generated with KMS in 04-07: the feature store does not contain direct personal data either. And the nightly synchronisation means the features are at most 24 hours old, which is perfectly acceptable for "orders in 90 days" and not acceptable for "products viewed in this session", which has to be computed hot.

An important warning about time: when building the training set, each row must carry the value the feature had at the moment of the event, not today's. If you train a "will they buy?" model with the customer's current order count, you are telling it the future. We will come back to this trap — data leakage — in 05-02.

Does AlpinaShop need a Feature Store today? With one model and three features, probably not: it is one more layer to maintain. With five models sharing features and a team of three people, yes. The practical rule: when the same feature is used by two models or computed by two people, the moment has arrived.

  1. Training: custom jobs and hyperparameters

A custom training job is your code running on Google's machines, without you managing any of them. Two ways of packaging it:

Form What you deliver When it suits
Pre-built container A .tar.gz with your Python script You use standard TensorFlow, PyTorch, scikit-learn or XGBoost
Your own container An image in Artifact Registry Unusual dependencies, pinned versions, total reproducibility

AlpinaShop already has Artifact Registry from 02-05 and from the catalogue pipeline, so the second option costs it nothing extra.

gcloud ai custom-jobs create \
  --project=alpinashop-datos \
  --region=europe-west1 \
  --display-name=entrena-recomendador-v1 \
  --worker-pool-spec="machine-type=n1-standard-8,replica-count=1,container-image-uri=europe-west1-docker.pkg.dev/alpinashop-prod/alpinashop/entrena-reco:1.0.3" \
  --args="--dataset=alpinashop_analitica,--epochs=15,--salida=gs://alpinashop-datalake/modelos/reco/v1"

Point by point:

  • --worker-pool-spec defines the machine. A single replica-count is single-machine training; with more replicas and the right configuration, distributed training (you will see it in 05-03).
  • The image is tagged with a version (1.0.3), never latest. If you need to reproduce last month's training run, latest is no longer what it was.
  • Artefacts are written to Cloud Storage, not inside the container, which disappears when it finishes.

The job writes logs to Cloud Logging (06-06) and dies on its own. There is no VM to switch off, which is the main advantage over spinning up a GPU instance yourself and leaving it running over the weekend.

Hyperparameters and automatic tuning

A parameter is learned by the model during training (the weights). A hyperparameter is decided by you beforehand: learning rate, tree depth, batch size, embedding dimension. And its effect on the result is enormous.

Trying combinations by hand is tedious and biased. Vertex AI Hyperparameter Tuning launches several runs in parallel and searches for the best combination with Bayesian optimisation: it learns from the previous trials instead of throwing darts.

# ajuste.yaml
studySpec:
  metrics:
  - metricId: auc_validacion
    goal: MAXIMIZE
  parameters:
  - parameterId: learning_rate
    doubleValueSpec: { minValue: 0.0001, maxValue: 0.1 }
    scaleType: UNIT_LOG_SCALE
  - parameterId: embedding_dim
    discreteValueSpec: { values: [16, 32, 64, 128] }
  algorithm: ALGORITHM_UNSPECIFIED   # Bayesian optimisation by default
trialJobSpec:
  workerPoolSpecs:
  - machineType: n1-standard-8
    replicaCount: 1
    containerSpec:
      imageUri: europe-west1-docker.pkg.dev/alpinashop-prod/alpinashop/entrena-reco:1.0.3
maxTrialCount: 20
parallelTrialCount: 4

Two details that save money and grief:

  • scaleType: UNIT_LOG_SCALE for the learning rate. The useful values are spread across orders of magnitude, not linearly: searching on a logarithmic scale explores 0.0001, 0.001 and 0.01 instead of twenty nearly identical values.
  • parallelTrialCount: 4 against maxTrialCount: 20. With high parallelism you finish sooner, but each round learns less from the previous one. With low parallelism the search is cleverer and slower. A quarter of the total is a reasonable balance.

And the cost warning: 20 trials cost 20 training runs. Before launching a tuning job, check that a single training run works and measure how long it takes and what it costs.

  1. Model Registry, versions and evaluation

The Model Registry is the project's model catalogue. Each model has numbered versions, and each version stores its artefact, its serving container, its metrics and its lineage.

gcloud ai models upload \
  --project=alpinashop-datos --region=europe-west1 \
  --display-name=recomendador-alpinashop \
  --artifact-uri=gs://alpinashop-datalake/modelos/reco/v1 \
  --container-image-uri=europe-docker.pkg.dev/vertex-ai/prediction/tf2-cpu.2-15:latest \
  --version-aliases=candidato

Aliases are the most useful piece and the least used. Instead of the shop's code invoking "version 7", it invokes the produccion alias. Changing model is moving an alias, and rolling back is too:

# Promote version 8 to production
gcloud ai models update-version recomendador-alpinashop@8 \
  --region=europe-west1 --add-aliases=produccion --remove-aliases=candidato

# And if it goes wrong, roll back in seconds
gcloud ai models update-version recomendador-alpinashop@7 \
  --region=europe-west1 --add-aliases=produccion

The evaluation is stored attached to the version. This is what makes it possible to answer, months later, "why was the model replaced on 3 June?" with data and not with memories. And it is the basis of the decision gate you will build in 05-07: a model is only deployed if its evaluation beats that of the model currently in production.

  1. Serving the model: online endpoints and batch prediction

There are two ways of using a model, and choosing badly is the most expensive architectural mistake in this module.

An endpoint is an always-on HTTPS service with one or more versions deployed. It answers in milliseconds, scales by itself, and charges for the time the machines are alive, whether there is traffic or not.

A batch prediction is a job that starts up, reads a file or a table, predicts millions of rows, writes the result and dies. It charges for the job, not for the waiting time.

Criterion Online endpoint Batch prediction
Latency Tens of milliseconds Minutes or hours
When it predicts At the instant of the request In a scheduled window
Cost Per machine-hour, 24×7 Per job executed
Cost with little traffic Bad: you pay for doing nothing Optimal
Scaling to millions of rows Expensive and inefficient Its natural case
Fresh data Yes, right now Those of the last run
AlpinaShop example Recommendation in the shopping cart Nightly scoring of the whole catalogue

The choice criterion, in one sentence: if the result depends on something that has just happened and that you could not have known beforehand, you need an endpoint; if not, batch.

And here comes a concrete AlpinaShop decision worth settling now. Product recommendations do not change every second: which products are bought together is a pattern that is stable over weeks. Precomputing every night the 20 recommendations for each of the 2,400 SKUs is a 48,000-row table that the shop reads in microseconds from Firestore or Memorystore (02-06).

Indicative cost for the comparison, to give the order of magnitude — always check current prices in the official documentation, they change: a modest endpoint switched on all month runs to tens of euros as a minimum, even with no traffic; the nightly batch job over 2,400 SKUs is a matter of cents. With two orders of magnitude of difference and no functional advantage whatsoever, the decision requires no debate.

Decision DA-002. AlpinaShop's product recommendations are computed in batch every night and served from a precomputed table. An online endpoint will be reserved only if a case appears that depends on the behaviour of the session in progress.

  1. Model Monitoring: when the world changes and the model does not

A model learns a snapshot of the world. The world moves, the snapshot does not.

Two distinct phenomena that are worth not confusing:

Phenomenon What changes Example at AlpinaShop
Data drift The distribution of the inputs Winter arrives: queries for crampons come in where before there were trekking sandals
Concept drift The relationship between inputs and outcome A competitor drops prices: the same customers no longer buy the same way
Training/serving skew Production does not resemble training from day one The mistake in section 7

Model Monitoring continuously compares the distributions of the real requests against a baseline (the training data) and warns when the distance exceeds a threshold.

gcloud ai model-monitoring-jobs create \
  --project=alpinashop-datos --region=europe-west1 \
  --display-name=monitor-recomendador \
  [email protected] \
  --endpoint=ENDPOINT_ID \
  --feature-thresholds=precio=0.3,categoria=0.3,pedidos_90d=0.2 \
  --prediction-sampling-rate=0.2
  • --feature-thresholds: threshold per feature. A 0.3 on categoria means "warn me if the mix of categories coming in moves further than that from the mix I trained with".
  • --prediction-sampling-rate=0.2: 20 % of the requests are analysed. Analysing 100 % detects sooner, costs more and hardly ever pays off.

A realistic warning: a drift alert is not a fault. In winter the distribution of categories has to change, and that is normal. The alert tells you "look at this", and judging whether retraining is needed is a human matter. A threshold that is too sensitive produces noise, noise produces indifference, and indifference means nobody looks at the alert that did matter.

  1. BigQuery ML: training with SQL without leaving the warehouse

And now, the alternative that many teams ought to try before any other.

BigQuery ML lets you create and use models with SQL, inside BigQuery, without moving a single byte and without writing Python. For a team that already lives in SQL — like Lucía after module 4 — it is the shortest route from data to prediction.

AlpinaShop's case: predicting the probability that a session with a cart ends in a purchase, in order to decide whether it is worth showing an incentive.

First, the training table. It is where the result is won or lost:

CREATE OR REPLACE TABLE `alpinashop-datos.alpinashop_analitica.ml_sesiones` AS
SELECT
  v.sesion_id,
  v.fecha,
  v.dispositivo,
  v.canal,
  v.paginas_vistas,
  v.minutos_sesion,
  v.productos_vistos,
  v.unidades_carrito,
  ROUND(v.importe_carrito, 2)                     AS importe_carrito,
  EXTRACT(DAYOFWEEK FROM v.fecha)                 AS dia_semana,
  IF(p.pedido_id IS NULL, 0, 1)                   AS compro
FROM `alpinashop-datos.alpinashop_analitica.visitas` v
LEFT JOIN `alpinashop-datos.alpinashop_analitica.v_pedidos_analitica` p
  ON p.sesion_id = v.sesion_id
WHERE v.unidades_carrito > 0
  AND v.fecha BETWEEN DATE '2024-01-01' AND DATE '2026-03-31';

What matters about this query is not the SQL, it is the decisions. The unidades_carrito > 0 filter narrows the population to sessions that got as far as having a cart: predicting over visitors who only looked at the home page would mix two different problems. And every column describes the session before the outcome: there is not one that is only known after buying. If you included metodo_pago, the model would reach 99 % accuracy and would be completely useless, because the payment method only exists if there was a purchase. That is data leakage, and it gets examined under a magnifying glass in 05-02.

Now the model, in a single statement:

CREATE OR REPLACE MODEL `alpinashop-datos.alpinashop_analitica.m_prob_compra`
OPTIONS(
  model_type              = 'LOGISTIC_REG',
  input_label_cols         = ['compro'],
  auto_class_weights       = TRUE,
  data_split_method        = 'CUSTOM',
  data_split_col           = 'es_test',
  enable_global_explain    = TRUE
) AS
SELECT
  * EXCEPT(sesion_id, fecha),
  fecha >= DATE '2026-01-01' AS es_test
FROM `alpinashop-datos.alpinashop_analitica.ml_sesiones`;

The options, one by one:

  • LOGISTIC_REG: logistic regression, a simple, fast, cheap and explainable classifier. Starting with the simplest model that could work is not conservatism: it is the only way of knowing whether a complex model contributes anything.
  • auto_class_weights = TRUE: if only 12 % of sessions end in a purchase, a lazy model can say "nobody buys" and be right 88 % of the time. This option compensates for the imbalance.
  • data_split_method = 'CUSTOM' with es_test based on the date: the split is temporal, not random. It trains with the past and is evaluated with the future, which is how it will work in production. A random split would mix March sessions into training and February ones into evaluation, and the result would be optimistic and false.
  • enable_global_explain = TRUE: enables feature importance.

Evaluating and predicting:

-- Metrics over the test period
SELECT * FROM ML.EVALUATE(MODEL `alpinashop-datos.alpinashop_analitica.m_prob_compra`);

-- Which features carry weight
SELECT * FROM ML.GLOBAL_EXPLAIN(MODEL `alpinashop-datos.alpinashop_analitica.m_prob_compra`);

-- Predict over today's open sessions
SELECT
  sesion_id,
  ROUND(predicted_compro_probs[OFFSET(0)].prob, 3) AS prob_compra
FROM ML.PREDICT(
  MODEL `alpinashop-datos.alpinashop_analitica.m_prob_compra`,
  (SELECT * FROM `alpinashop-datos.alpinashop_analitica.v_sesiones_abiertas`))
WHERE predicted_compro_probs[OFFSET(0)].prob > 0.6;

ML.EVALUATE returns precision, recall, f1_score, log_loss and roc_auc. In 05-02 you will see what each one means and why plain accuracy is the metric that misleads most.

BigQuery ML supports a good deal more than logistic regression: boosted trees, DNNs, k-means for segmentation, ARIMA_PLUS for time series, matrix factorization for recommendation, importing TensorFlow models, and calls to Gemini models from SQL with ML.GENERATE_TEXT. A model trained in BigQuery ML can be exported and registered in Vertex AI to serve it on an endpoint, so the choice is not a closed door.

  1. When BigQuery ML and when Vertex AI

Criterion BigQuery ML Vertex AI (AutoML or custom)
Language SQL Python (or no code with AutoML)
Where the data is Already in BigQuery Anywhere
Data types Tabular (and text via Gemini) Tabular, image, text, video
Control over the model Limited to what it offers Total
Time to first result Minutes Hours or days
Online serving Via export to Vertex AI Native
Profile of whoever uses it Data analyst Data or ML engineer
Typical cost Low, billed as a query Medium or high

The practical rule for AlpinaShop: if the data is in BigQuery, the problem is tabular and you want an answer this week, start with BigQuery ML. If that is not enough — because you need images, free text, your own architecture or online latency — move up to Vertex AI. The other way round, starting with the complex option, costs weeks and often ends in a worse model.

And an honest observation: in a small business, most useful ML problems are tabular and fit in BigQuery ML. Sophistication is reserved for where it genuinely contributes.

  1. Platform cost and the two switches you have to turn off

Vertex AI bills in parts, and most of the surprises come from two of them.

Item How it is billed Risk of surprise
Workbench Per hour of VM switched on High: it gets left open
Training Per machine-hour, while it lasts Low: it finishes by itself
Endpoint Per node-hour, in use or not Very high: it does not switch itself off
Batch prediction Per job executed Low
Online Feature Store Per serving node Medium
Pre-trained APIs Per unit processed Low and predictable
Generative models Per input and output token Medium, depending on volume

The two switches, said without beating about the bush:

1. Switch off the notebooks. Configure idle-timeout-seconds when creating the instance. An e2-standard-4 forgotten for a month costs more than all the project's training runs.

2. Undeploy the endpoints you are not using. It is the module's expensive mistake. An endpoint deployed for a demo on Tuesday is still billing in August, with no traffic and with nobody looking at it.

# See what is deployed and since when
gcloud ai endpoints list --region=europe-west1 --project=alpinashop-datos

# Withdraw a model from an endpoint (stops billing)
gcloud ai endpoints undeploy-model ENDPOINT_ID \
  --region=europe-west1 --deployed-model-id=DEPLOYED_MODEL_ID

Add to these resources the billing labels you defined in 01-04 (centro-coste:analitica) and a budget alert over them. Thorough cost optimisation arrives in 07-05; here the minimum hygiene is enough.

  1. AlpinaShop's problem: recommending without training anything

We close with the decision that gives the whole module its meaning, and it is not the one you would expect from a machine learning lesson.

The business problem is concrete: when a customer adds a pair of crampons to the cart, what does the site suggest? Today it suggests nothing. The cart page is empty of content and cross-selling is zero.

An enthusiastic ML engineer would propose training a recommendation model with customer and product embeddings. That is what you will see in 05-03, and it is a good solution. But before that you have to ask a question: what do you get without training anything?

And it turns out the answer already exists, computed in 04-03: the productos_juntos table, the co-occurrence matrix that Spark computed over five years of order lines, with support, confidence and lift for each pair of products.

CREATE OR REPLACE TABLE `alpinashop-datos.alpinashop_analitica.reco_baseline` AS
WITH ranking AS (
  SELECT
    sku_a                                          AS sku,
    sku_b                                          AS sku_recomendado,
    soporte, confianza, lift,
    ROW_NUMBER() OVER (
      PARTITION BY sku_a
      ORDER BY lift DESC, confianza DESC
    )                                              AS posicion
  FROM `alpinashop-datos.alpinashop_analitica.productos_juntos`
  WHERE soporte    >= 0.001   -- the pair appears in at least 1 in every 1000 orders
    AND confianza  >= 0.05    -- of those who buy A, at least 5% buy B
    AND lift       >  1.2     -- buying them together is 20% likelier than by chance
)
SELECT r.sku, r.sku_recomendado, r.posicion, r.lift, r.confianza
FROM ranking r
JOIN `alpinashop-datos.alpinashop_analitica.productos` p
  ON p.sku = r.sku_recomendado
WHERE r.posicion <= 10
  AND p.activo = TRUE
  AND p.stock  > 0;

Read it slowly, because every filter is a business decision:

  • soporte >= 0.001 eliminates coincidences. Two products that turned up together in three orders out of five years are not a pattern, they are noise.
  • confianza >= 0.05 requires the association to be reasonably frequent in a specific direction.
  • lift > 1.2 is the filter that avoids the classic mistake: without it, the recommender would suggest the shop's best-selling product to everybody. A lift of 1 means "they are bought together out of pure popularity". Only above that is there real signal.
  • activo = TRUE AND stock > 0 is the most boring rule and the one that saves the most money: never recommend what you cannot sell. No model, however sophisticated, corrects on its own for a catalogue with discontinued products.

This is a few dozen lines of SQL, it runs in seconds, costs cents, needs no training, needs no endpoint, is completely explainable ("we suggest this because 23 % of the people who buy crampons also buy this ice axe") and can go into production this week.

Is it as good as a trained model? No. It has three known limits: it does not personalise — it suggests the same thing to everybody for the same product —, it knows nothing about new products that do not yet have a history (cold start), and it does not exploit customer or session information.

Does that matter? It depends what you compare it against. And today it is compared against recommending nothing. Going from zero to a decent heuristic captures most of the value. Going from that heuristic to a trained model captures an additional increment, which may or may not be worth it.

Decision DA-003. AlpinaShop first deploys the heuristic recommender based on productos_juntos as the baseline in production. Any subsequent ML model will have to demonstrate that it beats this baseline in an A/B test with business metrics, not just on an offline metric. If it does not beat it, it is not deployed.

This decision is the most important in the module, and it deserves to be said plainly: the best machine learning decision is sometimes not to do machine learning. A mature team is distinguished by having a baseline to measure itself against, and by being willing to accept that sometimes the baseline wins.

Common Mistakes and Tips

Starting with the model and not with the decision. If nobody knows what is going to change on the site when the model predicts, the project has already failed. Write the sentence first: "when the model says X, the shop will do Y".

Having no baseline. An AUC of 0.84 means nothing on its own. What did a simple rule give? If the rule gave 0.81, the model contributes very little for what it costs to maintain.

Leaving endpoints and notebooks switched on. The most expensive mistake in this lesson. Put it in the calendar: a monthly review of gcloud ai endpoints list and of Workbench instances.

Confusing a temporal split with a random split. In any problem with a temporal dimension — and almost every shop problem has one — training with data later than the evaluation data produces fantastic metrics that do not reproduce in production.

Computing the features twice. The training/serving skew of section 7. If the same number is computed by two people in two languages, sooner or later they will stop matching.

Using latest in container images. It breaks reproducibility. Always tag with a version.

Putting personal data into the model out of inertia. A model does not need the email to predict. Train over the pseudonymised views from 04-07 and you will have less risk and fewer obligations, without losing any quality.

Tip: write a model card from day one. Four lines — what it is for, what data it was trained with, what metric it has, what known limitations — written while you still remember. In 05-07 you will see that it is a requirement, not an ornament.

Tip: fix the random seed. A model that gives a different result every time it is trained is impossible to debug.

Exercises

Exercise 1

Marta has received the month's Vertex AI invoice: €612, when the previous month it was €40. The team has not trained anything new. List the most likely causes in order of probability, state which command or view you would use to confirm them, and propose three preventive measures.

Exercise 2

Lucía wants to predict, for each SKU, how many units will be sold next week, so as to adjust the order to suppliers. She has ventas_diarias_sku in BigQuery with five years of history. Decide between BigQuery ML and Vertex AI justifying the choice, write the skeleton of the solution and answer: online endpoint or batch prediction, and why?

Exercise 3

Dani proposes training a model that automatically decides whether an order is fraudulent and, if so, cancels it with no human intervention. Analyse the proposal from three angles: technical, business and legal. Propose an alternative design.

Solutions

Solution 1

Causes, by probability:

  1. An endpoint deployed and forgotten. It is cause number one. An endpoint with two nodes switched on all month explains most of the jump on its own, without a single request.
  2. A Workbench instance left running, especially if somebody tried it with a GPU and did not remove it.
  3. A hyperparameter tuning job launched without looking: 40 trials are 40 training runs.
  4. An online Feature Store provisioned during a test and never deleted.
  5. Batch predictions in a loop because of a badly configured Scheduler firing every hour instead of every night.

How to confirm it:

gcloud ai endpoints list --region=europe-west1 --project=alpinashop-datos
gcloud workbench instances list --project=alpinashop-datos
gcloud ai custom-jobs list --region=europe-west1 --project=alpinashop-datos \
  --filter="createTime>2026-07-01"
gcloud ai feature-online-stores list --region=europe-west1 --project=alpinashop-datos

And in the billing report exported to BigQuery (01-04), the breakdown that gives the direct answer:

SELECT
  service.description                      AS servicio,
  sku.description                          AS concepto,
  ROUND(SUM(cost), 2)                      AS euros
FROM `alpinashop-datos.facturacion.gcp_billing_export_v1_XXXX`
WHERE DATE(usage_start_time) >= DATE '2026-07-01'
  AND service.description LIKE '%Vertex%'
GROUP BY servicio, concepto
ORDER BY euros DESC
LIMIT 15;

Preventive measures:

  1. A budget alert over the centro-coste:analitica label at 50 %, 80 % and 100 % of the expected amount (01-04). It does not avoid the spend, but it detects it in days rather than in a month.
  2. Compulsory idle-timeout-seconds on every Workbench instance, enforced by organization policy or by review at creation time.
  3. A scheduled monthly review of endpoints and notebooks, with the explicit rule: any endpoint with no traffic in 14 days is withdrawn. It can be automated by querying the endpoint's metrics in Cloud Monitoring (06-04).

Solution 2

Choice: BigQuery ML, and fairly clearly so.

Justification: the data is already in BigQuery in the ventas_diarias_sku table; the problem is a univariate time series per SKU, a case BigQuery ML covers natively with ARIMA_PLUS; the output is a weekly order to suppliers, so there is no need whatsoever for low latency; and the person who is going to maintain it is Lucía, who works in SQL. Moving this up to Vertex AI would add weeks of work and cost with no advantage at all.

CREATE OR REPLACE MODEL `alpinashop-datos.alpinashop_analitica.m_demanda_sku`
OPTIONS(
  model_type            = 'ARIMA_PLUS',
  time_series_timestamp_col = 'fecha',
  time_series_data_col      = 'unidades',
  time_series_id_col        = 'sku',
  holiday_region            = 'ES',
  auto_arima                = TRUE,
  data_frequency            = 'DAILY'
) AS
SELECT fecha, sku, unidades
FROM `alpinashop-datos.alpinashop_analitica.ventas_diarias_sku`
WHERE fecha >= DATE_SUB(CURRENT_DATE(), INTERVAL 3 YEAR);
-- 7-day forecast with confidence interval
SELECT
  sku,
  DATE(forecast_timestamp)                  AS fecha,
  ROUND(forecast_value, 1)                  AS unidades_previstas,
  ROUND(confidence_interval_lower_bound, 1) AS minimo,
  ROUND(confidence_interval_upper_bound, 1) AS maximo
FROM ML.FORECAST(
  MODEL `alpinashop-datos.alpinashop_analitica.m_demanda_sku`,
  STRUCT(7 AS horizon, 0.9 AS confidence_level))
ORDER BY sku, fecha;

Details that make the difference:

  • time_series_id_col = 'sku' trains one model per SKU in a single statement. With 2,400 SKUs this saves an entire project.
  • holiday_region = 'ES' incorporates Spanish public holidays, which matter a great deal in a mountain shop: Easter week and long weekends are real peaks.
  • The confidence interval is more useful than the point prediction. To decide an order to a supplier what you want is the range, not the magic number. With slow-moving products the interval will be wide, and that is honest information: the model is telling you it does not know.

Endpoint or batch: batch, no argument. The forecast is needed once a week to place an order. A 24×7 endpoint to answer one weekly query is the literal definition of pointless spending. You schedule it with Cloud Scheduler and Workflows (04-06) on Sunday nights and it writes into a table that Lucía reads on Monday.

Caution: SKUs with little history or that are highly seasonal will give bad forecasts. It is worth filtering out those with less than a year of data and handling them with a manual rule, and validating the model by comparing its forecast against the last four real weeks before trusting it.

Solution 3

Technical angle. Fraud is a problem of extremely imbalanced classes: perhaps 3 fraudulent orders in every 10,000. A model that says "there is never fraud" is right 99.97 % of the time, and is useless. In addition, the available data is almost certainly not labelled: nobody has historically marked which orders were fraud, and without labels there is no supervised learning. And fraud is adversarial: fraudsters change tactics as soon as they detect the defence, so the model degrades by design, not by accident.

Business angle. The two errors have to be valued separately, and they do not cost the same. A false negative costs the value of the order. A false positive cancels a legitimate customer's purchase: you lose the margin, you probably lose the customer, and you earn yourself a one-star review. With 0.03 % real fraud, even a good model will generate far more false positives than detected frauds. The expected cost of full automation is almost certainly negative.

Legal angle. This is the decisive one. Article 22 of the GDPR regulates decisions based solely on automated processing that produce legal effects or significantly affect a person. Cancelling a customer's order automatically and implicitly accusing them of fraud fits squarely into that. It generates information obligations, a right to obtain human intervention, to express a point of view and to contest the decision. On top of that comes the EU AI Act, with requirements on risk management, documentation, transparency and human oversight according to the system's classification. This analysis is not a substitute for legal advice: the specific classification and the applicable obligations must be determined by a compliance professional or the DPO before any implementation.

Alternative design: the model scores, the person decides.

flowchart LR
    A[New order] --> B[Risk score]
    B -->|< 0.7| C[Automatic approval]
    B -->|0.7 - 0.9| D[Manual review queue]
    B -->|> 0.9| E[Hold + priority review]
    D --> F[Human decision recorded]
    E --> F
    F --> G[Label for retraining]

Four properties that make it viable:

  1. No cancellation is automatic. There is always a person in the loop, which resolves the Article 22 problem and the human oversight requirement.
  2. The thresholds are business decisions, adjustable according to how much review capacity there is. If there is one person working half-time, you calibrate so the queue fits into their working day.
  3. Every human decision generates a label, which is precisely the data that does not exist today. In six months there will be a real labelled set to train on properly.
  4. Every decision is recorded with the model version, the score and the reason. That is what you show in an audit or in the face of a complaint.

And the first version of the score does not need to be a model: half a dozen rules — delivery address different from the billing one, account created less than an hour ago, amount well above average, three failed payment attempts — capture a good part of the obvious fraud, are explainable to the customer and can be implemented in a day. The same logic as in section 15.

Conclusion

You have seen why a model on a laptop is not a product, and what is needed around it for it to be one: reproducibility, scale, serving, traceability and watchfulness. Those five gaps are exactly the ones an ML platform fills.

You know the full life cycle — problem, data, features, training, evaluation, deployment, monitoring, retraining — and you know it is a loop, not a line: a model deployed and forgotten is a liability. And you know which Vertex AI component covers each stage, from Workbench to Pipelines, with the historical note that AI Platform and AutoML were separate products and today everything lives under the same umbrella.

You have set up Lucía's Workbench with a service account, with no public IP and with automatic shutdown, reading from module 4's governed views and aggregating in BigQuery instead of downloading gigabytes. You have created the managed dataset pedidos-historico pointing deliberately at v_pedidos_analitica and not at the table with emails. You have understood training/serving skew — the silent mistake of computing the same feature in two ways — and how Feature Store solves it, as well as when setting it up does not yet pay off.

You know how to launch custom training jobs with a versioned container, tune hyperparameters with Bayesian search without ruining yourself, version in the Model Registry with aliases that let you promote and roll back in seconds, and choose between an online endpoint and batch prediction with a clear criterion: if the result does not depend on something that has just happened, it goes in batch. That is where decision DA-002 came from. And you know how to watch for drift without turning the alerts into noise.

You have trained your first model with BigQuery ML in a single SQL statement, with a temporal split, automatic class weights and explainability enabled, and you have the criterion for when SQL is enough and when you have to move up to Vertex AI. And you have the two cost switches clearly identified: switch off the notebooks, withdraw the endpoints.

But what will be worth most from this lesson is decision DA-003. AlpinaShop is going to put into production a recommender that is not a model: a few dozen lines of SQL over the productos_juntos matrix that Spark computed, with support, confidence and lift filters, and with the most important rule of all — do not recommend what is not in stock. It is explainable, it costs cents, it is ready this week, and any model that comes afterwards will have to demonstrate that it beats it in business terms, not on a laboratory metric.

In 05-02, AutoML, we take the natural next step: training real models without writing code. You will see what AutoML does underneath — architecture search, feature engineering and ensembling —, what problem types it covers and with what minimum data, and you will apply it to two AlpinaShop cases: predicting a cart's purchase probability starting from visitas and v_pedidos_analitica, and classifying the catalogue photos with AutoML Vision. Along the way you will learn to really read a confusion matrix, to understand why the decision threshold is a business decision and not a technical one, and to spot the three traps that ruin more projects than anything else: data leakage, class imbalance and overfitting.

And you will carry the baseline with you. Because from now on, every model you train has a rival to beat.

Google Cloud Platform (GCP) Course

Module 1: Introduction to Google Cloud Platform

Module 2: Core GCP Services

Module 3: Networking and Security

Module 4: Data and Analytics

Module 5: Machine Learning and AI

Module 6: DevOps and Monitoring

Module 7: Advanced GCP Topics

Module 8: Final Project

© Copyright 2026. All rights reserved