El ABC de los Transformers Todo lo que necesitas saber
El ABC de los Transformers Todo lo que necesitas saber
Todo lo que necesitas saber sobre los Transformers y cómo implementarlos
![Imagen de autor](https://ai.miximages.com/miro.medium.com/v2/resize:fit:640/format:webp/1*vCqM2C1uL3JpkKcr3IolbA.png)
¿Por qué otro tutorial sobre los Transformers?
Probablemente ya hayas escuchado hablar sobre los Transformers, y todo el mundo habla de ello, ¿entonces por qué hacer un nuevo artículo al respecto?
Bueno, soy investigador, y esto me exige tener un conocimiento muy profundo de las herramientas que utilizo (porque si no las entiendes, ¿cómo puedes identificar dónde están equivocadas y cómo mejorarlas, verdad?).
A medida que me adentraba más en el mundo de los Transformers, me encontré sepultado bajo una montaña de recursos. Y sin embargo, a pesar de toda esa lectura, me quedé con una idea general de la arquitectura y con una serie de preguntas persistentes.
En esta guía, mi objetivo es llenar esa brecha de conocimiento. Una guía que te dará una sólida intuición sobre los Transformers, una inmersión profunda en la arquitectura y la implementación desde cero.
- De harapos a riquezas
- Dominando el Universo de los Datos Claves para una Carrera Exitosa en Ciencia de Datos
- LlamaIndex Mejora fácilmente tus solicitudes de LLM con datos personalizados
Te recomiendo encarecidamente que sigas el código en Github:
awesome-ai-tutorials/NLP/007 – Transformers From Scratch at main ·…
La mejor colección de tutoriales de IA para convertirte en un experto de la Ciencia de Datos. – awesome-ai-tutorials/NLP/007 – Transformers…
github.com
¡Disfruta! 🤗
Un poco de historia primero:
Muchos atribuyen el concepto del mecanismo de atención al renombrado artículo “Atención es todo lo que necesitas” del equipo de Google Brain. Sin embargo, esto es solo una parte de la historia.
Las raíces del mecanismo de atención se remontan a un artículo anterior titulado “Traducción Neural Automática mediante Alineación y Traducción Conjunta” escrito por Dzmitry Bahdanau, KyungHyun Cho y Yoshua Bengio.
El principal desafío de Bahdanau era abordar las limitaciones de las Redes Neuronales Recurrentes (RNNs). Específicamente, al codificar frases largas en vectores utilizando RNNs, a menudo se perdía información crucial.
Tomando como referencia los ejercicios de traducción, donde uno a menudo vuelve a visitar la oración de origen mientras traduce, Bahdanau buscó asignar pesos a los estados ocultos dentro de la RNN. Este enfoque produjo resultados impresionantes, como se muestra en el siguiente diagrama.
![Imagen del artículo 'Traducción Neural Automática mediante Alineación y Traducción Conjunta'](https://ai.miximages.com/miro.medium.com/v2/resize:fit:640/format:webp/1*dfe-wCLeqgBsqPb2D7QyzA.png)
Sin embargo, Bahdanau no fue el único abordando este problema. Inspirándose en su trabajo innovador, el equipo de Google Brain planteó una idea audaz:
“¿Por qué no eliminar todo y centrarse únicamente en el mecanismo de atención?”
Ellos creían que no era la RNN sino el mecanismo de atención el que impulsaba el éxito.
Esta convicción culminó en su artículo, adecuadamente titulado “Atención es todo lo que necesitas”.
¡Fascinante, ¿verdad?
La arquitectura Transformer
1. Lo primero, los Embeddings
Este diagrama representa la arquitectura Transformer. No te preocupes si al principio no entiendes nada, lo cubriremos absolutamente todo.
![Incrustaciones, Imagen del artículo modificada por el autor](https://ai.miximages.com/miro.medium.com/v2/resize:fit:640/format:webp/1*QX5CJXpXa55O_-or4Z6UyQ.png)
De texto a vectores: el proceso de incrustación. Imagina que nuestra entrada es una secuencia de palabras, digamos “El gato bebe leche”. Esta secuencia tiene una longitud denominada seq_len
. Nuestra tarea inmediata es convertir estas palabras en una forma comprensible para el modelo, específicamente vectores. Ahí es donde entra en juego el incrustador.
Cada palabra sufre una transformación para convertirse en un vector. Este proceso de transformación se denomina ‘incrustación’. Cada uno de estos vectores o ‘incrustaciones’ tiene un tamaño de d_model = 512
.
Ahora, ¿qué es exactamente este incrustador? En su núcleo, el incrustador es un mapeo lineal (matriz), denotado por E
. Puedes visualizarlo como una matriz de tamaño (d_model, vocab_size)
, donde vocab_size
es el tamaño de nuestro vocabulario.
Después del proceso de incrustación, obtenemos una colección de vectores cada uno con un tamaño de d_model
. Es crucial entender este formato, ya que es un tema recurrente y lo veremos en diversas etapas como la entrada del codificador, salida del codificador, etc.
Codifiquemos esta parte:
class Embeddings(nn.Module): def __init__(self, d_model, vocab): super(Embeddings, self).__init__() self.lut = nn.Embedding(vocab, d_model) self.d_model = d_model def forward(self, x): return self.lut(x) * math.sqrt(self.d_model)
Nota: multiplicamos por d_model
por fines de normalización (explicado posteriormente)
Nota 2: personalmente me pregunté si usamos un incrustador pre-entrenado, o al menos empezamos desde uno pre-entrenado y lo afinamos. Pero no, la incrustación se aprende completamente desde cero y se inicializa aleatoriamente.
Codificación Posicional
¿Por qué necesitamos la codificación posicional?
Dado nuestro arreglo actual, tenemos una lista de vectores que representan palabras. Si los alimentamos así al modelo de transformador, hay un elemento clave que falta: el orden secuencial de las palabras. Las palabras en los lenguajes naturales a menudo obtienen significado a partir de su posición. “Juan ama a María” tiene un sentimiento diferente a “María ama a Juan”. Para asegurar que nuestro modelo capture este orden, introducimos la codificación posicional.
Ahora, te puedes preguntar, “¿Por qué no simplemente agregar un incremento simple como +1 para la primera palabra, +2 para la segunda, y así sucesivamente?” Hay varios desafíos con este enfoque:
- Multidimensionalidad: Cada token se representa en 512 dimensiones. Un simple incremento no sería suficiente para capturar este espacio complejo.
- Preocupaciones de normalización: Idealmente, queremos que nuestros valores estén entre -1 y 1. Por lo tanto, agregar directamente números grandes (como +2000 para un texto largo) sería problemático.
- Dependencia de la longitud de la secuencia: El uso de incrementos directos no es agnóstico a la escala. Para un texto largo, donde la posición puede ser +5000, este número no refleja verdaderamente la posición relativa del token en su oración asociada. Y el significado de una palabra depende más de su posición relativa en una oración que de su posición absoluta en un texto.
Si estudiaste matemáticas, la idea de coordenadas circulares – específicamente funciones seno y coseno – debería resonar con tu intuición. Estas funciones proporcionan una forma única de codificar la posición que cumple nuestras necesidades.
Dado nuestra matriz de tamaño (seq_len, d_model)
, nuestro objetivo es agregar otra matriz, la Codificación Posicional, del mismo tamaño.
Aquí está el concepto principal:
- Para cada token, los autores sugieren proporcionar una coordenada seno a las dimensiones pares (2k) y una coordenada coseno a las dimensiones impares (2k+1).
- Si fijamos la posición del token y movemos la dimensión, podemos ver que el seno/coseno disminuye en frecuencia.
- Si observamos un token que está más lejos en el texto, este fenómeno ocurre más rápidamente (la frecuencia aumenta).
![Imagen del artículo](https://ai.miximages.com/miro.medium.com/v2/resize:fit:640/format:webp/1*1YdoohioAeUppjJYIMmkRw.png)
Todo esto se resume en el siguiente gráfico (pero no te rompas mucho la cabeza con esto). La clave es que la Codificación Posicional es una función matemática que permite al Transformer mantener una idea del orden de los tokens en la oración. Es un área de investigación muy activa.
![Incrustación posicional, imagen del autor](https://ai.miximages.com/miro.medium.com/v2/resize:fit:640/format:webp/1*sy_Lsif9wB_v2SZcLUyYQA.png)
class PositionalEncoding(nn.Module): "Implementa la función PE." def __init__(self, d_model, dropout, max_len=5000): super(PositionalEncoding, self).__init__() self.dropout = nn.Dropout(p=dropout) # Calcula las codificaciones posicionales una vez en escala logarítmica. pe = torch.zeros(max_len, d_model) position = torch.arange(0, max_len).unsqueeze(1) div_term = torch.exp( torch.arange(0, d_model, 2) * -(math.log(10000.0) / d_model) ) pe[:, 0::2] = torch.sin(position * div_term) pe[:, 1::2] = torch.cos(position * div_term) pe = pe.unsqueeze(0) self.register_buffer("pe", pe) def forward(self, x): x = x + self.pe[:, : x.size(1)].requires_grad_(False) return self.dropout(x)
El Mecanismo de Atención (Cabeza Única)
Sumerjámonos en el concepto principal del artículo de Google: el Mecanismo de Atención
Intuición de Alto Nivel:
En su esencia, el mecanismo de atención es un mecanismo de comunicación entre vectores/tokens. Permite que un modelo se centre en partes específicas de la entrada al producir una salida. Piénsalo como si pusieras un foco de atención en ciertas partes de tus datos de entrada. Este “foco de atención” puede ser más brillante en partes más relevantes (dándoles más atención) y más tenue en partes menos relevantes.
Para una oración, la atención ayuda a determinar la relación entre las palabras. Algunas palabras están estrechamente relacionadas entre sí en significado o función dentro de una oración, mientras que otras no lo están. El mecanismo de atención cuantifica estas relaciones.
Ejemplo:
Considera la oración: “Ella le dio su libro.”
Si nos enfocamos en la palabra “su”, el mecanismo de atención podría determinar que:
- Tiene una fuerte conexión con “libro” porque “su” indica posesión del “libro”.
- Tiene una conexión de igual a igual con “Ella” porque “Ella” y “su” probablemente se refieren a la misma entidad.
- Tiene una conexión más débil con otras palabras como “dio” o “le”.
Profundización Técnica en el mecanismo de atención
![Atención con Producto Escalar, imagen del artículo](https://ai.miximages.com/miro.medium.com/v2/resize:fit:640/format:webp/1*brgzA2gMjroqm8UPNKrwig.png)
Para cada token, generamos tres vectores:
- Consulta (Q):
Intuición: Piensa en la consulta como una “pregunta” que un token hace. Representa la palabra actual e intenta descubrir qué partes de la secuencia son relevantes para ella.
2. Clave (K):
Intuición: La clave puede considerarse una “identificación” para cada palabra en la secuencia. Cuando la consulta “hace” su pregunta, la clave ayuda a “responder” al determinar qué tan relevante es cada palabra de la secuencia para la consulta.
3. Valor (V):
Intuición: Una vez determinada la relevancia de cada palabra (a través de su clave) para la consulta, necesitamos información o contenido real de esas palabras para ayudar al token actual. Aquí es donde entra en juego el valor. Representa el contenido de cada palabra.
¿Cómo se generan Q, K, V?
![Q, K, V generation, image by author](https://ai.miximages.com/miro.medium.com/v2/resize:fit:640/format:webp/1*3PTo9xubnGbxyezLJc52_g.png)
La similitud entre una consulta y una clave es un producto punto (mide la similitud entre 2 vectores), dividido por la desviación estándar de esta variable aleatoria, para normalizar todo.
![Fórmula de atención, Imagen del artículo](https://ai.miximages.com/miro.medium.com/v2/resize:fit:640/format:webp/1*3rjC9HX6F9TvQylMihKSaA.png)
Vamos a ilustrar esto con un ejemplo:
Imaginemos que tenemos una consulta y queremos determinar el resultado de la atención con K y V:
![Q, K, V, Imagen del autor](https://ai.miximages.com/miro.medium.com/v2/resize:fit:640/format:webp/1*OHsoUBYXOJtHEkhus-GOYA.png)
Ahora calculemos las similitudes entre q1 y las claves:
![Producto punto, Imagen del autor](https://ai.miximages.com/miro.medium.com/v2/resize:fit:640/format:webp/1*JtQFxVnUf60-CsNjnJ_3qA.png)
Aunque los números 3/2 y 1/8 podrían parecer relativamente cercanos, la naturaleza exponencial de la función softmax amplificaría su diferencia.
![Pesos de atención, Imagen del autor](https://ai.miximages.com/miro.medium.com/v2/resize:fit:640/format:webp/1*kCA4RAAhFkI7mbxSKMzOWg.png)
Esta diferencia sugiere que q1 tiene una conexión más pronunciada con k1 que con k2.
Ahora veamos el resultado de la atención, que es una combinación ponderada (pesos de atención) de los valores:
![Atención, Imagen del autor](https://ai.miximages.com/miro.medium.com/v2/resize:fit:640/format:webp/1*BKfsaZhzZ5w2tuQQ4x51mA.png)
¡Genial! Repitiendo esta operación para cada token (q1 a través de qn) obtenemos una colección de n vectores.
En la práctica, esta operación se vectoriza en una multiplicación matricial para mayor eficacia.
Codifiquémoslo:
def attention(query, key, value, mask=None, dropout=None): "Calcular 'Atención de Producto Punto Escalada'" d_k = query.size(-1) scores = torch.matmul(query, key.transpose(-2, -1)) / math.sqrt(d_k) if mask is not None: scores = scores.masked_fill(mask == 0, -1e9) p_attn = scores.softmax(dim=-1) if dropout is not None: p_attn = dropout(p_attn) return torch.matmul(p_attn, value), p_attn
Atención Multidimensional
¿Cuál es el problema con la atención de un solo cabezal?
Con el enfoque de atención de un solo cabezal, cada token puede formular solo una consulta. Esto suele traducirse en que establece una relación fuerte con solo otro token, dado que softmax tiende a dar mucho peso a un valor mientras que reduce otros cercanos a cero. Sin embargo, al pensar en el lenguaje y las estructuras de las oraciones, una palabra a menudo tiene conexiones con múltiples palabras, no solo una.
Para abordar esta limitación, introducimos atención multi-cabeza. ¿La idea principal? Permitamos que cada token haga varias preguntas (consultas) simultáneamente ejecutando el proceso de atención en paralelo ‘h’ veces. El Transformer original utiliza 8 cabezas.
![Atención multi-cabeza, imagen del artículo](https://ai.miximages.com/miro.medium.com/v2/resize:fit:640/format:webp/1*hp0lgq2MvS_cDFD5eAgntQ.png)
Una vez que obtenemos los resultados de las 8 cabezas, los concatenamos en una matriz.
![Atención multi-cabeza, imagen del artículo](https://ai.miximages.com/miro.medium.com/v2/resize:fit:640/format:webp/1*85cT_sXuxhlLiL9TId-0OQ.png)
Esto también es sencillo de codificar, solo debemos tener cuidado con las dimensiones:
class MultiHeadedAttention(nn.Module): def __init__(self, h, d_model, dropout=0.1): "Toma el tamaño del modelo y el número de cabezas." super(MultiHeadedAttention, self).__init__() assert d_model % h == 0 # Asumimos que d_v siempre es igual a d_k self.d_k = d_model // h self.h = h self.linears = clones(nn.Linear(d_model, d_model), 4) self.attn = None self.dropout = nn.Dropout(p=dropout) def forward(self, query, key, value, mask=None): "Implementa la Figura 2" if mask is not None: # Máscara aplicada a todas las cabezas h. mask = mask.unsqueeze(1) nbatches = query.size(0) # 1) Realiza todas las proyecciones lineales en lote de d_model => h x d_k query, key, value = [ lin(x).view(nbatches, -1, self.h, self.d_k).transpose(1, 2) for lin, x in zip(self.linears, (query, key, value)) ] # 2) Aplica la atención en todos los vectores proyectados en lote. x, self.attn = attention(query, key, value, mask=mask, dropout=self.dropout) # 3) "Concatena" utilizando una vista y aplica una proyección lineal final. x = x.transpose(1, 2).contiguous().view(nbatches, -1, self.h * self.d_k) del query del key del value return self.linears[-1](x)
Ahora deberías empezar a entender por qué los Transformers son tan poderosos, aprovechan al máximo el paralelismo.
Ensamblaje de las partes del Transformer
En términos generales, un Transformer es la combinación de 3 elementos: un Codificador, un Decodificador y un Generador
![Codificador, Decodificador, Generador, Imagen del artículo modificada por el autor](https://ai.miximages.com/miro.medium.com/v2/resize:fit:640/format:webp/1*rt6QnkUCSA_kpj32rvecug.png)
1. El Codificador
- Propósito: Convertir una secuencia de entrada en una nueva secuencia (por lo general, de menor dimensión) que capture la esencia de los datos originales.
- Nota: Si has oído hablar del modelo BERT, utiliza solo esta parte de codificación del Transformer.
2. El Decodificador
- Propósito: Generar una secuencia de salida utilizando la secuencia codificada del Codificador.
- Nota: El decodificador en el Transformer es diferente del decodificador típico de un autoencoder. En el Transformer, el decodificador no solo considera la salida codificada, sino que también tiene en cuenta los tokens que ha generado hasta ahora.
3. El Generador
- Propósito: Convertir un vector en un token. Hace esto proyectando el vector al tamaño del vocabulario y luego seleccionando el token más probable con la función softmax.
Vamos a codificar eso:
class EncoderDecoder(nn.Module): """ Una arquitectura estándar de codificador-decodificador. Base para este y muchos otros modelos. """ def __init__(self, encoder, decoder, src_embed, tgt_embed, generator): super(EncoderDecoder, self).__init__() self.encoder = encoder self.decoder = decoder self.src_embed = src_embed self.tgt_embed = tgt_embed self.generator = generator def forward(self, src, tgt, src_mask, tgt_mask): "Tomar y procesar secuencias de origen y objetivo enmascaradas." return self.decode(self.encode(src, src_mask), src_mask, tgt, tgt_mask) def encode(self, src, src_mask): return self.encoder(self.src_embed(src), src_mask) def decode(self, memory, src_mask, tgt, tgt_mask): return self.decoder(self.tgt_embed(tgt), memory, src_mask, tgt_mask)class Generator(nn.Module): "Definir el paso de generación estándar lineal + softmax." def __init__(self, d_model, vocab): super(Generator, self).__init__() self.proj = nn.Linear(d_model, vocab) def forward(self, x): return log_softmax(self.proj(x), dim=-1)
Una observación aquí: “src” se refiere a la secuencia de entrada, y “tgt” se refiere a la secuencia que se está generando. Recuerda que generamos la salida de manera autoregresiva, token por token, por lo que también necesitamos hacer un seguimiento de la secuencia objetivo.
Apilando codificadores
El codificador del Transformer no es solo una capa. En realidad, es una pila de N capas. Específicamente:
- El codificador en el modelo original del Transformer consta de una pila de N=6 capas idénticas.
Dentro de la capa de codificación, podemos ver que hay dos bloques de subcapas muy similares ((1) y (2)): Una conexión residual seguida de una normalización de capa.
- Bloque (1): Mecanismo de autoatención: Ayuda al codificador a enfocarse en diferentes palabras de la entrada al generar la representación codificada.
- Bloque (2): Red neuronal de alimentación directa: Una pequeña red neuronal aplicada de manera independiente a cada posición.
![Capa de codificación, conexiones residuales y normalización de capa, imagen del artículo modificada por el autor](https://ai.miximages.com/miro.medium.com/v2/resize:fit:640/format:webp/1*tXcO7O6lzPji5jwqgUVmdg.png)
Ahora vamos a codificar eso:
Primero, la Conexión de Subcapa:
Seguimos la arquitectura general y podemos cambiar “subcapa” por “autoatención” o “FFN”
class SublayerConnection(nn.Module): """ Una conexión residual seguida de una normalización de capa. Nota: para simplificar el código, la normalización se realiza primero en lugar de al final. """ def __init__(self, size, dropout): super(SublayerConnection, self).__init__() self.norm = nn.LayerNorm(size) # Usar LayerNorm de PyTorch self.dropout = nn.Dropout(dropout) def forward(self, x, sublayer): "Aplicar la conexión residual a cualquier subcapa con el mismo tamaño." return x + self.dropout(sublayer(self.norm(x)))
Ahora podemos definir la capa completa del codificador:
class EncoderLayer(nn.Module): "El codificador se compone de autoatención y alimentación directa (definidas a continuación)" def __init__(self, size, self_attn, feed_forward, dropout): super(EncoderLayer, self).__init__() self.self_attn = self_attn self.feed_forward = feed_forward self.sublayer = clones(SublayerConnection(size, dropout), 2) self.size = size def forward(self, x, mask): # autoatención, bloque 1 x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, mask)) # alimentación directa, bloque 2 x = self.sublayer[1](x, self.feed_forward) return x
La capa del codificador está lista, ahora solo tenemos que unirlas para formar el codificador completo:
def clones(module, N): "Producir N capas idénticas." return nn.ModuleList([copy.deepcopy(module) for _ in range(N)])class Encoder(nn.Module): "El codificador principal es una pila de N capas" def __init__(self, layer, N): super(Encoder, self).__init__() self.layers = clones(layer, N) self.norm = nn.LayerNorm(layer.size) def forward(self, x, mask): "Pasar la entrada (y máscara) a través de cada capa sucesivamente." for layer in self.layers: x = layer(x, mask) return self.norm(x)
Decodificador
El decodificador, al igual que el codificador, está estructurado con múltiples capas idénticas apiladas una encima de la otra. El número de estas capas es típicamente 6 en el modelo Transformer original.
¿Cómo se diferencia el decodificador del codificador?
Se agrega una tercera subcapa para interactuar con el codificador: esto es Atención Cruzada
- La subcapa (1) es la misma que la del codificador. Es el mecanismo de Autoatención, lo que significa que generamos todo (Q, K, V) a partir de los tokens dados al decodificador
- La subcapa (2) es el nuevo mecanismo de comunicación: Atención Cruzada. Se llama así porque usamos la salida de la subcapa (1) para generar las Consultas, y usamos la salida del codificador para generar las Claves y Valores (K, V). En otras palabras, para generar una oración, debemos mirar tanto lo que hemos generado hasta ese momento por el decodificador (autoatención), como lo que preguntamos en primer lugar en el codificador (atención cruzada)
- La subcapa (3) es idéntica a la del codificador.
![Capa del decodificador, autoatención, atención cruzada, imagen del artículo modificada por el autor](https://ai.miximages.com/miro.medium.com/v2/resize:fit:640/format:webp/1*lMfv825ldovCMtoOJU4rxA.png)
Ahora codifiquemos la Capa del Decodificador. Si entendiste el mecanismo en la Capa del Codificador, esto debería ser bastante sencillo.
class DecoderLayer(nn.Module): "El decodificador está formado por autoatención, atención al origen y retroalimentación (definidos a continuación)" def __init__(self, size, self_attn, src_attn, feed_forward, dropout): super(DecoderLayer, self).__init__() self.size = size self.self_attn = self_attn self.src_attn = src_attn self.feed_forward = feed_forward self.sublayer = clones(SublayerConnection(size, dropout), 3) def forward(self, x, memory, src_mask, tgt_mask): "Sigue la Figura 1 (derecha) para las conexiones." m = memory x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, tgt_mask)) # Nueva subcapa (atención cruzada) x = self.sublayer[1](x, lambda x: self.src_attn(x, m, m, src_mask)) return self.sublayer[2](x, self.feed_forward)
Y ahora podemos encadenar las N=6 Capas del Decodificador para formar el Decodificador:
class Decoder(nn.Module): "Decodificador de N capas genérico con enmascaramiento." def __init__(self, layer, N): super(Decoder, self).__init__() self.layers = clones(layer, N) self.norm = nn.LayerNorm(layer.size) def forward(self, x, memory, src_mask, tgt_mask): for layer in self.layers: x = layer(x, memory, src_mask, tgt_mask) return self.norm(x)
En este punto, has entendido alrededor del 90% de lo que es un Transformer. Aún quedan algunos detalles:
Detalles del Modelo Transformer
Relleno:
- En un Transformer típico, hay una longitud máxima para las secuencias (por ejemplo, “max_len=5000”). Esto define la secuencia más larga que el modelo puede manejar.
- Sin embargo, las oraciones del mundo real pueden variar en longitud. Para manejar oraciones más cortas, utilizamos relleno.
- El relleno es la adición de “tokens de relleno” especiales para que todas las secuencias de un lote tengan la misma longitud.
![Relleno, imagen del autor](https://ai.miximages.com/miro.medium.com/v2/resize:fit:640/format:webp/1*77AfGQ9oAwCAb7dl9uxtew.png)
Enmascaramiento
El enmascaramiento asegura que durante el cálculo de la atención, ciertos tokens sean ignorados.
Dos escenarios para el enmascaramiento:
- enmascarado de src: Como hemos añadido tokens de relleno a las secuencias, no queremos que el modelo preste atención a estos tokens insignificantes. Por lo tanto, los enmascaramos.
- enmascarado de tgt o Enmascaramiento progresivo/causal: En el decodificador, al generar tokens secuencialmente, cada token solo debe ser influenciado por los tokens anteriores y no por los futuros. Por ejemplo, al generar la quinta palabra de una oración, no debería conocer la sexta palabra. Esto asegura una generación secuencial de tokens.
![Encubrimiento causal/encubrimiento posterior, imagen del autor](https://ai.miximages.com/miro.medium.com/v2/resize:fit:640/format:webp/1*AXS9zHonQGCS5tO0m7THtw.png)
Luego usamos esta máscara para agregar menos infinito para que el token correspondiente sea ignorado. Este ejemplo debería aclarar las cosas:
![Encubrimiento, un truco en el softmax, imagen del autor](https://ai.miximages.com/miro.medium.com/v2/resize:fit:640/format:webp/1*Qhq_Xz3iQnHBA0jKj4s2zA.png)
FFN: Red Neuronal de Avance
- La capa “Avance” en el diagrama del Transformador es un poco engañosa. No es solo una operación, sino una secuencia de ellas.
- El FFN consta de dos capas lineales. Curiosamente, los datos de entrada, que podrían tener una dimensión
d_model=512
, primero se transforman en una dimensión más altad_ff=2048
y luego se mapean nuevamente a su dimensión original (d_model=512
). - Esto se puede visualizar como si los datos se “expandieran” en medio de la operación antes de ser “comprimidos” nuevamente a su tamaño original.
![Imagen del artículo modificada por el autor](https://ai.miximages.com/miro.medium.com/v2/resize:fit:640/format:webp/1*qQM-g7k004Ok0B8iAJz-Eg.png)
Esto es fácil de codificar:
class PositionwiseFeedForward(nn.Module): "Implementa la ecuación de FFN." def __init__(self, d_model, d_ff, dropout=0.1): super(PositionwiseFeedForward, self).__init__() self.w_1 = nn.Linear(d_model, d_ff) self.w_2 = nn.Linear(d_ff, d_model) self.dropout = nn.Dropout(dropout) def forward(self, x): return self.w_2(self.dropout(self.w_1(x).relu()))
Conclusión
El éxito incomparable y la popularidad del modelo Transformador se pueden atribuir a varios factores clave:
- Flexibilidad. Los Transformers pueden trabajar con cualquier secuencia de vectores. Estos vectores pueden ser incrustaciones de palabras. Es fácil transponer esto a la Visión por Computadora convirtiendo una imagen en diferentes fragmentos y desplegando un fragmento en un vector. Incluso en Audio, podemos dividir un audio en diferentes fragmentos y vectorizarlos.
- Generalidad: Con un sesgo inductivo mínimo, el Transformador es libre de capturar patrones intrincados y matizados en los datos, lo que le permite aprender y generalizar mejor.
- Rapidez y Eficiencia: Aprovechando el inmenso poder computacional de las GPUs, los Transformers están diseñados para un procesamiento paralelo.
¡Gracias por leer! Antes de que te vayas:
Puedes ejecutar los experimentos con mi Repositorio de Transformadores en Github.
Para obtener más tutoriales increíbles, revisa mi compilación de tutoriales de AI en Github
GitHub — FrancoisPorcher/awesome-ai-tutorials: La mejor colección de tutoriales de IA para convertirte en un…
La mejor colección de tutoriales de IA para convertirte en un jefe de Ciencia de Datos — GitHub …
github.com
Deberías recibir mis artículos en tu bandeja de entrada. Suscríbete aquí.
Si quieres tener acceso a artículos premium en VoAGI, solo necesitas una membresía por $5 al mes. Si te registras con mi enlace, me estarás apoyando con una parte de tu tarifa sin costos adicionales.
Si encontraste este artículo esclarecedor y beneficioso, ¡considera seguirme y dejar una palmada para obtener más contenido en profundidad! Tu apoyo me ayuda a continuar produciendo contenido que contribuye a nuestra comprensión colectiva.
Referencias
- Attention is all you need
- The Annotated Transformer (una buena parte del código está inspirado en su publicación de blog)
- Conferencia de Andrej Karpathy en Stanford
Para profundizar más
Incluso con una guía completa, hay muchas otras áreas relacionadas con los Transformers. Aquí hay algunas ideas que quizás quieras explorar:
- Codificación Posicional: se han realizado mejoras significativas, puedes revisar “Relative positional Encoding” y “Rotary Positional Embedding (RoPE)”
- Layer Norm, y las diferencias con batch norm, group norm
- Conexiones Residuales, y su efecto en suavizar el gradiente
- Mejoras realizadas a BERT (Roberta, ELECTRA, Camembert)
- Destilación de modelos grandes en modelos más pequeños
- Aplicaciones de Transformers en otros campos (principalmente visión y audio)
- El vínculo entre Transformers y Redes Neuronales de Grafos
We will continue to update Zepes; if you have any questions or suggestions, please contact us!
Was this article helpful?
93 out of 132 found this helpful
Related articles
- El Impacto de los Modelos de Lenguaje Amplio en el Análisis de Texto Médico
- Las 5 mejores plataformas y herramientas de aprendizaje automático en la nube
- Conoce LoftQ LoRA-Fine-Tuning-Aware Quantization para grandes modelos de lenguaje.
- ¿Pueden los desarrolladores reducir el costo total de propiedad del software con IA?
- 8 Estrategias para acelerar el desarrollo de portales web
- Una guía completa para crear un asistente de IA para resumir videos de YouTube – Parte 1
- ¿Cómo optimizar los ingresos utilizando la fijación dinámica de precios?