dicembre 21, 2025
15 min di lettura

Domande per il Colloquio di Junior Machine Learning Engineer: Guida Completa

interview
career-advice
job-search
entry-level
Domande per il Colloquio di Junior Machine Learning Engineer: Guida Completa
MB

Milad Bonakdar

Autore

Padroneggia i fondamenti dell'ingegneria ML con domande essenziali per il colloquio che coprono Python, algoritmi di ML, training del modello, basi di deployment e MLOps per junior machine learning engineer.


Introduzione

Gli ingegneri di Machine Learning creano, distribuiscono e mantengono sistemi di ML in produzione. Ci si aspetta che gli ingegneri di ML junior abbiano solide competenze di programmazione, comprensione degli algoritmi di ML, esperienza con i framework di ML e conoscenza delle pratiche di distribuzione.

Questa guida tratta le domande essenziali per i colloqui per ingegneri di Machine Learning junior. Esploreremo la programmazione in Python, gli algoritmi di ML, l'addestramento e la valutazione dei modelli, le basi della distribuzione e i fondamenti di MLOps per aiutarti a prepararti per il tuo primo ruolo di ingegnere di ML.


Python e Programmazione (5 Domande)

1. Come gestisci dataset di grandi dimensioni che non entrano nella memoria?

Risposta: Diverse tecniche gestiscono dati più grandi della RAM disponibile:

  • Elaborazione Batch: Elabora i dati in blocchi
  • Generatori: Restituiscono i dati su richiesta
  • Dask/Ray: Framework di calcolo distribuito
  • Query di Database: Carica solo i dati necessari
  • File Mappati in Memoria: Accedi al disco come se fosse in memoria
  • Streaming di Dati: Elabora i dati man mano che arrivano
import pandas as pd
import numpy as np

# Errato: Carica l'intero dataset in memoria
# df = pd.read_csv('large_file.csv')  # Potrebbe bloccarsi

# Corretto: Leggi a blocchi
chunk_size = 10000
for chunk in pd.read_csv('large_file.csv', chunksize=chunk_size):
    # Elabora ogni blocco
    processed = chunk[chunk['value'] > 0]
    # Salva o aggrega i risultati
    processed.to_csv('output.csv', mode='a', header=False)

# Utilizzo dei generatori
def data_generator(filename, batch_size=32):
    while True:
        batch = []
        with open(filename, 'r') as f:
            for line in f:
                batch.append(process_line(line))
                if len(batch) == batch_size:
                    yield np.array(batch)
                    batch = []

# Dask per il calcolo distribuito
import dask.dataframe as dd
ddf = dd.read_csv('large_file.csv')
result = ddf.groupby('category').mean().compute()

Rarità: Molto Comune Difficoltà: Media


2. Spiega i decoratori in Python e fornisci un caso d'uso di ML.

Risposta: I decoratori modificano o migliorano le funzioni senza cambiarne il codice.

  • Casi d'Uso in ML:
    • Misurazione del tempo di esecuzione della funzione
    • Registrazione delle predizioni
    • Memorizzazione nella cache dei risultati
    • Convalida dell'input
import time
import functools

# Decoratore per la misurazione del tempo
def timer(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} ha impiegato {end - start:.2f} secondi")
        return result
    return wrapper

