Amb Python triat (07-01) i NumPy, pandas i Matplotlib a la mà (07-02), el taller de la Marta necessita la resta de la prestatgeria: les llibreries amb què es fa cadascuna de les tasques que hem recorregut al curs. Algunes ja les hem fet servir a fons (scikit-learn al mòdul 4, PyTorch al 5), d'altres les hem citat sense instal·lar (Hugging Face, experta, pgmpy, OR-Tools) i d'altres apareixen per primera vegada perquè pertanyen al que ve després d'entrenar: desar el model, servir-lo, seguir-ne les versions. Aquesta lliçó dibuixa el mapa de l'ecosistema per tasca (ML clàssic, deep learning, PLN, visió, IA generativa, lògica i probabilitat, optimització, experimentació, desplegament, dades a escala), mostra el mateix MLP 21→16→8→1 de 05-02 escrit en PyTorch i en Keras l'un al costat de l'altre perquè vegis que les idees són les mateixes amb una sintaxi diferent, dona criteris per triar llibreria, i executa el que enllaça l'entrenament amb el desplegament: desar i recarregar el pipeline de scikit-learn amb joblib i l'MLP amb state_dict, comprovant que prediuen exactament el mateix. Tanca amb la caixa d'eines de NovaMarket: quina llibreria per a cadascun dels nou casos d'ús. És important perquè l'ecosistema és enorme i canvia de pressa; el que no canvia és el mapa de tasques i els criteris. Com s'instal·len aquestes llibreries, en quin entorn i amb quines versions és assumpte de 07-04.

Contingut

  1. Mapa de l'ecosistema per tasca
  2. ML clàssic: scikit-learn i les llibreries de boosting
  3. Deep learning: PyTorch, TensorFlow/Keras i JAX
  4. El mateix MLP en PyTorch i en Keras, l'un al costat de l'altre
  5. PLN i models preentrenats: Hugging Face, spaCy, NLTK
  6. Visió: OpenCV, torchvision, Pillow
  7. IA generativa i LLM: SDK, Ollama, LangChain i LlamaIndex
  8. Lògica, probabilitat i optimització
  9. Experimentació, MLOps i dades a escala
  10. Desplegament: FastAPI i ONNX Runtime
  11. Criteris per triar una llibreria
  12. Codi: desar i recarregar el pipeline i l'MLP
  13. La caixa d'eines de NovaMarket
  14. Errors Comuns i Consells
  15. Exercicis
  16. Conclusió

  1. Mapa de l'ecosistema per tasca

Tot es recolza en la capa de 07-02 i s'organitza pel que vols fer, no per moda:

flowchart TB
    subgraph BASE["Base: 07-02"]
        NP[NumPy] --- PD[pandas] --- MPL[Matplotlib / seaborn]
    end
    subgraph APRENDRE["Aprendre de dades"]
        SK["scikit-learn<br/>ML clàssic"]
        XGB[XGBoost / LightGBM / CatBoost<br/>boosting]
        PT[PyTorch]
        TF[TensorFlow / Keras]
        JAX[JAX]
    end
    subgraph DOMINI["Per domini"]
        HF["Hugging Face transformers / datasets<br/>spaCy, NLTK: text"]
        CV["OpenCV, torchvision, Pillow: imatge"]
        LLM[SDK de LLM, Ollama<br/>LangChain, LlamaIndex]
        LOG["experta, pgmpy<br/>Drools, CLIPS: regles i probabilitat"]
        OPT["OR-Tools, PuLP: optimització"]
    end
    subgraph OPERAR["Operar"]
        MLF["MLflow, W&B, DVC: experiments"]
        API["FastAPI, ONNX Runtime: servir"]
        BIG["Spark, Dask, Polars: escala"]
    end
    BASE --> APRENDRE --> DOMINI --> OPERAR
    PT --> HF
    PT --> CV
