Aprenentatge No Supervisat
- Tipologia
- Agrupament (clustering)
- Agrupació amb K-means
- Detecció d’anomalies (Anomaly Detection)
- Anàlisi de components principals
- Errors comuns a evitar
- Apèndix: Detecció d’anomalies (ampliació)
Imagina que tens milions de clients però cap etiqueta que et digui qui són. O que gestiones una fàbrica i vols detectar peces defectuoses sense haver vist mai abans cap defecte. O que vols visualitzar dades amb 50 variables en un simple gràfic 2D.
L’aprenentatge no supervisat resol aquests problemes: descobreix patrons en dades sense necessitat d’etiquetes prèvies. Mentre que l’aprenentatge supervisat aprèn d’exemples etiquetats (spam/no spam, gat/gos), l’aprenentatge no supervisat explora les dades per trobar estructures ocultes pel seu compte.
Tipologia
L’aprenentatge no supervisat treballa amb dades sense etiquetes, buscant patrons o estructures ocultes en les dades.
Aquests són alguns tipus d’aprenentatge no supervisat:
-
Clustering (Agrupació)
- Objectiu: Agrupar dades en dos o més grups segons similituds
- Exemple: Segmentar clients segons el seu comportament de compra
-
Detecció d’anomalies
- Objectiu: Identificar esdeveniments o transaccions inusuals
- Exemple: Detectar frau en transaccions bancàries
-
Reducció de dimensionalitat
- Objectiu: Comprimir dades utilitzant menys variables
- Exemple: Reduir la complexitat d’imatges mantenint la informació essencial
Agrupament (clustering)
Un algorisme d’agrupament analitza un conjunt de punts de dades i troba automàticament aquells que són semblants o relacionats entre si.
Vegem què significa això, tot contrastant l’agrupament —que és un mètode d’aprenentatge no supervisat— amb l’aprenentatge supervisat que ja havíem vist en la classificació binària.
Supervisat vs. No supervisat
Imaginem un conjunt de dades amb dues característiques, x1 i x2.
-
Aprenentatge supervisat: Disposem d’un conjunt d’entrenament amb les entrades x i les etiquetes corresponents y. Podem representar aquestes dades i ajustar-hi, per exemple, una regressió logística o una xarxa neuronal que aprengui una frontera de decisió entre les classes.
En aquest cas, el conjunt de dades conté (x,y):
-
Aprenentatge no supervisat:
Només tenim les entrades x, però no les etiquetes y.Quan representem el conjunt de dades, només veiem punts (●), sense colors ni símbols que ens indiquin quina classe correspon a cada punt.
Com que no tenim les etiquetes y, no podem dir a l’algorisme quina és la resposta correcta. En comptes d’això, demanem a l’algorisme que trobi estructura interessant en les dades.
L’algorisme pot identificar grups de punts similars i calcular el seu centroide: el punt central que representa la posició mitjana de tots els punts d’un grup.
Agrupament: trobar estructura
El primer algorisme d’aprenentatge no supervisat que s’acostuma a estudiar és l’agrupament (clustering).
Aquest mètode busca si el conjunt de dades pot ser dividit en grups (clústers) de punts que siguin semblants entre si.
Per exemple, un algorisme d’agrupament podria descobrir que les dades provenen de dos clústers diferenciats.
Aplicacions de l’agrupament
L’agrupament és àmpliament utilitzat en camps molt diversos:
-
Processament de textos i notícies:
Agrupar articles similars per temàtica (p. ex. notícies sobre pandes o sobre mercats financers). -
Segmentació de mercat:
Classificar els usuaris en grups segons objectius comuns: millorar competències, desenvolupar carrera professional, o estar al dia en IA. -
Biologia i genètica:
Analitzar dades d’expressió genètica i agrupar persones amb trets similars. -
Astronomia:
Agrupar cossos celestes per identificar galàxies o estructures coherents a l’espai.
En definitiva, l’agrupament ajuda a descobrir patrons ocults en dades sense necessitat de tenir les etiquetes.
Agrupació amb K-means
K-means és el mètode d’agrupament molt utilitzat. Explorarem com funciona i per què és tan comú en aplicacions pràctiques.
Suposem que tenim un conjunt de dades amb 30 exemples d’entrenament no etiquetats (30 punts en un gràfic). El que volem fer és executar K-means sobre aquest conjunt de dades.
El primer que fa K-means és escollir aleatòriament la posició inicial dels centroides dels clústers.
En aquest exemple demanarem a l’algorisme que trobi dos clústers.
Per tant, inicialment col·loca dues creus (una vermella i una blava) en posicions qualsevol.
Aquestes primeres posicions són només suposicions inicials i no tenen per què ser bones. Però serveixen com a punt de partida.
Una idea fonamental és que K-means repeteix dues operacions fins a convergir:
-
Assignació de punts als centroides
Per a cada punt del conjunt de dades, l’algorisme comprova si està més a prop del centroide vermell o del centroide blau i l’assigna al clúster corresponent.
-
Actualització dels centroides
Un cop cada punt està assignat a un clúster, K-means calcula la mitjana de les posicions dels punts de cada clúster:
- El centroide vermell es mou a la posició mitjana de tots els punts vermells.
- El centroide blau es mou a la posició mitjana de tots els punts blaus.
D’aquesta manera, els centroides es recol·loquen en posicions més representatives.
Aquest procés es repeteix fins que els punts ja no canvien de clúster i els centroides deixen de moure’s. Quan això passa, diem que K-means ha convergit.
Algorisme K-means
L’algorisme K-means es pot resumir així:
-
Inicialització: Col·locar aleatòriament K centroides (μ1,μ2,…,μK). Cada centroide té la mateixa dimensió que les dades.
-
Repetir fins a convergència:
- Assignació: Cada punt s’assigna al centroide més proper (usant distància euclidiana).
- Actualització: Cada centroide es mou a la mitjana dels punts que té assignats.
Exemple numèric pas a pas
Dades: 4 punts en 2D amb K=2 clústers: A=(1,1), B=(2,1), C=(4,3), D=(5,4).
Inicialització: Escollim aleatòriament μ1=(1,1) i μ2=(2,1) com a centroides inicials.
Iteració 1:
Assignació — Calculem la distància euclidiana de cada punt als dos centroides:
| Punt | Dist. a μ1 | Dist. a μ2 | Assignació |
|---|---|---|---|
| A(1,1) | 0 | 1 | Clúster 1 |
| B(2,1) | 1 | 0 | Clúster 2 |
| C(4,3) | 3.6 | 2.8 | Clúster 2 |
| D(5,4) | 5.0 | 4.2 | Clúster 2 |
Actualització — Recalculem els centroides com la mitjana dels punts assignats:
- μ1=A=(1,1)
- μ2=B+C+D3=(2,1)+(4,3)+(5,4)3=(3.67,2.67)
Iteració 2:
Assignació — Amb els nous centroides μ1=(1,1) i μ2=(3.67,2.67):
| Punt | Dist. a μ1 | Dist. a μ2 | Assignació |
|---|---|---|---|
| A(1,1) | 0 | 3.1 | Clúster 1 |
| B(2,1) | 1 | 2.4 | Clúster 1 |
| C(4,3) | 3.6 | 0.5 | Clúster 2 |
| D(5,4) | 5.0 | 1.9 | Clúster 2 |
Actualització — Recalculem els centroides:
- μ1=A+B2=(1,1)+(2,1)2=(1.5,1)
- μ2=C+D2=(4,3)+(5,4)2=(4.5,3.5)
Convergència: Si executem una tercera iteració, els punts no canvien de clúster. L’algorisme ha convergit amb resultat: A,B i C,D.
Casos especials
Si un cluster no té cap punt assignat, la mitjana no està definida.
Les opcions habituals són:
- Eliminar el cluster i treballar amb K−1 clusters.
- Reinicialitzar aleatòriament el centroide i esperar que rebi punts en la següent iteració.
Aplicació pràctica
K-means s’aplica sovint a dades que no estan clarament separades.
Exemple: disseny de samarretes petites, mitjanes i grans segons alçada i pes dels clients.
- Els punts poden variar contínuament sense clústers evidents.
- K-means amb K = 3 pot agrupar els punts en tres clústers representatius.
- Els centroides resultants indiquen les mesures més representatives per a cada talla.
K-means intenta optimitzar una funció de cost específica, i aquesta és la raó per la qual convergeix després de diverses iteracions.
Funció de Cost
K-means optimitza una funció de cost anomenada funció de distorsió:
J=1mm∑i=1|xi−μCi|2
Aquesta fórmula mesura la mitjana de la distància al quadrat entre cada punt i el centroide del seu clúster. Com més petita sigui J, més compactes són els clústers.
Per què funciona K-means?
- El pas d’assignació minimitza J respecte a les assignacions (cada punt va al centroide més proper).
- El pas d’actualització minimitza J respecte a les posicions dels centroides (la mitjana minimitza la suma de distàncies al quadrat).
Com que cada pas redueix o manté J, l’algoritme sempre convergeix.
Inicialització i mínims locals
Inicialització de centroides
El primer pas de K-means és escollir ubicacions inicials μ1,…,μK.
La manera més comuna és seleccionar aleatòriament (K) exemples del training set.
Important: K≤m, on m és el nombre d’exemples.
Problema de mínims locals
Diferents inicialitzacions poden produir clústers molt diferents, atrapats en mínims locals de la funció de cost J. La solució és executar múltiples inicialitzacions:
- Repetir K-means N vegades amb inicialitzacions aleatòries.
- Calcular la funció de cost J per cada execució.
- Seleccionar els centroides amb el menor cost.
Normalment N∈[50,1000]. Això millora l’agrupament i minimitza la distorsió.
Resumint:
- Escollir centroides inicials és crucial.
- Múltiples inicialitzacions redueixen el risc de mínims locals.
- Escollir el conjunt amb menor distorsió ((J)) dona un millor resultat final.
Selecció de K
L’algorisme K-means requereix com a entrada un paràmetre K, el nombre de clusters que volem identificar. Decidir el valor correcte de K és sovint ambigu, ja que diferents observadors poden interpretar les mateixes dades de manera diferent.
Mètode del colze
Una tècnica habitual és el mètode del colze:
- Executar K-means amb diversos valors de K (per exemple, de 1 a 10).
- Plotar la inèrcia J (eix Y) en funció de K (eix X).
A mesura que K augmenta, J disminueix (més centroides → punts més a prop del seu centroide). El colze és el punt on la corba deixa de baixar ràpidament i s’aplana. Minimitzar J directament no funciona, ja que J disminueix amb més clusters.
Selecció pràctica de K
La millor manera de triar K és segons l’ús final dels clusters.
Exemples:
- Talles de samarretes: K=3 → S, M, L; K=5 → XS, S, M, L, XL. La decisió depèn del trade-off entre ajust i costos de producció.
- Compressió d’imatges: K determina qualitat vs espai ocupat.
Resumint:
- Escollir K depèn del propòsit i no sempre té una resposta única.
- El mètode del colze és útil però no infal·lible.
- Comparar diferents K segons la utilitat dels clusters és la millor estratègia.
Implementació amb scikit-learn
from sklearn.cluster import KMeans
# Entrenar el model
kmeans = KMeans(n_clusters=3, random_state=42, n_init=10)
kmeans.fit(X)
# Resultats
labels = kmeans.labels_ # Assignació de cada punt
centers = kmeans.cluster_centers_ # Posició dels centroides
inertia = kmeans.inertia_ # Valor de J (funció de cost), també anomenat "inèrcia"
# Predir clúster per a noves dades
new_labels = kmeans.predict(X_new)
Per què “inèrcia”? El terme prové de la física: el moment d’inèrcia mesura com la massa es distribueix al voltant d’un eix de rotació. De manera anàloga, la inèrcia de K-means mesura com els punts es distribueixen al voltant dels seus centroides. Com més compactes són els clústers, menor és la inèrcia.
Paràmetres principals:
n_clusters: El nombre de clústers K que volem trobar.n_init: Nombre d’inicialitzacions aleatòries. L’algorisme s’executan_initvegades amb diferents centroides inicials i retorna el millor resultat (el que té menor J). Valors típics: 10-100 per problemes petits, fins a 1000 per problemes crítics.random_state: Llavor per reproduïbilitat dels resultats.
Resum K-means: Agrupa dades en K clústers basant-se en distàncies. Itera entre assignar punts i moure centroides fins convergir. Executa múltiples vegades amb inicialitzacions diferents. Utilitza el mètode del colze o el context del problema per escollir K.
Detecció d’anomalies (Anomaly Detection)
La detecció d’anomalies és una tècnica d’aprenentatge automàtic no supervisat que permet identificar observacions que difereixen significativament del comportament normal esperat. A diferència dels algoritmes supervisats, no necessitem grans quantitats d’exemples anòmals etiquetats per entrenar el model.
Per què és important?
En moltes aplicacions del món real, les anomalies són rares però crítiques:
- Fabricació de motors d’avió: Un motor defectuós pot tenir conseqüències catastròfiques. Necessitem detectar qualsevol comportament anòmal abans que l’avió voli.
- Detecció de frau financer: Les transaccions fraudulentes són poc freqüents (menys del 0.1% en targetes de crèdit), però cada frau no detectat representa pèrdues econòmiques.
- Monitoratge de servidors: Un servidor compromès o amb una fallada pot mostrar patrons de comportament inusuals en memòria, CPU o xarxa.
En tots aquests casos, tenim moltes dades de funcionament normal, però molt poques (o cap) mostra d’anomalies. Això fa que els mètodes tradicionals supervisats no siguin aplicables.
Aplicacions habituals
| Àmbit | Característiques analitzades | Objectiu |
|---|---|---|
| Frau financer | Import, ubicació, freqüència, hora, tipus de producte | Detectar transaccions sospitoses |
| Fabricació | Temperatura, vibracions, pressió, toleràncies dimensionals | Identificar unitats defectuoses |
| Ciberseguretat | Tràfic de xarxa, inicis de sessió, accés a fitxers | Detectar intrusions o comportaments maliciosos |
| Salut | Constants vitals, patrons d’activitat, valors analítiques | Alertar de situacions d’emergència |
Isolation Forest
Isolation Forest és l’algoritme més utilitzat actualment per a detecció d’anomalies en aplicacions reals. La seva idea central és molt intuïtiva: les anomalies són més fàcils d’aïllar que les observacions normals.
Imaginem que tenim un conjunt de punts en un espai 2D. La majoria estan agrupats en una regió densa, però alguns punts estan molt allunyats d’aquest cluster principal.
Si volem “aïllar” un punt normal (dins del cluster), necessitarem fer moltes divisions per separar-lo de la resta. En canvi, si volem aïllar un punt anòmal (lluny del cluster), amb poques divisions ja l’haurem separat completament.
Aquesta és exactament la lògica que utilitza Isolation Forest:
- Construeix múltiples arbres de decisió aleatoris
- Cada arbre intenta aïllar punts mitjançant divisions aleatòries
- Mesura la longitud del camí necessària per aïllar cada punt
- Camins curts → probable anomalia
- Camins llargs → probable observació normal
Com funciona l’algoritme
Isolation Forest crea un “bosc” d’arbres d’aïllament. Cada arbre es construeix així:
- Selecciona aleatòriament una característica xj del conjunt de dades
- Selecciona aleatòriament un valor de tall entre el mínim i el màxim de xj
- Divideix les dades en dos grups segons aquest tall
- Repeteix recursivament per a cada subgrup fins que cada punt quedi aïllat
Exemple visual (arbre simplified):
[200 punts]
|
x1 < 5.2? (tall aleatori)
/ \
[180 punts] [20 punts] ← Grup petit (sospitós!)
| |
x2 < 3.1? x2 < 8.5?
/ \ / \
[120] [60] [18] [2] ← Aïllat ràpidament!
| | | |
... ... ... ANOMALIA
Un punt anòmal queda aïllat amb menys divisions (camí curt a l’arbre). Un punt normal requereix més divisions (camí llarg).
Implementació amb scikit-learn
from sklearn.ensemble import IsolationForest
import numpy as np
# Generar dades sintètiques per demostració
np.random.seed(42)
X_normal = np.random.randn(200, 2) # 200 punts normals
X_anomalies = np.random.uniform(low=-4, high=4, size=(10, 2)) # 10 anomalies
X = np.vstack([X_normal, X_anomalies])
# Entrenar Isolation Forest
clf = IsolationForest(
contamination=0.05, # Esperem ~5% d'anomalies
random_state=42,
n_estimators=100 # Nombre d'arbres al bosc
)
clf.fit(X)
# Predir: 1 = normal, -1 = anomalia
predictions = clf.predict(X)
anomalies = X[predictions == -1]
print(f"Anomalies detectades: {len(anomalies)}")
El paràmetre de contaminació
El paràmetre més important d’Isolation Forest és contamination, que indica la proporció esperada d’anomalies al conjunt de dades.
- Valor per defecte: 0.1 (10%)
- Valor típic: Entre 0.01 i 0.1, depenent del problema
- Com ajustar-lo: Utilitzar un conjunt de validació amb algunes anomalies etiquetades (veure Avaluació i divisió de dades)
Trade-off important:
- Contamination massa alt → Molts falsos positius (etiquetem normals com anòmals)
- Contamination massa baix → Molts falsos negatius (perdem anomalies reals)
Scores d’anomalia
A més de les prediccions binàries (-1 o 1), Isolation Forest proporciona un score continu per cada observació:
# Obtenir scores d'anomalia
scores = clf.decision_function(X)
# Valors més negatius = més anòmals
# Valors més positius = més normals
# Ordenar per score per veure els casos més sospitosos
import pandas as pd
df_scores = pd.DataFrame({
'score': scores,
'prediction': predictions
})
df_sorted = df_scores.sort_values('score')
print(df_sorted.head(10)) # 10 casos més anòmals
Aquest score és útil per:
- Prioritzar casos per revisió manual
- Establir llindars personalitzats segons el context de negoci
- Visualitzar la distribució d’anomalies detectades
Avantatges i limitacions
| Avantatges | Limitacions |
|---|---|
| No assumeix cap distribució específica de les dades | Cal ajustar el paràmetre contamination |
| Funciona bé en alta dimensionalitat (moltes features) | Pot tenir problemes si hi ha múltiples clusters normals |
| Computacionalment eficient fins i tot amb grans datasets | No proporciona probabilitats explícites com models generatius |
| No requereix normalització de dades | Sensible a característiques irrellevants (cal feature engineering) |
Avaluació i divisió de dades
Un dels aspectes més delicats de la detecció d’anomalies és com avaluar el model. Tot i que és una tècnica no supervisada, és molt útil disposar d’algunes anomalies etiquetades per poder mesurar el rendiment.
La divisió de dades en detecció d’anomalies és diferent de l’aprenentatge supervisat clàssic:
Regla clau: El conjunt d’entrenament ha de contenir només exemples normals (o una proporció molt alta de normals). Si s’escapa alguna anomalia al conjunt d’entrenament, normalment no passa res. L’algoritme és robust a una petita contaminació.
Però quantes normals usar per entrenar? Això depèn del context. Hi ha dues estratègies principals:
Estratègia 1: Split 60/20/20 (ajust d’hiperparàmetres)
Quan usar-la:
- Necessites ajustar el paràmetre
contaminationexperimentalment - Tens un nombre limitat d’exemples normals (milers, no centenars de milers)
- Vols seguir les millors pràctiques de ML supervisat (separar train/val/test)
- Pots permetre’t “perdre” dades d’entrenament per tenir validació
Exemple pràctic amb motors d’avió:
Suposem que una fàbrica ha recollit dades de 10.000 motors durant anys:
- 10.000 motors normals (funcionen correctament)
- 20 motors defectuosos (anomalies detectades posteriorment)
Divisió recomanada:
Entrenament: 6.000 motors normals (60%, només y=0)
Validació: 2.000 motors normals + 10 defectuosos (20% + 50% anomalies)
Test: 2.000 motors normals + 10 defectuosos (20% + 50% anomalies)
Si tenim molt poques anomalies (menys de 20), podem prescindir del conjunt de test:
Entrenament: 6.000 motors normals
Validació: 4.000 motors normals + 20 defectuosos
Implementació de la divisió 60/20/20:
import numpy as np
# Assumim que tenim 'X' (característiques) i 'y' (0=normal, 1=anomalia)
# Separar normals i anomalies
normal_indices = np.where(y == 0)[0]
anomaly_indices = np.where(y == 1)[0]
# Entrenament: 60% dels normals
n_train = int(0.6 * len(normal_indices))
train_idx = normal_indices[:n_train]
X_train = X[train_idx]
# Validació: 20% normals + 50% anomalies
val_normal_idx = normal_indices[n_train:n_train + int(0.2*len(normal_indices))]
val_anomaly_idx = anomaly_indices[:len(anomaly_indices)//2]
val_idx = np.concatenate([val_normal_idx, val_anomaly_idx])
X_val = X[val_idx]
y_val = y[val_idx]
# Test: Resta de dades
test_normal_idx = normal_indices[n_train + int(0.2*len(normal_indices)):]
test_anomaly_idx = anomaly_indices[len(anomaly_indices)//2:]
test_idx = np.concatenate([test_normal_idx, test_anomaly_idx])
X_test = X[test_idx]
y_test = y[test_idx]
print(f"Train: {len(X_train)} normals")
print(f"Val: {len(X_val)} total ({sum(y_val==1)} anomalies)")
print(f"Test: {len(X_test)} total ({sum(y_test==1)} anomalies)")
Ajust del paràmetre contamination amb validació:
from sklearn.metrics import f1_score
resultats = []
for c in [0.01, 0.02, 0.05, 0.1, 0.15]:
clf = IsolationForest(contamination=c, random_state=42)
clf.fit(X_train) # Només normals!
y_pred = clf.predict(X_val)
y_pred_binary = (y_pred == -1).astype(int)
f1 = f1_score(y_val, y_pred_binary)
resultats.append({'contamination': c, 'f1_score': f1})
print(f"Contaminació={c:.2f}, F1-score={f1:.3f}")
# Seleccionar el valor amb millor F1-score
best_c = max(resultats, key=lambda x: x['f1_score'])['contamination']
print(f"\nMillor contamination: {best_c}")
Estratègia 2: Usar TOTES les normals (producció)
Quan usar-la:
- Tens moltes dades normals (centenars de milers o milions)
- Les anomalies són molt rares (< 0.5% del total)
- El paràmetre
contaminationes pot fixar segons coneixement del domini - Vols maximitzar l’aprenentatge del comportament normal
- No necessites ajustar hiperparàmetres experimentalment
Exemple pràctic: Detecció de frau bancari:
- 284.315 transaccions normals (abundants!)
- 492 transaccions fraudulentes (0.173%, molt rares)
- El
contaminationes fixa a 0.002 segons estadístiques del sector
Divisió recomanada:
Entrenament: 284.315 normals (100% de normals disponibles)
Test: 284.807 total (totes les dades, incloent 492 fraus)
Per què usar TOTES les normals?
- Dades normals abundants: No cal “estalviar-ne” per validació
- Anomalies precioses: Amb només 492 fraus, dividir-les redueix el poder estadístic del test
- Contamination fix: El sector bancari sap que ~0.2% de transaccions són frau
- Maximitza aprenentatge: Més exemples normals → millor model del “comportament normal”
Implementació 100%/100%:
import numpy as np
# Separar normals i anomalies
normal_indices = np.where(y == 0)[0]
X_train = X[normal_indices] # TOTES les normals
# Test: totes les dades (manté desbalanceig natural)
X_test = X
y_test = y
print(f"Train: {len(X_train)} normals (100%)")
print(f"Test: {len(X_test)} total ({sum(y_test==1)} anomalies, {100*sum(y_test==1)/len(y_test):.3f}%)")
# Entrenar amb totes les normals
clf = IsolationForest(contamination=0.002, random_state=42, n_estimators=100)
clf.fit(X_train)
# Avaluar directament sobre totes les dades
y_pred = clf.predict(X_test)
y_pred_binary = (y_pred == -1).astype(int)
from sklearn.metrics import precision_score, recall_score, f1_score
precision = precision_score(y_test, y_pred_binary, zero_division=0)
recall = recall_score(y_test, y_pred_binary)
f1 = f1_score(y_test, y_pred_binary)
print(f"\nResultats:")
print(f" Precision: {precision:.3f}")
print(f" Recall: {recall:.3f}")
print(f" F1-Score: {f1:.3f}")
Taula comparativa
| Criteri | 60/20/20 Split | 100% Normals |
|---|---|---|
| Dades normals | Milers | Centenars de milers+ |
| Anomalies | Desenes | Centenars (però % baix) |
| Contamination | Ajustat amb validació | Fix (coneixement domini) |
| Cas d’ús | Dataset petit, tuning necessari | Producció fraud/defectes |
| Exemple | 10K motors, 20 defectuosos | 284K transaccions, 492 fraus |
| Validació | Conjunt explícit | Directament al test |
Mètriques d’avaluació
En detecció d’anomalies, la classe positiva (anomalia) és molt minoritària. Per això, la accuracy NO és una bona mètrica. Utilitzem precision, recall i F1-score (veure ml_metrics.md per més detalls).
En funció del context, prioritzarem diferents mètriques:
- Detecció de frau bancari: Preferim alta recall (detectar tots els fraus), acceptant alguns falsos positius que es poden revisar manualment.
- Alertes mèdiques crítiques: Necessitem alta recall (no podem perdre cap cas greu), encara que generi falses alarmes.
- Control de qualitat manufacturera: Equilibri entre precision i recall per no descartar massa productes bons.
Detecció d’anomalies vs aprenentatge supervisat
Quan disposem d’algunes anomalies etiquetades, sorgeix una pregunta natural: per què no utilitzar aprenentatge supervisat en lloc de detecció d’anomalies?
La resposta depèn de diversos factors. Vegem-ne les diferències clau:
Taula comparativa
| Criteri | Detecció d’anomalies | Aprenentatge supervisat |
|---|---|---|
| Exemples positius | Molt pocs (0-50) | Molts (centenars o milers) |
| Tipus d’anomalies | Noves i variades | Conegudes i repetitives |
| Què aprèn | Com és el “normal” | Com són positius i negatius |
| Futurs positius | Poden ser molt diferents | Similars als entrenats |
| Assumpcions | Anomalies futures seran “estranyes” | Patrons coneguts es repetiran |
Quan usar detecció d’anomalies
✅ Usa anomaly detection quan:
- Tens menys de 50 exemples d’anomalies
- Les anomalies futures poden ser de tipus nous (diferents de les observades)
- El cost de no detectar una anomalia nova és molt alt
- Les anomalies són diverses i canvien amb el temps
Exemples:
- Frau financer: Els defraudadors constantment inventen noves tècniques. Un frau detectat avui pot ser molt diferent dels fraus del mes passat.
- Ciberseguretat: Els atacs informàtics evolucionen. Un sistema supervisat només detectaria atacs coneguts, mentre que anomaly detection pot alertar de patrons nous i sospitosos.
- Detecció de defectes nous: En una línia de producció, poden aparèixer tipus de defectes mai vistos abans a causa de nous materials, màquines o processos.
📘 Per a més detalls, consulta l’apèndix: quan usar aprenentatge supervisat i enginyeria de característiques.
Anàlisi de components principals
PCA (Principal Components Analysis) és un algoritme d’aprenentatge no supervisat que s’utilitza sovint per a la visualització i reducció de la dimensionalitat de dades. Si teniu un conjunt de dades amb moltes característiques —per exemple 10, 50 o fins i tot milers—, no és possible representar-lo directament en un gràfic.
El PCA busca noves característiques (anomenades components principals) que capturen la màxima variabilitat de les dades i redueixen el nombre de dimensions a dues o tres, fent possible la seva visualització i una millor comprensió.
Com es construeixen els components? Cada component principal és una combinació lineal de les característiques originals:
z1=w11x1+w12x2+⋯+w1nxn
Els pesos w es calculen de manera que z1 capturi la màxima variància possible, z2 la màxima variància restant (i sigui ortogonal a z1), i així successivament. A més, es restringeix que la norma dels pesos sigui 1 (w211+w212+⋯=1): sense aquesta restricció, podríem augmentar la variància indefinidament simplement escalant els pesos. Amb norma 1, PCA busca una direcció en l’espai de característiques, no una magnitud.
Això significa que les noves característiques no són cap de les originals, sinó mescles de totes elles, cosa que pot fer-les difícils d’interpretar.
La maledicció de la dimensionalitat
Una motivació important per usar PCA és evitar la maledicció de la dimensionalitat (curse of dimensionality).
Imagina que vols trobar el veí més proper d’un punt. En 2D, els punts estan relativament a prop i és fàcil identificar quins són similars. Però si afegeixes més dimensions, l’espai creix exponencialment: passa d’una línia a un quadrat, després a un cub, i així successivament. En aquest espai immens, les dades es tornen tan disperses que tots els punts acaben “igual de lluny” entre si. Algoritmes com K-NN o K-means, que depenen de distàncies, deixen de funcionar bé.
A més, per cobrir adequadament un espai amb moltes dimensions necessitem exponencialment més dades. I si no en tenim prou, els models troben patrons espuris que no generalitzen (sobreajust).
PCA ajuda comprimint les dades en un espai més petit i manejable, on les distàncies tornen a ser significatives.
Exemple senzill: cotxes
Suposem que tenim un conjunt de dades de cotxes de passatgers, amb moltes característiques: longitud, amplada, diàmetre de les rodes, alçada, etc.
Si volem reduir el nombre de característiques per a visualització, podem aplicar PCA.
Exemple 1: dues característiques
Suposem que tenim només dues característiques:
- x1 = longitud del cotxe
- x2 = amplada del cotxe
A la majoria de països, l’amplada dels cotxes varia poc perquè han de cabre a les carreteres de carril únic. Per exemple, als Estats Units, la majoria de cotxes tenen aproximadament 1,8 m d’amplada.
Si representem gràficament la longitud i l’amplada dels cotxes, trobarem que:
- x1 varia molt (hi ha cotxes curts i llargs)
- x2 varia poc
En aquest cas, el PCA automàticament decidiria prioritzar x1, ja que conté la major part de la informació rellevant.
Exemple 2: longitud i diàmetre de rodes
Ara x1 és la longitud del cotxe i x2 és el diàmetre de les rodes.
Encara que x2 variï una mica, PCA probablement decidirà que x1 és suficient per representar la variació principal.
Exemple 3: longitud i alçada
Ara x1 = longitud i x2 = alçada del cotxe.
Ambdues característiques varien força i contenen informació útil.
En lloc de triar només x1 o només x2, PCA crea un nou eix combinat, que anomenarem z.
Aquest eix z és una combinació lineal de x1 i x2 que captura la major part de la variació de les dades:
z=w1x1+w2x2
La coordenada d’un cotxe sobre l’eix z resumeix aproximadament la “grandària total” del cotxe, sense perdre informació rellevant.
Exemple amb més dimensions
Suposem que tenim un conjunt de dades tridimensional amb x1,x2,x3.
Encara que les dades estiguin en 3D, sovint viuen sobre una superfície gairebé bidimensional.
PCA permet reduir de 3 característiques a 2 noves coordenades z1,z2 per a visualitzar les dades en un gràfic 2D.
Exemple amb països
Si tenim dades de molts països amb característiques com:
- x1 = PIB total
- x2 = PIB per càpita
- x3 = Índex de Desenvolupament Humà
- …fins a 50 característiques
No podem representar-ho directament en un gràfic.
PCA redueix aquestes 50 característiques a dues o tres:
Z1,Z2=PCA(X1,X2,…,X50)
Per exemple:
- Z1 podria correspondre aproximadament a la grandària del país o PIB total
- Z2 podria correspondre a la riqueza per persona
Això permet visualitzar països grans i petits, amb PIB alt o baix, en un gràfic 2D fàcil d’interpretar.
PCA amb scikit-learn
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
# Normalitzar dades (important per PCA!)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# Reduir a 2 dimensions
pca = PCA(n_components=2)
X_reduced = pca.fit_transform(X_scaled)
# Variància explicada per cada component
print(pca.explained_variance_ratio_) # Ex: [0.72, 0.15]
# Això vol dir: PC1 captura 72%, PC2 captura 15% de la variància
Selecció del nombre de components
Un dels aspectes clau de PCA és decidir quants components conservar. Menys components significa un model més simple, però també implica perdre informació. La clau per prendre aquesta decisió és la variància explicada.
Variància explicada (explained variance ratio)
Cada component principal captura una part de la variabilitat total de les dades. L’atribut explained_variance_ratio_ retorna un array on cada valor indica la proporció de variància capturada per aquell component:
- Els valors estan ordenats de major a menor (PC1 sempre captura més que PC2)
- La suma de tots els valors és 1.0 (100% de la variància)
Exemple:
explained_variance_ratio_ = [0.72, 0.15, 0.08, 0.03, 0.02]
→ PC1 sol: 72% de la informació
→ PC1 + PC2: 87% de la informació
→ PC1 + PC2 + PC3: 95% de la informació
Si amb 2 components ja capturem el 87% de la variància, les altres 48 característiques originals eren probablement redundants o molt correlacionades entre si.
Variància retinguda
Quan reduïm de n característiques a k components, “perdem” part de la informació. La variància retinguda és la suma acumulada dels primers k valors de explained_variance_ratio_, i és la mètrica clau per decidir quants components conservar.
Per què descartem les direccions amb poca variància?
La intuïció clau és que variància alta = informació útil. Si una característica (o direcció en l’espai de dades) varia molt poc, aporta poca capacitat per distingir entre observacions:
- Una característica on tots els valors són gairebé iguals no ajuda a diferenciar res
- En canvi, una característica amb molta variació permet separar o agrupar les dades
PCA ordena les direccions de més a menys variància. Quan descartem els últims components (els de baixa variància), estem eliminant:
- Soroll: fluctuacions aleatòries que no aporten informació real
- Redundància: informació ja capturada pels primers components
- Detalls irrellevants: variacions mínimes que no afecten l’anàlisi
Per això, descartar components de baixa variància sovint millora els models: eliminem soroll sense perdre el senyal important.
Hi ha un compromís:
- Més components → més variància retinguda → model més complex
- Menys components → menys variància retinguda → model més simple però pot perdre patrons importants
Els llindars habituals són 90%, 95% o 99%, depenent de l’aplicació.
Criteris segons el propòsit
| Propòsit | Components recomanats | Raonament |
|---|---|---|
| Visualització | 2-3 | Permet representar les dades en un gràfic 2D o 3D |
| Compressió | 90-99% variància | Reté la informació essencial amb menys espai |
| Preprocessat per ML | Validació creuada | Trobar el punt òptim entre retenir senyal i descartar soroll |
PCA com a tècnica de regularització:
PCA no només serveix per reduir dimensions o visualitzar. També pot millorar el rendiment d’un model predictiu. Com? Els components de baixa variància sovint contenen més soroll que senyal. En descartar-los:
- Reduïm el sobreajust (overfitting): el model no aprèn patrons espuris del soroll
- Millorem la generalització: el model es concentra en les característiques realment informatives
- Accelerem l’entrenament: menys dimensions significa menys càlcul
En alguns casos, retenir menys del 90% de la variància pot donar millors resultats que usar totes les característiques originals. L’única manera de saber-ho és validar experimentalment amb cross-validation.
Selecció automàtica amb scikit-learn
import numpy as np
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
# Normalitzar dades
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# Opció 1: Selecció automàtica per llindar de variància
pca = PCA(n_components=0.95) # Retenir 95% de la variància
X_reduced = pca.fit_transform(X_scaled)
print(f"Components seleccionats: {pca.n_components_}")
# Ex: "Components seleccionats: 10" (de 50 originals)
# Opció 2: Explorar variància acumulada manualment
pca_full = PCA()
pca_full.fit(X_scaled)
variancia_acumulada = np.cumsum(pca_full.explained_variance_ratio_)
print(variancia_acumulada)
# Ex: [0.72, 0.87, 0.95, 0.98, 1.0]
# → Amb 3 components ja tenim el 95%
Consells pràctics
- Per defecte, comença amb un llindar del 95% de variància retinguda
- Per visualització, utilitza sempre 2 o 3 components
- Per models predictius, valida amb cross-validation sobre la tasca final: a vegades menys components (que eliminen soroll) milloren el rendiment
Resum
PCA és un algoritme potent per reduir la dimensionalitat:
- Pren dades amb moltes característiques (alta dimensionalitat)
- Les redueix a 2 o 3 característiques principals
- Permet visualitzar i entendre millor les dades
És especialment útil per explorar conjunts de dades complexos, detectar patrons i anomalies, i preparar les dades per altres algorismes d’aprenentatge automàtic.
Errors comuns a evitar
Aquests són alguns errors freqüents quan s’apliquen tècniques d’aprenentatge no supervisat:
K-means:
- Escollir K arbitràriament sense usar el mètode del colze o considerar el context
- No normalitzar les característiques (les variables amb escales grans dominaran)
- Executar l’algoritme només una vegada (pot quedar atrapat en mínims locals)
Detecció d’anomalies:
- Configurar malament el paràmetre de contaminació (massa falsos positius o falsos negatius)
- No escalar o preparar adequadament les característiques quan tenen escales molt diferents
- Interpretar l’anomaly score com una probabilitat
PCA:
- Oblidar normalitzar les dades abans d’aplicar PCA (imprescindible!)
- Reduir a massa poques dimensions i perdre informació important
- Interpretar els components principals com si fossin les variables originals
Apèndix: Detecció d’anomalies (ampliació)
Aquesta secció conté material addicional sobre detecció d’anomalies per a qui vulgui aprofundir en el tema.
Quan usar aprenentatge supervisat
✅ Usa supervised learning quan:
- Tens centenars o milers d’exemples positius
- Les anomalies futures seran similars a les passades
- Els patrons són repetitius i estables
- Necessites alta precisió en casos coneguts
Exemples:
- Classificació de correu brossa: Els correus spam tendeixen a repetir patrons similars (ofertes, enllaços sospitosos, majúscules excessives). Els futurs spams seran similars als passats.
- Diagnòstic mèdic de malalties conegudes: Els símptomes d’una pneumònia o diabetis són estables i ben documentats.
- Detecció de defectes coneguts: Si una fàbrica de smartphones sap que el 2% de pantalles tenen un ratllat específic per una màquina defectuosa, pot entrenar un classificador per detectar aquest defecte concret.
Taula d’exemples aplicats
| Aplicació | Recomanació | Raó |
|---|---|---|
| Frau amb targeta de crèdit | Anomaly detection | Noves tècniques de frau emergeixen constantment |
| Classificació de correu brossa | Supervised learning | Patrons de spam són repetitius i coneguts |
| Defectes nous en fabricació | Anomaly detection | Tipus de defecte imprevisibles i canviants |
| Diagnòstic de grip vs pneumònia | Supervised learning | Símptomes ben establerts i documentats |
| Detecció d’intrusions a xarxes | Anomaly detection | Atacs nous i sofisticats apareixen regularment |
| Predicció meteorològica | Supervised learning | Només hi ha uns quants tipus de temps coneguts |
Enfocament híbrid
En alguns casos, es poden combinar ambdues tècniques:
- Fase 1: Usar anomaly detection per identificar casos sospitosos
- Fase 2: Revisió manual i etiquetatge d’aquests casos
- Fase 3: Quan s’acumulin prou exemples (>100), entrenar un model supervisat per als patrons coneguts
- Fase 4: Mantenir anomaly detection per detectar nous tipus no coberts pel supervisat
Aquest enfocament aprofita el millor de cada món: eficiència per patrons coneguts i capacitat de detectar anomalies noves.
Enginyeria de característiques
En detecció d’anomalies, l’elecció de bones característiques és crítica. A diferència de l’aprenentatge supervisat, on el model pot aprendre quines característiques ignorar, en anomaly detection no tenim senyal supervisada per guiar aquest procés.
Per què és tan important?
Isolation Forest (i altres mètodes no supervisats) no saben què fa que una observació sigui anòmala sense que nosaltres els hi indiquem a través de les característiques.
Exemple: En detecció de frau, tenir només “import de transacció” podria no ser suficient, perquè una transacció de 1.000€ pot ser normal per un client i anòmala per un altre. Però la característica import / mitjana_habitual_usuari és molt més informativa.
Creació de característiques rellevants
Principi general: Crear característiques que capturin desviacions del comportament normal.
A continuació es mostren alguns exemples per dominis.
Targetes de crèdit:
# Característiques bàsiques
df['hour_of_day'] = df['timestamp'].dt.hour
df['day_of_week'] = df['timestamp'].dt.dayofweek
# Característiques relatives a l'usuari
df['amount_vs_avg'] = df['amount'] / df.groupby('user_id')['amount'].transform('mean')
df['time_since_last'] = df.groupby('user_id')['timestamp'].diff().dt.total_seconds()
# Característiques de freqüència
df['transactions_last_hour'] = df.groupby('user_id').rolling('1H', on='timestamp').count()
Monitoratge de servidors:
# Ràtios que capten comportaments anòmals
df['cpu_per_network'] = df['cpu_load'] / (df['network_traffic'] + 1) # +1 evita divisió per 0
df['memory_growth_rate'] = df.groupby('server_id')['memory_used'].diff()
df['disk_io_vs_cpu'] = df['disk_io'] / (df['cpu_load'] + 0.01)
Fabricació:
# Relacions entre variables físiques
df['temp_vibration_ratio'] = df['temperature'] / (df['vibration'] + 0.001)
df['pressure_deviation'] = np.abs(df['pressure'] - df['pressure'].rolling(10).mean())
Transformacions de distribucions
Moltes vegades, les característiques originals tenen distribucions molt esbiaixades (skewed). Aplicar transformacions pot ajudar els algoritmes a funcionar millor.
A continuació es mostren transformacions habituals:
| Transformació | Quan usar-la | Exemple |
|---|---|---|
log(X + c) | Distribució molt esbiaixada a la dreta | Imports monetaris, freqüències |
sqrt(X) | Esbiaixament moderat | Comptadors, taxes |
X^(1/3) o altres potències | Valors extrems molt grans | Volums, poblacions |
1/X | Relació inversa | Temps entre esdeveniments |
⚠️ Important: Qualsevol transformació aplicada al conjunt d’entrenament s’ha d’aplicar també als conjunts de validació i test.
Anàlisi d’errors iteratiu
El procés de millorar un sistema de detecció d’anomalies és iteratiu:
Workflow recomanat:
- Entrenar model amb característiques actuals
- Avaluar sobre validation set (amb anomalies etiquetades)
- Identificar FALSOS NEGATIUS (anomalies no detectades)
- Analitzar què diferencia aquests casos dels normals
- Crear noves característiques que captin aquestes diferències
- Tornar a 1
Exemple pràctic:
Suposem que el nostre model no detecta un servidor compromès que està enviant dades a l’exterior.
Anàlisi:
- CPU: Normal (3.2 GHz) ✓
- Memòria: Normal (40% usat) ✓
- Tràfic de xarxa: Alt però no extremadament (podria ser legítim) ⚠️
Solució: Crear una nova característica:
# Ràtio de tràfic de xarxa vs càrrega de CPU
df['network_per_cpu'] = df['network_traffic'] / (df['cpu_load'] + 0.01)
Aquesta característica serà anormalment alta per un servidor que envia dades sense fer càlculs intensius, permetent detectar l’anomalia.
Normalització i escalat
Isolation Forest és relativament robust a escales diferents, però sovint és recomanable normalitzar les característiques:
from sklearn.preprocessing import StandardScaler
# Normalitzar (mitjana=0, desviació estàndard=1)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_val_scaled = scaler.transform(X_val) # Usar mateixos paràmetres!
# Entrenar amb dades normalitzades
clf = IsolationForest(contamination=0.05, random_state=42)
clf.fit(X_train_scaled)
Això és especialment important si les característiques tenen unitats molt diferents (ex: temperatura en ºC vs pressió en Pa).
Consells pràctics i workflow complet
1. Context de negoci primer
L’algorisme més sofisticat no serveix de res sense coneixement del domini:
- Parla amb experts del sector (enginyers, auditors, metges…)
- Entén què fa que una observació sigui realment problemàtica
- Defineix el cost dels falsos positius vs falsos negatius
Pregunta clau: És millor revisar 100 casos normals per no perdre 1 anomalia? O preferim perdre algunes anomalies per evitar sobrecàrrega de revisions?
2. Comença simple, itera ràpid
No intentis crear el model perfecte des del principi:
Iteració 1: 2-3 característiques bàsiques
→ Avalua → Identifica errors
Iteració 2: Afegeix 1-2 característiques noves
→ Avalua → Millora?
Iteració 3: Prova transformacions
→ Avalua → Ajusta contamination
Cada iteració hauria de durar minuts o hores, no dies.
3. Quan considerar aprenentatge supervisat
Si després d’uns mesos d’ús del sistema tens:
- Més de 100 anomalies etiquetades → Planteja’t un model supervisat complementari
- Patrons repetitius clars → Random Forest o XGBoost poden funcionar millor
- Nous tipus d’anomalies emergeixen → Manté l’anomaly detection com a primera línia
Enfocament híbrid recomanat:
- Isolation Forest detecta casos sospitosos
- Revisió humana etiqueta aquests casos
- Model supervisat per patrons coneguts
- Isolation Forest per detectar patrons nous