# Decoratore per la registrazione delle predizioni
def log_predictions(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        predictions = func(*args, **kwargs)
        print(f"Effettuate {len(predictions)} predizioni")
        print(f"Distribuzione delle predizioni: {np.bincount(predictions)}")
        return predictions
    return wrapper

# Utilizzo
@timer
@log_predictions
def predict_batch(model, X):
    return model.predict(X)

# Decoratore per la memorizzazione nella cache (memoizzazione)
def cache_results(func):
    cache = {}
    @functools.wraps(func)
    def wrapper(*args):
        if args not in cache:
            cache[args] = func(*args)
        return cache[args]
    return wrapper

@cache_results
def expensive_feature_engineering(data_id):
    # Calcolo costoso
    return processed_features

Rarità: Comune Difficoltà: Media


3. Qual è la differenza tra @staticmethod e @classmethod?

Risposta: Entrambi definiscono metodi che non richiedono un'istanza.

  • @staticmethod: Nessun accesso alla classe o all'istanza
  • @classmethod: Riceve la classe come primo argomento
class MLModel:
    model_type = "classifier"
    
    def __init__(self, name):
        self.name = name
    
    # Metodo regolare - necessita di un'istanza
    def predict(self, X):
        return self.model.predict(X)
    
    # Metodo statico - funzione di utilità
    @staticmethod
    def preprocess_data(X):
        # Nessun accesso a self o cls
        return (X - X.mean()) / X.std()
    
    # Metodo di classe - pattern factory
    @classmethod
    def create_default(cls):
        # Ha accesso a cls
        return cls(name=f"default_{cls.model_type}")
    
    @classmethod
    def from_config(cls, config):
        return cls(name=config['name'])

# Utilizzo
# Metodo statico - non è necessaria un'istanza
processed = MLModel.preprocess_data(X_train)

# Metodo di classe - crea un'istanza
model = MLModel.create_default()
model2 = MLModel.from_config({'name': 'my_model'})

Rarità: Media Difficoltà: Media


4. Come gestisci le eccezioni nelle pipeline di ML?

Risposta: Una corretta gestione degli errori previene i fallimenti della pipeline e aiuta il debug.

import logging
from typing import Optional

# Imposta il logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class ModelTrainingError(Exception):
    """Eccezione personalizzata per i fallimenti dell'addestramento del modello"""
    pass

def train_model(X, y, model_type='random_forest'):
    try:
        logger.info(f"Inizio l'addestramento con {model_type}")
        
        # Convalida gli input
        if X.shape[0] != y.shape[0]:
            raise ValueError("X e y devono avere lo stesso numero di campioni")
        
        if X.shape[0] < 100:
            raise ModelTrainingError("Dati di addestramento insufficienti")
        
        # Addestra il modello
        if model_type == 'random_forest':
            from sklearn.ensemble import RandomForestClassifier
            model = RandomForestClassifier()
        else:
            raise ValueError(f"Tipo di modello sconosciuto: {model_type}")
        
        model.fit(X, y)
        logger.info("Addestramento completato con successo")
        return model
        
    except ValueError as e:
        logger.error(f"Errore di validazione: {e}")
        raise
    except ModelTrainingError as e:
        logger.error(f"Errore di addestramento: {e}")
        # Potrebbe ricadere su un modello più semplice
        return train_fallback_model(X, y)
    except Exception as e:
        logger.error(f"Errore inatteso: {e}")
        raise
    finally:
        logger.info("Tentativo di addestramento terminato")

# Context manager per la gestione delle risorse
class ModelLoader:
    def __init__(self, model_path):
        self.model_path = model_path
        self.model = None
    
    def __enter__(self):
        logger.info(f"Caricamento del modello da {self.model_path}")
        self.model = load_model(self.model_path)
        return self.model
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        logger.info("Pulizia delle risorse")
        if self.model:
            del self.model
        return False  # Non sopprimere le eccezioni

# Utilizzo
with ModelLoader('model.pkl') as model:
    predictions = model.predict(X_test)

Rarità: Comune Difficoltà: Media


5. Cosa sono i generatori Python e perché sono utili in ML?

Risposta: I generatori restituiscono valori uno alla volta, risparmiando memoria.

  • Vantaggi:
    • Efficienza della memoria
    • Valutazione pigra (lazy evaluation)
    • Sequenze infinite
  • Casi d'Uso di ML:
    • Caricamento dei dati
    • Elaborazione batch
    • Aumento dei dati (data augmentation)
import numpy as np

# Generatore per l'elaborazione batch
def batch_generator(X, y, batch_size=32):
    n_samples = len(X)
    indices = np.arange(n_samples)
    np.random.shuffle(indices)
    
    for start_idx in range(0, n_samples, batch_size):
        end_idx = min(start_idx + batch_size, n_samples)
        batch_indices = indices[start_idx:end_idx]
        yield X[batch_indices], y[batch_indices]

# Utilizzo nell'addestramento
for epoch in range(10):
    for X_batch, y_batch in batch_generator(X_train, y_train):
        model.train_on_batch(X_batch, y_batch)

# Generatore di aumento dei dati
def augment_images(images, labels):
    for img, label in zip(images, labels):
        # Originale
        yield img, label
        # Riflessa
        yield np.fliplr(img), label
        # Ruotata
        yield np.rot90(img), label

# Generatore infinito per l'addestramento
def infinite_batch_generator(X, y, batch_size=32):
    while True:
        indices = np.random.choice(len(X), batch_size)
        yield X[indices], y[indices]

# Utilizzo con steps_per_epoch
gen = infinite_batch_generator(X_train, y_train)
# model.fit(gen, steps_per_epoch=100, epochs=10)

Rarità: Comune Difficoltà: Media


Algoritmi e Teoria di ML (5 Domande)

6. Spiega la differenza tra bagging e boosting.

Risposta: Entrambi sono metodi di ensemble, ma funzionano in modo diverso:

  • Bagging (Bootstrap Aggregating):
    • Addestramento parallelo su sottoinsiemi casuali
    • Riduce la varianza
    • Esempio: Random Forest
  • Boosting:
    • Addestramento sequenziale, ogni modello corregge gli errori precedenti
    • Riduce il bias
    • Esempi: AdaBoost, Gradient Boosting, XGBoost
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score

# Genera dati
X, y = make_classification(n_samples=1000, n_features=20, random_state=42)

# Bagging - Random Forest
rf = RandomForestClassifier(n_estimators=100, random_state=42)
rf_scores = cross_val_score(rf, X, y, cv=5)
print(f"Random Forest CV: {rf_scores.mean():.3f} (+/- {rf_scores.std():.3f})")

# Boosting - Gradient Boosting
gb = GradientBoostingClassifier(n_estimators=100, random_state=42)
gb_scores = cross_val_score(gb, X, y, cv=5)
print(f"Gradient Boosting CV: {gb_scores.mean():.3f} (+/- {gb_scores.std():.3f})")

# XGBoost (boosting avanzato)
import xgboost as xgb
xgb_model = xgb.XGBClassifier(n_estimators=100, random_state=42)
xgb_scores = cross_val_score(xgb_model, X, y, cv=5)
print(f"XGBoost CV: {xgb_scores.mean():.3f} (+/- {xgb_scores.std():.3f})")

Rarità: Molto Comune Difficoltà: Media


7. Come gestisci dataset sbilanciati?

Risposta: I dati sbilanciati possono influenzare i modelli verso la classe maggioritaria.

  • Tecniche:
    • Ricampionamento: SMOTE, undersampling
    • Pesi di classe: Penalizza la misclassificazione
    • Metodi di ensemble: Balanced Random Forest
    • Valutazione: Utilizza F1, precision, recall (non accuracy)
    • Regolazione della soglia: Ottimizza la soglia di decisione
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, confusion_matrix
from imblearn.over_sampling import SMOTE
from imblearn.under_sampling import RandomUnderSampler
from collections import Counter

# Crea un dataset sbilanciato
X, y = make_classification(
    n_samples=1000, n_features=20,
    weights=[0.95, 0.05],  # 95% classe 0, 5% classe 1
    random_state=42
)

print(f"Distribuzione originale: {Counter(y)}")

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)

