Ja domines la maquinària recurrent: cel·les amb memòria (LSTM/GRU), capes apilades i bidireccionals. Però hi ha un obstacle previ abans d'aplicar-la a les ressenyes de TecnoMarket: les xarxes neuronals només processen números, i una ressenya és text. Aquesta lliçó resol aquest pont —tokenització, vocabulari, padding i, sobretot, embeddings— i l'aplica al projecte estrella del processament del llenguatge natural (PLN/NLP): l'anàlisi de sentiment. Seguint la metodologia del curs, primer construirem un prototip amb el dataset públic IMDB (ressenyes de cinema) i després el llegirem en clau TecnoMarket: classificar ressenyes de clients i alimentar el dashboard d'atenció al client. Tancarem amb un panorama d'altres tasques NLP amb RNN i els seus límits, que motiven l'evolució cap a l'atenció.
Contingut
- El problema: les xarxes mengen números, no paraules
- Tokenització i vocabulari
- Longituds diferents: padding i truncament
- Embeddings: d'índexs a significat
- La capa Embedding de Keras
- Prototip: anàlisi de sentiment amb IMDB (Embedding + LSTM)
- Lectura TecnoMarket: el dashboard d'atenció al client
- Panorama: altres tasques NLP amb RNN i els seus límits
El problema: les xarxes mengen números, no paraules
Tot el que has entrenat fins ara rebia números: píxels entre 0 i 255 (MNIST, 02-05), vendes, seqüències sintètiques. "La bateria dura poc" no és un tensor. El pipeline estàndard per convertir text en entrada de xarxa té tres etapes:
graph LR
A["Text cru<br>'La bateria dura poc'"] --> B["Tokenització<br>['la','bateria','dura','poc']"]
B --> C["Índexs del vocabulari<br>[5, 214, 89, 47]"]
C --> D["Padding<br>[5, 214, 89, 47, 0, 0, 0, 0]"]
D --> E["Embedding<br>matriu 8 × 32 de números reals"]
E --> F["LSTM → classificació"]
Anem etapa per etapa.
Tokenització i vocabulari
Tokenitzar és trossejar el text en unitats (tokens): el més intuïtiu, paraules. Després es construeix un vocabulari: un diccionari que assigna a cada token un índex enter, normalment ordenat per freqüència (índexs baixos = paraules més comunes).
from tensorflow.keras.layers import TextVectorization
import numpy as np
ressenyes = [
"la bateria dura poc",
"enviament rapid i producte impecable",
"el producte arriba trencat",
"impecable qualitat preu",
]
vectoritzador = TextVectorization(
max_tokens=1000, # mida màxima del vocabulari
output_sequence_length=8 # longitud fixa de sortida (padding/truncament automàtics)
)
vectoritzador.adapt(ressenyes) # aprèn el vocabulari A PARTIR dels textos
print(vectoritzador.get_vocabulary()[:8])
# ['', '[UNK]', 'producte', 'impecable', 'i', 'trencat', 'rapid', 'preu']
print(vectoritzador(["enviament rapid i producte impecable"]).numpy())
# [[ 9 6 4 2 3 0 0 0]]Detalls que importen:
- L'índex 0 es reserva per al padding i l'1 per a
[UNK](unknown): qualsevol paraula que no sigui al vocabulari (errors ortogràfics, paraules rares) es mapeja a[UNK]. Limitar el vocabulari (p. ex., a les 10 000 paraules més freqüents) és un compromís: menys paràmetres a canvi de perdre paraules rares. - La tokenització real també resol minúscules, accents i puntuació (
TextVectorizationaplica una estandardització per defecte). En models moderns es fan servir subparaules (trossejar "recarregable" en "re-carrega-ble"), però la idea de fons és la mateixa.
Longituds diferents: padding i truncament
Les ressenyes tenen longituds diferents, però un tensor és rectangular: totes les files han de mesurar igual. La solució és fixar una longitud L:
- Seqüències més curtes → s'omplen amb zeros (padding).
- Seqüències més llargues → es tallen (truncament).
Triar L és un compromís: massa curta perd el final de les ressenyes llargues (on sol haver-hi la conclusió!); massa llarga malbarata còmput en zeros. Una regla pràctica: mira el percentil 90-95 de les longituds reals del teu corpus.
Embeddings: d'índexs a significat
Els índexs [5, 214, 89, 47] ja són números, però números arbitraris: que "bateria" sigui 214 i "pila" sigui 611 no diu res de la seva semblança. Dues opcions per donar-los a la xarxa:
| One-hot | Embedding | |
|---|---|---|
| Representació d'una paraula | Vector de mida vocabulari amb un únic 1 (p. ex., 10 000 dims) | Vector dens curt de números reals (p. ex., 32-300 dims) |
| Dimensió amb vocab. de 10 000 | 10 000 | 32-300 (la tries tu) |
| Captura similitud? | No: tots els parells de paraules són equidistants | Sí: paraules d'ús semblant acaben amb vectors propers |
| S'aprèn? | No, és fixa | Sí, per retropropagació com qualsevol pes |
Un embedding és una taula de consulta: una matriu de forma (vocabulari, dimensió) on la fila i és el vector de la paraula i. Aquests vectors comencen aleatoris i s'aprenen durant l'entrenament: si "trencat" i "defectuós" apareixen en contextos semblants i prediuen la mateixa etiqueta, la retropropagació empeny els seus vectors a acostar-se — el repartiment de la culpa (02-03) arriba fins a la mateixa representació de cada paraula.
Et sona aquesta idea? És la mateixa de 03-04: allà una CNN convertia cada imatge de producte en un vector de característiques i mesuràvem la semblança entre productes amb la similitud cosinus. Un embedding de paraules és exactament això per al llenguatge: un espai on la geometria codifica el significat. Amb embeddings ben entrenats, cosinus(v_bateria, v_pila) és alt, cosinus(v_bateria, v_catifa) és baix, i s'observen regularitats famoses com v_rei − v_home + v_dona ≈ v_reina.
# Similitud cosinus entre files d'una matriu d'embedding (repàs de 03-04)
def cosinus(a, b):
return a @ b / (np.linalg.norm(a) * np.linalg.norm(b))La capa Embedding de Keras
En Keras, la taula és una capa més:
from tensorflow.keras import layers
emb = layers.Embedding(
input_dim=10000, # mida del vocabulari
output_dim=32, # dimensió de cada vector de paraula
mask_zero=True # opcional: marca el padding (índex 0) perquè la LSTM l'ignori
)
# Entrada: (batch, L) índexs enters
# Sortida: (batch, L, 32) un vector de 32 números per paraula- Paràmetres:
10000 × 32 = 320 000. Sol ser la capa més gran del model — una altra raó per limitar el vocabulari. - Fixa't en la forma de sortida:
(batch, passos, característiques)— exactament el format 3D que esperen les capes recurrents de 04-01. Embedding i LSTM encaixen com peces de Lego. mask_zero=Truepropaga una "màscara" perquè les capes següents no processin els zeros del padding com si fossin paraules.
Prototip: anàlisi de sentiment amb IMDB (Embedding + LSTM)
Metodologia del curs: prototip amb dataset públic primer. IMDB porta 50 000 ressenyes de cinema en anglès, etiquetades com a positives (1) o negatives (0), ja tokenitzades a índexs — perfecte per centrar-nos en el model. És una tasca many-to-one de manual.
import numpy as np
from tensorflow import keras
from tensorflow.keras import layers
VOCAB = 10000 # ens quedem amb les 10 000 paraules més freqüents
L = 200 # longitud fixa de les seqüències
# 1. Carregar i preparar
(x_train, y_train), (x_test, y_test) = keras.datasets.imdb.load_data(num_words=VOCAB)
x_train = keras.preprocessing.sequence.pad_sequences(x_train, maxlen=L)
x_test = keras.preprocessing.sequence.pad_sequences(x_test, maxlen=L)
print(x_train.shape) # (25000, 200): 25 000 ressenyes de 200 índexs
# 2. Model: Embedding -> LSTM -> decisió
model = keras.Sequential([
layers.Input(shape=(L,)),
layers.Embedding(VOCAB, 32, mask_zero=True), # (200,) -> (200, 32)
layers.LSTM(64), # llegeix la ressenya, la resumeix en 64 números
layers.Dense(1, activation="sigmoid") # probabilitat de "positiva"
])
model.compile(optimizer="adam",
loss="binary_crossentropy", # BCE: classificació binària (02-04)
metrics=["accuracy"])
model.summary()
# Embedding: 320 000 params | LSTM: 4·64·(32+64+1) = 24 832 | Dense: 65
# 3. Entrenar i avaluar
historial = model.fit(x_train, y_train, epochs=4, batch_size=64,
validation_split=0.2)
print(model.evaluate(x_test, y_test))
# Típic: ~86-88 % d'accuracy en testCom llegir els resultats, com vam fer amb MNIST a 02-05:
- La sigmoide final dona la probabilitat de ressenya positiva; amb BCE com a pèrdua, és la recepta estàndard de classificació binària de 02-04.
- Vigila les corbes: en IMDB, cap a l'època 3-4 l'accuracy d'entrenament continua pujant però la de validació s'estanca o baixa — el model comença a memoritzar. Atura't aquí (les tècniques sistemàtiques contra el sobreajust arriben a 05-04).
- Un 87 % amb un model tan petit és notable: l'atzar donaria un 50 %. Els errors restants solen ser ressenyes amb ironia, negacions complexes o sentiment mixt — justament dependències llargues i subtils.
Lectura TecnoMarket: el dashboard d'atenció al client
Segon pas de la metodologia: traslladar-ho a les dades (fictícies) de TecnoMarket. L'equip d'atenció al client rep centenars de ressenyes i missatges al dia; l'objectiu és prioritzar-los automàticament.
Canvis respecte al prototip:
- Dades pròpies: ressenyes reals de la botiga amb etiquetes pròpies. Aquí el model passa de binari a tres classes:
positiva,negativaiurgent(menciona danys, frau o terminis legals). N'hi ha prou de canviar el cap del model:Dense(3, activation="softmax")amb pèrduacategorical_crossentropy— la guia d'activacions/pèrdues per tipus de problema de 02-02 i 02-04 et diu exactament què tocar. - Vocabulari propi en català: s'aprèn amb
TextVectorization.adapt()sobre el corpus de TecnoMarket ("no refreda", "pantalla trencada", "devolució" han de ser al vocabulari). - Llindar de confiança i cua de revisió humana, la mateixa arquitectura de desplegament que vam muntar per a les fotos de producte a 03-04:
def enrutar_ressenya(text, model, vectoritzador, llindar=0.80):
"""Classifica una ressenya i decideix el seu destí al dashboard."""
x = vectoritzador([text])
probs = model.predict(x, verbose=0)[0] # p. ex. [0.05, 0.15, 0.80]
classe = ["positiva", "negativa", "urgent"][int(np.argmax(probs))]
confianca = float(probs.max())
if confianca < llindar:
return {"desti": "revisio_humana", "suggeriment": classe,
"confianca": confianca}
if classe == "urgent":
return {"desti": "safata_prioritaria", "confianca": confianca}
return {"desti": f"arxiu_{classe}", "confianca": confianca}El principi és el mateix que a 03-04: el model automatitza els casos clars i deriva els dubtosos a persones. En atenció al client això no és opcional: classificar malament una ressenya urgent (una nevera que goteja sobre una regleta) costa molt més que una revisió manual de més. Per això, més enllà de l'accuracy, aquí interessa especialment no deixar escapar urgències encara que costi algun fals positiu.
Panorama: altres tasques NLP amb RNN i els seus límits
L'anàlisi de sentiment és many-to-one, però les RNN van cobrir durant anys gairebé tot el NLP:
| Tasca | Topologia | Exemple TecnoMarket |
|---|---|---|
| NER (reconeixement d'entitats) | Many-to-many sincronitzada | Marcar a cada ressenya quins tokens són producte, marca o número de comanda, per enllaçar-la amb el catàleg |
| Traducció automàtica | Seq2seq (many-to-many desfasada) | Traduir ressenyes de la botiga internacional al català per a l'equip de suport |
| Resum | Seq2seq | Condensar 40 ressenyes d'un producte en tres frases per a la fitxa interna |
L'esquema seq2seq mereix dues línies conceptuals: un encoder (una LSTM) llegeix la frase d'origen sencera i la comprimeix en el seu estat final; un decoder (una altra LSTM) genera la frase destí paraula a paraula a partir d'aquest estat. Elegant… i amb un coll d'ampolla evident: tota la frase d'origen, tingui 5 o 60 paraules, ha de cabre en un únic vector d'estat. Amb frases llargues, el decoder "oblida" el principi, per molt LSTM que sigui. A més, la recurrència obliga a processar paraula a paraula en ordre, sense paral·lelitzar.
La solució històrica va ser permetre que el decoder "mirés enrere" a totes les posicions de l'encoder i decidís a quina atendre a cada pas: el mecanisme d'atenció, que va acabar eliminant la recurrència del tot i donant lloc als transformers. No el desenvolupem aquí: és el tema de la lliçó 05-05. La generació de text amb RNN (escriure caràcter a caràcter) tampoc no la practiquem ara: és el projecte guiat 07-02.
Errors Comuns i Consells
- Aprendre el vocabulari (o el vectoritzador) amb dades de test.
adapt()s'ha d'executar només sobre el conjunt d'entrenament; si no, informacions del test s'esmunyen dins el model i l'avaluació queda inflada. És el mateix principi de fuga de dades que veurem amb els scalers a 04-04. - Truncar pel costat equivocat.
pad_sequencestrunca pel principi per defecte (truncating='pre'). En ressenyes, la conclusió sol ser al final, així que aquest default sol convenir; però decideix-ho conscientment, no per accident. - Vocabulari desmesurat. 100 000 paraules × 128 dims = 12,8 M de paràmetres només a l'Embedding, la majoria de paraules vistes 2 o 3 vegades (impossibles d'aprendre bé). Retalla a les freqüents i deixa que
[UNK]absorbeixi la resta. - Oblidar
mask_zero=True(o equivalent) amb molt padding. Si la meitat de cada seqüència són zeros i la LSTM els processa com si fossin paraules, dilueixes el senyal. Amb màscara, la xarxa se'ls salta. - Avaluar només amb accuracy en classes desequilibrades. Si el 90 % de ressenyes són positives, un model que sempre digui "positiva" té un 90 % d'accuracy i zero utilitat. Mira la matriu de confusió per classe, especialment el recall d'
urgent.
Exercicis
- Pipeline a mà: amb el vocabulari
{'': 0, '[UNK]': 1, 'producte': 2, 'impecable': 3, 'trencat': 4, 'arriba': 5}i longitud fixaL = 6, converteix en seqüència d'índexs la ressenya "arriba trencat el producte" (la paraula "el" no és al vocabulari). Quina forma tindrà la sortida després d'unEmbedding(6, 4)? - Recompte de paràmetres: calcula els paràmetres totals del model IMDB de la lliçó (Embedding 10 000×32, LSTM(64) amb entrada 32, Dense(1) sobre 64). Quin percentatge correspon a l'Embedding?
- Disseny TecnoMarket: el dashboard classifica en positiva/negativa/urgent amb llindar 0.80. Per a la ressenya X el model retorna
[0.42, 0.45, 0.13]. Què fa la funcióenrutar_ressenyai per què és el comportament correcte?
Solucions
- Tokens:
['arriba', 'trencat', 'el', 'producte']→ índexs[5, 4, 1, 2]("el" →[UNK]= 1). Amb padding a 6 (farciment al final):[5, 4, 1, 2, 0, 0]. Després d'Embedding(6, 4): forma(6, 4)— 6 passos de temps amb un vector de 4 números cadascun (amb batch:(1, 6, 4)). - Embedding:
10 000 × 32 = 320 000. LSTM:4·64·(32+64+1) = 24 832. Dense:64 + 1 = 65. Total:344 897. L'Embedding suposa320 000 / 344 897 ≈ 93 %dels paràmetres — per això la mida del vocabulari domina la mida del model. - La classe més probable és
negativa(0.45), però la confiança màxima (0.45) queda molt per sota del llindar 0.80, així que retornadesti: revisio_humanaambsuggeriment: negativa. És el correcte: el model està essencialment indecís entre positiva i negativa (0.42 vs. 0.45) — probablement una ressenya mixta o irònica — i decidir automàticament seria jugar-s'ho a cara o creu; una persona resol el cas en segons.
Conclusió
Has tancat el pont entre el llenguatge i les xarxes: tokenització i vocabulari per trossejar i indexar, padding per fer-ho rectangular, i embeddings —vectors densos apresos on la proximitat geomètrica captura la semblança semàntica, com els embeddings d'imatges de 03-04— per donar significat als índexs. Amb Embedding + LSTM has construït un classificador de sentiment del 87 % sobre IMDB i l'has traslladat al dashboard de TecnoMarket amb tres classes i el llindar de confiança amb revisió humana de 03-04. També has vist el mapa de tasques NLP (NER, seq2seq per a traducció i resum) i el coll d'ampolla del vector únic que va motivar l'atenció i els transformers (05-05). Queda el segon projecte estrella del mòdul: les seqüències de TecnoMarket que no són paraules sinó números amb data — la predicció de la demanda diària, on ens esperen les sèries temporals, les finestres lliscants i algun error clàssic que aprendrem a esquivar.
Curs de Deep Learning
Mòdul 1: Introducció al Deep Learning
- Què és el Deep Learning?
- Història i evolució del Deep Learning
- Aplicacions del Deep Learning
- Conceptes bàsics de xarxes neuronals
- Preparació de l'entorn de treball
Mòdul 2: Fonaments de Xarxes Neuronals
- Perceptró i Perceptró Multicapa
- Funció d'activació
- Propagació cap endavant i cap enrere
- Optimització i funció de pèrdua
- La teva primera xarxa neuronal completa
Mòdul 3: Xarxes Neuronals Convolucionals (CNN)
- Introducció a les CNN
- Capes convolucionals i de pooling
- Arquitectures populars de CNN
- Aplicacions de CNN en reconeixement d'imatges
Mòdul 4: Xarxes Neuronals Recurrents (RNN)
- Introducció a les RNN
- LSTM i GRU
- Aplicacions de RNN en processament del llenguatge natural
- Seqüències i sèries temporals
Mòdul 5: Tècniques Avançades en Deep Learning
- Xarxes Generatives Adversàries (GAN)
- Autoencoders
- Transfer Learning
- Regularització i tècniques de millora
- Mecanismes d'atenció i Transformers
Mòdul 6: Eines i Frameworks
- Introducció a TensorFlow
- Introducció a PyTorch
- Comparació de frameworks
- Entorns de desenvolupament i recursos addicionals
- Desar, carregar i desplegar models
Mòdul 7: Projectes Pràctics
- Classificació d'imatges amb CNN
- Generació de text amb RNN
- Detecció d'anomalies amb Autoencoders
- Creació d'una GAN per a generació d'imatges
- Fine-tuning d'un model preentrenat
