Aprovechando los puntos de control de modelos de lenguaje pre-entrenados para modelos codificador-decodificador.

Utilizando puntos de control de modelos de lenguaje pre-entrenados para modelos codificador-decodificador.

Los modelos codificador-decodificador basados en transformadores fueron propuestos en Vaswani et al. (2017) y recientemente han experimentado un aumento de interés, por ejemplo, Lewis et al. (2019), Raffel et al. (2019), Zhang et al. (2020), Zaheer et al. (2020), Yan et al. (2020).

Al igual que BERT y GPT2, los modelos codificador-decodificador pre-entrenados masivos han demostrado mejorar significativamente el rendimiento en una variedad de tareas de secuencia a secuencia Lewis et al. (2019), Raffel et al. (2019). Sin embargo, debido al enorme costo computacional asociado con el pre-entrenamiento de modelos codificador-decodificador, el desarrollo de tales modelos se limita principalmente a grandes empresas e institutos.

En el artículo “Aprovechando puntos de control pre-entrenados para tareas de generación de secuencias” (2020), Sascha Rothe, Shashi Narayan y Aliaksei Severyn inicializan el modelo codificador-decodificador con puntos de control de solo codificador y/o decodificador pre-entrenados (por ejemplo, BERT, GPT2) para evitar el costoso pre-entrenamiento. Los autores demuestran que estos modelos codificador-decodificador iniciados de esta manera obtienen resultados competitivos en comparación con modelos codificador-decodificador pre-entrenados masivos, como T5 y Pegasus, en múltiples tareas de secuencia a secuencia a una fracción del costo de entrenamiento.

En este cuaderno, explicaremos en detalle cómo se pueden iniciar de forma rápida los modelos codificador-decodificador, daremos consejos prácticos basados en Rothe et al. (2020) y, finalmente, presentaremos un ejemplo de código completo que muestra cómo iniciar rápidamente los modelos codificador-decodificador con 🤗Transformers.

Este cuaderno se divide en 4 partes:

  • Introducción: Resumen breve de los modelos de lenguaje pre-entrenados en PLN y la necesidad de iniciar rápidamente los modelos codificador-decodificador.
  • Iniciar rápidamente los modelos codificador-decodificador (Teoría): Explicación ilustrativa de cómo se inician rápidamente los modelos codificador-decodificador.
  • Iniciar rápidamente los modelos codificador-decodificador (Análisis): Resumen de “Aprovechando puntos de control pre-entrenados para tareas de generación de secuencias”
      • ¿Qué combinaciones de modelos son efectivas para iniciar rápidamente los modelos codificador-decodificador? ¿Cómo difiere de una tarea a otra?
  • Iniciar rápidamente los modelos codificador-decodificador con 🤗Transformers (Práctica): Ejemplo de código completo que muestra en detalle cómo utilizar el marco EncoderDecoderModel para iniciar rápidamente los modelos codificador-decodificador basados en transformadores.

Se recomienda encarecidamente (probablemente incluso es necesario) haber leído esta publicación de blog sobre modelos codificador-decodificador basados en transformadores.

Comencemos dando un poco de contexto sobre cómo iniciar rápidamente los modelos codificador-decodificador.

Introducción

Recientemente, los modelos de lenguaje pre-entrenados{}^1 1 han revolucionado el campo del procesamiento del lenguaje natural (PLN).

Los primeros modelos de lenguaje pre-entrenados se basaron en redes neuronales recurrentes (RNN), como propuso Dai et al. (2015). Dai et. al demostró que pre-entrenar un modelo basado en RNN con datos no etiquetados y posteriormente ajustarlo finamente{}^2 2 en una tarea específica produce mejores resultados que entrenar un modelo inicializado aleatoriamente directamente en esa tarea. Sin embargo, solo fue en 2018 cuando los modelos de lenguaje pre-entrenados fueron ampliamente aceptados en PLN. ELMO de Peters et al. y ULMFit de Howard et al. fueron los primeros modelos de lenguaje pre-entrenados que mejoraron significativamente el estado del arte en una serie de tareas de comprensión del lenguaje natural (NLU). Unos meses después, OpenAI y Google publicaron modelos de lenguaje pre-entrenados basados en transformadores, llamados GPT de Radford et al. y BERT de Devlin et al., respectivamente. La mayor eficiencia de los modelos de lenguaje basados en transformadores sobre las RNN permitió que GPT2 y BERT se pre-entrenaran con grandes cantidades de datos de texto no etiquetados. Una vez pre-entrenados, se demostró que BERT y GPT requerían muy poco ajuste fino para superar los resultados del estado del arte en más de una docena de tareas de NLU 3 {}^3 3.

La capacidad de los modelos de lenguaje pre-entrenados para transferir eficazmente el conocimiento agnóstico de la tarea al conocimiento específico de la tarea resultó ser un gran catalizador para el NLU. Mientras que antes los ingenieros e investigadores tenían que entrenar un modelo de lenguaje desde cero, ahora se pueden ajustar finamente puntos de control de modelos de lenguaje pre-entrenados de gran tamaño disponibles públicamente a una fracción del costo y tiempo. Esto puede ahorrar millones en la industria y permite una prototipación más rápida y mejores puntos de referencia en investigación.

Los modelos de lenguaje pre-entrenados han establecido un nuevo nivel de rendimiento en las tareas de NLU y cada vez más investigaciones se han basado en aprovechar dichos modelos de lenguaje pre-entrenados para mejorar los sistemas de NLU. Sin embargo, los modelos independientes de BERT y GPT han tenido menos éxito en tareas de secuencia a secuencia, como la resumen de texto, traducción automática, reformulación de frases, etc.

Las tareas de secuencia a secuencia se definen como una asignación de una secuencia de entrada X 1 : n \mathbf{X}_{1:n} X 1 : n ​ a una secuencia de salida Y 1 : m \mathbf{Y}_{1:m} Y 1 : m ​ de una longitud de salida m m m desconocida de antemano. Por lo tanto, un modelo de secuencia a secuencia debe definir la distribución de probabilidad condicional de la secuencia de salida Y 1 : m \mathbf{Y}_{1:m} Y 1 : m ​ condicionada a la secuencia de entrada X 1 : n \mathbf{X}_{1:n} X 1 : n ​:

p θ modelo ( Y 1 : m ∣ X 1 : n ) . p_{\theta_{\text{modelo}}}(\mathbf{Y}_{1:m} | \mathbf{X}_{1:n}). p θ modelo ​ ​ ( Y 1 : m ​ ∣ X 1 : n ​ ) .

Sin pérdida de generalidad, una secuencia de palabras de entrada de n n n palabras se representa aquí como el vector secuencia X 1 : n = x 1 , … , x n \mathbf{X}_{1:n} = \mathbf{x}_1, \ldots, \mathbf{x}_n X 1 : n ​ = x 1 ​ , … , x n ​ y una secuencia de salida de m m m palabras como Y 1 : m = y 1 , … , y m \mathbf{Y}_{1:m} = \mathbf{y}_1, \ldots, \mathbf{y}_m Y 1 : m ​ = y 1 ​ , … , y m ​ .

Veamos cómo se ajustarían BERT y GPT2 para modelar tareas de secuencia a secuencia.

BERT

BERT es un modelo solo de codificación, que asigna una secuencia de entrada X 1 : n \mathbf{X}_{1:n} X 1 : n ​ a una secuencia codificada contextualizada X ‾ 1 : n \mathbf{\overline{X}}_{1:n} X 1 : n ​:

f θ BERT : X 1 : n → X ‾ 1 : n . f_{\theta_{\text{BERT}}}: \mathbf{X}_{1:n} \to \mathbf{\overline{X}}_{1:n}. f θ BERT ​ ​ : X 1 : n ​ → X 1 : n ​ .

La secuencia codificada contextualizada X ‾ 1 : n \mathbf{\overline{X}}_{1:n} X 1 : n ​ de BERT luego puede ser procesada aún más por una capa de clasificación para tareas de clasificación de NLU, como el análisis de sentimientos, inferencia de lenguaje natural, etc. Para hacer esto, se agrega la capa de clasificación, es decir, típicamente una capa de agrupación seguida de una capa de avance, como una capa final sobre BERT para asignar la secuencia codificada contextualizada X ‾ 1 : n \mathbf{\overline{X}}_{1:n} X 1 : n ​ a una clase c c c:

f θ p,c : X ‾ 1 : n → c . f_{\theta{\text{p,c}}}: \mathbf{\overline{X}}_{1:n} \to c. f θ p,c ​ : X 1 : n ​ → c .

Se ha demostrado que agregar una capa de agrupación y clasificación, definida como θ p,c \theta_{\text{p,c}} θ p,c ​ , sobre un modelo pre-entrenado de BERT θ BERT \theta_{\text{BERT}} θ BERT ​ y posteriormente afinar el modelo completo { θ p,c , θ BERT } \{\theta_{\text{p,c}}, \theta_{\text{BERT}}\} { θ p,c ​ , θ BERT ​ } puede producir resultados de vanguardia en una variedad de tareas de NLU, cf. al BERT de Devlin et al. .

Visualicemos BERT.

El modelo BERT se muestra en gris. El modelo apila múltiples bloques BERT, cada uno de los cuales está compuesto por capas de auto-atención bidireccional (mostradas en la parte inferior del recuadro rojo) y dos capas de alimentación directa (mostradas en la parte superior del recuadro rojo).

Cada bloque BERT utiliza auto-atención bidireccional para procesar una secuencia de entrada x ′ 1 , … , x ′ n (mostrada en gris claro) a una secuencia de salida contextualizada más “refinada” x ′ ′ 1 , … , x ′ ′ n (mostrada en gris ligeramente más oscuro) 4 {}^4 4 . La secuencia de salida contextualizada del último bloque BERT, es decir, X ‾ 1 : n , luego se puede asignar a una única clase de salida c agregando una capa de clasificación específica para la tarea (mostrada en naranja) como se explicó anteriormente.

Los modelos solo codificadores solo pueden asignar una secuencia de entrada a una secuencia de salida de longitud de salida conocida a priori. En conclusión, la dimensión de salida no depende de la secuencia de entrada, lo que hace desventajoso e impráctico usar modelos solo codificadores para tareas de secuencia a secuencia.

En cuanto a todos los modelos solo codificadores, la arquitectura de BERT corresponde exactamente a la arquitectura de la parte codificadora de los modelos codificador-decodificador basados en transformadores, como se muestra en la sección “Codificador” en el cuaderno Codificador-Decodificador.

GPT2

GPT2 es un modelo solo decodificador, que utiliza auto-atención uni-direccional (es decir, “causal”) para definir una asignación desde una secuencia de entrada Y 0 : m − 1 a una secuencia de vectores de logits “siguiente palabra” L 1 : m :

f θ GPT2 : Y 0 : m − 1 → L 1 : m .

Al procesar los vectores de logits L 1 : m con la operación softmax, el modelo puede definir la distribución de probabilidad de la secuencia de palabras Y 1 : m . Para ser exactos, la distribución de probabilidad de la secuencia de palabras Y 1 : m se puede factorizar en m − 1 distribuciones condicionales de “siguiente palabra”:

p θ GPT2 ( Y 1 : m ) = ∏ i = 1 m p θ GPT2 ( y i ∣ Y 0 : i − 1 ) . p θ GPT2 ( y i ∣ Y 0 : i − 1 ) presenta la distribución de probabilidad de la siguiente palabra y i dada todas las palabras anteriores y 0 , … , y i − 1 3 {}^3 3 y se define como la operación softmax aplicada al vector de logits l i . En resumen, las siguientes ecuaciones son verdaderas.

p θ gpt2 ( y i ∣ Y 0 : i − 1 ) = Softmax ( l i ) = Softmax ( f θ GPT2 ( Y 0 : i − 1 ) ) . p_{\theta_{\text{gpt2}}}(\mathbf{y}_i | \mathbf{Y}_{0:i-1}) = \textbf{Softmax}(\mathbf{l}_i) = \textbf{Softmax}(f_{\theta_{\text{GPT2}}}(\mathbf{Y}_{0: i – 1})). p θ gpt2 ​ ​ ( y i ​ ∣ Y 0 : i − 1 ​ ) = Softmax ( l i ​ ) = Softmax ( f θ GPT2 ​ ​ ( Y 0 : i − 1 ​ ) ) .

Para obtener más detalles, consulta la sección del decodificador de la publicación de blog del codificador-decodificador.

Vamos a visualizar ahora GPT2 también.