# 1. Pesi di classe
model_weighted = RandomForestClassifier(class_weight='balanced', random_state=42)
model_weighted.fit(X_train, y_train)
print("\nCon pesi di classe:")
print(classification_report(y_test, model_weighted.predict(X_test)))

# 2. SMOTE (oversampling della classe minoritaria)
smote = SMOTE(random_state=42)
X_train_smote, y_train_smote = smote.fit_resample(X_train, y_train)
print(f"Dopo SMOTE: {Counter(y_train_smote)}")

model_smote = RandomForestClassifier(random_state=42)
model_smote.fit(X_train_smote, y_train_smote)
print("\nCon SMOTE:")
print(classification_report(y_test, model_smote.predict(X_test)))

# 3. Regolazione della soglia
y_proba = model_weighted.predict_proba(X_test)[:, 1]
threshold = 0.3  # Soglia inferiore favorisce la classe minoritaria
y_pred_adjusted = (y_proba >= threshold).astype(int)
print("\nCon soglia regolata:")
print(classification_report(y_test, y_pred_adjusted))

Rarità: Molto Comune Difficoltà: Media


8. Cos'è la cross-validation e perché è importante?

Risposta: La cross-validation valuta le prestazioni del modello in modo più affidabile rispetto a una singola divisione train-test.

  • Tipi:
    • K-Fold: Dividi in k fold
    • Stratified K-Fold: Preserva la distribuzione delle classi
    • Time Series Split: Rispetta l'ordine temporale
  • Vantaggi:
    • Stima delle prestazioni più robusta
    • Utilizza tutti i dati per l'addestramento e la validazione
    • Rileva l'overfitting
