MLOps: Od experimentu k produkci
Každý datový vědec zažil tento okamžik: model funguje perfektně v notebooku Jupyter, metriky jsou vynikající, tým během dema tleská. Pak přichází osudová otázka: "Kdy to zavedeme do výroby?". A začíná ticho. Podle odhadů odvětví až 85 % projektů strojového učení se nikdy nedostane do produkčního prostředí. Ne proto, že modely nefungují, ale proto, že infrastruktura, proces a disciplínu, aby fungovaly spolehlivě a nepřetržitě.
MLOps (Machine Learning Operations) byl vytvořen přesně proto, aby zaplnil tuto mezeru. Nejde o jedinou technologii, ale o soubor postupů, nástrojů a kultury přeměňují izolované experimenty na robustní produkční ML systémy. V tomto článku uvidíme co znamená MLOps, proč se stal nepostradatelným a jak jej konkrétně začít uplatňovat, i s omezeným rozpočtem.
Co se naučíte
- proč se většina projektů ML nedostane do produkce a jak MLOps problém řeší
- Klíčové rozdíly mezi DevOps a MLOps
- 3 úrovně zralosti MLOps podle modelu Google
- Kompletní životní cyklus modelu ML ve výrobě
- Jak provádět experimentální sledování s MLflow
- Jak obsluhovat model pomocí FastAPI a Docker
- Open source stack pro začátek s méně než 5 000 EUR/rok
Co je MLOps a proč je potřeba
MLOps a aplikace principů DevOps k životnímu cyklu strojového učení. Stejně jako DevOps spojil vývoj a operace pro tradiční software, MLOps se sjednotí datová věda, inženýrství a operace pro systémy ML. Cílem je automatizovat např zajistit reprodukovatelnost všech fází: od přípravy dat po školení, od validace k nasazení, od monitorování po přeškolení.
DevOps vs MLOps: Klíčové rozdíly
Ti, kteří přicházejí ze světa softwaru, si mohou myslet, že stačí použít stejné postupy DevOps na ML modely. Ve skutečnosti existují zásadní rozdíly, které dělají z MLOps disciplínu samu o sobě.
| čekám | DevOps | MLOps |
|---|---|---|
| Artefakt | Zdrojový kód | Kód + Data + Model |
| Verzování | Git pro kód | Git + DVC pro data a modely |
| Testování | Unit testy, integrační testy | Validace dat, validace modelu, A/B testování |
| CI/CD | Vytvářejte, testujte a nasazujte kód | Trénujte, ověřujte, nasazujte model |
| Sledování | Latence, chyby, doba provozuschopnosti | Datový drift, koncept drift, výkon modelu |
| Degradace | Explicitní chyby | Tichá degradace v průběhu času |
| Reprodukovatelnost | Stejný kód = stejný výstup | Stejný kód + stejná data + stejné semeno = stejný výstup |
Nejkritičtější rozdíl je tichá degradace. Softwarová služba tradiční funguje nebo nefunguje: pokud je chyba, vyhodí chybu. Model ML může nadále vracet předpovědi bez technických chyb, ale s progresivní přesností horší, protože vstupní data se oproti tréninku změnila. Bez sledování konkrétní, nikdo si toho nevšimne, dokud si uživatelé nezačnou stěžovat.
Problém "Údolí smrti" ML
Gartner odhaduje, že 30 % generativních projektů umělé inteligence bude po fázi opuštěno proof-of-concept do konce roku 2025, kvůli nízké kvalitě dat, kontrolám nepřiměřené riziko, eskalující náklady nebo nejasná obchodní hodnota. adresy MLOps každý z těchto problémů systematicky.
Trh MLOps: čísla a trendy
Trh MLOps roste impozantním tempem. Podle průmyslových analýz, globální hodnota trhu MLOps se v roce 2025 odhadovala na 2 až 3 miliardy dolarů, s projekcemi, které předpokládají, že do roku 2035 dosáhne 25 až 56 miliard dolarů, s CAGR (složená roční míra růstu) mezi 29 % a 42 % v závislosti na zdrojích.
Tato čísla odrážejí konkrétní realitu: společnosti masivně investují uvést ML modely do výroby. Podle odhadů trhu více než 70 %. Velké podniky v Severní Americe provozují pracovní zátěž AI ve výrobě a více než 55 % jich má integrované systémy automatického monitorování modelů. Nicméně téměř dvě třetiny organizace stále uvízly v pilotní fázi a nejsou schopny škálovat AI na podnikové úrovni.
3 úrovně vyspělosti MLOps
Google definoval třívrstvý model vyspělosti MLOps, který se stal standardem de facto odvětví. Každá úroveň představuje rostoucí stupeň automatizace a reliability in the ML lifecycle.
Úroveň 0: Manuální proces
Na úrovni 0 je každá fáze manuální. Datový vědec pracuje ve svém notebooku, trénuje model lokálně, exportuje jej jako soubor a doručí jej technickému týmu, který zabalí do API. Není zde žádná automatizace, není zde monitorování, není zde automatické přeškolování.
| Charakteristický | Úroveň 0 |
|---|---|
| Výcvik | Manuál, v notebooku |
| Nasadit | Manuál, doručení souboru .pkl nebo .h5 |
| Sledování | Žádný nebo manuální |
| Rekvalifikace | Pouze na výslovnou žádost |
| Reprodukovatelnost | Chudý nebo nepřítomný |
Tato úroveň je běžná v organizacích, které začínají používat ML na své případy použití. To může stačit, když jsou modely zřídka aktualizovány a data se mění jen málo, ale neškáluje.
Úroveň 1: ML Pipeline Automation
Na úrovni 1 je školení automatizováno prostřednictvím a ML potrubí. Ne ano již nenasazuje jeden model, ale celé potrubí, které jej vyrábí. To umožňuje průběžné školení: Když dorazí nová data, potrubí se přeškolí model automaticky.
| Charakteristický | Úroveň 1 |
|---|---|
| Výcvik | Automaticky přes potrubí |
| Nasadit | Automatizované potrubí |
| Sledování | Výkon modelu + přeškolení spouště |
| Rekvalifikace | Automaticky při nových datech nebo degradaci |
| Reprodukovatelnost | Dobré (verze potrubí) |
Úroveň 1 je dostatečná, když se data často mění, ale zůstává zachován přístup ML stabilní. Potrubí je stejné, ale pravidelně se spouští s čerstvými daty.
Úroveň 2: CI/CD pro strojové učení
Na úrovni 2 je přidán kompletní systém CI/CD specifické pro ML. Mění se nejen data, ale také kód kanálu, funkce, hyperparametry, architektura modelu. Každá změna prochází automatickým testováním, validací a řízené nasazení.
| Charakteristický | Úroveň 2 |
|---|---|
| Výcvik | Automaticky + CI/CD na samotném potrubí |
| Nasadit | Modrá/zelená, kanárská, A/B testování |
| Sledování | Komplexní: datový drift, koncept drift, výkon, latence |
| Rekvalifikace | Automaticky s ověřením a vrácením zpět |
| Reprodukovatelnost | Kompletní (verze kódu + dat + prostředí) |
Dosažení úrovně 2 a cíle pro vyspělé organizace. Vyžaduje to investice významné v infrastruktuře a kultuře, ale je to jediný způsob, jak spravovat desítky nebo stovky modelů v udržitelné výrobě.
Životní cyklus MLOps
Životní cyklus modelu ML ve výrobě je iterativní proces, kterým prochází šest hlavních fází. Na rozdíl od tradičního vývoje softwaru tento cyklus ne nikdy nekončí: model ve výrobě vyžaduje nepřetržitou údržbu.
+----------+ +---------+ +----------+
| DATA |---->| TRAIN |---->| EVALUATE |
| Collect | | Feature | | Validate |
| Clean | | Train | | Compare |
| Version | | Tune | | Approve |
+----------+ +---------+ +----------+
^ |
| v
+----------+ +---------+ +----------+
| RETRAIN |<----| MONITOR |<----| DEPLOY |
| Trigger | | Drift | | Stage |
| Schedule | | Metrics | | Canary |
| Auto | | Alert | | Release |
+----------+ +---------+ +----------+
1. Datum: Sběr, čištění a verzování
Vše začíná daty. V této fázi se shromažďují a čistí nezpracovaná data (zpracování chybějících hodnot, odlehlých hodnot, duplicit), jsou transformovány do užitečných funkcí a verze sami sebe. Verze dat je zásadní: reprodukovat model musíte vědět přesně které údaje byly použity k výcviku. Nástroje jako DVC (Data Version Control) umožňují verzování velké datové sady podobné Gitu.
2. Trénink: Hlavní inženýrství a školení
S připravenými daty vytvoříte funkce, vyberete algoritmus a trénujete model. Každý experiment (kombinace hyperparametrů, vlastností, architektury) je sledován s příslušnými parametry a metrikami. Nástroje jako MLflow učinit tento proces systematickým a reprodukovatelným.
3. Vyhodnoťte: Validace a srovnání
Trénovaný model je validován podle předem definovaných metrik (přesnost, F1-skóre, RMSE, AUC) a porovnány s verzí, která se aktuálně vyrábí. Pokud nový model nepřekročí minimální limity nebo se nezlepší oproti předchozímu, není povýšen.
4. Nasazení: Staging, Canary a Release
Schválený model prochází progresivními prostředími: příprava pro testování integrace, kanárek pro ověřování s omezeným reálným provozem a nakonec výroba kompletní. Strategie jako modro/zelené nasazení e vypuštění kanárků minimalizovat riziko.
5. Monitor: Drift, metriky a výstrahy
Ve výrobě je model průběžně sledován. Metriky jsou sledovány techniky (latence, propustnost, chyby) a metriky ML (přesnost na reálných datech, distribuce předpovědí, datový drift). Upozornění se spouštějí, když metriky klesají pod prahové hodnoty.
6. Přeškolení: Spouštění a automatizace
Když monitorování zjistí degradaci, aktivuje se přeškolení. To může být plánované (např. týdně), založené na spouštěči (např. přesnost pod 90 %) nebo manuální. Nový model znovu prochází fázemi vyhodnocení a nasazení.
Open-Source MLOps Stack
Jednou z výhod MLOps je, že existuje vyspělý open-source ekosystém, který jej pokrývá každou fázi životního cyklu. Není potřeba kupovat drahé podnikové platformy pro začátek: se správnými nástroji můžete postavit kompletní potrubí MLOps.
| Fáze | Nástroj | Funkce |
|---|---|---|
| Verze dat | DVC | Verze datových sad a modelů, integrované s Git |
| Sledování experimentu | MLflow | Protokolování parametrů, metrik, artefaktů pro každý experiment |
| Registr modelů | Registr modelu MLflow | Verze a propagace modelu (staging/produkce) |
| Pipeline Orchestrace | Prefekt / proudění vzduchu | Orchestrace pracovního postupu, plánování, opakování |
| Servírování modelů | FastAPI + Docker | REST API pro poskytování předpovědí, kontejnerizované |
| Kontejnerizace | Docker + K8s | Reprodukovatelná prostředí, horizontální škálovatelnost |
| Sledování | Prometheus + Grafana | Metriky, dashboardy, upozornění |
| Validace dat | Velká očekávání | Automatické testy kvality dat |
Od notebooku k potrubí: Praktický příklad
Podívejme se na nejběžnější krok, kterému čelí každý tým ML: transformace kódu napsané v notebooku Jupyter v modulárním a reprodukovatelném potrubí. Vezměme si a skutečný příklad klasifikace a podívejme se, jak ji restrukturalizovat.
Předtím: Monolitický notebook
Zde je typický notebook, kde vše žije v jediném souboru, bez oddělení odpovědnost, bez protokolování, bez verzování.
# Cella 1: Tutto nello stesso notebook
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, f1_score
import pickle
# Caricamento dati
df = pd.read_csv("data/customers.csv")
# Feature engineering inline
df["age_group"] = pd.cut(df["age"], bins=[0, 25, 45, 65, 100],
labels=["young", "adult", "senior", "elderly"])
df["total_spend"] = df["orders"] * df["avg_order_value"]
# Split
X = df[["age", "total_spend", "visits", "days_since_last"]]
y = df["churned"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# Training - parametri hardcoded
model = RandomForestClassifier(n_estimators=100, max_depth=10)
model.fit(X_train, y_train)
# Valutazione - print a schermo
y_pred = model.predict(X_test)
print(f"Accuracy: {accuracy_score(y_test, y_pred)}")
print(f"F1: {f1_score(y_test, y_pred)}")
# Salvataggio - pickle senza versioning
with open("model.pkl", "wb") as f:
pickle.dump(model, f)
print("Modello salvato!")
Problémy s monolitickým notebookem
- Nehratelné: žádná semena, žádné verzování dat
- Nesledováno: parametry a metriky žijí pouze ve výstupu notebooku
- Netestovatelné: žádné izolované funkce k testování
- Nelze nasadit: okurka není API
- Nelze udržovat: úprava funkce vyžaduje opětovné spuštění všeho
Po: Modulární potrubí
Restrukturalizujeme kód do samostatných modulů, z nichž každý nese specifickou odpovědnost. Každá funkce je testovatelná, každý parametr je konfigurovatelný, každá metrika je sledována.
"""Modulo per la preparazione e trasformazione dei dati."""
import pandas as pd
from pathlib import Path
from typing import Tuple
def load_data(path: str) -> pd.DataFrame:
"""Carica il dataset dal path specificato."""
filepath = Path(path)
if not filepath.exists():
raise FileNotFoundError(f"Dataset non trovato: {path}")
return pd.read_csv(filepath)
def create_features(df: pd.DataFrame) -> pd.DataFrame:
"""Crea le feature derivate per il modello."""
result = df.copy()
result["age_group"] = pd.cut(
result["age"],
bins=[0, 25, 45, 65, 100],
labels=["young", "adult", "senior", "elderly"]
)
result["total_spend"] = result["orders"] * result["avg_order_value"]
return result
def split_data(
df: pd.DataFrame,
target_col: str = "churned",
feature_cols: list = None,
test_size: float = 0.2,
random_state: int = 42
) -> Tuple[pd.DataFrame, pd.DataFrame, pd.Series, pd.Series]:
"""Split dei dati in train/test con seed fisso per riproducibilità."""
from sklearn.model_selection import train_test_split
if feature_cols is None:
feature_cols = ["age", "total_spend", "visits", "days_since_last"]
X = df[feature_cols]
y = df[target_col]
return train_test_split(X, y, test_size=test_size, random_state=random_state)
"""Modulo per il training e la valutazione del modello."""
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, f1_score, precision_score, recall_score
from typing import Dict, Any
import pandas as pd
def train_model(
X_train: pd.DataFrame,
y_train: pd.Series,
n_estimators: int = 100,
max_depth: int = 10,
random_state: int = 42
) -> RandomForestClassifier:
"""Addestra un RandomForestClassifier con parametri configurabili."""
model = RandomForestClassifier(
n_estimators=n_estimators,
max_depth=max_depth,
random_state=random_state
)
model.fit(X_train, y_train)
return model
def evaluate_model(
model: RandomForestClassifier,
X_test: pd.DataFrame,
y_test: pd.Series
) -> Dict[str, float]:
"""Valuta il modello e restituisce un dizionario di metriche."""
y_pred = model.predict(X_test)
return {
"accuracy": accuracy_score(y_test, y_pred),
"f1_score": f1_score(y_test, y_pred),
"precision": precision_score(y_test, y_pred),
"recall": recall_score(y_test, y_pred),
}
"""Pipeline principale che orchestra tutte le fasi."""
from src.data.preprocessing import load_data, create_features, split_data
from src.models.trainer import train_model, evaluate_model
import yaml
from pathlib import Path
def run_pipeline(config_path: str = "config.yaml") -> None:
"""Esegue l'intera pipeline ML con configurazione esterna."""
# 1. Carica configurazione
with open(config_path) as f:
config = yaml.safe_load(f)
# 2. Data preparation
print("[1/4] Caricamento dati...")
df = load_data(config["data"]["path"])
df = create_features(df)
# 3. Split
print("[2/4] Split train/test...")
X_train, X_test, y_train, y_test = split_data(
df,
test_size=config["data"]["test_size"],
random_state=config["data"]["random_state"]
)
# 4. Training
print("[3/4] Training modello...")
model = train_model(
X_train, y_train,
n_estimators=config["model"]["n_estimators"],
max_depth=config["model"]["max_depth"],
random_state=config["model"]["random_state"]
)
# 5. Evaluation
print("[4/4] Valutazione...")
metrics = evaluate_model(model, X_test, y_test)
for name, value in metrics.items():
print(f" {name}: {value:.4f}")
if __name__ == "__main__":
run_pipeline()
# config.yaml - Tutti i parametri in un unico file
data:
path: "data/customers.csv"
test_size: 0.2
random_state: 42
feature_cols:
- age
- total_spend
- visits
- days_since_last
model:
algorithm: "random_forest"
n_estimators: 100
max_depth: 10
random_state: 42
evaluation:
metrics:
- accuracy
- f1_score
- precision
- recall
min_accuracy: 0.85
Výhody modulárního potrubí
- Reprodukovatelné: pevné semeno, outsourcovaná konfigurace, verzovaná data
- Testovatelné: každá funkce je izolovaná a může mít vyhrazené testy jednotek
- Udržovatelné: úprava funkcí nemá vliv na školení a naopak
- Konfigurovatelné: změnit hyperparametry, aniž byste se dotkli kódu
- Automatické: potrubí lze provozovat pomocí CI/CD
Sledování experimentů s MLflow
Kolikrát jste změnili hyperparametr a pak jste si nepamatovali, jakou měl kombinaci s nejlepším výsledkem? THE'experimentální sledování řeší tento problém automaticky zaznamenávat parametry, metriky a artefakty každého experimentu.
MLflow a nejpopulárnější open-source nástroj pro experimentální sledování. Nabízí server s webovým uživatelským rozhraním pro prohlížení a porovnávání experimentů, Python API pro protokolování a registr modelů pro správu životního cyklu modelů.
Nastavení a první experiment
# Installazione
pip install mlflow
# Avvio del tracking server locale
mlflow server --host 127.0.0.1 --port 5000
"""Pipeline ML con experiment tracking via MLflow."""
import mlflow
import mlflow.sklearn
from src.data.preprocessing import load_data, create_features, split_data
from src.models.trainer import train_model, evaluate_model
def run_tracked_pipeline(config: dict) -> None:
"""Esegue la pipeline tracciando tutto con MLflow."""
# Imposta il tracking URI (server locale o remoto)
mlflow.set_tracking_uri("http://127.0.0.1:5000")
mlflow.set_experiment("churn-prediction")
with mlflow.start_run(run_name="rf-baseline") as run:
# Log dei parametri
mlflow.log_param("algorithm", "RandomForest")
mlflow.log_param("n_estimators", config["model"]["n_estimators"])
mlflow.log_param("max_depth", config["model"]["max_depth"])
mlflow.log_param("test_size", config["data"]["test_size"])
mlflow.log_param("random_state", config["data"]["random_state"])
# Data preparation
df = load_data(config["data"]["path"])
df = create_features(df)
X_train, X_test, y_train, y_test = split_data(
df,
test_size=config["data"]["test_size"],
random_state=config["data"]["random_state"]
)
# Log dimensioni dataset
mlflow.log_param("train_samples", len(X_train))
mlflow.log_param("test_samples", len(X_test))
mlflow.log_param("n_features", X_train.shape[1])
# Training
model = train_model(
X_train, y_train,
n_estimators=config["model"]["n_estimators"],
max_depth=config["model"]["max_depth"]
)
# Evaluation
metrics = evaluate_model(model, X_test, y_test)
# Log delle metriche
for name, value in metrics.items():
mlflow.log_metric(name, value)
# Log del modello come artefatto
mlflow.sklearn.log_model(
model,
artifact_path="model",
registered_model_name="churn-classifier"
)
# Log della configurazione come artefatto
mlflow.log_artifact("config.yaml")
print(f"Run ID: {run.info.run_id}")
print(f"Metriche: {metrics}")
Po provedení několika experimentů otevřete http://127.0.0.1:5000 v prohlížeči.
Uživatelské rozhraní MLflow zobrazuje tabulku se všemi experimenty, což vám umožňuje porovnávat
metriky, seřadit podle výkonu a zobrazit metriky vs grafy metrik.
Registr modelů: Modelování verzí
Stejně jako je kód verzován pomocí Git, modely ML musí být verzovány s a Registr modelů. MLflow Model Registry nabízí centralizovaný systém řídit životní cyklus modelů ve třech fázích.
| Stadión | Popis | Kdo to používá |
|---|---|---|
| Žádné / inscenace | Model ve fázi testování a ověřování | Datový vědec, QA |
| Výroba | Schválený model, slouží skutečnému provozu | Servisní API, koncoví uživatelé |
| Archivováno | Model vyřazen z provozu, ponechán k auditu | Soulad, vrácení zpět |
"""Gestione del ciclo di vita del modello con MLflow Model Registry."""
from mlflow.tracking import MlflowClient
client = MlflowClient("http://127.0.0.1:5000")
# Recupera l'ultima versione del modello in staging
latest_versions = client.get_latest_versions(
name="churn-classifier",
stages=["Staging"]
)
if latest_versions:
version = latest_versions[0].version
print(f"Modello in staging: v{version}")
# Promuovi a Production dopo validazione
client.transition_model_version_stage(
name="churn-classifier",
version=version,
stage="Production",
archive_existing_versions=True # Archivia versione precedente
)
print(f"Modello v{version} promosso a Production")
# Carica il modello in produzione per inference
import mlflow.pyfunc
model = mlflow.pyfunc.load_model("models:/churn-classifier/Production")
prediction = model.predict(new_data)
Nasazení: FastAPI + Docker
Model ML ve výrobě je obvykle vystaven jako REST API. FastAPI a ideální volba pro Python: a rychlý (založený na ASGI), generuje automatickou dokumentaci (OpenAPI/Swagger) a má vynikající validaci dat přes Pydantic. Kontejnerování s Přístavní dělník, dostáváme artefakt použitelný kdekoli.
"""API REST per servire predizioni del modello ML."""
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
import mlflow.pyfunc
import pandas as pd
import logging
from typing import List
# Configurazione logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = FastAPI(
title="Churn Prediction API",
description="API per predizioni di churn basata su ML",
version="1.0.0"
)
class PredictionRequest(BaseModel):
"""Schema della richiesta di predizione."""
age: int = Field(..., ge=0, le=120, description="Eta del cliente")
total_spend: float = Field(..., ge=0, description="Spesa totale")
visits: int = Field(..., ge=0, description="Numero visite")
days_since_last: int = Field(..., ge=0, description="Giorni dall'ultima visita")
class PredictionResponse(BaseModel):
"""Schema della risposta di predizione."""
prediction: int
probability: float
model_version: str
# Caricamento modello all'avvio
MODEL_NAME = "churn-classifier"
MODEL_STAGE = "Production"
model = None
model_version = "unknown"
@app.on_event("startup")
async def load_model():
"""Carica il modello MLflow all'avvio del server."""
global model, model_version
try:
model_uri = f"models:/{MODEL_NAME}/{MODEL_STAGE}"
model = mlflow.pyfunc.load_model(model_uri)
model_version = model.metadata.run_id[:8]
logger.info(f"Modello caricato: {MODEL_NAME} ({model_version})")
except Exception as e:
logger.error(f"Errore caricamento modello: {e}")
raise
@app.get("/health")
async def health_check():
"""Endpoint di health check."""
return {"status": "healthy", "model_loaded": model is not None}
@app.post("/predict", response_model=PredictionResponse)
async def predict(request: PredictionRequest):
"""Genera una predizione di churn per un cliente."""
if model is None:
raise HTTPException(status_code=503, detail="Modello non caricato")
try:
input_data = pd.DataFrame([request.model_dump()])
prediction = model.predict(input_data)
probability = float(prediction[0]) if hasattr(prediction[0], '__float__') else 0.0
return PredictionResponse(
prediction=int(prediction[0]),
probability=probability,
model_version=model_version
)
except Exception as e:
logger.error(f"Errore predizione: {e}")
raise HTTPException(status_code=500, detail="Errore nella predizione")
@app.post("/predict/batch", response_model=List[PredictionResponse])
async def predict_batch(requests: List[PredictionRequest]):
"""Genera predizioni batch per più clienti."""
if model is None:
raise HTTPException(status_code=503, detail="Modello non caricato")
input_data = pd.DataFrame([r.model_dump() for r in requests])
predictions = model.predict(input_data)
return [
PredictionResponse(
prediction=int(p),
probability=float(p),
model_version=model_version
)
for p in predictions
]
# Dockerfile per il serving del modello ML
FROM python:3.11-slim
WORKDIR /app
# Dipendenze di sistema
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
&& rm -rf /var/lib/apt/lists/*
# Dipendenze Python
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Codice applicazione
COPY src/serving/ ./serving/
COPY config.yaml .
# Porta del servizio
EXPOSE 8000
# Healthcheck
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
# Avvio con uvicorn
CMD ["uvicorn", "serving.app:app", "--host", "0.0.0.0", "--port", "8000"]
# Build dell'immagine
docker build -t churn-api:v1.0.0 .
# Avvio del container
docker run -d \
--name churn-api \
-p 8000:8000 \
-e MLFLOW_TRACKING_URI=http://mlflow-server:5000 \
churn-api:v1.0.0
# Test dell'API
curl -X POST http://localhost:8000/predict \
-H "Content-Type: application/json" \
-d '{"age": 35, "total_spend": 1250.50, "visits": 12, "days_since_last": 45}'
Monitoring ve výrobě
Rozmístění není koncem práce, ale začátkem nové kritické fáze: sledování. Model ve výrobě časem degraduje, protože svět změny a data s nimi. Monitorování se musí týkat tří hlavních oblastí.
Metriky ke sledování
| Kategorie | Metriky | Nástroj |
|---|---|---|
| Infrastruktura | Latence (p50, p95, p99), propustnost, chyby HTTP, CPU/RAM | Prometheus + Grafana |
| Model | Přesnost, F1-skóre, rozdělení predikcí, spolehlivost | MLflow + vlastní metriky |
| Data | Datový drift, rysový drift, chybějící hodnoty, rozdělení vstupů | Evidentně AI / Velká očekávání |
Data Drift vs Concept Drift
Je důležité rozlišovat mezi dvěma typy degradace modelu:
- Posun data: distribuce vstupních dat se mění s ohledem na tréninkový set. Příklad: začne dostávat model vyškolený na zákaznících ve věku 25–45 let požadavky pro zákazníky 60+.
- Koncept Drift: mění se vztah mezi vstupem a výstupem. Příklad: po pandemie, vzorce odchodu zákazníků jsou úplně jiné, ale rysy na vstupu mají stejné rozdělení.
"""Rilevamento data drift con test statistici."""
import numpy as np
from scipy import stats
from typing import Dict, Tuple
def detect_drift(
reference_data: np.ndarray,
production_data: np.ndarray,
feature_names: list,
threshold: float = 0.05
) -> Dict[str, Dict]:
"""
Rileva data drift confrontando distribuzioni con il test KS.
Args:
reference_data: dati di training (riferimento)
production_data: dati di produzione (attuali)
feature_names: nomi delle feature
threshold: soglia p-value per il drift (default 0.05)
Returns:
Report di drift per ogni feature
"""
drift_report = {}
for i, feature in enumerate(feature_names):
ref_values = reference_data[:, i]
prod_values = production_data[:, i]
# Test Kolmogorov-Smirnov
ks_stat, p_value = stats.ks_2samp(ref_values, prod_values)
drift_detected = p_value < threshold
drift_report[feature] = {
"ks_statistic": round(ks_stat, 4),
"p_value": round(p_value, 4),
"drift_detected": drift_detected,
"ref_mean": round(float(np.mean(ref_values)), 4),
"prod_mean": round(float(np.mean(prod_values)), 4),
}
if drift_detected:
print(f"DRIFT RILEVATO su '{feature}': "
f"KS={ks_stat:.4f}, p={p_value:.4f}")
return drift_report
Kdy znovu aktivovat rekvalifikaci
Ne každý drift vyžaduje okamžitou rekvalifikaci. Definujte jasné prahy: datový drift na kritických rysech, pokles přesnosti větší než 5 % nebo rozložení předpovědí výrazně nevyvážené. Vyhněte se nadměrné rekvalifikaci, kterou může zavést nestabilní.
Jak začít s rozpočtem nižším než 5 000 EUR/rok
MLOps nutně neznamená, že podnikové platformy stojí stovky tisíc eur. Pro italský MSP nebo malý tým je možné vybudovat infrastrukturu MLOps efektivní s open-source nástroji a minimálními náklady.
Zásobník navržený pro malé a střední podniky
| Komponent | Řešení | Roční náklady |
|---|---|---|
| Kód | GitHub zdarma / GitLab CE | 0 EUR |
| Verze dat | DVC + Google Cloud Storage (5 GB zdarma) | 0 - 50 EUR |
| Sledování experimentu | MLflow na levném virtuálním počítači | 200 - 500 EUR |
| Výcvik | Google Colab Pro / spot VM | 120 - 600 EUR |
| Porce | FastAPI na VM (2 vCPU, 4 GB RAM) | 300 - 800 EUR |
| Sledování | Prometheus + Grafana (s vlastním hostitelem) | 0 EUR (na stejném VM) |
| CI/CD | Akce GitHub (2000 min/měsíc zdarma) | 0 EUR |
| Registr kontejnerů | Registr kontejnerů GitHub | 0 EUR |
Odhadovaná celková částka: 620 - 1 950 EUR/rok, výrazně pod hranicí 5 000 EUR. Tento zásobník podporuje až 5–10 modelů ve výrobě se středním objemem provozu (tisíce předpovědí za den).
Tipy pro snížení nákladů
- Spot/preemptable VM: až 70% úspora za neurgentní školení
- Automatické škálování: zmenšit na nulu, když nejsou žádné požadavky
- Komprese modelu: menší modely = méně obslužných zdrojů
- Dávkový závěr: pokud nepotřebujete předpovědi v reálném čase, použijte noční dávky
- Více nájemců: jediná infrastruktura MLflow/Grafana pro všechny projekty
Struktura projektu MLOps
Abychom skončili něčím okamžitě použitelným, zde je struktura složek doporučeno pro projekt MLOps. Tato organizace navazuje na oddělení odpovědnost a snadnost automatizace, testování a spolupráce.
churn-prediction/
data/
raw/ # Dati grezzi (versionati con DVC)
processed/ # Dati trasformati
data.dvc # File di tracking DVC
src/
data/
preprocessing.py # Pulizia e feature engineering
validation.py # Validazione qualità dati
models/
trainer.py # Logica di training
evaluator.py # Valutazione e metriche
serving/
app.py # FastAPI application
schemas.py # Pydantic schemas
monitoring/
drift_detector.py # Rilevamento drift
metrics.py # Metriche custom
pipeline.py # Orchestrazione pipeline
tests/
test_preprocessing.py
test_trainer.py
test_api.py
config.yaml # Configurazione pipeline
Dockerfile # Container per serving
docker-compose.yaml # Stack locale completo
requirements.txt # Dipendenze Python
.dvc/ # Configurazione DVC
.github/
workflows/
train.yaml # CI/CD per training
deploy.yaml # CI/CD per deployment
mlflow/ # Artefatti MLflow (locale)
README.md
Závěry a další kroky
MLOps není luxus vyhrazený pro velké technologické společnosti. A nutnost pro každý, kdo chce uvést modely ML do výroby spolehlivým a udržitelným způsobem. V tomto článku jsme pokryli základy: od pochopení problému (proč projekty ML selhávají) ke konkrétnímu řešení (modulární potrubí, experiment sledování, registr modelů, kontejnerové poskytování a monitorování).
Klíčem je začít postupně. Není potřeba dosáhnout úrovně 2 modelu zralosti Google od prvního dne. Začněte od úrovně 0 s osvědčenými postupy:
- hned: Rozdělte kód notebooku do modulů. Použijte soubor config.yaml.
- 1. týden: Přidejte MLflow ke sledování experimentů.
- 2. týden: Kontejnerujte model pomocí FastAPI + Docker.
- 1. měsíc: Implementujte kanál CI/CD pomocí akcí GitHub.
- 2. měsíc: Přidejte monitorování pomocí Prometheus a základní upozornění.
- 3. měsíc: Implementujte DVC pro verzování dat.
V dalších článcích série se budeme hlouběji zabývat každou komponentou: správou dat s DVC, vytváření kanálů CI/CD specifických pro ML, pokročilé monitorování s Evidentně AI a škálovatelné nasazení na Kubernetes. Každý předmět bude praktický, s Pracovní kód a pokyny krok za krokem.
Plán seriálu
- Článek 2: DVC - verzování dat pro ML
- Článek 3: MLflow Deep Dive – pokročilé sledování experimentu
- Článek 4: CI/CD pro strojové učení s akcemi GitHub
- Článek 5: Feature Store a Feature Engineering ve výrobě
- Článek 6: Škálovatelné poskytování modelů s Kubernetes
- Článek 7: Pokročilé monitorování: Data Drift a evidentně AI
- Článek 8: Správa, dodržování a odpovědné ML







