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
- Mapa de l'ecosistema per tasca
- ML clàssic: scikit-learn i les llibreries de boosting
- Deep learning: PyTorch, TensorFlow/Keras i JAX
- El mateix MLP en PyTorch i en Keras, l'un al costat de l'altre
- PLN i models preentrenats: Hugging Face, spaCy, NLTK
- Visió: OpenCV, torchvision, Pillow
- IA generativa i LLM: SDK, Ollama, LangChain i LlamaIndex
- Lògica, probabilitat i optimització
- Experimentació, MLOps i dades a escala
- Desplegament: FastAPI i ONNX Runtime
- Criteris per triar una llibreria
- Codi: desar i recarregar el pipeline i l'MLP
- La caixa d'eines de NovaMarket
- Errors Comuns i Consells
- Exercicis
- Conclusió
- 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ó) |
- 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 afegeixenpredictipredict_proba; els transformadors hi afegeixentransform(ifit_transform). Tant és que siguiLogisticRegression,RandomForestClassifier,StandardScaleroOneHotEncoder: mateixa forma. - L'estat après es desa en atributs amb guió baix final:
esc = StandardScaler().fit([[1.0], [2.0], [3.0]])deixaesc.mean_ = [2.]iesc.scale_ = [0.816], iesc.transform([[4.0]])retorna[[2.449]]. Per això un transformador ajustat amb entrenament aplica les mateixes mitjanes en producció (04-03). Pipelineencadena transformadors i un predictor final;ColumnTransformeraplica transformadors diferents a columnes diferents (les nostres 21 columnes de 04-03);GridSearchCV/RandomizedSearchCVicross_val_scoreseleccionen models (04-06);sklearn.metricsmesura (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]
- 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ó ibackward()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.graddonatensor(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.Modulei es defineixforward.DatasetiDataLoader: 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.
- 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.Linear ↔ layers.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.
- 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 ambpipelineen tres línies, o s'afinen (fine-tuning) ambTrainer.datasetscarrega conjunts de dades públics amb el mateix estil;tokenizersfa 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.
- 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,
timmper a més arquitectures,albumentationsper a augments.
- 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.
- Lògica, probabilitat i optimització
- Regles: a 06-02 vam escriure el motor
Regla/MotorInferenciaen 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:
pgmpyimplementa les xarxes bayesianes de 06-03 amb inferència eficient (eliminació de variables, no enumeració) i aprenentatge de paràmetres des de dades;pomegranatei 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
optimizecobreixen 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.
- 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.csvsó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.
- 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, ajustableUna 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).
- 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.
- 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.dumpserialitza 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, inp.allcloseconfirma 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 alSequential. Per carregar-lo cal reconstruir la mateixa arquitectura amb el mateix codi i desprésload_state_dict; per aixòcrear_mlpva 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
prepa part ambjoblibi 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.loadde fitxers aliens té el risc de pickle de 07-01; per compartir pesos amb tercers existeix el format safetensors.
- 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
Pipelinecomplet; amb PyTorch, desa l'state_dicti el transformador, i el codi de l'arquitectura en un mòdul versionat. - Carregar un
state_dicten 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__itorch.__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
- Conceptes Fonamentals: Agents, Entorns i Racionalitat
- Tipus d'Intel·ligència Artificial
- Les Dades com a Matèria Primera de la IA
- Ètica i Consideracions en IA
Mòdul 3: Algorismes en IA
- Introducció als Algorismes
- Algorismes de Cerca
- Cerca amb Adversari: Jocs i Minimax
- Algorismes d'Optimització
Mòdul 4: Aprenentatge Automàtic (Machine Learning)
- Conceptes Bàsics de Machine Learning
- Tipus d'Aprenentatge Automàtic
- Preparació de Dades i Característiques
- Algorismes de Machine Learning
- Avaluació i Validació de Models
- Sobreajust, Regularització i Ajust d'Hiperparàmetres
Mòdul 5: Xarxes Neuronals i Deep Learning
- Introducció a les Xarxes Neuronals
- Arquitectura de Xarxes Neuronals
- Com Aprèn una Xarxa: Descens del Gradient i Retropropagació
- Deep Learning i les seves Aplicacions
- Transformers, Grans Models de Llenguatge i IA Generativa
Mòdul 6: Lògica i Sistemes Experts
- Lògica en IA
- Sistemes Experts
- Raonament amb Incertesa: Probabilitat i Xarxes Bayesianes
- Aplicacions dels Sistemes Experts
Mòdul 7: Eines i Llenguatges de Programació en IA
- Llenguatges de Programació per a IA
- Python Científic: NumPy, pandas i Matplotlib
- Eines i Llibreries Populars
- Entorns de Desenvolupament
Mòdul 8: Projectes i Casos d'Estudi
Mòdul 9: Exercicis i Pràctiques
- Exercicis d'Algorismes
- Pràctiques de Machine Learning
- Projectes de Xarxes Neuronals
- Projecte Integrador: de la Idea al Prototip