from sklearn.model_selection import (
    cross_val_score, KFold, StratifiedKFold,
    TimeSeriesSplit, cross_validate
)
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris

# Carica i dati
data = load_iris()
X, y = data.data, data.target

model = RandomForestClassifier(random_state=42)

# K-Fold standard
kfold = KFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(model, X, y, cv=kfold)
print(f"Punteggi K-Fold: {scores}")
print(f"Media: {scores.mean():.3f} (+/- {scores.std():.3f})")

# Stratified K-Fold (preserva la distribuzione delle classi)
stratified_kfold = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
stratified_scores = cross_val_score(model, X, y, cv=stratified_kfold)
print(f"\nStratified K-Fold: {stratified_scores.mean():.3f}")

# Time Series Split (per dati temporali)
tscv = TimeSeriesSplit(n_splits=5)
ts_scores = cross_val_score(model, X, y, cv=tscv)
print(f"Time Series CV: {ts_scores.mean():.3f}")

# Metriche multiple
cv_results = cross_validate(
    model, X, y, cv=5,
    scoring=['accuracy', 'precision_macro', 'recall_macro', 'f1_macro'],
    return_train_score=True
)

print(f"\nAccuracy: {cv_results['test_accuracy'].mean():.3f}")
print(f"Precision: {cv_results['test_precision_macro'].mean():.3f}")
print(f"Recall: {cv_results['test_recall_macro'].mean():.3f}")
print(f"F1: {cv_results['test_f1_macro'].mean():.3f}")

Rarità: Molto Comune Difficoltà: Facile


9. Spiega precision, recall e F1-score.

Risposta: Metriche di classificazione per valutare le prestazioni del modello:

  • Precision: Dei positivi previsti, quanti sono corretti
    • Formula: TP / (TP + FP)
    • Utilizzare quando: I falsi positivi sono costosi
  • Recall: Dei positivi effettivi, quanti sono stati trovati
    • Formula: TP / (TP + FN)
    • Utilizzare quando: I falsi negativi sono costosi
  • F1-Score: Media armonica di precision e recall
    • Formula: 2 × (Precision × Recall) / (Precision + Recall)
    • Utilizzare quando: È necessario un equilibrio tra precision e recall
from sklearn.metrics import (
    precision_score, recall_score, f1_score,
    classification_report, confusion_matrix
)
import numpy as np

# Esempio di predizioni
y_true = np.array([0, 1, 1, 0, 1, 1, 0, 1, 0, 0])
y_pred = np.array([0, 1, 0, 0, 1, 1, 0, 1, 1, 0])

