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

  1. El problema: les xarxes mengen números, no paraules
  2. Tokenització i vocabulari
  3. Longituds diferents: padding i truncament
  4. Embeddings: d'índexs a significat
  5. La capa Embedding de Keras
  6. Prototip: anàlisi de sentiment amb IMDB (Embedding + LSTM)
  7. Lectura TecnoMarket: el dashboard d'atenció al client
  8. 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ó (TextVectorization aplica 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=True propaga 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 test

Com 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:

  1. Dades pròpies: ressenyes reals de la botiga amb etiquetes pròpies. Aquí el model passa de binari a tres classes: positiva, negativa i urgent (menciona danys, frau o terminis legals). N'hi ha prou de canviar el cap del model: Dense(3, activation="softmax") amb pèrdua categorical_crossentropy — la guia d'activacions/pèrdues per tipus de problema de 02-02 i 02-04 et diu exactament què tocar.
  2. 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).
  3. 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_sequences trunca 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

  1. Pipeline a mà: amb el vocabulari {'': 0, '[UNK]': 1, 'producte': 2, 'impecable': 3, 'trencat': 4, 'arriba': 5} i longitud fixa L = 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'un Embedding(6, 4)?
  2. 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?
  3. 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_ressenya i per què és el comportament correcte?

Solucions

  1. 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)).
  2. Embedding: 10 000 × 32 = 320 000. LSTM: 4·64·(32+64+1) = 24 832. Dense: 64 + 1 = 65. Total: 344 897. L'Embedding suposa 320 000 / 344 897 ≈ 93 % dels paràmetres — per això la mida del vocabulari domina la mida del model.
  3. 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 retorna desti: revisio_humana amb suggeriment: 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

Mòdul 2: Fonaments de Xarxes Neuronals

Mòdul 3: Xarxes Neuronals Convolucionals (CNN)

Mòdul 4: Xarxes Neuronals Recurrents (RNN)

Mòdul 5: Tècniques Avançades en Deep Learning

Mòdul 6: Eines i Frameworks

Mòdul 7: Projectes Pràctics

Mòdul 8: Consideracions Ètiques i Futur del Deep Learning

© Copyright 2026. Tots els drets reservats