Tasca Llibreries principals On la vam veure
ML clàssic (taules) scikit-learn; XGBoost, LightGBM, CatBoost Mòdul 4
Deep learning PyTorch; TensorFlow/Keras; JAX Mòdul 5
Text i models preentrenats Hugging Face transformers/datasets, spaCy, NLTK 05-05 (ressenyes, cas 4)
Imatge OpenCV, torchvision, Pillow 05-04 (fotos d'incidències)
IA generativa i LLM SDK de proveïdors, Ollama, LangChain, LlamaIndex 05-05 (assistent, cas 7)
Regles i probabilitat experta, pgmpy; Drools, CLIPS Mòdul 6 (casos 8 i 9)
Optimització OR-Tools, PuLP, SciPy 03-04 (rutes i assignació, casos 5 i 6)
Experiments i MLOps MLflow, Weights & Biases, DVC Nou (menció)
Desplegament FastAPI, ONNX Runtime 07-01 (entrenar en Python, servir en X)
Dades a escala Spark, Dask, Polars Nou (menció)

  1. ML clàssic: scikit-learn i les llibreries de boosting

scikit-learn és la llibreria del mòdul 4 i la més important d'aprendre bé, perquè la seva API uniforme l'han copiat gairebé totes les altres:

  • Tot objecte és un estimador amb fit(X, y); els predictors hi afegeixen predict i predict_proba; els transformadors hi afegeixen transform (i fit_transform). Tant és que sigui LogisticRegression, RandomForestClassifier, StandardScaler o OneHotEncoder: mateixa forma.
  • L'estat après es desa en atributs amb guió baix final: esc = StandardScaler().fit([[1.0], [2.0], [3.0]]) deixa esc.mean_ = [2.] i esc.scale_ = [0.816], i esc.transform([[4.0]]) retorna [[2.449]]. Per això un transformador ajustat amb entrenament aplica les mateixes mitjanes en producció (04-03).
  • Pipeline encadena transformadors i un predictor final; ColumnTransformer aplica transformadors diferents a columnes diferents (les nostres 21 columnes de 04-03); GridSearchCV/RandomizedSearchCV i cross_val_score seleccionen models (04-06); sklearn.metrics mesura (04-05). El pipeline sencer es desa i se serveix com una sola peça (secció 12).

El que scikit-learn no cobreix bé és el gradient boosting de màxim rendiment sobre taules grans, i aquí hi ha tres llibreries amb API compatible (fit/predict, i es poden ficar en un Pipeline): XGBoost (la clàssica de les competicions), LightGBM (més ràpida i lleugera en memòria, de Microsoft) i CatBoost (gestiona categòriques sense one-hot, de Yandex). Per al predictor de devolucions de NovaMarket, després de la regressió logística (AUC 0,844) i el bosc de 04-04, un boosting seria el candidat natural per gratar unes centèsimes; scikit-learn inclou HistGradientBoostingClassifier, una implementació pròpia del mateix tipus que n'hi ha prou per començar sense instal·lar res. Cap de les tres no és a l'entorn del curs; el seu codi és idèntic en forma:

# Il·lustratiu (XGBoost no està instal·lat a l'entorn del curs): la mateixa API que scikit-learn
from xgboost import XGBClassifier
model = XGBClassifier(n_estimators=300, max_depth=4, learning_rate=0.05)
model.fit(X_train, y_train)
prob = model.predict_proba(X_test)[:, 1]

  1. Deep learning: PyTorch, TensorFlow/Keras i JAX

PyTorch és la llibreria del mòdul 5 i avui la dominant en recerca i en la majoria de projectes nous. Les seves quatre peces, que ja has manejat:

  • Tensors: arrays amb dtype, forma i broadcasting com NumPy (07-02), que poden viure en GPU (.to("cuda")).
  • Autograd: si un tensor té requires_grad=True, PyTorch anota cada operació i backward() calcula els gradients (05-03). Exemple mínim executable: x = torch.tensor(2.0, requires_grad=True); y = x**2 + 3*x; y.backward(); x.grad dona tensor(7.), la derivada 2x + 3 en x = 2.
  • nn.Module: la classe base de capes i xarxes. nn.Sequential (05-02) és la manera ràpida; per a arquitectures amb branques s'hereta d'nn.Module i es defineix forward.
  • Dataset i DataLoader: serveixen les dades en lots barrejats (DataLoader(TensorDataset(X, y), batch_size=32, shuffle=True) produeix lots de forma (32, 21); amb 100 files en surten 4).

TensorFlow (Google) va ser l'estàndard de 2016-2019 i continua molt present en producció i en mòbils (TensorFlow Lite); la seva API d'alt nivell és Keras, que des de la versió 3 pot executar-se sobre TensorFlow, PyTorch o JAX. Keras és la més amable per començar (Sequential, compile, fit) i amaga el bucle d'entrenament que en PyTorch escrius a mà. JAX (Google) és NumPy amb diferenciació automàtica i compilació XLA, favorit en recerca de gran escala; a sobre hi viuen Flax i Optax. L'elecció pràctica avui: PyTorch per comunitat, exemples i models preentrenats; Keras si valores la concisió o vens de TensorFlow; JAX per a recerca numèrica avançada.

  1. El mateix MLP en PyTorch i en Keras, l'un al costat de l'altre

La xarxa 21→16→8→1 de 05-02 (497 paràmetres) escrita a les dues llibreries. La versió PyTorch s'executa a l'entorn del curs; la de Keras és il·lustrativa (mateixa arquitectura, sense sortides inventades):

# PyTorch (executable): l'arquitectura, i el bucle d'entrenament explícit
import torch, torch.nn as nn

class MLP(nn.Module):
    def __init__(self):
        super().__init__()
        self.capes = nn.Sequential(nn.Linear(21, 16), nn.ReLU(),
                                   nn.Linear(16, 8), nn.ReLU(),
                                   nn.Linear(8, 1))          # logit; la sigmoide va a la pèrdua
    def forward(self, x):
        return self.capes(x)

xarxa = MLP()
print(sum(p.numel() for p in xarxa.parameters()))          # 497
optimitzador = torch.optim.Adam(xarxa.parameters(), lr=0.001)
perdua = nn.BCEWithLogitsLoss()
# for xb, yb in carregador:                                 # el bucle de 05-03
#     optimitzador.zero_grad(); L = perdua(xarxa(xb), yb); L.backward(); optimitzador.step()
# Keras (il·lustratiu, no instal·lat a l'entorn del curs): mateixa arquitectura, bucle amagat dins de fit
from tensorflow import keras
from keras import layers

xarxa = keras.Sequential([
    layers.Input(shape=(21,)),
    layers.Dense(16, activation="relu"),
    layers.Dense(8, activation="relu"),
    layers.Dense(1, activation="sigmoid"),                   # aquí sí: la sigmoide a la capa
])
xarxa.summary()                                              # taula de capes i paràmetres (497)
xarxa.compile(optimizer=keras.optimizers.Adam(learning_rate=0.001),
              loss="binary_crossentropy", metrics=["AUC"])
# xarxa.fit(X_train, y_train, epochs=100, batch_size=64, validation_split=0.2)
# xarxa.predict(X_test)

Correspondències: nn.Linearlayers.Dense; l'activació en PyTorch és un mòdul a part i en Keras un argument; en PyTorch la sigmoide l'aplica BCEWithLogitsLoss per estabilitat numèrica i en Keras sol anar a la capa amb binary_crossentropy; el bucle zero_grad / backward / step de 05-03 és el que compile + fit fan per tu. Si n'entens una, llegeixes l'altra en deu minuts: la dificultat del deep learning és a 05-03, no a la sintaxi.

  1. PLN i models preentrenats: Hugging Face, spaCy, NLTK

  • Hugging Face transformers és la porta als models preentrenats de 05-05: milers de models (BERT, RoBERTa, T5, Llama, Whisper...) que es descarreguen per nom i es fan servir amb pipeline en tres línies, o s'afinen (fine-tuning) amb Trainer. datasets carrega conjunts de dades públics amb el mateix estil; tokenizers fa la tokenització ràpida (escrita en Rust, 07-01). El Hub de Hugging Face és el repositori on es comparteixen models i dades, amb llicències i fitxes de model (model cards, recorda 02-04).
  • spaCy: PLN "industrial": tokenització, lematització, entitats anomenades, anàlisi sintàctica, amb models per idioma (inclosos el català i el castellà) i velocitat de producció. Ideal per extreure de les ressenyes de NovaMarket quin producte i quin component s'esmenta.
  • NLTK: la llibreria acadèmica clàssica (corpus, stemming, n-grames); útil per aprendre, menys per produir.
# Il·lustratiu (transformers no està instal·lat a l'entorn del curs): classificar ressenyes (cas 4)
from transformers import pipeline
classificador = pipeline("sentiment-analysis",
                         model="nlptown/bert-base-multilingual-uncased-sentiment")   # model multilingüe d'1-5 estrelles
classificador(["Els auriculars NovaSound sonen de meravella, molt còmodes.",
               "La cafetera NovaBrew va arribar amb la gerra trencada i ningú no respon."])
# retorna una llista de diccionaris amb 'label' (estrelles) i 'score' (confiança); no en reproduïm xifres aquí

Darrere de pipeline hi ha la mateixa cadena de 05-05: tokenitzador, model transformer en PyTorch, capa de classificació. I darrere de tot, tensors.

  1. Visió: OpenCV, torchvision, Pillow

  • Pillow (PIL): obrir, redimensionar, retallar, convertir imatges. El bàsic.
  • OpenCV (cv2): la llibreria de visió per computador clàssica, escrita en C++: filtres, detecció de vores, contorns, calibratge de càmeres, vídeo en temps real. Preprocessa les fotos d'incidències abans que les vegi una CNN, o resol tota sola tasques simples (llegir un codi de barres, mesurar una caixa).
  • torchvision: models preentrenats de visió (ResNet, EfficientNet, ViT...), transformacions d'augment de dades i conjunts de dades estàndard, integrats amb PyTorch. La CNN de 05-04 sobre fotos sintètiques 16×16 (98,7 %) es convertiria en producció en un model de torchvision afinat sobre fotos reals dels magatzems de Zaragoza i Getafe.
  • A sobre: Ultralytics (YOLO) per a detecció d'objectes, timm per a més arquitectures, albumentations per a augments.

  1. IA generativa i LLM: SDK, Ollama, LangChain i LlamaIndex

Per a l'assistent de NovaMarket (cas 7) i les crides a LLM amb RAG que vam esbossar a 05-05, la caixa té tres prestatges, i convé mirar-los amb to neutral perquè és el racó que canvia més de pressa:

  • SDK de proveïdors: cada proveïdor de LLM ofereix una llibreria Python que envia missatges a la seva API i retorna text (o crides a eines, o embeddings). El patró és sempre el mateix: client, llista de missatges amb rols, resposta. El seu cost és per token i les seves dades surten de l'empresa: revisa 02-04 i l'RGPD abans d'enviar ressenyes o dades de clients.
  • Models locals: Ollama (i llama.cpp, vLLM per servir a escala) executen models oberts a la teva màquina o servidor, amb una API compatible amb la dels proveïdors. Menys capacitat, més privacitat, cost fix: l'argument que el Diego voldrà sentir per a l'assistent intern.
  • Orquestració: LangChain i LlamaIndex encadenen passos (recuperar documents, construir el prompt, cridar el model, analitzar la resposta, cridar eines): són la manera estàndard de muntar el RAG de 05-05 sobre la base de coneixement de NovaMarket (polítiques de devolució, fitxes de producte, incidències resoltes) i agents que executen accions. Es recolzen en bases de dades vectorials (FAISS, Chroma, pgvector...) per cercar per similitud d'embeddings. Són útils, canvien d'API amb freqüència i afegeixen una capa que de vegades convé substituir per cent línies pròpies quan el flux és simple.

  1. Lògica, probabilitat i optimització

  • Regles: a 06-02 vam escriure el motor Regla/MotorInferencia en Python pur. Els seus equivalents amb més múscul: experta (motor de regles en Python hereu de CLIPS, amb fets i regles com a classes decorades), CLIPS (el motor clàssic en C, amb enllaços a Python) i Drools (Java, l'estàndard empresarial, amb taules de decisió DMN com les de 06-04). Per a les devolucions (cas 8) el motor propi n'hi ha prou mentre les regles siguin desenes; a partir de centenars, i amb usuaris de negoci que les vulguin editar, un BRMS.
  • Probabilitat: pgmpy implementa les xarxes bayesianes de 06-03 amb inferència eficient (eliminació de variables, no enumeració) i aprenentatge de paràmetres des de dades; pomegranate i PyMC (inferència bayesiana general) són alternatives. Per al diagnòstic d'incidències (cas 9), pgmpy és el substitut natural de la nostra xarxa per enumeració.
  • Optimització: OR-Tools (Google) resol rutes de vehicles, assignació i planificació amb restriccions (els casos 5 i 6 de 03-04, on vam fer servir cerca local pròpia); PuLP i SciPy optimize cobreixen programació lineal i optimització numèrica. Per a la furgoneta de NovaMarket, OR-Tools donaria en segons rutes que la nostra cerca local de 03-04 només aproximava.

  1. Experimentació, MLOps i dades a escala

Quan passes d'un notebook a un projecte (07-04 i mòdul 8), apareixen preguntes noves: quins hiperparàmetres van donar AUC 0,844? Amb quines dades? Quin model és en producció? Eines, només per situar-les:

  • MLflow: registra paràmetres, mètriques i artefactes de cada execució (mlflow.log_param, log_metric, log_model) i té un registre de models amb versions i etapes; s'instal·la en local. Weights & Biases (W&B) fa el mateix com a servei amb panells rics. DVC versiona dades i models grans juntament amb Git (07-04).
  • Dades a escala: quan comandes.csv són 3.000 comandes/dia × anys i no cap en memòria, Polars (DataFrame en Rust, API semblant a pandas, molt més ràpid en una màquina), Dask (pandas distribuït) i Spark/PySpark (l'estàndard en clusters, 07-01). La regla: pandas fins a uns pocs gigabytes; després, Polars en una màquina; Spark quan hi ha cluster i equip de dades.

  1. Desplegament: FastAPI i ONNX Runtime

Servir el predictor de devolucions vol dir que el web de NovaMarket enviï les dades d'una comanda i rebi una probabilitat. FastAPI és la llibreria Python més usada per a això: defineixes la forma de la petició amb Pydantic, escrius una funció, i obtens una API HTTP amb documentació automàtica. Exemple il·lustratiu (FastAPI no està instal·lat a l'entorn del curs; el model el carreguem amb joblib com a la secció 12):

# Il·lustratiu: servidor de predicció de devolucions (fitxer servir.py; es llança amb `uvicorn servir:app`)
from fastapi import FastAPI
from pydantic import BaseModel
import joblib, pandas as pd

app = FastAPI(title="NovaMarket - risc de devolució")
model = joblib.load("models/model_devolucions.joblib")      # es carrega UNA vegada en arrencar

class Comanda(BaseModel):                                    # esquema d'entrada, validat automàticament
    import_comanda: float
    num_articles: int
    dies_lliurament: float
    client_nou: int
    categoria: str
    codi_postal_zona: str
    metode_pagament: str
    tipus_enviament: str
    dia_setmana: int
    mes: int
    cap_de_setmana: int
    dies_des_inici: int
    import_per_article: float
    comandes_previes: int
    taxa_devolucio_previa: float

@app.post("/risc-devolucio")
def risc(comanda: Comanda):
    X = pd.DataFrame([comanda.model_dump()])                 # una fila amb les mateixes 15 columnes de l'entrenament
    prob = float(model.predict_proba(X)[0, 1])
    return {"probabilitat_devolucio": round(prob, 3),
            "revisar": prob > 0.5}                           # el llindar de 04-05, ajustable

Una petició POST /risc-devolucio amb un JSON de comanda retorna una cosa com {"probabilitat_devolucio": 0.048, "revisar": false}. ONNX Runtime és l'alternativa quan el consumidor no és Python (07-01): s'exporta el model a .onnx (amb torch.onnx.export o skl2onnx) i s'executa des de Java, C++ o el navegador. Altres peces del desplegament, per al mapa: BentoML i TorchServe (servidors de models), Streamlit i Gradio (interfícies ràpides per a demos), Docker (07-04).

  1. Criteris per triar una llibreria

Criteri Pregunta que cal fer-se Exemple
Maduresa i manteniment Quants anys porta? Continua rebent versions? Hi ha un equip o una fundació al darrere? scikit-learn, NumPy i PyTorch són apostes segures; una llibreria de RAG amb sis mesos, no tant
Comunitat i documentació Trobes respostes als errors? Hi ha tutorials, llibres, cursos? La comunitat de PyTorch i Hugging Face és una raó per triar-los
Llicència Permet ús comercial? Obliga a alliberar el teu codi? (MIT, BSD, Apache: permissives; GPL: copyleft; llicències de models: revisar) Gairebé tot l'ecosistema Python és BSD/MIT/Apache; alguns models preentrenats restringeixen usos
Rendiment Aguanta el teu volum? Fa servir GPU? El nucli és en C/C++/Rust? LightGBM davant d'un bosc de scikit-learn en 10 milions de files; Polars davant de pandas
Integració Encaixa amb el que ja tens (pandas, scikit-learn, el teu servidor, el teu format de model)? XGBoost dins d'un Pipeline; ONNX per a Java
Cost i dependència És gratis? Et lliga a un proveïdor (núvol, API de LLM)? En pots sortir? Model local amb Ollama davant d'API de pagament
Adequació al problema Resol la teva tasca o la forces? Regles per a la política de devolucions (06-04), no una xarxa neuronal

I un criteri de sentit comú que el Diego aplaudiria: la millor llibreria és la més simple que resol el problema i amb la qual l'equip ja sap treballar.

  1. Codi: desar i recarregar el pipeline i l'MLP

El que uneix entrenament i desplegament és un fitxer. Entrenem el predictor de devolucions de 04-03/04-04 (regressió logística sobre el ColumnTransformer de 21 columnes) i l'MLP de 05-02, desem tots dos, els recarreguem com faria el servidor i comprovem que les prediccions són idèntiques:

import numpy as np, joblib, torch, torch.nn as nn
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score
from novamarket_ml import generar_comandes_ml, embrutar_comandes, preparar_comandes, crear_preparacio

# Dades de 04-03: comandes embrutades i preparades (15 columnes -> 21 després del ColumnTransformer)
X, y = preparar_comandes(embrutar_comandes(generar_comandes_ml(3000, 42), 42))
Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.25, random_state=42, stratify=y)

# --- 1) scikit-learn: pipeline complet i joblib
pipe = Pipeline([("prep", crear_preparacio()),
                 ("model", LogisticRegression(max_iter=1000))]).fit(Xtr, ytr)
prob_abans = pipe.predict_proba(Xte)[:, 1]
print(f"AUC test regressió logística: {roc_auc_score(yte, prob_abans):.3f}")

joblib.dump(pipe, "model_devolucions.joblib")             # desa TOT el pipeline (preparació + model)
pipe_carregat = joblib.load("model_devolucions.joblib")   # en un altre procés, un altre dia, el servidor
prob_despres = pipe_carregat.predict_proba(Xte)[:, 1]
print("iguals després de recarregar (sklearn):", np.allclose(prob_abans, prob_despres))

# --- 2) PyTorch: MLP 21->16->8->1 i state_dict
prep = crear_preparacio().fit(Xtr)                         # el preprocessament s'ajusta amb train...
Xtr_t = torch.tensor(prep.transform(Xtr), dtype=torch.float32)
Xte_t = torch.tensor(prep.transform(Xte), dtype=torch.float32)
ytr_t = torch.tensor(ytr.values, dtype=torch.float32).unsqueeze(1)

def crear_mlp():
    return nn.Sequential(nn.Linear(21, 16), nn.ReLU(),
                         nn.Linear(16, 8), nn.ReLU(),
                         nn.Linear(8, 1))

torch.manual_seed(42)
xarxa = crear_mlp()
opt = torch.optim.Adam(xarxa.parameters(), lr=0.003)
perdua = nn.BCEWithLogitsLoss()
for epoca in range(150):                                   # entrenament breu, lot complet
    opt.zero_grad(); L = perdua(xarxa(Xtr_t), ytr_t); L.backward(); opt.step()
xarxa.eval()
with torch.no_grad():
    prob_xarxa = torch.sigmoid(xarxa(Xte_t)).numpy().ravel()
print(f"AUC test MLP: {roc_auc_score(yte, prob_xarxa):.3f}   paràmetres: {sum(p.numel() for p in xarxa.parameters())}")

torch.save(xarxa.state_dict(), "mlp_devolucions.pt")       # només els pesos
joblib.dump(prep, "preparacio.joblib")                     # ...i es desa a part: l'MLP no l'inclou
print(list(xarxa.state_dict().keys()))

xarxa2 = crear_mlp()                                       # mateixa arquitectura, pesos aleatoris nous
xarxa2.load_state_dict(torch.load("mlp_devolucions.pt"))   # se sobreescriuen amb els desats
xarxa2.eval()
prep2 = joblib.load("preparacio.joblib")
with torch.no_grad():
    prob_xarxa2 = torch.sigmoid(xarxa2(torch.tensor(prep2.transform(Xte), dtype=torch.float32))).numpy().ravel()
print("iguals després de recarregar (PyTorch):", np.allclose(prob_xarxa, prob_xarxa2))

# --- 3) una comanda nova, tal com arribaria a l'API de la secció 10
nou = Xte.iloc[[0]]                                        # DataFrame d'una fila (doble claudàtor)
print(pipe_carregat.predict_proba(nou)[0, 1].round(3),
      torch.sigmoid(xarxa2(torch.tensor(prep2.transform(nou), dtype=torch.float32))).item())

Sortida:

AUC test regressió logística: 0.844
iguals després de recarregar (sklearn): True
AUC test MLP: 0.841   paràmetres: 497
['0.weight', '0.bias', '2.weight', '2.bias', '4.weight', '4.bias']
iguals després de recarregar (PyTorch): True
0.048 0.0756823718547821

Explicació:

  • L'AUC 0,844 és exactament el de 04-04: mateixes dades, mateixa llavor, mateix pipeline. joblib.dump serialitza l'objecte sencer (imputadors amb les seves medianes, escalador amb les seves mitjanes, one-hot amb les seves categories, regressió amb els seus coeficients) en un fitxer d'uns 7 KB, i np.allclose confirma que les 750 probabilitats coincideixen després de recarregar. Això és el que carrega el servidor FastAPI de la secció 10.
  • En PyTorch desem només l'state_dict: un diccionari ordenat de tensors les claus del qual (0.weight, 0.bias...) són les posicions de les capes al Sequential. Per carregar-lo cal reconstruir la mateixa arquitectura amb el mateix codi i després load_state_dict; per això crear_mlp va en un mòdul compartit (07-04). El fitxer pesa uns 5 KB (497 floats més metadades).
  • El detall que més desplegaments trenca: la xarxa no inclou el preprocessament. Desem prep a part amb joblib i l'apliquem abans de la xarxa; si en producció s'escalés amb altres mitjanes, les prediccions serien diferents sense que res donés error. El pipeline de scikit-learn evita aquest risc portant-ho tot a dins; amb PyTorch cal disciplinar-se.
  • Per a la mateixa comanda nova (portàtil de 73 € d'un client recurrent amb tres comandes prèvies sense devolucions), la regressió dona 4,8 % i l'MLP 7,6 %: models diferents, mateixa conclusió, baix risc. torch.load de fitxers aliens té el risc de pickle de 07-01; per compartir pesos amb tercers existeix el format safetensors.

  1. La caixa d'eines de NovaMarket

Cas d'ús Tasca Llibreria triada Alternatives / evolució
1 Recomanació Filtratge col·laboratiu, similitud scikit-learn (veïns, factorització) + pandas implicit, models de dues torres en PyTorch
2 Previsió de demanda Sèries temporals pandas (resample, rolling) + scikit-learn (regressió amb retards) statsmodels, Prophet, sktime, xarxes recurrents
3 Frau / devolucions Classificació tabular scikit-learn Pipeline + regressió logística / bosc; HistGradientBoosting LightGBM/XGBoost, MLP en PyTorch
4 Ressenyes Classificació de text, extracció Hugging Face transformers (pipeline, afinat), spaCy scikit-learn amb TF-IDF com a base ràpida
5 Rutes Optimització combinatòria OR-Tools Cerca local pròpia (03-04), PuLP
6 Assignació Programació amb restriccions OR-Tools (CP-SAT) PuLP
7 Assistent LLM + RAG SDK de proveïdor o Ollama; LangChain o LlamaIndex; base vectorial Codi propi si el flux és simple
8 Regles de devolucions Motor de regles Motor propi de 06-02 experta, Drools amb DMN
9 Diagnòstic d'incidències Xarxa bayesiana pgmpy Motor de regles + probabilitats pròpies (06-03)
Transversal Desar i servir joblib, state_dict, FastAPI ONNX Runtime; MLflow per al registre
Transversal Dades pandas + SQL Polars si creix; Spark si hi ha cluster

Errors Comuns i Consells

  • Aprendre cinc llibreries alhora. Domina scikit-learn (API, Pipeline, validació) i una de deep learning; la resta s'aprèn en dies perquè copien la mateixa API.
  • Desar el model sense el preprocessament (o a l'inrevés): prediccions diferents en producció sense cap error. Amb scikit-learn, desa el Pipeline complet; amb PyTorch, desa l'state_dict i el transformador, i el codi de l'arquitectura en un mòdul versionat.
  • Carregar un state_dict en una arquitectura diferent: PyTorch falla amb un error de claus o de formes; si canvies la xarxa, reentrena o converteix explícitament.
  • Confiar que el fitxer carregarà sempre: pickle/joblib depenen de les versions (07-01, 07-04). Apunta sklearn.__version__ i torch.__version__ al costat del model i prova la recàrrega a l'entorn de destinació.
  • Instal·lar de tot "per si de cas": cada llibreria porta dependències i conflictes (07-04). Afegeix-ne quan la necessitis.
  • Triar per benchmark d'Internet: els rànquings canvien cada pocs mesos; els criteris de la secció 11 i una prova amb les teves dades, no.
  • Ficar un LLM on n'hi havia prou amb una regla o un groupby: cost, latència i opacitat (06-04). Comença pel que és simple.

Exercicis

Exercici 1. Substitueix a la secció 12 la regressió logística per HistGradientBoostingClassifier de scikit-learn (importa'l de sklearn.ensemble; deixa la resta del Pipeline igual). Entrena, mesura l'AUC de test, desa amb joblib, recarrega i comprova amb np.allclose que les prediccions coincideixen. Millora la regressió logística? Compara també la mida en bytes dels dos fitxers .joblib (os.path.getsize) i explica la diferència.

Exercici 2. Escriu (il·lustratiu, sense executar) l'MLP de la secció 4 amb una capa oculta més de 32 neurones i dropout 0,2 tant en PyTorch (nn.Module) com en Keras. Indica a cada versió on va el dropout i calcula el nombre de paràmetres de la nova xarxa 21→32→16→8→1.

Exercici 3. Omple la taula de la secció 13 per a un desè cas d'ús que la Marta proposa: detecció de productes danyats a les fotos que pugen els clients en obrir una incidència (cas 9 ampliat amb imatge). Indica tasca, llibreria triada, alternatives i quin format faries servir per servir el model si el desenvolupador Java vol integrar-lo a l'app d'incidències sense dependre d'un servei Python. Justifica-ho amb els criteris de la secció 11.

Solucions

Solució 1.

from sklearn.ensemble import HistGradientBoostingClassifier
import os
pipe_hgb = Pipeline([("prep", crear_preparacio()),
                     ("model", HistGradientBoostingClassifier(max_depth=4, learning_rate=0.05,
                                                             max_iter=200, random_state=42))]).fit(Xtr, ytr)
p1 = pipe_hgb.predict_proba(Xte)[:, 1]
print(f"AUC test HGB: {roc_auc_score(yte, p1):.3f}")
joblib.dump(pipe_hgb, "model_hgb.joblib")
p2 = joblib.load("model_hgb.joblib").predict_proba(Xte)[:, 1]
print(np.allclose(p1, p2), os.path.getsize("model_devolucions.joblib"), os.path.getsize("model_hgb.joblib"))

Sortida: AUC test HGB: 0.805, True 6650 337074. El boosting no millora aquí la regressió logística (0,805 davant de 0,844): amb 2.249 comandes i una "veritat oculta" gairebé lineal al logit (04-01), el model lineal és l'adequat, i el boosting amb 200 arbres de profunditat 4 sobreajusta (recorda 04-06: la complexitat cal guanyar-se-la amb dades; amb menys iteracions o early_stopping=True s'hi acostaria). Sí que serveix per comprovar que l'API és idèntica i que joblib desa qualsevol pipeline. El fitxer pesa unes 50 vegades més (uns 330 KB davant de 7 KB) perquè desa 200 arbres amb els seus llindars, davant de 21 coeficients i un biaix: la mida del model és un altre criteri de desplegament (memòria del servidor, temps de càrrega, dispositius).

Solució 2.

# PyTorch (il·lustratiu)
class MLP2(nn.Module):
    def __init__(self):
        super().__init__()
        self.capes = nn.Sequential(nn.Linear(21, 32), nn.ReLU(), nn.Dropout(0.2),
                                   nn.Linear(32, 16), nn.ReLU(), nn.Dropout(0.2),
                                   nn.Linear(16, 8), nn.ReLU(),
                                   nn.Linear(8, 1))
    def forward(self, x):
        return self.capes(x)
# Keras (il·lustratiu)
xarxa = keras.Sequential([layers.Input(shape=(21,)),
                          layers.Dense(32, activation="relu"), layers.Dropout(0.2),
                          layers.Dense(16, activation="relu"), layers.Dropout(0.2),
                          layers.Dense(8, activation="relu"),
                          layers.Dense(1, activation="sigmoid")])

El dropout va després de l'activació de la capa que es vol regularitzar, a totes dues llibreries com una capa/mòdul sense paràmetres; en PyTorch s'activa només en mode train() i es desactiva amb eval(), en Keras fit i predict ho gestionen sols. Paràmetres: (21 × 32 + 32) + (32 × 16 + 16) + (16 × 8 + 8) + (8 × 1 + 1) = 704 + 528 + 136 + 9 = 1.377, gairebé el triple que els 497 de la xarxa original, amb les mateixes 2.249 comandes: la raó d'afegir dropout.

Solució 3. Cas 10, "productes danyats en foto": tasca de classificació d'imatges (o detecció, si es vol localitzar el dany). Llibreria triada: PyTorch + torchvision amb un model preentrenat (una ResNet petita) afinat sobre les fotos etiquetades d'incidències, i Pillow/OpenCV per al preprocessament (redimensionar, normalitzar); és el que és madur, amb comunitat i models disponibles, llicència permissiva i rendiment en CPU acceptable per a unes desenes de fotos al dia. Alternatives: Keras amb la mateixa idea; Ultralytics/YOLO si cal localitzar el dany; un servei de visió d'un proveïdor al núvol si no hi ha prou fotos etiquetades (amb la cautela de 02-04 sobre enviar dades a fora). Per integrar-lo a l'app Java sense servei Python: exportar a ONNX amb torch.onnx.export i executar-lo amb ONNX Runtime per a Java (07-01); el preprocessament d'imatge es replica en Java (redimensionar i normalitzar és senzill, a diferència d'un ColumnTransformer). Criteris aplicats: maduresa i comunitat (PyTorch/torchvision), integració (ONNX per a Java), rendiment (CPU suficient per al volum de NovaMarket), cost (model local, sense API de pagament), adequació (les imatges reals demanen xarxa preentrenada, no la CNN sintètica de 05-04).

Conclusió

Hem dibuixat el mapa d'eines del taller de la Marta per tasques: scikit-learn i la seva API fit/predict/transform amb Pipeline per a l'ML clàssic, i les llibreries de boosting com a reforç; PyTorch (tensors, autograd, nn.Module, DataLoader) davant de TensorFlow/Keras i JAX, amb l'MLP 21→16→8→1 escrit a les dues sintaxis per comprovar que la idea és la mateixa; Hugging Face, spaCy i NLTK per al text; OpenCV, torchvision i Pillow per a la imatge; els SDK de LLM, Ollama i els orquestradors LangChain/LlamaIndex per a l'assistent amb RAG; experta, pgmpy i els motors de regles per a la meitat simbòlica; OR-Tools i PuLP per a l'optimització; MLflow, W&B i DVC per no perdre el rastre dels experiments; FastAPI i ONNX Runtime per servir; Spark, Dask i Polars quan les dades creixen. Hem fixat criteris per triar (maduresa, comunitat, llicència, rendiment, integració, cost, adequació) i hem executat la peça que uneix entrenament i desplegament: el pipeline de devolucions desat amb joblib (AUC 0,844) i l'MLP desat com a state_dict amb el seu preprocessament a part, tots dos recarregats amb prediccions idèntiques. La caixa d'eines de NovaMarket assigna una llibreria a cadascun dels nou casos d'ús.

Falta l'última peça del taller, i és la que evita que tot això s'ensorri el dia que un altre company intenti executar el codi: l'entorn. Quina versió de Python i de cada llibreria, en quin entorn virtual, amb quin fitxer de dependències; com fer servir Jupyter sense caure en les seves trampes; quin IDE; què versionar amb Git i què no; com passar dels scripts solts dels mòduls 3-6 a una estructura de projecte amb mòduls, un entrenar.py i tests; i quan cal GPU, núvol o Docker. És 07-04, Entorns de Desenvolupament, amb què tanquem el mòdul.

Fonaments d'Intel·ligència Artificial (IA)

Mòdul 1: Introducció a la Intel·ligència Artificial

Mòdul 2: Principis Bàsics de la IA

Mòdul 3: Algorismes en IA

Mòdul 4: Aprenentatge Automàtic (Machine Learning)

Mòdul 5: Xarxes Neuronals i Deep Learning

Mòdul 6: Lògica i Sistemes Experts

Mòdul 7: Eines i Llenguatges de Programació en IA

Mòdul 8: Projectes i Casos d'Estudi

Mòdul 9: Exercicis i Pràctiques

Mòdul 10: Recursos Addicionals

© Copyright 2026. Tots els drets reservats