# Calcola le metriche
precision = precision_score(y_true, y_pred)
recall = recall_score(y_true, y_pred)
f1 = f1_score(y_true, y_pred)

print(f"Precision: {precision:.3f}")  # 0.800
print(f"Recall: {recall:.3f}")        # 0.800
print(f"F1-Score: {f1:.3f}")          # 0.800

# Matrice di confusione
cm = confusion_matrix(y_true, y_pred)
print(f"\nMatrice di Confusione:\n{cm}")
# [[4 1]
#  [1 4]]

# Rapporto di classificazione (tutte le metriche)
print("\nRapporto di Classificazione:")
print(classification_report(y_true, y_pred))

# Esempio di trade-off
from sklearn.metrics import precision_recall_curve
import matplotlib.pyplot as plt

# Ottieni predizioni di probabilità
y_proba = model.predict_proba(X_test)[:, 1]

# Calcola precision-recall a diverse soglie
precisions, recalls, thresholds = precision_recall_curve(y_test, y_proba)

# Trova la soglia ottimale (massimizza F1)
f1_scores = 2 * (precisions * recalls) / (precisions + recalls + 1e-10)
optimal_idx = np.argmax(f1_scores)
optimal_threshold = thresholds[optimal_idx]
print(f"Soglia ottimale: {optimal_threshold:.3f}")

Rarità: Molto Comune Difficoltà: Facile


10. Cos'è la regolarizzazione e quando la useresti?

Risposta: La regolarizzazione previene l'overfitting penalizzando la complessità del modello.

  • Tipi:
    • L1 (Lasso): Aggiunge il valore assoluto dei coefficienti
    • L2 (Ridge): Aggiunge i coefficienti al quadrato
    • Elastic Net: Combina L1 e L2
  • Quando usare:
    • Alta varianza (overfitting)
    • Molte feature
    • Multicollinearità
from sklearn.linear_model import Ridge, Lasso, ElasticNet, LinearRegression
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score
import numpy as np

# Genera dati con molte feature
X, y = make_regression(
    n_samples=100, n_features=50,
    n_informative=10, noise=10, random_state=42
)

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)

# Nessuna regolarizzazione
lr = LinearRegression()
lr.fit(X_train, y_train)
lr_score = r2_score(y_test, lr.predict(X_test))
print(f"Linear Regression R²: {lr_score:.3f}")

# Regolarizzazione L2 (Ridge)
ridge = Ridge(alpha=1.0)
ridge.fit(X_train, y_train)
ridge_score = r2_score(y_test, ridge.predict(X_test))
print(f"Ridge R²: {ridge_score:.3f}")

# Regolarizzazione L1 (Lasso)
lasso = Lasso(alpha=0.1)
lasso.fit(X_train, y_train)
lasso_score = r2_score(y_test, lasso.predict(X_test))
print(f"Lasso R²: {lasso_score:.3f}")
print(f"Coefficienti non nulli di Lasso: {np.sum(lasso.coef_ != 0)}")

# Elastic Net (L1 + L2)
elastic = ElasticNet(alpha=0.1, l1_ratio=0.5)
elastic.fit(X_train, y_train)
elastic_score = r2_score(y_test, elastic.predict(X_test))
print(f"Elastic Net R²: {elastic_score:.3f}")

# Ottimizzazione degli iperparametri per alpha
from sklearn.model_selection import GridSearchCV

param_grid = {'alpha': [0.001, 0.01, 0.1, 1.0, 10.0]}
grid = GridSearchCV(Ridge(), param_grid, cv=5)
grid.fit(X_train, y_train)
print(f"\nAlpha migliore: {grid.best_params_['alpha']}")
print(f"Miglior punteggio CV: {grid.best_score_:.3f}")

Rarità: Molto Comune Difficoltà: Media


Addestramento e Distribuzione del Modello (5 Domande)

11. Come salvi e carichi i modelli in produzione?

Risposta: La serializzazione del modello consente la distribuzione e il riutilizzo.

