BERT y GPT: una columna cada uno
20 min read
El Transformer de la lección anterior —el proyecto, montado y ejecutado de principio a fin— sabe hacer una cosa y ninguna más: correr. Sus pesos siguen siendo los que devolvió el generador de números aleatorios, y corregirlos pide un conjunto de entrenamiento. El que esa arquitectura pide es de los caros: pares de frases, cada una con su traducción, escritos uno a uno por alguien que sabe los dos idiomas. Texto suelto, en cambio, sobra —hay muchísimo más del que nadie va a leer nunca—. La pregunta de hoy es qué se le puede enseñar a un modelo con un texto y nada más.
Hay una segunda deuda que vence hoy, y es mucho más vieja. El bloque 1 se despidió —su última lección, sobre GloVe y los límites de los embeddings estáticos— con una entrada del vocabulario que no cabía en su propia maquinaria: banco tiene una fila de y tiene una sola, de modo que el banco cerró a las dos y el banco estaba mojado se llevan el mismo vector y ninguna de las dos frases tiene manera de pedir otro. Las dos deudas resultan ser la misma, y ésa es la tesis de hoy: lo que hace falta para aprender de texto suelto es exactamente lo que le da a banco dos vectores.
Pregúntate de qué depende el vector que un modelo le asigna a la posición de banco. En el bloque 1 la respuesta cabía en una palabra: de banco. La tabla se consulta con la entrada y devuelve su fila, y la frase alrededor podría no existir. Un Transformer contesta distinto, porque su auto-atención mezcla filas: lo que sale en una posición depende de las posiciones que esa posición pudo mirar.
Y cuáles pudo mirar lo decide una sola cosa, la máscara de la lección sobre el encoder, el decoder y las máscaras. Con ella puesta, la posición de banco ve el banco y se acaba ahí; sin ella ve la frase entera, cerró a las dos incluido. No es un detalle interno del decoder: es el interruptor que decide cuánto del texto llega a cada posición, y las dos posiciones de ese interruptor son los dos modelos de esta lección.
De qué depende el vector de una posición
La lección sobre el problema de representar el lenguaje escribió la representación como una función:
una entrada del vocabulario dentro, un vector fuera. Lo que esa firma no admite es la frase, y sin la frase nada distingue las dos apariciones de banco. Cambiar los números de no toca el problema; hay que cambiar el argumento.
Un modelo causal lo cambia por lo escrito hasta esa posición. Llamando a las secuencias finitas de tokens —la estrella de la lección sobre la tokenización—:
y el vector de la posición es . Fíjate en que no hace falta pasarle aparte: la posición es el último índice del prefijo. Un modelo bidireccional lo cambia por la frase entera, y entonces sí hace falta decir de qué posición se habla:
con el vector de la posición igual a . Las dos últimas firmas son las de una representación contextual: el mismo vocabulario, la misma tabla debajo, y un argumento que ya no es una entrada.
Las tres salen de la misma aritmética y lo único que las separa es la máscara. La segunda tiene además una consecuencia que conviene ver antes de las cuentas: depende del prefijo y de nada más, así que dos frases que empiezan igual reciben el mismo vector en esa posición. el banco cerró a las dos y el banco estaba mojado comparten el banco, de modo que un modelo causal le da a banco un vector por prefijo, no uno por frase. La deuda del bloque 1, en esa posición, la paga la tercera firma y sólo ella.
Dos maneras de quitar una columna
GPT (Generative Pre-trained Transformer) se queda con la columna derecha. Le sobra una subcapa —la atención encoder-decoder no tiene a quién mirar cuando no hay encoder—, así que sus bloques llevan dos, igual que los del encoder, con la diferencia de que la auto-atención lleva máscara en todos ellos, que es lo que la lección anterior midió. Lo que se le pide predecir es el token siguiente, y la fórmula no es nueva: es la del modelo de lenguaje de caracteres de la lección sobre el modelo de lenguaje a nivel de carácter, con tokens donde había caracteres y el Transformer donde estaba la RNN (recurrent neural network),
Eso es lo que contesta a la pregunta de arriba: cualquier texto sirve de conjunto de entrenamiento, y uno de tokens trae ejemplos ya hechos, con su etiqueta puesta por el propio texto. Nadie tiene que escribir nada más.
BERT (Bidirectional Encoder Representations from Transformers) se queda con la izquierda y le quita la máscara, y ahí esa fórmula deja de servir. Desarrollémoslo, porque es la única cuenta de la lección. La lección anterior demostró que respetar el pasado se conserva al componer subcapas, y que por eso la barra vertical de dice la verdad. Quita la máscara y el argumento se cae por donde entró: la auto-atención de la primera subcapa ya mezcla en su fila la fila , así que lo que sale en la posición contiene . Pedirle que prediga es pedirle que copie. La pérdida se va al suelo, la barra vertical condiciona sobre su propia respuesta y de la frase no ha aprendido nada.
De ahí sale el modelado de lenguaje enmascarado (masked language modelling, MLM): tapa una parte de las posiciones con [MASK] —la marca que tapa una posición— y pide justo las tapadas. Con la frase ya tapada, cada posición escondida aporta
y es la media de ese número sobre las tapadas. La respuesta ya no está en la entrada porque se ha borrado de la entrada, que es la diferencia entera. Y trae su precio: BERT cobra la fórmula de GPT sólo en las posiciones que tapó —un 15 % de ellas, en el artículo original—, mientras que GPT la cobra en las .
Lo que las dos familias comparten viene después, y es lo que las hizo importar. Primero el preentrenamiento: muchas vueltas contra texto crudo, sin tarea ninguna a la vista, hasta que la representación contextual valga algo. Después, la tarea de quien lo use. Lo que vino luego —modelos mucho mayores, otras maneras de codificar la posición, atenciones más baratas para textos largos— cambió los detalles de las quince cajas y no la aritmética que escribiste en la lección anterior.
Banco, en dos frases
La celda pone las dos frases una al lado de otra, con banco en la misma posición en las dos, y las mete por una llamada de auto-atención con un interruptor para la máscara. Compara los dos vectores de esa posición con la similitud coseno de la lección sobre la bolsa de palabras. Son tres números, y dos de ellos valen por construcción, no por suerte.
f1 = "el banco cerró a las dos".split()
f2 = "el banco estaba mojado".split()
V = sorted(set(f1 + f2))
ix = {w: i for i, w in enumerate(V)}
d, t = 32, 1 # <banco> ocupa la posicion 1 en las dos
rng = np.random.default_rng(0)
E = rng.normal(size=(len(V), d)) / np.sqrt(d)
Wq, Wk, Wv = [rng.normal(size=(d, d)) / np.sqrt(d) for _ in range(3)]
def entrada(frase): # la tabla mas la codificacion posicional
i = np.arange(d // 2)
pos = np.arange(len(frase))[:, None]
PE = np.zeros((len(frase), d))
PE[:, 0::2] = np.sin(pos / 10000 ** (2 * i / d))
PE[:, 1::2] = np.cos(pos / 10000 ** (2 * i / d))
return np.sqrt(d) * E[[ix[w] for w in frase]] + PE
def auto_atencion(X, causal):
S = (X @ Wq) @ (X @ Wk).T / np.sqrt(d)
if causal:
S = np.where(np.triu(np.ones(S.shape, dtype=bool), k=1), -np.inf, S)
A = np.exp(S - S.max(axis=1, keepdims=True))
A = A / A.sum(axis=1, keepdims=True)
return A @ (X @ Wv)
def cos(u, v):
return float(u @ v / (np.linalg.norm(u) * np.linalg.norm(v)))
X1, X2 = entrada(f1), entrada(f2)
print("el mismo array a la entrada:", bool(np.allclose(X1[t], X2[t])))
print("coseno a la entrada : %.6f" % cos(X1[t], X2[t]))
print("coseno con mascara : %.6f" % cos(auto_atencion(X1, True)[t], auto_atencion(X2, True)[t]))
print("coseno sin mascara : %.6f" % cos(auto_atencion(X1, False)[t], auto_atencion(X2, False)[t]))
La primera ejecución descarga el intérprete de Python (~15 MB). Después queda en la caché del navegador.
Los dos primeros salen y salen exactos, cada uno por su motivo. A la entrada las dos filas son el mismo array: la misma fila de , la misma posición y por tanto la misma fila de sumada encima. Con máscara, la fila de los pesos de atención sólo tiene peso en las columnas y , y esas dos filas de son idénticas en las dos frases, así que las puntuaciones son idénticas y lo que sale también. El tercero baja a , y ése es el que hacía falta: quitada la máscara, cerró a las dos y estaba mojado entran en la mezcla y el vector de banco deja de ser el mismo.
Con pesos que nadie ha entrenado, cuánto se separan esos dos vectores no significa nada —lo decidió el generador—. Lo que es de la arquitectura y no del generador es que puedan separarse: la firma admite la frase, así que la frase alcanza al resultado. Que los separe bien —el banco del dinero lejos del banco del parque— es lo que el preentrenamiento tiene que conseguir, y no hay número en esta celda que lo demuestre.
Comprueba tu intuición
Cuatro preguntas: qué cambiaron los dos respecto de la figura del artículo, por qué un encoder sin máscara no puede entrenarse contra el token siguiente, cuál de las tres firmas le da a banco dos vectores, y qué se puede preentrenar con un texto suelto.
BERT y GPT salen los dos de la figura 1 de Attention is All You Need. Marca lo que cada uno cambió respecto de ella.
Select every correct option. This is graded all-or-nothing: there is no partial credit.
Un encoder sin máscara entrenado con la pérdida aprendería un modelo de lenguaje que además mira a la derecha: lo mejor de los dos mundos.
el banco cerró a las dos y el banco estaba mojado. ¿Cuál de las tres firmas le da a banco dos vectores distintos en la posición que ocupa?
Tienes un archivo de novelas en español y nada más: ni traducciones, ni etiquetas, ni preguntas. Marca lo que puedes preentrenar con él.
Select every correct option. This is graded all-or-nothing: there is no partial credit.
Ninguno de los dos sirve, recién preentrenado, para lo que tú quieras. Lo que sale de esa primera fase es una representación contextual buena y ninguna tarea: BERT no clasifica reseñas y GPT no contesta preguntas, porque nadie les ha dicho todavía qué es una reseña ni qué es una pregunta. Lo que falta es la segunda mitad de la receta, y es la mitad corta —unas pocas vueltas sobre unos pocos miles de ejemplos etiquetados, partiendo de los pesos del preentrenamiento en lugar del generador de números aleatorios.
Eso es el fine-tuning, y es la lección siguiente, la última: hacerlo de verdad, sobre un modelo preentrenado de verdad. No corre en el navegador, y la lección empieza diciendo por qué —los pesos que hay que mover no caben donde han cabido las celdas de este curso—. Se hace en un cuaderno de Colab, con la máquina que hace falta al otro lado.
Further reading2 sources · 2 papers
Where this lesson comes from, and where to go next. None of it is needed to carry on with the course.
- Improving Language Understanding by Generative Pre-Training
El GPT de la lección: un decoder de doce bloques con máscara en todas las auto-atenciones, preentrenado a predecir el token siguiente y luego afinado por tarea. Buena parte del artículo va del afinado, que es la lección siguiente.
- BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding
Su tarea #1, el lenguaje enmascarado, es tu argumento: sin máscara el modelo «se ve a sí mismo» y predecir lo siguiente sería copiar; tapando el 15 % y pidiéndolo, no. Su segunda tarea, la de pares de frases, queda fuera.