Analógamente a BERT, GPT2 está compuesto por una pila de bloques GPT2. A diferencia del bloque BERT, el bloque GPT2 utiliza auto-atención uni-direccional para procesar algunos vectores de entrada y ′ 0 , … , y ′ m − 1 ​ (mostrados en azul claro en la parte inferior derecha) a una secuencia de vectores de salida y ′ ′ 0 , … , y ′ ′ m − 1 ​ (mostrados en azul oscuro en la parte superior derecha). Además de la pila de bloques GPT2, el modelo también tiene una capa lineal, llamada LM Head, que mapea los vectores de salida del último bloque GPT2 a los vectores de logit l 1 , … , l m ​ . Como se mencionó anteriormente, un vector de logit l i ​ se puede utilizar para muestrear un nuevo vector de entrada y i ​ 5 {}^5 5 .

GPT2 se utiliza principalmente para la generación de texto en dominio abierto. Primero, se alimenta al modelo una entrada de inicio Y 0 : i − 1 ​ para obtener la distribución condicional p θ gpt2 ( y ∣ Y 0 : i − 1 ​ ) . Luego, se selecciona la siguiente palabra y i ​ a partir de la distribución (representada por las flechas grises en el gráfico anterior) y se agrega a la entrada. De manera auto-regresiva, la palabra y i + 1 ​ se puede seleccionar a partir de p θ gpt2 ( y ∣ Y 0 : i ​ ) y así sucesivamente.

Por lo tanto, GPT2 es adecuado para la generación de lenguaje, pero menos para la generación condicional. Al establecer la entrada de inicio Y 0 : i − 1 ​ igual a la secuencia de entrada X 1 : n ​ , GPT2 puede usarse muy bien para la generación condicional. Sin embargo, la arquitectura del modelo tiene una desventaja fundamental en comparación con la arquitectura codificador-decodificador, como se explica en Raffel et al. (2019) en la página 17. En resumen, la auto-atención uni-direccional limita innecesariamente la representación del modelo de la secuencia de entrada X 1 : n ​ , ya que x i ​ no puede depender de x i + 1 , ∀ i ∈ { 1 , … , n } .

Encoder-Decodificador

Debido a que los modelos solo de codificación requieren conocer la longitud de salida de antemano, parecen inadecuados para tareas de secuencia a secuencia. Los modelos solo de decodificación pueden funcionar bien para tareas de secuencia a secuencia, pero también tienen ciertas limitaciones arquitectónicas como se explicó anteriormente.

El enfoque predominante actual para abordar las tareas de secuencia a secuencia son los modelos encoder-decodificador basados en transformers, a menudo también llamados modelos seq2seq transformers. Los modelos encoder-decodificador se introdujeron en Vaswani et al. (2017) y desde entonces se ha demostrado que funcionan mejor en tareas de secuencia a secuencia que los modelos de lenguaje independientes (es decir, modelos solo de decodificación), como Raffel et al. (2020). En esencia, un modelo encoder-decodificador es la combinación de un codificador independiente, como BERT, y un modelo decodificador independiente, como GPT2. Para obtener más detalles sobre la arquitectura exacta de los modelos encoder-decodificador basados en transformers, consulte esta publicación de blog.

Ahora sabemos que los puntos de control de modelos de codificador y decodificador independientes pre-entrenados grandes y de libre acceso, como BERT y GPT, pueden mejorar el rendimiento y reducir el costo de entrenamiento para muchas tareas de NLU. También sabemos que los modelos encoder-decodificador son essencialmente la combinación de modelos de codificador y decodificador independientes. Esto plantea naturalmente la pregunta de cómo se pueden aprovechar los puntos de control de modelos independientes para modelos encoder-decodificador y qué combinaciones de modelos son más eficientes en ciertas tareas de secuencia a secuencia.

En 2020, Sascha Rothe, Shashi Narayan y Aliaksei Severyn investigaron exactamente esta pregunta en su artículo Aprovechando los puntos de control pre-entrenados para tareas de generación de secuencias. El artículo ofrece un gran análisis de diferentes combinaciones de modelos encoder-decodificador y técnicas de ajuste fino, que estudiaremos con más detalle más adelante.

Componer un modelo encoder-decodificador a partir de puntos de control de modelos independientes pre-entrenados se define como iniciar en caliente el modelo encoder-decodificador. Las siguientes secciones muestran cómo funciona iniciar en caliente un modelo encoder-decodificador en teoría, cómo se puede poner la teoría en práctica con 🤗Transformers y también ofrecen consejos prácticos para obtener un mejor rendimiento.


1 {}^1 1 Un modelo de lenguaje pre-entrenado se define como una red neuronal:

  • que ha sido entrenada en datos de texto sin etiquetar, es decir, de manera agnóstica a la tarea y no supervisada, y
  • que procesa una secuencia de palabras de entrada en una incrustación dependiente del contexto. Por ejemplo, el modelo de bolsa de palabras continuo y el modelo skip-gram de Mikolov et al. (2013) no se consideran modelos de lenguaje pre-entrenados porque las incrustaciones no dependen del contexto.

2 {}^2 2 El ajuste fino se define como el entrenamiento específico de la tarea de un modelo que se ha inicializado con los pesos de un modelo de lenguaje pre-entrenado.

3 {}^3 3 El vector de entrada y 0 \mathbf{y}_0 y 0 ​ corresponde aquí al vector de incrustación BOS \text{BOS} BOS requerido para predecir la primera palabra de salida y 1 \mathbf{y}_1 y 1 ​ .

4 {}^4 4 Sin pérdida de generalidad, excluimos las capas de normalización para no sobrecargar las ecuaciones e ilustraciones.

5 {}^5 5 Para obtener más detalles sobre por qué se utiliza la auto-atención unidireccional para los modelos “solo de decodificación”, como GPT2, y cómo funciona exactamente el muestreo, consulte la sección de decodificación de la publicación de blog sobre modelos encoder-decodificador.

Iniciar en caliente modelos encoder-decodificador (Teoría)

Después de leer la introducción, ahora estamos familiarizados con los modelos solo de codificación y solo de decodificación. Hemos notado que la arquitectura del modelo encoder-decodificador esencialmente consiste en la composición de un modelo de codificador independiente y un modelo de decodificador independiente, lo que nos llevó a la pregunta de cómo se pueden iniciar en caliente los modelos encoder-decodificador a partir de puntos de control de modelos independientes.

Existen múltiples posibilidades para iniciar en caliente un modelo encoder-decodificador. Se puede:

  1. inicializar tanto la parte de codificación como la de decodificación a partir de un punto de control de un modelo solo de codificación, como BERT,
  2. inicializar la parte de codificación a partir de un punto de control de un modelo solo de codificación, como BERT, y la parte de decodificación a partir de un punto de control de un modelo solo de decodificación, como GPT2,
  3. inicializar solo la parte de codificación con un punto de control de un modelo solo de codificación, o
  4. inicializar solo la parte de decodificación con un punto de control de un modelo solo de decodificación.

En lo siguiente, nos centraremos en las posibilidades 1 y 2. Las posibilidades 3 y 4 son triviales después de haber comprendido las dos primeras.

Repaso del Modelo Codificador-Decodificador

Primero, hagamos un rápido repaso de la arquitectura codificador-decodificador.

El codificador (mostrado en verde) es una pila de bloques codificadores. Cada bloque codificador está compuesto por una capa de autoatención bidireccional y dos capas de alimentación directa. El decodificador (mostrado en naranja) es una pila de bloques decodificadores, seguida de una capa densa llamada Head de LM. Cada bloque decodificador está compuesto por una capa de autoatención unidireccional, una capa de atención cruzada y dos capas de alimentación directa.

El codificador asigna la secuencia de entrada X 1 : n \mathbf{X}_{1:n} X 1 : n ​ a una secuencia codificada contextualizada X ‾ 1 : n \mathbf{\overline{X}}_{1:n} X 1 : n ​ de la misma manera exacta en que lo hace BERT. Luego, el decodificador asigna la secuencia codificada contextualizada X ‾ 1 : n \mathbf{\overline{X}}_{1:n} X 1 : n ​ y una secuencia objetivo Y 0 : m − 1 \mathbf{Y}_{0:m-1} Y 0 : m − 1 ​ a los vectores de logit L 1 : m \mathbf{L}_{1:m} L 1 : m ​. De manera análoga a GPT2, luego se utilizan los logit para definir la distribución de la secuencia objetivo Y 1 : m \mathbf{Y}_{1:m} Y 1 : m ​ condicionada a la secuencia de entrada X 1 : n \mathbf{X}_{1:n} X 1 : n ​ mediante una operación softmax.

Para expresarlo en términos matemáticos, primero, la distribución condicional se factoriza en m − 1 m – 1 m − 1 distribuciones condicionales de la siguiente palabra y i \mathbf{y}_i y i ​ mediante la regla de Bayes.

p θ enc, dec ( Y 1 : m ∣ X 1 : n ) = p θ dec ( Y 1 : m ∣ X ‾ 1 : n ) = ∏ i = 1 m p θ dec ( y i ∣ Y 0 : i − 1 , X ‾ 1 : n ) , con X ‾ 1 : n = f θ enc ( X 1 : n ) . p_{\theta_{\text{enc, dec}}}(\mathbf{Y}_{1:m} | \mathbf{X}_{1:n}) = p_{\theta_{\text{dec}}}(\mathbf{Y}_{1:m} | \mathbf{\overline{X}}_{1:n}) = \prod_{i=1}^m p_{\theta_{\text{dec}}}(\mathbf{y}_i | \mathbf{Y}_{0: i -1}, \mathbf{\overline{X}}_{1:n}), \text{ con } \mathbf{\overline{X}}_{1:n} = f_{\theta_{\text{enc}}}(\mathbf{X}_{1:n}). p θ enc, dec ​ ​ ( Y 1 : m ​ ∣ X 1 : n ​ ) = p θ dec ​ ​ ( Y 1 : m ​ ∣ X 1 : n ​ ) = i = 1 ∏ m ​ p θ dec ​ ​ ( y i ​ ∣ Y 0 : i − 1 ​ , X 1 : n ​ ) , con X 1 : n ​ = f θ enc ​ ​ ( X 1 : n ​ ) .

Cada distribución condicional de “siguiente palabra” se define mediante la operación softmax del vector de logit de la siguiente manera.

p θ dec ( y i ∣ Y 0 : i − 1 , X ‾ 1 : n ) = Softmax ( l i ) . p_{\theta_{\text{dec}}}(\mathbf{y}_i | \mathbf{Y}_{0: i -1}, \mathbf{\overline{X}}_{1:n}) = \textbf{Softmax}(\mathbf{l}_i). p θ dec ​ ​ ( y i ​ ∣ Y 0 : i − 1 ​ , X 1 : n ​ ) = Softmax ( l i ​ ) .

Para más detalles, consulte el cuaderno Encoder-Decoder.

Calentamiento inicial del codificador-decodificador con BERT

Ahora vamos a ilustrar cómo se puede utilizar un modelo BERT pre-entrenado para calentar inicialmente el modelo codificador-decodificador. Los parámetros de peso pre-entrenados de BERT se utilizan para inicializar tanto los parámetros de peso del codificador como los del decodificador. Para hacer esto, se compara la arquitectura de BERT con la arquitectura del codificador y todas las capas del codificador que también existen en BERT se inicializarán con los parámetros de peso pre-entrenados de las capas respectivas. Todas las capas del codificador que no existen en BERT simplemente tendrán sus parámetros de peso inicializados de forma aleatoria.

Vamos a visualizarlo.

Podemos ver que la arquitectura del codificador corresponde 1 a 1 a la arquitectura de BERT. Los parámetros de peso de la capa de autoatención bidireccional y las dos capas de alimentación hacia adelante de todos los bloques del codificador se inicializan con los parámetros de peso de las capas respectivas de BERT. Esto se ilustra de manera ejemplar para el segundo bloque del codificador (recuadros rojos en la parte inferior) cuyos parámetros de peso θ enc self-attn , 2 θ_{\text{enc}}^{\text{self-attn}, 2} θ enc self-attn , 2 ​ y θ enc feed-forward , 2 θ_{\text{enc}}^{\text{feed-forward}, 2} θ enc feed-forward , 2 ​ se establecen en los parámetros de peso de BERT θ BERT feed-forward , 2 θ_{\text{BERT}}^{\text{feed-forward}, 2} θ BERT feed-forward , 2 ​ y θ BERT self-attn , 2 θ_{\text{BERT}}^{\text{self-attn}, 2} θ BERT self-attn , 2 ​ , respectivamente, en la inicialización.