import joblib
import pickle
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris

# Addestra il modello
X, y = load_iris(return_X_y=True)
model = RandomForestClassifier()
model.fit(X, y)

# Metodo 1: Joblib (consigliato per scikit-learn)
joblib.dump(model, 'model.joblib')
loaded_model = joblib.load('model.joblib')

# Metodo 2: Pickle
with open('model.pkl', 'wb') as f:
    pickle.dump(model, f)

with open('model.pkl', 'rb') as f:
    loaded_model = pickle.load(f)

# Per TensorFlow/Keras
import tensorflow as tf

# Salva l'intero modello
# keras_model.save('model.h5')
# loaded_model = tf.keras.models.load_model('model.h5')

# Salva solo i pesi
# keras_model.save_weights('model_weights.h5')
# new_model.load_weights('model_weights.h5')

# Per PyTorch
import torch

# Salva il dizionario di stato del modello
# torch.save(model.state_dict(), 'model.pth')
# model.load_state_dict(torch.load('model.pth'))

# Salva l'intero modello
# torch.save(model, 'model_complete.pth')
# model = torch.load('model_complete.pth')

# Controllo della versione per i modelli
import datetime

model_version = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
model_path = f'models/model_{model_version}.joblib'
joblib.dump(model, model_path)
print(f"Modello salvato in {model_path}")

Rarità: Molto Comune Difficoltà: Facile


12. Come crei un'API REST per il serving del modello?

Risposta: Le API REST rendono i modelli accessibili alle applicazioni.

from flask import Flask, request, jsonify
import joblib
import numpy as np

app = Flask(__name__)

# Carica il modello all'avvio
model = joblib.load('model.joblib')

@app.route('/predict', methods=['POST'])
def predict():
    try:
        # Ottieni i dati dalla richiesta
        data = request.get_json()
        features = np.array(data['features']).reshape(1, -1)
        
        # Effettua la predizione
        prediction = model.predict(features)
        probability = model.predict_proba(features)
        
        # Restituisci la risposta
        return jsonify({
            'prediction': int(prediction[0]),
            'probability': probability[0].tolist(),
            'status': 'success'
        })
    
    except Exception as e:
        return jsonify({
            'error': str(e),
            'status': 'error'
        }), 400

@app.route('/health', methods=['GET'])
def health():
    return jsonify({'status': 'healthy'})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

# Alternativa FastAPI (moderna, più veloce)
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI()

class PredictionRequest(BaseModel):
    features: list

class PredictionResponse(BaseModel):
    prediction: int
    probability: list
    status: str

@app.post("/predict", response_model=PredictionResponse)
async def predict(request: PredictionRequest):
    try:
        features = np.array(request.features).reshape(1, -1)
        prediction = model.predict(features)
        probability = model.predict_proba(features)
        
        return PredictionResponse(
            prediction=int(prediction[0]),
            probability=probability[0].tolist(),
            status="success"
        )
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))

# Utilizzo:
# curl -X POST "http://localhost:5000/predict" \
#      -H "Content-Type: application/json" \
#      -d '{"features": [5.1, 3.5, 1.4, 0.2]}'

Rarità: Molto Comune Difficoltà: Media


13. Cos'è Docker e perché è utile per la distribuzione di ML?

Risposta: I container Docker impacchettano le applicazioni con tutte le dipendenze.

  • Vantaggi:
    • Riproducibilità
    • Coerenza tra gli ambienti
    • Facile distribuzione
    • Isolamento
# Dockerfile per il modello di ML
FROM python:3.9-slim

WORKDIR /app

# Copia i requisiti
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copia il modello e il codice
COPY model.joblib .
COPY app.py .

# Espone la porta
EXPOSE 5000

# Esegui l'applicazione
CMD ["python", "app.py"]
# Costruisci l'immagine Docker
docker build -t ml-model:v1 .

# Esegui il container
docker run -p 5000:5000 ml-model:v1

# Docker Compose per configurazioni multi-container
# docker-compose.yml
version: '3.8'
services:
  model:
    build: .
    ports:
      - "5000:5000"
    environment:
      - MODEL_PATH=/app/model.joblib
    volumes:
      - ./models:/app/models

Rarità: Comune Difficoltà: Media


14. Come monitori le prestazioni del modello in produzione?

Risposta: Il monitoraggio rileva il degrado del modello e garantisce l'affidabilità.

  • Cosa monitorare:
    • Metriche di predizione: Accuracy, latenza
    • Data drift: Cambiamenti nella distribuzione degli input
    • Model drift: Degrado delle prestazioni
    • Metriche di sistema: CPU, memoria, errori
import logging
from datetime import datetime
import numpy as np

class ModelMonitor:
    def __init__(self, model):
        self.model = model
        self.predictions = []
        self.actuals = []
        self.latencies = []
        self.input_stats = []
        
        # Imposta il logging
        logging.basicConfig(
            filename='model_monitor.log',
            level=logging.INFO,
            format='%(asctime)s - %(message)s'
        )
    
    def predict(self, X):
        import time
        
        # Traccia le statistiche degli input
        self.input_stats.append({
            'mean': X.mean(),
            'std': X.std(),
            'min': X.min(),
            'max': X.max()
        })
        
        # Misura la latenza
        start = time.time()
        prediction = self.model.predict(X)
        latency = time.time() - start
        
        self.predictions.append(prediction)
        self.latencies.append(latency)
        
        # Registra la predizione
        logging.info(f"Predizione: {prediction}, Latenza: {latency:.3f}s")
        
        # Avvisa se la latenza è troppo alta
        if latency > 1.0:
            logging.warning(f"Alta latenza rilevata: {latency:.3f}s")
        
        return prediction
    
    def log_actual(self, y_true):
        self.actuals.append(y_true)
        
        # Calcola l'accuracy se abbiamo abbastanza dati
        if len(self.actuals) >= 100:
            accuracy = np.mean(
                np.array(self.predictions[-100:]) == np.array(self.actuals[-100:])
            )
            logging.info(f"Accuracy mobile (ultimi 100): {accuracy:.3f}")
            
            if accuracy < 0.8:
                logging.error(f"Prestazioni del modello degradate: {accuracy:.3f}")
    
    def check_data_drift(self, reference_stats):
        if not self.input_stats:
            return
        
        current_stats = self.input_stats[-1]
        
        # Semplice rilevamento del drift (confronta le medie)
        mean_diff = abs(current_stats['mean'] - reference_stats['mean'])
        if mean_diff > 2 * reference_stats['std']:
            logging.warning(f"Data drift rilevato: differenza media = {mean_diff:.3f}")

# Utilizzo
monitor = ModelMonitor(model)
prediction = monitor.predict(X_new)
# Successivamente, quando l'etichetta effettiva è disponibile
monitor.log_actual(y_true)

Rarità: Comune Difficoltà: Media


15. Cos'è CI/CD per il machine learning?

Risposta: CI/CD automatizza il test e la distribuzione dei modelli di ML.

  • Integrazione Continua:
    • Test automatizzati
    • Controlli di qualità del codice
    • Convalida del modello
  • **Distribuzione Contin
Newsletter subscription

Consigli di carriera settimanali che funzionano davvero

Ricevi le ultime idee direttamente nella tua casella di posta

Decorative doodle

Il Tuo Prossimo Colloquio Dista Solo un Curriculum

Crea un curriculum professionale e ottimizzato in pochi minuti. Non servono competenze di design—solo risultati comprovati.

Crea il mio curriculum

Condividi questo post

Batti il Tasso di Rifiuto ATS del 75%

3 curriculum su 4 non raggiungono mai un occhio umano. La nostra ottimizzazione delle parole chiave aumenta il tuo tasso di successo fino all'80%, assicurando che i reclutatori vedano effettivamente il tuo potenziale.