Antes de ajustar finamente, el codificador se comporta exactamente como un modelo BERT pre-entrenado. Suponiendo que la secuencia de entrada x 1 , … , x n \mathbf{x}_1, \ldots, \mathbf{x}_n x 1 ​ , … , x n ​ (mostrada en verde) pasada al codificador es igual a la secuencia de entrada x 1 BERT , … , x n BERT \mathbf{x}_1^{\text{BERT}}, \ldots, \mathbf{x}_n^{\text{BERT}} x 1 BERT ​ , … , x n BERT ​ (mostrada en gris) pasada a BERT, esto significa que las respectivas secuencias de vectores de salida x ‾ 1 , … , x ‾ n \mathbf{\overline{x}}_1, \ldots, \mathbf{\overline{x}}_n x 1 ​ , … , x n ​ (mostradas en verde oscuro) y x ‾ 1 BERT , … , x ‾ n BERT \mathbf{\overline{x}}_1^{\text{BERT}}, \ldots, \mathbf{\overline{x}}_n^{\text{BERT}} x 1 BERT ​ , … , x n BERT ​ (mostradas en gris oscuro) también deben ser iguales.

A continuación, vamos a ilustrar cómo se calienta inicialmente el decodificador.

La arquitectura del decodificador es diferente de la arquitectura de BERT de tres maneras.

  1. Primero, el decodificador debe estar condicionado a la secuencia codificada contextualizada X ‾ 1 : n \mathbf{\overline{X}}_{1:n} X 1 : n ​ mediante capas de atención cruzada. En consecuencia, se agregan capas de atención cruzada inicializadas de forma aleatoria entre la capa de autoatención y las dos capas de alimentación hacia adelante en cada bloque de BERT. Esto se representa de manera ejemplar para el segundo bloque con + θ dec cross-attention, 2 +\theta_{\text{dec}}^{\text{cross-attention, 2}} + θ dec cross-attention, 2 ​ e ilustrado por el nuevo grafo completamente conectado añadido en rojo en el recuadro inferior derecho. Esto cambia necesariamente el comportamiento de cada bloque de BERT modificado para que un vector de entrada, por ejemplo, y ′ 0 \mathbf{y’}_0 y ′ 0 ​ ahora produzca un vector de salida aleatorio y ′ ′ 0 \mathbf{y”}_0 y ′ ′ 0 ​ (resaltado por el borde rojo alrededor del vector de salida y ′ ′ 0 \mathbf{y”}_0 y ′ ′ 0 ​ ).

  2. Segundo, las capas de autoatención bidireccionales de BERT deben cambiarse a capas de autoatención unidireccionales para cumplir con la generación auto-regresiva. Debido a que tanto la capa de autoatención bidireccional como la capa de autoatención unidireccional se basan en las mismas proyecciones de pesos de clave, consulta y valor, los pesos de la capa de autoatención del decodificador se pueden inicializar con los pesos de la capa de autoatención de BERT. Por ejemplo, los parámetros de peso de las proyecciones de consulta, clave y valor de la capa de autoatención unidireccional del decodificador se inicializan con los de la capa de autoatención bidireccional de BERT θ BERT self-attn , 2 = { W BERT , k self-attn , 2 , W BERT , v self-attn , 2 , W BERT , q self-attn , 2 } → θ dec self-attn , 2 = { W dec , k self-attn , 2 , W dec , v self-attn , 2 , W dec , q self-attn , 2 } . \theta_{\text{BERT}}^{\text{self-attn}, 2} = \{\mathbf{W}_{\text{BERT}, k}^{\text{self-attn}, 2}, \mathbf{W}_{\text{BERT}, v}^{\text{self-attn}, 2}, \mathbf{W}_{\text{BERT}, q}^{\text{self-attn}, 2} \} \to \theta_{\text{dec}}^{\text{self-attn}, 2} = \{\mathbf{W}_{\text{dec}, k}^{\text{self-attn}, 2}, \mathbf{W}_{\text{dec}, v}^{\text{self-attn}, 2}, \mathbf{W}_{\text{dec}, q}^{\text{self-attn}, 2} \}. θ BERT self-attn , 2 ​ = { W BERT , k self-attn , 2 ​ , W BERT , v self-attn , 2 ​ , W BERT , q self-attn , 2 ​ } → θ dec self-attn , 2 ​ = { W dec , k self-attn , 2 ​ , W dec , v self-attn , 2 ​ , W dec , q self-attn , 2 ​ } . Sin embargo, en la autoatención unidireccional, cada token solo se enfoca en todos los tokens anteriores, por lo que las capas de autoatención del decodificador producen vectores de salida diferentes a las capas de autoatención de BERT, aunque comparten los mismos pesos. Comparar, por ejemplo, el grafo conectado causalmente del decodificador en el recuadro derecho versus el grafo completamente conectado de BERT en el recuadro izquierdo.

  3. Tercero, el decodificador produce una secuencia de vectores de logitos L 1 : m \mathbf{L}_{1:m} L 1 : m ​ para definir la distribución de probabilidad condicional p θ dec ( Y 1 : n ∣ X ‾ ) p_{\theta_{\text{dec}}}(\mathbf{Y}_{1:n} | \mathbf{\overline{X}}) p θ dec ​ ​ ( Y 1 : n ​ ∣ X ) . Como resultado, se agrega una capa de LM Head encima del último bloque del decodificador. Los parámetros de peso de la capa de LM Head generalmente corresponden

    Para concluir, al iniciar en caliente el decodificador desde un modelo pre-entrenado de BERT, solo se inicializan de forma aleatoria los pesos de la capa de atención cruzada. Todos los demás pesos, incluyendo los de la capa de auto-atención y la cabeza LM, se inicializan con los parámetros de peso pre-entrenados de BERT.

    Habiendo iniciado en caliente el modelo codificador-decodificador, los pesos se ajustan posteriormente en una tarea secuencia a secuencia, como la sumarización.

    Iniciando en caliente el Codificador-Decodificador con BERT y GPT2

    En lugar de iniciar en caliente tanto el codificador como el decodificador con un punto de control de BERT, podemos aprovechar el punto de control de BERT para el codificador y un punto de control de GPT2 para el decodificador. A primera vista, un punto de control solo para el decodificador de GPT2 parece ser más adecuado para iniciar en caliente el decodificador porque ya ha sido entrenado en modelado de lenguaje causal y utiliza capas de auto-atención unidireccionales.

    Veamos cómo se puede utilizar un punto de control de GPT2 para iniciar en caliente el decodificador.

    Podemos ver que el decodificador es más similar a GPT2 que a BERT. Los parámetros de peso de la cabeza LM del decodificador se pueden inicializar directamente con los parámetros de peso de la cabeza LM de GPT2, por ejemplo, θ GPT2 lm-head → θ dec lm-head \theta_{\text{GPT2}}^{\text{lm-head}} \to \theta_{\text{dec}}^{\text{lm-head}} θ GPT2 lm-head ​ → θ dec lm-head ​ . Además, los bloques del decodificador y GPT2 utilizan ambos capas de auto-atención unidireccionales, por lo que los vectores de salida de la capa de auto-atención del decodificador son equivalentes a los vectores de salida de GPT2 asumiendo que los vectores de entrada son los mismos, por ejemplo, y ′ 0 GPT2 = y ′ 0 \mathbf{y’}_0^{\text{GPT2}} = \mathbf{y’}_0 y ′ 0 GPT2 ​ = y ′ 0 ​ . A diferencia del decodificador inicializado con BERT, el decodificador inicializado con GPT2 mantiene el grafo conectado causal de la capa de auto-atención como se puede ver en las cajas rojas en la parte inferior.

    No obstante, el decodificador inicializado con GPT2 también debe condicionar el decodificador en X ‾ 1 : n \mathbf{\overline{X}}_{1:n} X 1 : n ​ . Por lo tanto, se añaden de forma aleatoria parámetros de peso inicializados para la capa de atención cruzada en cada bloque del decodificador. Esto se ilustra por ejemplo para el segundo bloque del codificador con + θ dec cross-attention, 2 +\theta_{\text{dec}}^{\text{cross-attention, 2}} + θ dec cross-attention, 2 ​ .

    Aunque GPT2 se parece más a la parte del decodificador de un modelo codificador-decodificador que BERT, un decodificador inicializado con GPT2 también producirá vectores de logits aleatorios L 1 : m \mathbf{L}_{1:m} L 1 : m ​ sin ajuste fino debido a las capas de atención cruzada inicializadas de forma aleatoria en cada bloque del decodificador. Sería interesante investigar si un decodificador inicializado con GPT2 produce mejores resultados o se puede ajustar más eficientemente.

    Compartir Pesos del Codificador-Decodificador

    En Raffel et al. (2020), los autores muestran que un modelo codificador-decodificador inicializado de forma aleatoria que comparte los pesos del codificador con el decodificador, y por lo tanto reduce la huella de memoria a la mitad, funciona solo ligeramente peor que su versión “no compartida”. Compartir los pesos del codificador con el decodificador significa que todas las capas del decodificador que se encuentran en la misma posición en el codificador comparten los mismos parámetros de peso, es decir, el mismo nodo en el grafo de cálculo de la red. Por ejemplo, las matrices de proyección de consulta, clave y valor de la capa de auto-atención en el tercer bloque del codificador, definidas como W Enc , k self-attn , 3 \mathbf{W}^{\text{self-attn}, 3}_{\text{Enc}, k} W Enc , k self-attn , 3 ​ , W Enc , v self-attn , 3 \mathbf{W}^{\text{self-attn}, 3}_{\text{Enc}, v} W Enc , v self-attn , 3 ​ , W Enc , q self-attn , 3 \mathbf{W}^{\text{self-attn}, 3}_{\text{Enc}, q} W Enc , q self-attn , 3 ​ son idénticas a las respectivas matrices de proyección de consulta, clave y valor de la capa de auto-atención en el tercer bloque del decodificador 2 {}^2 2 :

    En el caso de W k self-attn , 3 = W enc , k self-attn , 3 ≡ W dec , k self-attn , 3 , \mathbf{W}^{\text{self-attn}, 3}_{k} = \mathbf{W}^{\text{self-attn}, 3}_{\text{enc}, k} \equiv \mathbf{W}^{\text{self-attn}, 3}_{\text{dec}, k}, W k self-attn , 3 ​ = W enc , k self-attn , 3 ​ ≡ W dec , k self-attn , 3 ​ , W q self-attn , 3 = W enc , q self-attn , 3 ≡ W dec , q self-attn , 3 , \mathbf{W}^{\text{self-attn}, 3}_{q} = \mathbf{W}^{\text{self-attn}, 3}_{\text{enc}, q} \equiv \mathbf{W}^{\text{self-attn}, 3}_{\text{dec}, q}, W q self-attn , 3 ​ = W enc , q self-attn , 3 ​ ≡ W dec , q self-attn , 3 ​ , W v self-attn , 3 = W enc , v self-attn , 3 ≡ W dec , v self-attn , 3 , \mathbf{W}^{\text{self-attn}, 3}_{v} = \mathbf{W}^{\text{self-attn}, 3}_{\text{enc}, v} \equiv \mathbf{W}^{\text{self-attn}, 3}_{\text{dec}, v}, W v self-attn , 3 ​ = W enc , v self-attn , 3 ​ ≡ W dec , v self-attn , 3 ​ ,

    Como resultado, los pesos de proyección clave W k self-attn , 3 , W v self-attn , 3 , W q self-attn , 3 \mathbf{W}^{\text{self-attn}, 3}_{k}, \mathbf{W}^{\text{self-attn}, 3}_{v}, \mathbf{W}^{\text{self-attn}, 3}_{q} W k self-attn , 3 ​ , W v self-attn , 3 ​ , W q self-attn , 3 ​ se actualizan dos veces en cada paso de propagación hacia atrás, una vez cuando el gradiente se retropropaga a través del tercer bloque decodificador y otra vez cuando el gradiente se retropropaga a través del tercer bloque codificador.

    De la misma manera, podemos iniciar de manera rápida un modelo codificador-decodificador compartiendo los pesos del codificador con el decodificador. Tener la capacidad de compartir los pesos entre el codificador y el decodificador requiere que la arquitectura del decodificador (excluyendo los pesos de atención cruzada) sea idéntica a la arquitectura del codificador. Por lo tanto, compartir pesos entre codificador y decodificador solo es relevante si el modelo codificador-decodificador se inicia de manera rápida desde un único punto de control pre-entrenado del codificador.

    ¡Genial! Esa fue la teoría sobre el inicio rápido de modelos codificador-decodificador. Ahora veamos algunos resultados.


    1 {}^1 1 Sin pérdida de generalidad, excluimos las capas de normalización para no saturar las ecuaciones e ilustraciones. 2 {}^2 2 Para obtener más detalles sobre cómo funcionan las capas de autoatención, consulte esta sección de la publicación de blog del modelo codificador-decodificador basado en transformadores para la parte del codificador (y esta sección para la parte del decodificador, respectivamente).

    Iniciando rápidamente modelos codificador-decodificador (Análisis)

    En esta sección, resumiremos los hallazgos sobre el inicio rápido de modelos codificador-decodificador presentados en “Aprovechando puntos de control pre-entrenados para tareas de generación de secuencias” por Sascha Rothe, Shashi Narayan y Aliaksei Severyn. Los autores compararon el rendimiento de modelos codificador-decodificador iniciados de manera rápida con modelos codificador-decodificador inicializados aleatoriamente en varias tareas de secuencia a secuencia, notablemente resumen , traducción , división de oraciones y fusión de oraciones .

    Para ser más precisos, se utilizaron puntos de control pre-entrenados disponibles públicamente de BERT , RoBERTa y GPT2 en diferentes variaciones para iniciar rápidamente un modelo codificador-decodificador. Por ejemplo, un codificador inicializado con BERT se emparejó con un decodificador inicializado con BERT dando como resultado un modelo BERT2BERT o un codificador inicializado con RoBERTa se emparejó con un decodificador inicializado con GPT2 para obtener un modelo RoBERTa2GPT2. Además, se investigó el efecto de compartir los pesos del codificador y el decodificador (como se explica en la sección anterior) para RoBERTa, es decir, RoBERTaShare , y para BERT, es decir, BERTShare . Se utilizaron modelos codificador-decodificador inicializados aleatoriamente o parcialmente aleatoriamente como referencia, como un modelo codificador-decodificador completamente inicializado aleatoriamente, denominado Rnd2Rnd o un decodificador inicializado con BERT emparejado con un codificador inicializado aleatoriamente, definido como Rnd2BERT .

    La siguiente tabla muestra una lista completa de todas las variantes de modelos investigados, incluyendo el número de pesos inicializados aleatoriamente, es decir, “aleatorio”, y el número de pesos inicializados a partir de los puntos de control pre-entrenados respectivos, es decir, “aprovechado”. Todos los modelos se basan en una arquitectura de 12 capas con incrustaciones de tamaño oculto de 768 dimensiones, correspondientes a los puntos de control bert-base-cased, bert-base-uncased, roberta-base y gpt2 en el centro de modelos de 🤗Transformers.

    El modelo Rnd2Rnd, que se basa en la arquitectura BERT2BERT, contiene 221M de parámetros de peso, todos los cuales se inicializan aleatoriamente. Las otras dos bases de referencia “basadas en BERT”, Rnd2BERT y BERT2Rnd, tienen aproximadamente la mitad de sus pesos, es decir, 112M de parámetros, inicializados aleatoriamente. Los otros 109M de parámetros de peso se aprovechan del punto de control pre-entrenado bert-base-uncased para la parte del codificador o descodificador, respectivamente. Los modelos BERT2BERT, BERT2GPT2 y RoBERTa2GPT2 tienen todos sus parámetros de peso de codificador aprovechados (de bert-base-uncased, roberta-base, respectivamente) y la mayoría de los pesos de parámetros de descodificador también (de gpt2, bert-base-uncased, respectivamente). Se inicializan aleatoriamente 26M de pesos de parámetros del descodificador, que corresponden a las 12 capas de atenciones cruzadas. Se comparan RoBERTa2GPT2 y BERT2GPT2 con la base de referencia Rnd2GPT2. Además, cabe señalar que las variantes de modelo compartido BERTShare y RoBERTaShare tienen significativamente menos parámetros porque todos los parámetros de peso del codificador se comparten con los respectivos parámetros de peso del descodificador.

    Experimentos

    Los modelos anteriores fueron entrenados y evaluados en cuatro tareas de secuencia a secuencia de complejidad creciente: fusión a nivel de oración, división a nivel de oración, traducción y resumen abstracto. La siguiente tabla muestra qué conjuntos de datos se utilizaron para cada tarea.

    Dependiendo de la tarea, se utilizó un régimen de entrenamiento ligeramente diferente. Por ejemplo, según el tamaño del conjunto de datos y la tarea específica, el número de pasos de entrenamiento varía de 200K a 500K, el tamaño del lote se establece en 128 o 256, la longitud de entrada varía de 128 a 512 y la longitud de salida varía entre 32 a 128. Sin embargo, se debe enfatizar que dentro de cada tarea, todos los modelos fueron entrenados y evaluados utilizando los mismos hiperparámetros para garantizar una comparación justa. Para obtener más información sobre la configuración de hiperparámetros específicos de la tarea, se recomienda al lector que consulte la sección de Experimentos en el documento.

    A continuación, se dará una visión general condensada de los resultados para cada tarea.

    Fusión y división de oraciones (DiscoFuse, WikiSplit)

    Fusión de oraciones es la tarea de combinar múltiples oraciones en una sola oración coherente. Por ejemplo, las dos oraciones:

    Como bloqueador de carrera, Zeitler se mueve relativamente bien. Zeitler a menudo tiene dificultades en el punto de contacto en el espacio.

    deben ser conectadas con una palabra de enlace adecuada, como:

    Como bloqueador de carrera, Zeitler se mueve relativamente bien. Sin embargo, él a menudo tiene dificultades en el punto de contacto en el espacio.

    Como se puede ver, la palabra de enlace “sin embargo” proporciona una transición coherente de la primera oración a la segunda. Un modelo capaz de generar este tipo de palabra de enlace ha aprendido de manera plausible a inferir que las dos oraciones contrastan entre sí.

    La tarea inversa se llama División de oraciones y consiste en dividir una sola oración compleja en varias más simples que, juntas, conservan el mismo significado. La división de oraciones se considera como una tarea importante en la simplificación de textos, cf. a Botha et al. (2018).

    Como ejemplo, la oración:

    Street Rod es el primero de una serie de dos juegos lanzados para PC y Commodore 64 en 1989

    puede simplificarse en

    Street Rod es el primero de una serie de dos juegos. Fue lanzado para PC y Commodore 64 en 1989

    Se puede observar que la oración larga intenta transmitir dos piezas importantes de información. Una es que el juego fue el primero de dos juegos que se lanzaron para PC, y la segunda es el año en que se lanzó. La división de oraciones, por lo tanto, requiere que el modelo comprenda qué parte de la oración se debe dividir en dos oraciones, lo que hace que la tarea sea más difícil que la fusión de oraciones.

    Una métrica común para evaluar el rendimiento de los modelos en tareas de fusión y división de oraciones es SARI (Wu et al. (2016)), que se basa en gran medida en la puntuación F1 de la etiqueta y la salida del modelo.

    Vamos a ver cómo se desempeñan los modelos en la fusión y división de oraciones.

    Las dos primeras columnas muestran el rendimiento de los modelos codificador-decodificador en los datos de evaluación de DiscoFuse. La primera columna indica los resultados de los modelos codificador-decodificador entrenados con el 100% de los datos de entrenamiento, mientras que la segunda columna muestra los resultados de los modelos entrenados solo con el 10% de los datos de entrenamiento. Observamos que los modelos iniciados de forma cálida tienen un rendimiento significativamente mejor que los modelos basales de inicialización aleatoria Rnd2Rnd, Rnd2Bert y Rnd2GPT2. Un modelo RoBERTa2GPT2 iniciado de forma cálida y entrenado solo con el 10% de los datos de entrenamiento tiene un rendimiento similar al de un modelo Rnd2Rnd entrenado con el 100% de los datos de entrenamiento. Curiosamente, el modelo de referencia Bert2Rnd tiene un rendimiento igual de bueno que un modelo Bert2Bert iniciado de forma cálida, lo que indica que iniciar de forma cálida la parte del codificador es más efectivo que iniciar de forma cálida la parte del decodificador. Los mejores resultados se obtienen con RoBERTa2GPT2, seguido de RobertaShare. Compartir los parámetros de peso del codificador y decodificador parece aumentar ligeramente el rendimiento del modelo.

    En la tarea de división de oraciones más difícil, emerge un patrón similar. Los modelos codificador-decodificador iniciados de forma cálida tienen un rendimiento significativamente mejor que los modelos codificador-decodificador cuyo codificador se inicializa de forma aleatoria, y los modelos codificador-decodificador con parámetros de peso compartidos obtienen mejores resultados que aquellos con parámetros de peso no acoplados. En la división de oraciones, los modelos BertShare tienen el mejor rendimiento, seguidos de cerca por RobertaShare.

    Además de las variantes de modelos de 12 capas, los autores también entrenaron y evaluaron un modelo RobertaShare (large) de 24 capas que supera significativamente a todos los modelos de 12 capas.

    Traducción Automática (WMT14)

    A continuación, los autores evaluaron modelos codificador-decodificador iniciados de forma cálida en el conjunto de datos WMT14, que es probablemente el benchmark más común en traducción automática (MT) – el conjunto de datos En → De y De → En. En este cuaderno, presentamos los resultados en el conjunto de datos de evaluación newstest2014. Debido a que el benchmark requiere que el modelo comprenda tanto un vocabulario en inglés como en alemán, los modelos codificador-decodificador iniciados de forma cálida con BERT se iniciaron desde el punto de control pre-entrenado multilingüe bert-base-multilingual-cased. Debido a que no hay un punto de control multilingüe RoBERTa disponible públicamente, se excluyeron los modelos codificador-decodificador iniciados de forma cálida con RoBERTa para MT. Los modelos iniciados con GPT2 se inicializaron desde el punto de control pre-entrenado gpt2 como en el experimento anterior. Los resultados de la traducción se informan utilizando la métrica de puntuación BLUE-4 1 {}^1 1.

    Nuevamente, observamos un impulso significativo en el rendimiento al iniciar de forma cálida la parte del codificador, con BERT2Rnd y BERT2BERT obteniendo los mejores resultados tanto en las tareas En → De como en De → En. Los modelos inicializados con GPT2 tienen un rendimiento significativamente peor, incluso peor que el modelo de referencia Rnd2Rnd en En → De. Teniendo en cuenta que el punto de control gpt2 fue entrenado solo con texto en inglés, no es sorprendente que los modelos BERT2GPT2 y Rnd2GPT2 tengan dificultades para generar traducciones en alemán. Esta hipótesis se respalda por los resultados competitivos (por ejemplo, 31.4 vs. 32.7) de BERT2GPT2 en la tarea De → En, para la cual el vocabulario de GPT2 se adapta al formato de salida en inglés. A diferencia de los resultados obtenidos en la fusión y división de oraciones, compartir los parámetros de peso del codificador y decodificador no aumenta el rendimiento en la traducción automática. Las posibles razones para esto, según los autores, incluyen que la capacidad del modelo codificador-decodificador es un factor importante en MT y que el codificador y el decodificador deben lidiar con gramática y vocabulario diferentes.

    Dado que el punto de control bert-base-multilingual-cased fue entrenado en más de 100 idiomas, su vocabulario probablemente es indeseablemente grande para las tareas de traducción En → De y De → En. Por lo tanto, los autores pre-entrenaron un punto de control grande de solo codificador BERT en el subconjunto en inglés y alemán del volcado de Wikipedia y posteriormente lo utilizaron para iniciar de forma cálida un modelo codificador-decodificador BERT2Rnd y BERTShare. Gracias al vocabulario mejorado, se observa otro impulso significativo en el rendimiento, con BERT2Rnd (large, personalizado) superando significativamente a todos los demás modelos.

    Resumen (CNN/Dailymail, BBC XSum, Gigaword)

    Finalmente, los modelos codificador-decodificador fueron evaluados en la tarea de secuencia a secuencia más desafiante: la sumarización. Los autores seleccionaron tres conjuntos de datos de sumarización con diferentes características para la evaluación: Gigaword (generación de titulares), BBC XSum (sumarización extrema) y CNN/Dailymail (sumarización abtractiva).

    El conjunto de datos Gigaword contiene sumarizaciones abtractivas a nivel de oración, lo que requiere que el modelo aprenda comprensión a nivel de oración, abstracción y parafraseo. Una muestra típica de datos en Gigaword, como

    “*el presidente venezolano Hugo Chávez dijo el jueves que ha ordenado una investigación sobre un presunto complot golpista supuestamente involucrando a oficiales militares activos y retirados.*”,

    tendría un titular correspondiente como etiqueta, por ejemplo:

    “Chávez ordena investigación sobre presunto complot golpista”.

    El conjunto de datos BBC XSum consiste en textos de entrada similares a artículos mucho más largos, con las etiquetas siendo principalmente sumarizaciones de una sola oración. Este conjunto de datos requiere que el modelo aprenda inferencia a nivel de documento y también un alto nivel de parafraseo abstracto. Algunas muestras de datos del conjunto de datos BBC XSum se muestran aquí.

    Para el conjunto de datos CNN/Dailymail, los documentos, que tienen una longitud similar a los del conjunto de datos BBC XSum, deben resumirse en puntos destacados de la historia. Por lo tanto, las etiquetas a menudo consisten en múltiples oraciones. Además de la comprensión a nivel de documento, el conjunto de datos CNN/Dailymail requiere que los modelos sean buenos para copiar la información más relevante. Algunos ejemplos se pueden ver aquí.

    Los modelos se evalúan utilizando la métrica Rouge, y se muestran a continuación los puntajes Rouge-2.

    Bien, echemos un vistazo a los resultados.

    Observamos nuevamente que el inicio en caliente de la parte codificadora brinda una mejora significativa en comparación con los modelos con codificadores inicializados al azar, lo cual es especialmente visible en tareas de abstracción a nivel de documento, es decir, CNN/Dailymail y BBC XSum. Esto muestra que las tareas que requieren un alto nivel de abstracción se benefician más de una parte codificadora pre-entrenada que aquellas que solo requieren abstracción a nivel de oración. Excepto para Gigaword, los modelos codificador-decodificador basados en GPT2 parecen ser inadecuados para la sumarización.

    Además, los modelos compartidos de codificador-decodificador son los modelos con mejor rendimiento para la sumarización. RoBERTaShare y BERTShare son los modelos con mejor rendimiento en todos los conjuntos de datos, mientras que la diferencia es especialmente significativa en el conjunto de datos BBC XSum, en el que RoBERTaShare (large) supera a BERT2BERT y BERT2Rnd en aproximadamente 3 puntos Rouge-2 y a Rnd2Rnd en más de 8 puntos Rouge-2. Como señalaron los autores, “esto se debe probablemente a que las oraciones de resumen de la BBC siguen una distribución similar a la de las oraciones en el documento, mientras que esto no es necesariamente el caso de los titulares de Gigaword y los puntos destacados de CNN/DailyMail”. Esto significa intuitivamente que en BBC XSum, las oraciones de entrada procesadas por el codificador son muy similares en estructura a la sumarización de una sola oración procesada por el decodificador, es decir, misma longitud, elección de palabras similar, sintaxis similar.

    Conclusión

    Bien, saquemos una conclusión y tratemos de derivar algunos consejos prácticos.

    • Hemos observado en todas las tareas que iniciar en caliente la parte codificadora brinda un impulso significativo en el rendimiento en comparación con los modelos codificador-decodificador que tienen un codificador inicializado al azar. Por otro lado, parece ser menos importante iniciar en caliente el decodificador, ya que BERT2BERT se encuentra a la par con BERT2Rnd en la mayoría de las tareas. Una razón intuitiva sería que, dado que una parte codificadora inicializada con BERT o RoBERTa no tiene ninguno de sus parámetros de peso inicializados al azar, el codificador puede aprovechar completamente el conocimiento adquirido de los puntos de control pre-entrenados de BERT o RoBERTa, respectivamente. En contraste, el decodificador iniciado en caliente siempre tiene partes de sus parámetros de peso inicializados al azar, lo que posiblemente dificulta mucho más aprovechar de manera efectiva el conocimiento adquirido por el punto de control utilizado para inicializar el decodificador.

    • A continuación, hemos notado que a menudo es beneficioso compartir los pesos del codificador y decodificador, especialmente si la distribución objetivo es similar a la distribución de entrada (por ejemplo, BBC XSum). Sin embargo, para los conjuntos de datos cuya distribución de datos objetivo difiere significativamente de la distribución de datos de entrada y para los cuales se sabe que la capacidad del modelo juega un papel importante, por ejemplo, WMT14, compartir los pesos del codificador-decodificador parece ser desventajoso.

    • Finalmente, hemos visto que es muy importante que el vocabulario de los puntos de control pre-entrenados “independientes” se ajuste al vocabulario requerido para resolver la tarea de secuencia a secuencia. Por ejemplo, un codificador-decodificador BERT2GPT2 iniciado en caliente tendrá un rendimiento deficiente en En → De MT porque GPT2 fue pre-entrenado en inglés, mientras que el idioma objetivo es alemán. El rendimiento generalmente pobre de BERT2GPT2, Rnd2GPT2 y RoBERTa2GPT2 en comparación con BERT2BERT, BERTShared y RoBERTaShared sugiere que es más efectivo tener un vocabulario compartido. Además, muestra que inicializar la parte decodificadora con un punto de control GPT2 pre-entrenado no es más efectivo que inicializarlo con un punto de control BERT pre-entrenado, a pesar de que GPT2 es más similar al decodificador en su arquitectura.

    Para cada una de las tareas anteriores, los modelos más eficientes fueron trasladados a 🤗Transformers y se pueden acceder aquí:

    • RoBERTaShared (grande) – Wikisplit: google/roberta2roberta_L-24_wikisplit.
    • RoBERTaShared (grande) – Discofuse: google/roberta2roberta_L-24_discofuse.
    • BERT2BERT (grande) – WMT en → \to → de: google/bert2bert_L-24_wmt_en_de.
    • BERT2BERT (grande) – WMT de → \to → en: google/bert2bert_L-24_wmt_de_en.
    • RoBERTaShared (grande) – CNN/Dailymail: google/roberta2roberta_L-24_cnn_daily_mail.
    • RoBERTaShared (grande) – BBC XSum: google/roberta2roberta_L-24_bbc.
    • RoBERTaShared (grande) – Gigaword: google/roberta2roberta_L-24_gigaword.

    1 {}^1 1 Para obtener las puntuaciones BLEU-4, se utilizó un script de la implementación oficial de Transformer de Tensorflow https://github.com/tensorflow/models/tree master/official/nlp/transformer. Tenga en cuenta que, a diferencia del tensor2tensor/utils/ get_ende_bleu.sh utilizado por Vaswani et al. (2017), este script no divide los compuestos de sustantivos, pero las comillas UTF-8 se normalizaron a comillas ASCII después de haber notado que el conjunto de entrenamiento preprocesado contiene solo comillas ASCII.

    2 {}^2 2 La capacidad del modelo es una definición informal de lo bueno que es el modelo para modelar patrones complejos. A veces también se define como la capacidad de un modelo para aprender de cada vez más datos. La capacidad del modelo se mide ampliamente por el número de parámetros entrenables: cuantos más parámetros, mayor será la capacidad del modelo.

    Hemos explicado la teoría de los modelos codificador-decodificador de inicio rápido, analizado resultados empíricos en múltiples conjuntos de datos y hemos obtenido conclusiones prácticas. Ahora vamos a recorrer un ejemplo completo de código que muestra cómo se puede iniciar rápidamente y ajustar finamente un modelo BERT2BERT en la tarea de resumen de CNN/Dailymail. Utilizaremos las bibliotecas 🤗datasets y 🤗Transformers.

    Además, la siguiente lista proporciona una versión condensada de este y otros cuadernos sobre cómo iniciar rápidamente otras combinaciones de modelos codificador-decodificador.

    • para BERT2BERT en CNN/Dailymail (una versión condensada de este cuaderno), haga clic aquí.
    • para RoBERTaShare en BBC XSum, haga clic aquí.
    • para BERT2Rnd en WMT14 En → \to → De, haga clic aquí.
    • para RoBERTa2GPT2 en DiscoFuse, haga clic aquí.

    Nota: Este cuaderno solo utiliza algunas muestras de datos de entrenamiento, validación y prueba con fines de demostración. Para ajustar finamente un modelo codificador-decodificador en todos los datos de entrenamiento, el usuario debe cambiar los parámetros de entrenamiento y preprocesamiento de datos según se resalta en los comentarios.

    Preprocesamiento de datos

    En esta sección, mostramos cómo se pueden preprocesar los datos para el entrenamiento. Más importante aún, intentamos brindar al lector una idea del proceso de decisión de cómo preprocesar los datos.

    Necesitaremos que se instalen los conjuntos de datos y transformers.

    !pip install datasets==1.0.2
    !pip install transformers==4.2.1

    Comencemos descargando el conjunto de datos de CNN/Dailymail.</p

    Los datos de entrada parecen consistir en artículos de noticias cortos. Curiosamente, las etiquetas parecen ser resúmenes similares a viñetas. En este punto, probablemente deberíamos echar un vistazo a un par de ejemplos adicionales para tener una mejor idea de los datos.

    También debemos notar aquí que el texto distingue entre mayúsculas y minúsculas. Esto significa que debemos tener cuidado si queremos usar modelos que no distingan entre mayúsculas y minúsculas. Como CNN/Dailymail es un conjunto de datos de resumen, el modelo se evaluará utilizando la métrica ROUGE. Al verificar la descripción de ROUGE en 🤗datasets, cf. aquí, podemos ver que la métrica no distingue entre mayúsculas y minúsculas, lo que significa que las letras mayúsculas se normalizarán a minúsculas durante la evaluación. Por lo tanto, podemos usar de manera segura puntos de control sin distinción de mayúsculas y minúsculas, como bert-base-uncased.

    ¡Genial! A continuación, obtengamos una idea de la longitud de los datos de entrada y las etiquetas.

    Dado que los modelos calculan la longitud en longitud de token, utilizaremos el tokenizador bert-base-uncased para calcular la longitud del artículo y el resumen.

    Primero, cargamos el tokenizador.

    from transformers import BertTokenizerFast
    tokenizer = BertTokenizerFast.from_pretrained("bert-base-uncased")

    A continuación, utilizamos .map() para calcular la longitud del artículo y su resumen. Dado que sabemos que la longitud máxima que bert-base-uncased puede procesar es de 512, también nos interesa el porcentaje de muestras de entrada que superan la longitud máxima. Del mismo modo, calculamos el porcentaje de resúmenes que tienen más de 64 y 128 tokens respectivamente.

    Podemos definir la función .map() de la siguiente manera.

    # mapear la longitud del artículo y del resumen a un diccionario, así como si la muestra tiene más de 512 tokens
    def map_to_length(x):
      x["article_len"] = len(tokenizer(x["article"]).input_ids)
      x["article_longer_512"] = int(x["article_len"] > 512)
      x["summary_len"] = len(tokenizer(x["highlights"]).input_ids)
      x["summary_longer_64"] = int(x["summary_len"] > 64)
      x["summary_longer_128"] = int(x["summary_len"] > 128)
      return x

    Debería ser suficiente analizar las primeras 10000 muestras. Podemos acelerar el mapeo utilizando varios procesos con num_proc=4.

    sample_size = 10000
    data_stats = train_data.select(range(sample_size)).map(map_to_length, num_proc=4)

    Habiendo calculado la longitud de las primeras 10000 muestras, ahora debemos promediarlas. Para esto, podemos utilizar la función .map() con batched=True y batch_size=-1 para tener acceso a todas las 10000 muestras dentro de la función .map().

    def compute_and_print_stats(x):
      if len(x["article_len"]) == sample_size:
        print(
            "Promedio del artículo: {}, %-Artículos > 512: {}, Promedio del resumen: {}, %-Resúmenes > 64: {}, %-Resúmenes > 128: {}".format(
                sum(x["article_len"]) / sample_size,
                sum(x["article_longer_512"]) / sample_size, 
                sum(x["summary_len"]) / sample_size,
                sum(x["summary_longer_64"]) / sample_size,
                sum(x["summary_longer_128"]) / sample_size,
            )
        )
    
    output = data_stats.map(
      compute_and_print_stats, 
      batched=True,
      batch_size=-1,
    )
    
        SALIDA:
        -------
        Promedio del artículo: 847.6216, %-Artículos > 512: 0.7355, Promedio del resumen: 57.7742, %-Resúmenes > 64: 0.3185, %-Resúmenes > 128: 0.0

    Podemos ver que en promedio un artículo contiene 848 tokens, con aproximadamente 3/4 de los artículos siendo más largos que la max_length del modelo 512. El resumen tiene en promedio una longitud de 57 tokens. Más del 30% de nuestros resúmenes de las 10000 muestras son más largos que 64 tokens, pero ninguno es más largo que 128 tokens.

    bert-base-cased está limitado a 512 tokens, lo que significa que tendríamos que recortar posiblemente información importante del artículo. Debido a que la mayor parte de la información importante se encuentra al principio de los artículos y porque queremos ser eficientes computacionalmente, decidimos utilizar bert-base-cased con una max_length de 512 en este cuaderno. Esta elección no es óptima, pero ha demostrado dar buenos resultados en CNN/Dailymail. Alternativamente, se podría utilizar modelos de secuencia de largo alcance, como Longformer, como codificador.

    En cuanto a la longitud del resumen, podemos ver que una longitud de 128 ya incluye todas las etiquetas del resumen. 128 está fácilmente dentro de los límites de bert-base-cased, por lo que decidimos limitar la generación a 128.

    Nuevamente, haremos uso de la función .map() – esta vez para transformar cada lote de entrenamiento en un lote de entradas del modelo.

    "article" y "highlights" se tokenizan y se preparan como "input_ids" del Codificador y "decoder_input_ids" del Decodificador, respectivamente.

    Las "etiquetas" se desplazan automáticamente a la izquierda para el entrenamiento de modelado del lenguaje.

    Por último, es muy importante recordar ignorar la pérdida de las etiquetas acolchadas. En 🤗Transformers, esto se puede hacer estableciendo la etiqueta en -100. Genial, vamos a escribir nuestra función de mapeo entonces.

    encoder_max_length=512
    decoder_max_length=128
    
    def process_data_to_model_inputs(batch):
      # tokenizar las entradas y etiquetas
      entradas = tokenizer(batch["article"], padding="max_length", truncation=True, max_length=encoder_max_length)
      salidas = tokenizer(batch["highlights"], padding="max_length", truncation=True, max_length=decoder_max_length)
    
      batch["input_ids"] = entradas.input_ids
      batch["attention_mask"] = entradas.attention_mask
      batch["labels"] = salidas.input_ids.copy()
    
      # como BERT desplaza automáticamente las etiquetas, las etiquetas corresponden exactamente a `decoder_input_ids`.
      # Debemos asegurarnos de que se ignore el token de PAD
      batch["labels"] = [[-100 if token == tokenizer.pad_token_id else token for token in etiquetas] for etiquetas in batch["labels"]]
    
      return batch

    En este cuaderno, entrenamos y evaluamos el modelo solo en algunos ejemplos de entrenamiento para demostración y establecemos el batch_size en 4 para evitar problemas de falta de memoria.

    La siguiente línea reduce los datos de entrenamiento solo a los primeros 32 ejemplos. La celda se puede comentar o no ejecutar para una ejecución completa de entrenamiento. Se obtuvieron buenos resultados con un batch_size de 16.

    train_data = train_data.select(range(32))

    Muy bien, vamos a preparar los datos de entrenamiento.

    # batch_size = 16
    batch_size=4
    
    train_data = train_data.map(
        process_data_to_model_inputs, 
        batched=True, 
        batch_size=batch_size, 
        remove_columns=["article", "highlights", "id"]
    )

    Al observar el conjunto de datos de entrenamiento procesado, podemos ver que los nombres de columna article, highlights e id han sido reemplazados por los argumentos esperados por el EncoderDecoderModel.

    train_data
    
    SALIDA:
    -------
    Dataset(features: {'attention_mask': Sequence(feature=Value(dtype='int64', id=None), length=-1, id=None), 'decoder_attention_mask': Sequence(feature=Value(dtype='int64', id=None), length=-1, id=None), 'decoder_input_ids': Sequence(feature=Value(dtype='int64', id=None), length=-1, id=None), 'input_ids': Sequence(feature=Value(dtype='int64', id=None), length=-1, id=None), 'labels': Sequence(feature=Value(dtype='int64', id=None), length=-1, id=None)}, num_rows: 32)

    Hasta ahora, los datos se han manipulado utilizando el formato de List de Python. Vamos a convertir los datos en Tensores de PyTorch para entrenar en la GPU.

    train_data.set_format(
        type="torch", columns=["input_ids", "attention_mask", "labels"],
    )

    Genial, el procesamiento de datos de entrenamiento está terminado. De manera análoga, podemos hacer lo mismo para los datos de validación.

    Primero, cargamos el 10% del conjunto de datos de validación:

    val_data = datasets.load_dataset("cnn_dailymail", "3.0.0", split="validation[:10%]")

    Para fines de demostración, luego reducimos los datos de validación a solo 8 muestras,

    val_data = val_data.select(range(8))

    se aplica la función de mapeo,

    val_data = val_data.map(
        process_data_to_model_inputs, 
        batched=True, 
        batch_size=batch_size, 
        remove_columns=["article", "highlights", "id"]
    )

    y, finalmente, los datos de validación también se convierten en tensores de PyTorch.

    val_data.set_format(
        type="torch", columns=["input_ids", "attention_mask", "labels"],
    )

    ¡Genial! Ahora podemos pasar a iniciar el modelo EncoderDecoderModel .

    Iniciando el Modelo Codificador-Decodificador

    Esta sección explica cómo se puede iniciar un modelo codificador-decodificador utilizando el checkpoint bert-base-cased.

    Comencemos importando el modelo EncoderDecoderModel . Para obtener información más detallada sobre la clase EncoderDecoderModel , se recomienda consultar la documentación .

    from transformers import EncoderDecoderModel

    A diferencia de otras clases de modelos en 🤗Transformers, la clase EncoderDecoderModel tiene dos métodos para cargar pesos pre-entrenados, a saber:

    1. el método “estándar” .from_pretrained(...) se deriva del método general PretrainedModel.from_pretrained(...) y, por lo tanto, corresponde exactamente al de otras clases de modelos. La función espera un solo identificador de modelo, por ejemplo .from_pretrained("google/bert2bert_L-24_wmt_de_en") y cargará un solo archivo de checkpoint .pt en la clase EncoderDecoderModel.

    2. un método especial .from_encoder_decoder_pretrained(...), que se puede utilizar para iniciar un modelo codificador-decodificador a partir de dos identificadores de modelo, uno para el codificador y otro para el decodificador. El primer identificador de modelo se utiliza para cargar el codificador , a través de AutoModel.from_pretrained(...) (ver documentación aquí ) y el segundo identificador de modelo se utiliza para cargar el decodificador a través de AutoModelForCausalLM (ver documentación aquí .

    Muy bien, vamos a iniciar nuestro modelo BERT2BERT. Como se mencionó anteriormente, iniciaremos tanto el codificador como el decodificador con el checkpoint "bert-base-cased".

    bert2bert = EncoderDecoderModel.from_encoder_decoder_pretrained("bert-base-uncased", "bert-base-uncased")
    
    SALIDA:
    -------
    """Algunos pesos del checkpoint del modelo en bert-base-uncased no se utilizaron al inicializar BertLMHeadModel: ['cls.seq_relationship.weight', 'cls.seq_relationship.bias']
        - Esto es esperado si estás inicializando BertLMHeadModel desde el checkpoint de un modelo entrenado en otra tarea o con otra arquitectura (por ejemplo, inicializando un modelo BertForSequenceClassification a partir de un modelo BertForPretraining).
        - Esto NO es esperado si estás inicializando BertLMHeadModel desde el checkpoint de un modelo que esperas que sea exactamente idéntico (inicializando un modelo BertForSequenceClassification a partir de un modelo BertForSequenceClassification).
        Algunos pesos de BertLMHeadModel no se inicializaron a partir del checkpoint del modelo en bert-base-uncased y se inicializan de nuevo: ['bert.encoder.layer.0.crossattention.self.query.weight', 'bert.encoder.layer.0.crossattention.self.query.bias', 'bert.encoder.layer.0.crossattention.self.key.weight', 'bert.encoder.layer.0.crossattention.self.key.bias', 'bert.encoder.layer.0.crossattention.self.value.weight', 'bert.encoder.layer.0.crossattention.self.value.bias', 'bert.encoder.layer.0.crossattention.output.dense.weight', 'bert.encoder.layer.0.crossattention.output.dense.bias', 'bert.encoder.layer.0.crossattention.output.LayerNorm.weight', 'bert.encoder.layer.0.crossattention.output.LayerNorm.bias', 'bert.encoder.layer.1.crossattention.self.query.weight', 'bert.encoder.layer.1.crossattention.self.query.bias', 'bert.encoder.layer.1.crossattention.self.key.weight', 'bert.encoder.layer.1.crossattention.self.key.bias', 'bert.encoder.layer.1.crossattention.self.value.weight', 'bert.encoder.layer.1.crossattention.self.value.bias', 'bert.encoder.layer.1.crossattention.output.dense.weight', 'bert.encoder.layer.1.crossattention.output.dense.bias', 'bert.encoder.layer.1.crossattention.output.LayerNorm.weight', 'bert.encoder.layer.1.crossattention.output.LayerNorm.bias', 'bert.encoder.layer.2.crossattention.self.query.weight', 'bert.encoder.layer.2.crossattention.self.query.bias', 'bert.encoder.layer.2.crossattention.self.key.weight', 'bert.encoder.layer.2.crossattention.self.key.bias', 'bert.encoder.layer.2.crossattention.self.value.weight', 'bert.encoder.layer.2.crossattention.self.value.bias', 'bert.encoder.layer.2.crossattention.output.dense.weight', 'bert.encoder.layer.2.crossattention.output.dense.bias', 'bert.encoder.layer.2.crossattention.output.LayerNorm.weight', 'bert.encoder.layer.2.crossattention.output.LayerNorm.bias', 'bert.encoder.layer.3.crossattention.self.query.weight', 'bert.encoder.layer.3.crossattention.self.query.bias', 'bert.encoder.layer.3.crossattention.self.key.weight', 'bert.encoder.layer.3.crossattention.self.key.bias', 'bert.encoder.layer.3.crossattention.self.value.weight', 'bert.encoder.layer.3.crossattention.self.value.bias', 'bert.encoder.layer.3.crossattention.output.dense.weight', 'bert.encoder.layer.3.crossattention.output.dense.bias', 'bert.encoder.layer.3.crossattention.output.LayerNorm.weight', 'bert.encoder.layer.3.crossattention.output.LayerNorm.bias', 'bert.encoder.layer.4.crossattention.self.query.weight', 'bert.encoder.layer.4.crossattention.self.query.bias', 'bert.encoder.layer.4.crossattention.self.key.weight', 'bert.encoder.layer.4.crossattention.self.key.bias', 'bert.encoder.layer.4.crossattention.self.value.weight', 'bert.encoder.layer.4.crossattention.self.value.bias', 'bert.encoder.layer.4.crossattention.output.dense.weight', 'bert.encoder.layer.4.crossattention.output.dense.bias', 'bert.encoder.layer.4.crossattention.output.LayerNorm.weight', 'bert.encoder.layer.4.crossattention.output.LayerNorm.bias', 'bert.encoder.layer.5.crossattention.self.query.weight', 'bert.encoder.layer.5.crossattention.self.query.bias', 'bert.encoder.layer.5.crossattention.self.key.weight', 'bert.encoder.layer.5.crossattention.self.key.bias', 'bert.encoder.layer.5.crossattention.self.value.weight', 'bert.encoder.layer.5.crossattention.self.value.bias', 'bert.encoder.layer.5.crossattention.output.dense.weight', 'bert.encoder.layer.5.crossattention.output.dense.bias', 'bert.encoder.layer.5.crossattention.output.LayerNorm.weight', 'bert.encoder.layer.5.crossattention.output.LayerNorm.bias', 'bert.encoder.layer.6.crossattention.self.query.weight', 'bert.encoder.layer.6.crossattention.self.query.bias', 'bert.encoder.layer.6.crossattention.self.key.weight', 'bert.encoder.layer.6.crossattention.self.key.bias', 'bert.encoder.layer.6.crossattention.self.value.weight', 'bert.encoder.layer.6.crossattention.self.value.bias', 'bert.encoder.layer.6.crossattention.output.dense.weight', 'bert.encoder.layer.6.crossattention.output.dense.bias', 'bert.encoder.layer.6.crossattention.output.LayerNorm.weight', 'bert.encoder.layer.6.crossattention.output.LayerNorm.bias', 'bert.encoder.layer.7.crossattention.self.query.weight', 'bert.encoder.layer.7.crossattention.self.query.bias', 'bert.encoder.layer.7.crossattention.self.key.weight', 'bert.encoder.layer.7.crossattention.self.key.bias', 'bert.encoder.layer.7.crossattention.self.value.weight', 'bert.encoder.layer.7.crossattention.self.value.bias', 'bert.encoder.layer.7.crossattention.output.dense.weight', 'bert.encoder.layer.7.crossattention.output.dense.bias', 'bert.encoder.layer.7.crossattention.output.LayerNorm.weight', 'bert.encoder.layer.7.crossattention.output.LayerNorm.bias', 'bert.encoder.layer.8.crossattention.self.query.weight', 'bert.encoder.layer.8.crossattention.self.query.bias', 'bert.encoder.layer.8.crossattention.self.key.weight', 'bert.encoder.layer.8.crossattention.self.key.bias', 'bert.encoder.layer.8.crossattention.self.value.weight', 'bert.encoder.layer.8.crossattention.self.value.bias',

    Por una vez, deberíamos echar un buen vistazo a la advertencia aquí. Podemos ver que no se utilizaron dos pesos correspondientes a una capa "cls". Esto no debería ser un problema porque no necesitamos la capa CLS de BERT para tareas de secuencia a secuencia. Además, notamos que muchos pesos están "nuevamente" o inicializados al azar. Al observar más de cerca, todos estos pesos corresponden a la capa de atención cruzada, que es exactamente lo que esperaríamos después de haber leído la teoría anteriormente.

    Echemos un vistazo más de cerca al modelo.

    bert2bert
    
    SALIDA:
    -------
        EncoderDecoderModel(
          (encoder): BertModel(
            (embeddings): BertEmbeddings(
              (word_embeddings): Embedding(30522, 768, padding_idx=0)
              (position_embeddings): Embedding(512, 768)
              (token_type_embeddings): Embedding(2, 768)
              (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
            (encoder): BertEncoder(
              (layer): ModuleList(
                (0): BertLayer(
                  (attention): BertAttention(
                    (self): BertSelfAttention(
                      (query): Linear(in_features=768, out_features=768, bias=True)
                      (key): Linear(in_features=768, out_features=768, bias=True)
                      (value): Linear(in_features=768, out_features=768, bias=True)
                      (dropout): Dropout(p=0.1, inplace=False)
                    )
                    (output): BertSelfOutput(
                      (dense): Linear(in_features=768, out_features=768, bias=True)
                      (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                      (dropout): Dropout(p=0.1, inplace=False)
                    )
                  )
                  (intermediate): BertIntermediate(
                    (dense): Linear(in_features=768, out_features=3072, bias=True)
                  )
                  (output): BertOutput(
                    (dense): Linear(in_features=3072, out_features=768, bias=True)
                    (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                    (dropout): Dropout(p=0.1, inplace=False)
                  )
                ),
                            ...
                            ,
                (11): BertLayer(
                  (attention): BertAttention(
                    (self): BertSelfAttention(
                      (query): Linear(in_features=768, out_features=768, bias=True)
                      (key): Linear(in_features=768, out_features=768, bias=True)
                      (value): Linear(in_features=768, out_features=768, bias=True)
                      (dropout): Dropout(p=0.1, inplace=False)
                    )
                    (output): BertSelfOutput(
                      (dense): Linear(in_features=768, out_features=768, bias=True)
                      (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                      (dropout): Dropout(p=0.1, inplace=False)
                    )
                  )
                  (intermediate): BertIntermediate(
                    (dense): Linear(in_features=768, out_features=3072, bias=True)
                  )
                  (output): BertOutput(
                    (dense): Linear(in_features=3072, out_features=768, bias=True)
                    (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                    (dropout): Dropout(p=0.1, inplace=False)
                  )
                )
              )
            )
            (pooler): BertPooler(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (activation): Tanh()
            )
          )
          (decoder): BertLMHeadModel(
            (bert): BertModel(
              (embeddings): BertEmbeddings(
                (word_embeddings): Embedding(30522, 768, padding_idx=0)
                (position_embeddings): Embedding(512, 768)
                (token_type_embeddings): Embedding(2, 768)
                (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                (dropout): Dropout(p=0.1, inplace=False)
              )
              (encoder): BertEncoder(
                (layer): ModuleList(
                  (0): BertLayer(
                    (attention): BertAttention(
                      (self): BertSelfAttention(
                        (query): Linear(in_features=768, out_features=768, bias=True)
                        (key): Linear(in_features=768, out_features=768, bias=True)
                        (value): Linear(in_features=768, out_features=768, bias=True)
                        (dropout): Dropout(p=0.1, inplace=False)
                      )
                      (output): BertSelfOutput(
                        (dense): Linear(in_features=768, out_features=768, bias=True)
                        (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                        (dropout): Dropout(p=0.1, inplace=False)
                      )
                    )
                    (crossattention): BertAttention(
                      (self): BertSelfAttention(
                        (query): Linear(in_features=768, out_features=768, bias=True)
                        (key): Linear(in_features=768, out_features=768, bias=True)
                        (value): Linear(in_features=768, out_features=768, bias=True)
                        (dropout): Dropout(p=0.1, inplace=False)
                      )
                      (output): BertSelfOutput(
                        (dense): Linear(in_features=768, out_features=768, bias=True)
                        (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                        (dropout): Dropout(p=0.1, inplace=False)
                      )
                    )
                    (intermediate): BertIntermediate(
                      (dense): Linear(in_features=768, out_features=3072, bias=True)
                    )
                    (output): BertOutput(
                      (dense): Linear(in_features=3072, out_features=768, bias=True)
                      (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                      (dropout): Dropout(p=0.1, inplace=False)
                    )
                  ),
                                ...,
                  (11): BertLayer(
                    (attention): BertAttention(
                      (self): BertSelfAttention(
                        (query): Linear(in_features=768, out_features=768, bias=True)
                        (key): Linear(in_features=768, out_features=768, bias=True)
                        (value): Linear(in_features=768, out_features=768, bias=True)
                        (dropout): Dropout(p=0.1, inplace=False)
                      )
                      (output): BertSelfOutput(
                        (dense): Linear(in_features=768, out_features=768, bias=True)
                        (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                        (dropout): Dropout(p=0.1, inplace=False)
                      )
                    )
                    (crossattention): BertAttention(
                      (self): BertSelfAttention(
                        (query): Linear(in_features=768, out_features=768, bias=True)
                        (key): Linear(in_features=768, out_features=768, bias=True)
                        (value): Linear(in_features=768, out_features=768, bias=True)
                        (dropout): Dropout(p=0.1, inplace=False)
                      )
                      (output): BertSelfOutput(
                        (dense): Linear(in_features=768, out_features=768, bias=True)
                        (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                        (dropout): Dropout(p=0.1, inplace=False)
                      )
                    )
                    (intermediate): BertIntermediate(
                      (dense): Linear(in_features=768, out_features=3072, bias=True)
                    )
                    (output): BertOutput(
                      (dense): Linear(in_features=3072, out_features=768, bias=True)
                      (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                      (dropout): Dropout(p=0.1, inplace=False)
                    )
                  )
                )
              )
            )
            (cls): BertOnlyMLMHead(
              (predictions): BertLMPredictionHead(
                (transform): BertPredictionHeadTransform(
                  (dense): Linear(in_features=768, out_features=768, bias=True)
                  (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                )
                (decoder): Linear(in_features=768, out_features=30522, bias=True)
              )
            )
          )
        )

    Vemos que bert2bert.encoder es una instancia de BertModel y que bert2bert.decoder es una de BertLMHeadModel. Sin embargo, ambas instancias ahora se combinan en un solo torch.nn.Module y, por lo tanto, se pueden guardar como un único archivo de punto de control .pt.

    Vamos a probarlo usando el método estándar .save_pretrained(...).

    bert2bert.save_pretrained("bert2bert")

    De manera similar, el modelo se puede cargar nuevamente usando el método estándar .from_pretrained(...).

    bert2bert = EncoderDecoderModel.from_pretrained("bert2bert")

    Increíble. También vamos a guardar la configuración.

    bert2bert.config
    
    SALIDA:
    -------
        EncoderDecoderConfig {
          "_name_or_path": "bert2bert",
          "architectures": [
            "EncoderDecoderModel"
          ],
          "decoder": {
            "_name_or_path": "bert-base-uncased",
            "add_cross_attention": true,
            "architectures": [
              "BertForMaskedLM"
            ],
            "attention_probs_dropout_prob": 0.1,
            "bad_words_ids": null,
            "bos_token_id": null,
            "chunk_size_feed_forward": 0,
            "decoder_start_token_id": null,
            "do_sample": false,
            "early_stopping": false,
            "eos_token_id": null,
            "finetuning_task": null,
            "gradient_checkpointing": false,
            "hidden_act": "gelu",
            "hidden_dropout_prob": 0.1,
            "hidden_size": 768,
            "id2label": {
              "0": "LABEL_0",
              "1": "LABEL_1"
            },
            "initializer_range": 0.02,
            "intermediate_size": 3072,
            "is_decoder": true,
            "is_encoder_decoder": false,
            "label2id": {
              "LABEL_0": 0,
              "LABEL_1": 1
            },
            "layer_norm_eps": 1e-12,
            "length_penalty": 1.0,
            "max_length": 20,
            "max_position_embeddings": 512,
            "min_length": 0,
            "model_type": "bert",
            "no_repeat_ngram_size": 0,
            "num_attention_heads": 12,
            "num_beams": 1,
            "num_hidden_layers": 12,
            "num_return_sequences": 1,
            "output_attentions": false,
            "output_hidden_states": false,
            "pad_token_id": 0,
            "prefix": null,
            "pruned_heads": {},
            "repetition_penalty": 1.0,
            "return_dict": false,
            "sep_token_id": null,
            "task_specific_params": null,
            "temperature": 1.0,
            "tie_encoder_decoder": false,
            "tie_word_embeddings": true,
            "tokenizer_class": null,
            "top_k": 50,
            "top_p": 1.0,
            "torchscript": false,
            "type_vocab_size": 2,
            "use_bfloat16": false,
            "use_cache": true,
            "vocab_size": 30522,
            "xla_device": null
          },
          "encoder": {
            "_name_or_path": "bert-base-uncased",
            "add_cross_attention": false,
            "architectures": [
              "BertForMaskedLM"
            ],
            "attention_probs_dropout_prob": 0.1,
            "bad_words_ids": null,
            "bos_token_id": null,
            "chunk_size_feed_forward": 0,
            "decoder_start_token_id": null,
            "do_sample": false,
            "early_stopping": false,
            "eos_token_id": null,
            "finetuning_task": null,
            "gradient_checkpointing": false,
            "hidden_act": "gelu",
            "hidden_dropout_prob": 0.1,
            "hidden_size": 768,
            "id2label": {
              "0": "LABEL_0",
              "1": "LABEL_1"
            },
            "initializer_range": 0.02,
            "intermediate_size": 3072,
            "is_decoder": false,
            "is_encoder_decoder": false,
            "label2id": {
              "LABEL_0": 0,
              "LABEL_1": 1
            },
            "layer_norm_eps": 1e-12,
            "length_penalty": 1.0,
            "max_length": 20,
            "max_position_embeddings": 512,
            "min_length": 0,
            "model_type": "bert",
            "no_repeat_ngram_size": 0,
            "num_attention_heads": 12,
            "num_beams": 1,
            "num_hidden_layers": 12,
            "num_return_sequences": 1,
            "output_attentions": false,
            "output_hidden_states": false,
            "pad_token_id": 0,
            "prefix": null,
            "pruned_heads": {},
            "repetition_penalty": 1.0,
            "return_dict": false,
            "sep_token_id": null,
            "task_specific_params": null,
            "temperature": 1.0,
            "tie_encoder_decoder": false,
            "tie_word_embeddings": true,
            "tokenizer_class": null,
            "top_k": 50,
            "top_p": 1.0,
            "torchscript": false,
            "type_vocab_size": 2,
            "use_bfloat16": false,
            "use_cache": true,
            "vocab_size": 30522,
            "xla_device": null
          },
          "is_encoder_decoder": true,
          "model_type": "encoder_decoder"
        }

    La configuración está compuesta de manera similar por una configuración del codificador y una configuración del decodificador, ambas son instancias de BertConfig en nuestro caso. Sin embargo, la configuración general es del tipo EncoderDecoderConfig y, por lo tanto, se guarda como un único archivo .json.

    En conclusión, se debe recordar que una vez que se instancia un objeto EncoderDecoderModel, proporciona la misma funcionalidad que cualquier otro modelo codificador-decodificador en 🤗Transformers, por ejemplo, BART, T5, ProphetNet, ... La única diferencia es que un EncoderDecoderModel proporciona la función adicional from_encoder_decoder_pretrained(...) que permite que la clase del modelo se inicie rápidamente a partir de cualquier dos puntos de control de codificador y decodificador.

    En una nota aparte, si se quisiera crear un modelo codificador-decodificador compartido, se puede pasar adicionalmente el parámetro tie_encoder_decoder=True de la siguiente manera:

    shared_bert2bert = EncoderDecoderModel.from_encoder_decoder_pretrained("bert-base-cased", "bert-base-cased", tie_encoder_decoder=True)

    Como comparación, podemos ver que el modelo compartido tiene muchos menos parámetros como se esperaba.

    print(f"\n\nNum Params. Compartidos: {shared_bert2bert.num_parameters()}, No Compartidos: {bert2bert.num_parameters()}")
    
    SALIDA:
    -------
    Num Params. Compartidos: 137298244, No Compartidos: 247363386

    En este cuaderno, sin embargo, entrenaremos un modelo Bert2Bert no compartido, por lo que continuaremos con bert2bert y no con shared_bert2bert.

    # liberar memoria
    del shared_bert2bert

    Hemos iniciado rápidamente un modelo bert2bert, pero aún no hemos definido todos los parámetros relevantes utilizados para la decodificación de búsqueda de beam.

    Comencemos estableciendo los tokens especiales. bert-base-cased no tiene un decoder_start_token_id o eos_token_id, por lo que usaremos su cls_token_id y sep_token_id, respectivamente. Además, debemos definir un pad_token_id en la configuración y asegurarnos de que se establezca el vocab_size correcto.

    bert2bert.config.decoder_start_token_id = tokenizer.cls_token_id
    bert2bert.config.eos_token_id = tokenizer.sep_token_id
    bert2bert.config.pad_token_id = tokenizer.pad_token_id
    bert2bert.config.vocab_size = bert2bert.config.encoder.vocab_size

    A continuación, definamos todos los parámetros relacionados con la decodificación de búsqueda de beam. Dado que bart-large-cnn produce buenos resultados en CNN/Dailymail, simplemente copiaremos sus parámetros de decodificación de búsqueda de beam.

    Para obtener más detalles sobre lo que hace cada uno de estos parámetros, consulte esta publicación de blog o los documentos.

    bert2bert.config.max_length = 142
    bert2bert.config.min_length = 56
    bert2bert.config.no_repeat_ngram_size = 3
    bert2bert.config.early_stopping = True
    bert2bert.config.length_penalty = 2.0
    bert2bert.config.num_beams = 4

    Muy bien, ahora comencemos a ajustar finamente el modelo BERT2BERT iniciado rápidamente.

    Ajuste Fino de Modelos Codificador-Decodificador Iniciados Rápidamente

    En esta sección, mostraremos cómo se puede utilizar el Seq2SeqTrainer para ajustar finamente un modelo codificador-decodificador iniciado rápidamente.

    Comencemos importando el Seq2SeqTrainer y sus argumentos de entrenamiento Seq2SeqTrainingArguments.

    from transformers import Seq2SeqTrainer, Seq2SeqTrainingArguments

    Además, necesitamos un par de paquetes de Python para que funcione el Seq2SeqTrainer.

    !pip install git-python==1.0.3
    !pip install rouge_score
    !pip install sacrebleu

    El Seq2SeqTrainer extiende el entrenador de 🤗Transformer para modelos codificador-decodificador. En resumen, permite usar la función generate(...) durante la evaluación, lo cual es necesario para validar el rendimiento de los modelos codificador-decodificador en la mayoría de las tareas de secuencia a secuencia, como la sumarización.

    Para obtener más información sobre el Trainer, se debe leer este breve tutorial.

    Comencemos configurando el Seq2SeqTrainingArguments.

    El argumento predict_with_generate debe establecerse como True, para que el Seq2SeqTrainer ejecute la función generate(...) en los datos de validación y pase la salida generada como predictions a la función compute_metric(...) que definiremos más adelante. Los argumentos adicionales se derivan de TrainingArguments y se pueden leer aquí. Para una ejecución completa del entrenamiento, se deben cambiar esos argumentos según sea necesario. Los valores predeterminados buenos están comentados a continuación.

    Para obtener más información sobre el Seq2SeqTrainer, se recomienda al lector echar un vistazo al código.

    training_args = Seq2SeqTrainingArguments(
        predict_with_generate=True,
        evaluation_strategy="steps",
        per_device_train_batch_size=tamaño_lote_entrenamiento,
        per_device_eval_batch_size=tamaño_lote_validación,
        fp16=True, 
        output_dir="./",
        logging_steps=2,
        save_steps=10,
        eval_steps=4,
        # logging_steps=1000,
        # save_steps=500,
        # eval_steps=7500,
        # warmup_steps=2000,
        # save_total_limit=3,
    )

    También necesitamos definir una función para calcular correctamente el puntaje ROUGE durante la validación. Dado que activamos predict_with_generate, la función compute_metrics(...) espera predictions que se obtuvieron utilizando la función generate(...). Como la mayoría de las tareas de resumen, CNN/Dailymail se evalúa típicamente utilizando el puntaje ROUGE.

    Primero carguemos la métrica ROUGE utilizando la biblioteca 🤗datasets.

    rouge = datasets.load_metric("rouge")

    A continuación, definiremos la función compute_metrics(...). La métrica rouge calcula el puntaje a partir de dos listas de cadenas. Por lo tanto, decodificamos tanto las predictions como las labels, asegurándonos de que -100 se reemplace correctamente por pad_token_id y eliminamos todos los caracteres especiales configurando skip_special_tokens=True.

    def compute_metrics(pred):
        labels_ids = pred.label_ids
        pred_ids = pred.predictions
    
        pred_str = tokenizer.batch_decode(pred_ids, skip_special_tokens=True)
        labels_ids[labels_ids == -100] = tokenizer.pad_token_id
        label_str = tokenizer.batch_decode(labels_ids, skip_special_tokens=True)
    
        rouge_output = rouge.compute(predictions=pred_str, references=label_str, rouge_types=["rouge2"])["rouge2"].mid
    
        return {
            "rouge2_precision": round(rouge_output.precision, 4),
            "rouge2_recall": round(rouge_output.recall, 4),
            "rouge2_fmeasure": round(rouge_output.fmeasure, 4),
        }

    Genial, ahora podemos pasar todos los argumentos al Seq2SeqTrainer y comenzar la adaptación fina. La ejecución de la siguiente celda llevará aproximadamente 10 minutos ☕.

    Adaptar BERT2BERT en los datos de entrenamiento completos de CNN/Dailymail lleva aproximadamente 8 horas en una sola GPU TITAN RTX.

    # instanciar el entrenador
    trainer = Seq2SeqTrainer(
        model=bert2bert,
        tokenizer=tokenizer,
        args=training_args,
        compute_metrics=compute_metrics,
        train_dataset=train_data,
        eval_dataset=val_data,
    )
    trainer.train()

    Impresionante, ahora deberíamos estar completamente equipados para ajustar finamente un modelo codificador-decodificador con inicio en caliente. Para verificar el resultado de nuestro ajuste fino, echemos un vistazo a los puntos de control guardados.

    !ls
    
    OUTPUT:
    -------
        bert2bert      checkpoint-20  runs     seq2seq_trainer.py
        checkpoint-10  __pycache__    sample_data  seq2seq_training_args.py

    Finalmente, podemos cargar el punto de control como de costumbre a través del método EncoderDecoderModel.from_pretrained(...).

    dummy_bert2bert = EncoderDecoderModel.from_pretrained("./checkpoint-20")

    Evaluación

    En un paso final, es posible que deseemos evaluar el modelo BERT2BERT en los datos de prueba.

    Para comenzar, en lugar de cargar el modelo ficticio, carguemos un modelo BERT2BERT que se ajustó en el conjunto de datos de entrenamiento completo. También cargamos su tokenizador, que es solo una copia del tokenizador de bert-base-cased.

    from transformers import BertTokenizer
    
    bert2bert = EncoderDecoderModel.from_pretrained("patrickvonplaten/bert2bert_cnn_daily_mail").to("cuda")
    tokenizer = BertTokenizer.from_pretrained("patrickvonplaten/bert2bert_cnn_daily_mail")

    A continuación, cargamos solo el 2% de los datos de prueba de CNN/Dailymail. Para la evaluación completa, obviamente se debe utilizar el 100% de los datos.

    test_data = datasets.load_dataset("cnn_dailymail", "3.0.0", split="test[:2%]")

    Ahora, podemos aprovechar nuevamente la útil función map() de 🤗dataset para generar un resumen para cada muestra de prueba.

    Para cada muestra de datos:

    • primero, tokenizamos el "article",
    • segundo, generamos los identificadores de token de salida, y
    • tercero, decodificamos los identificadores de token de salida para obtener nuestro resumen predicho.
    def generate_summary(batch):
        # truncar en la longitud máxima de BERT 512
        inputs = tokenizer(batch["article"], padding="max_length", truncation=True, max_length=512, return_tensors="pt")
        input_ids = inputs.input_ids.to("cuda")
        attention_mask = inputs.attention_mask.to("cuda")
    
        outputs = bert2bert.generate(input_ids, attention_mask=attention_mask)
    
        output_str = tokenizer.batch_decode(outputs, skip_special_tokens=True)
    
        batch["pred_summary"] = output_str
    
        return batch

    Ejecutemos la función map para obtener el diccionario de resultados que tiene el resumen predicho del modelo almacenado para cada muestra. La ejecución de la siguiente celda puede llevar aproximadamente 10 minutos ☕.

    batch_size = 16  # cambiar a 64 para una evaluación completa
    
    results = test_data.map(generate_summary, batched=True, batch_size=batch_size, remove_columns=["article"])

    Finalmente, calculamos el puntaje ROUGE.

    rouge.compute(predictions=results["pred_summary"], references=results["highlights"], rouge_types=["rouge2"])["rouge2"].mid
    
    SALIDA:
    -------
        Score(precision=0.10389454113300968, recall=0.1564771201053348, fmeasure=0.12175271663717585)

    Eso es todo. Hemos mostrado cómo iniciar un modelo BERT2BERT y ajustarlo/evaluarlo en el conjunto de datos de CNN/Dailymail.

    El modelo BERT2BERT completamente entrenado se ha subido al repositorio de modelos de 🤗 bajo el nombre patrickvonplaten/bert2bert_cnn_daily_mail.

    El modelo logra un puntaje ROUGE-2 de 18.22 en los datos de evaluación completos, lo cual es incluso un poco mejor de lo reportado en el artículo.

    Para ver algunos ejemplos de resumen, se recomienda al lector utilizar la API de inferencia en línea del modelo, aquí.

    Muchas gracias a Sascha Rothe, Shashi Narayan y Aliaksei Severyn de Google Research, y a Victor Sanh, Sylvain Gugger y Thomas Wolf de 🤗Hugging Face por corregir y brindar comentarios muy apreciados.

    We will continue to update Zepes; if you have any questions or suggestions, please contact us!

    Share:

    Was this article helpful?

    93 out of 132 found this helpful

Discover more

Inteligencia Artificial

Los Anunciantes más Grandes del Mundo Aceptan el Poder de la IA Un Cambio de Paradigma en la Publicidad

En un movimiento que podría remodelar el panorama publicitario, algunos de los anunciantes más renombrados del mundo ...

Ciencia de Datos

Aprendizaje Profundo en Sistemas de Recomendación Una introducción.

Los sistemas de recomendación se encuentran entre las aplicaciones de Aprendizaje Automático industrial de más rápido...

Inteligencia Artificial

Computación de siguiente nivel NVIDIA y AMD ofrecen potentes estaciones de trabajo para acelerar la IA, el renderizado y la simulación.

Para permitir a profesionales de todo el mundo construir y ejecutar aplicaciones de inteligencia artificial desde sus...

Inteligencia Artificial

¿Es ChatGPT realmente neutral? Un estudio empírico sobre el sesgo político en agentes conversacionales impulsados por IA

Un estudio reciente llevado a cabo por investigadores del Reino Unido y Brasil ha iluminado preocupaciones con respec...

Inteligencia Artificial

Presentamos OpenLLM Biblioteca de código abierto para LLMs

Una plataforma fácil de usar para operar modelos de lenguaje grandes (LLMs) en producción, con características como a...

Inteligencia Artificial

Jugando ¿Dónde está Wally? en 3D OpenMask3D es un modelo de IA que puede segmentar instancias en 3D con consultas de vocabulario abierto.

La segmentación de imágenes ha avanzado mucho en la última década, gracias al avance de las redes neuronales. Ahora e...