Cómo generar texto utilizando diferentes métodos de decodificación para la generación de lenguaje con Transformers

Generación de texto con Transformers utilizando métodos de decodificación

Introducción

En los últimos años, ha habido un creciente interés en la generación de lenguaje de forma abierta gracias al surgimiento de modelos de lenguaje basados en transformadores entrenados en millones de páginas web, como el famoso modelo GPT2 de OpenAI. Los resultados en generación de lenguaje abierto condicionado son impresionantes, por ejemplo, GPT2 sobre unicornios, XLNet, lenguaje controlado con CTRL. Además de la mejora en la arquitectura de los transformadores y los enormes datos de entrenamiento no supervisado, los mejores métodos de decodificación también han desempeñado un papel importante.

Esta publicación del blog ofrece una breve descripción de diferentes estrategias de decodificación y, lo que es más importante, muestra cómo puedes implementarlas con muy poco esfuerzo utilizando la popular biblioteca transformers!

Todas las siguientes funcionalidades se pueden utilizar para la generación de lenguaje auto-regresiva (aquí un repaso). En resumen, la generación de lenguaje auto-regresiva se basa en la suposición de que la distribución de probabilidad de una secuencia de palabras se puede descomponer en el producto de las distribuciones condicionales de las palabras siguientes:

P(w1:T∣W0)=∏t=1TP(wt∣w1:t−1,W0) ,con w1:0=∅, P(w_{1:T} | W_0 ) = \prod_{t=1}^T P(w_{t} | w_{1: t-1}, W_0) \text{ ,con } w_{1: 0} = \emptyset, P(w1:T​∣W0​)=t=1∏T​P(wt​∣w1:t−1​,W0​) ,con w1:0​=∅,

y W0W_0W0​ siendo la secuencia de palabras iniciales del contexto. La longitud TTT de la secuencia de palabras generalmente se determina sobre la marcha y corresponde al instante de tiempo t=Tt=Tt=T en el que se genera el token EOS a partir de P(wt∣w1:t−1,W0)P(w_{t} | w_{1: t-1}, W_{0})P(wt​∣w1:t−1​,W0​).

La generación de lenguaje auto-regresiva ahora está disponible para GPT2, XLNet, OpenAi-GPT, CTRL, TransfoXL, XLM, Bart, T5 en PyTorch y Tensorflow >= 2.0!

Daremos un recorrido de los métodos de decodificación más prominentes en la actualidad, principalmente la búsqueda voraz, la búsqueda de haz, el muestreo de las mejores K y el muestreo de las mejores P.

Instalemos rápidamente transformers y carguemos el modelo. Usaremos GPT2 en Tensorflow 2.1 para la demostración, pero la API es exactamente la misma para PyTorch.

!pip install -q git+https://github.com/huggingface/transformers.git
!pip install -q tensorflow==2.1

import tensorflow as tf
from transformers import TFGPT2LMHeadModel, GPT2Tokenizer


tokenizer = GPT2Tokenizer.from_pretrained("gpt2")

# agregamos el token EOS como token PAD para evitar advertencias
model = TFGPT2LMHeadModel.from_pretrained("gpt2", pad_token_id=tokenizer.eos_token_id)

La búsqueda voraz simplemente selecciona la palabra con la mayor probabilidad como su siguiente palabra: wt=argmaxwP(w∣w1:t−1)w_t = argmax_{w}P(w | w_{1:t-1})wt​=argmaxw​P(w∣w1:t−1​) en cada instante de tiempo ttt. El siguiente boceto muestra la búsqueda voraz.

Comenzando desde la palabra “The”,\text{“The”},”The”, el algoritmo elige de forma voraz la siguiente palabra de mayor probabilidad “nice”\text{“nice”}”nice” y así sucesivamente, de modo que la secuencia final de palabras generadas es (“The”,”nice”,”woman”)(\text{“The”}, \text{“nice”}, \text{“woman”})(“The”,”nice”,”woman”) con una probabilidad total de 0.5×0.4=0.20.5 \times 0.4 = 0.20.5×0.4=0.2 .

A continuación generaremos secuencias de palabras usando GPT2 en el contexto (“I”,”disfruto”,”caminar”,”con”,”mi”,”adorable”,”perro”)(\text{“I”}, \text{“disfruto”}, \text{“caminar”}, \text{“con”}, \text{“mi”}, \text{“adorable”}, \text{“perro”})(“I”,”disfruto”,”caminar”,”con”,”mi”,”adorable”,”perro”). Veamos cómo se puede utilizar la búsqueda ávida en transformers:

# codificar el contexto en el que se condiciona la generación
input_ids = tokenizer.encode('Disfruto caminar con mi adorable perro', return_tensors='tf')

# generar texto hasta que la longitud del resultado (que incluye la longitud del contexto) alcance 50
resultado_avidos = model.generate(input_ids, max_length=50)

print("Resultado:\n" + 100 * '-')
print(tokenizer.decode(resultado_avidos[0], skip_special_tokens=True))

¡Listo! Hemos generado nuestro primer texto corto con GPT2 😊. Las palabras generadas que siguen al contexto son razonables, ¡pero el modelo comienza a repetirse rápidamente! Este es un problema muy común en la generación de lenguaje en general y parece ser aún más común en la búsqueda ávida y la búsqueda de haz – echa un vistazo a Vijayakumar et al., 2016 y Shao et al., 2017.

Sin embargo, la principal desventaja de la búsqueda ávida es que se pierden palabras de alta probabilidad ocultas detrás de una palabra de baja probabilidad, como se puede ver en nuestro ejemplo anterior:

La palabra “tiene”\text{“tiene”}”tiene” con su alta probabilidad condicional de 0.90.90.9 está oculta detrás de la palabra “perro”\text{“perro”}”perro”, que solo tiene la segunda probabilidad condicional más alta, por lo que la búsqueda ávida no encuentra la secuencia de palabras “El”,”perro”,”tiene”\text{“El”}, \text{“perro”}, \text{“tiene”}”El”,”perro”,”tiene” .

Afortunadamente, ¡tenemos la búsqueda de haz para resolver este problema!

La búsqueda de haz reduce el riesgo de perder secuencias de palabras de alta probabilidad ocultas al mantener las hipótesis más probables num_beams en cada paso de tiempo y, finalmente, elegir la hipótesis con la probabilidad total más alta. Ilustremos esto con num_beams=2:

En el paso de tiempo 1, además de la hipótesis más probable (“El”,”agradable”)(\text{“El”}, \text{“agradable”})(“El”,”agradable”), la búsqueda de haz también registra la segunda hipótesis más probable (“El”,”perro”)(\text{“El”}, \text{“perro”})(“El”,”perro”). En el paso de tiempo 2, la búsqueda de haz encuentra que la secuencia de palabras (“El”,”perro”,”tiene”)(\text{“El”}, \text{“perro”}, \text{“tiene”})(“El”,”perro”,”tiene”), tiene una probabilidad más alta de 0.360.360.36 que (“El”,”agradable”,”mujer”)(\text{“El”}, \text{“agradable”}, \text{“mujer”})(“El”,”agradable”,”mujer”), que tiene una probabilidad de 0.20.20.2 . ¡Genial, ha encontrado la secuencia de palabras más probable en nuestro ejemplo de juguete!

La búsqueda de haz siempre encontrará una secuencia de salida con una probabilidad más alta que la búsqueda ávida, pero no se garantiza que encuentre la salida más probable.

Veamos cómo se puede usar la búsqueda de haz en transformers. Configuramos num_beams > 1 y early_stopping=True para que la generación finalice cuando todas las hipótesis de haz alcancen el token EOS.

# activar la búsqueda de haz y early_stopping
resultado_haz = model.generate(
    input_ids, 
    max_length=50, 
    num_beams=5, 
    early_stopping=True
)

print("Resultado:\n" + 100 * '-')
print(tokenizer.decode(resultado_haz[0], skip_special_tokens=True))

Aunque el resultado es sin duda más fluido, la salida todavía incluye repeticiones de las mismas secuencias de palabras. Un remedio simple es introducir penalizaciones para n-gramas (también conocidos como secuencias de palabras de n palabras) como se introdujo en Paulus et al. (2017) y Klein et al. (2017). La penalización de n-gramas más común se asegura de que ningún n-grama aparezca dos veces estableciendo manualmente la probabilidad de las siguientes palabras que podrían crear un n-grama ya visto en 0.

Vamos a probarlo configurando no_repeat_ngram_size=2 para que ningún 2-grama aparezca dos veces:

# establecer no_repeat_ngram_size a 2
beam_output = model.generate(
    input_ids, 
    max_length=50, 
    num_beams=5, 
    no_repeat_ngram_size=2, 
    early_stopping=True
)

print("Resultado:\n" + 100 * '-')
print(tokenizer.decode(beam_output[0], skip_special_tokens=True))

¡Genial, eso se ve mucho mejor! Podemos ver que la repetición ya no aparece. Sin embargo, las penalizaciones de n-gramos deben usarse con cuidado. ¡Un artículo generado sobre la ciudad de Nueva York no debería usar una penalización de 2-gramos, de lo contrario, el nombre de la ciudad solo aparecería una vez en todo el texto!

Otra característica importante de la búsqueda de beams es que podemos comparar los beams principales después de la generación y elegir el beam generado que mejor se ajuste a nuestro propósito.

En transformers, simplemente establecemos el parámetro num_return_sequences en el número de beams con mayor puntuación que se deben devolver. ¡Asegúrate de que num_return_sequences <= num_beams!

# establecer return_num_sequences > 1
beam_outputs = model.generate(
    input_ids, 
    max_length=50, 
    num_beams=5, 
    no_repeat_ngram_size=2, 
    num_return_sequences=5, 
    early_stopping=True
)

# ahora tenemos 3 secuencias de salida
print("Resultado:\n" + 100 * '-')
for i, beam_output in enumerate(beam_outputs):
  print("{}: {}".format(i, tokenizer.decode(beam_output, skip_special_tokens=True)))

Como se puede ver, las cinco hipótesis de beam son solo marginalmente diferentes entre sí, lo cual no debería ser muy sorprendente cuando se usan solo 5 beams.

En la generación abierta, recientemente se han presentado algunas razones por las cuales la búsqueda de beams podría no ser la mejor opción posible:

  • La búsqueda de beams puede funcionar muy bien en tareas donde la longitud de la generación deseada es más o menos predecible, como en la traducción automática o la summarización – ver Murray et al. (2018) y Yang et al. (2018). Pero esto no es así en la generación abierta, donde la longitud de salida deseada puede variar mucho, por ejemplo, en la generación de diálogos e historias.

  • Hemos visto que la búsqueda de beams sufre mucho de generación repetitiva. Esto es especialmente difícil de controlar con penalizaciones de n-gramos u otras en la generación de historias, ya que encontrar un buen equilibrio entre “sin repetición” forzada y ciclos repetitivos de n-gramos idénticos requiere mucho ajuste fino.

  • Como argumentan Ari Holtzman et al. (2019), el lenguaje humano de alta calidad no sigue una distribución de palabras siguientes de alta probabilidad. En otras palabras, como humanos, queremos que el texto generado nos sorprenda y no sea aburrido/predictible. Los autores muestran esto de manera clara al representar gráficamente la probabilidad que un modelo daría al texto humano frente a lo que hace la búsqueda de beams.

Así que dejemos de ser aburridos e introduzcamos algo de aleatoriedad 🤪.

Muestreo

En su forma más básica, el muestreo significa seleccionar aleatoriamente la siguiente palabra wtw_twt​ según su distribución de probabilidad condicional:

wt∼P(w∣w1:t−1) w_t \sim P(w|w_{1:t-1}) wt​∼P(w∣w1:t−1​)

Tomando el ejemplo anterior, la siguiente gráfica visualiza la generación de lenguaje cuando se utiliza el muestreo.

Es evidente que la generación de lenguaje utilizando el muestreo ya no es determinista. La palabra (“car”)(\text{“car”})(“car”) se selecciona aleatoriamente de la distribución de probabilidad condicionada P(w∣”The”)P(w | \text{“The”})P(w∣”The”), seguida de la selección de (“drives”)(\text{“drives”})(“drives”) de P(w∣”The”,”car”)P(w | \text{“The”}, \text{“car”})P(w∣”The”,”car”) .

En transformers, establecemos do_sample=True y desactivamos el muestreo Top-K (más sobre esto luego) a través de top_k=0. A continuación, fijaremos random_seed=0 con fines ilustrativos. Siéntete libre de cambiar el random_seed para experimentar con el modelo.

# establecer la semilla para reproducir los resultados. Siéntase libre de cambiar la semilla para obtener resultados diferentes
tf.random.set_seed(0)

# activar el muestreo y desactivar el top_k estableciendo el muestreo top_k en 0
sample_output = model.generate(
    input_ids,
    do_sample=True,
    max_length=50,
    top_k=0
)

print("Resultado:\n" + 100 * '-')
print(tokenizer.decode(sample_output[0], skip_special_tokens=True))

¡Interesante! El texto parece estar bien, pero al observarlo más de cerca, no es muy coherente. Los trigramas new hand sense y local batte harness son muy extraños y no parecen haber sido escritos por un humano. Ese es el gran problema cuando se generan secuencias de palabras de forma aleatoria: los modelos a menudo generan incoherencias sin sentido, cf. Ari Holtzman et al. (2019).

Un truco consiste en hacer que la distribución P(w∣w1:t−1)P(w|w_{1:t-1})P(w∣w1:t−1​) sea más afilada (aumentando la probabilidad de palabras con alta probabilidad y disminuyendo la probabilidad de palabras con baja probabilidad) al reducir la llamada temperatura del softmax.

Una ilustración de cómo aplicar la temperatura a nuestro ejemplo anterior podría ser la siguiente.

La distribución condicional de la siguiente palabra en el paso t=1t=1t=1 se vuelve mucho más afilada, dejando casi ninguna posibilidad de que se seleccione la palabra (“car”)(\text{“car”})(“car”).

Veamos cómo podemos enfriar la distribución en la biblioteca estableciendo temperature=0.7:

# establecer la semilla para reproducir los resultados. Siéntase libre de cambiar la semilla para obtener resultados diferentes
tf.random.set_seed(0)

# utilizar la temperatura para disminuir la sensibilidad a candidatos con baja probabilidad
sample_output = model.generate(
    input_ids,
    do_sample=True,
    max_length=50,
    top_k=0,
    temperature=0.7
)

print("Resultado:\n" + 100 * '-')
print(tokenizer.decode(sample_output[0], skip_special_tokens=True))

¡Bien! Hay menos trigramas extraños y el resultado es un poco más coherente ahora. Si bien la aplicación de la temperatura puede hacer que una distribución sea menos aleatoria, en su límite, cuando se establece temperature →0\to 0→0, el muestreo con escala de temperatura se vuelve igual a la decodificación codiciosa y sufrirá los mismos problemas que antes.

Muestreo Top-K

Fan et al. (2018) introdujo un método de muestreo simple pero muy poderoso llamado muestreo Top-K. En el muestreo Top-K, se filtran las K palabras siguientes más probables y la masa de probabilidad se redistribuye solo entre esas K palabras siguientes. GPT2 adoptó este método de muestreo, que fue una de las razones de su éxito en la generación de historias.

Ampliamos el rango de palabras utilizadas para ambos pasos de muestreo en el ejemplo anterior de 3 palabras a 10 palabras para ilustrar mejor el muestreo Top-K.

Habiendo establecido K=6K = 6K=6, en ambos pasos de muestreo limitamos nuestro grupo de muestreo a 6 palabras. Si bien las 6 palabras más probables, definidas como Vtop-KV_{\text{top-K}}Vtop-K​, abarcan solo aproximadamente dos tercios de la masa de probabilidad total en el primer paso, incluyen casi toda la masa de probabilidad en el segundo paso. Sin embargo, vemos que elimina con éxito los candidatos bastante extraños (“not”, “the”, “small”, “told”)(\text{“not”}, \text{“the”}, \text{“small”}, \text{“told”})(“not”, “the”, “small”, “told”) en el segundo paso de muestreo.

Veamos cómo se puede usar Top-K en la biblioteca estableciendo top_k=50:

# establecer la semilla para reproducir los resultados. Siéntase libre de cambiar la semilla para obtener resultados diferentes
tf.random.set_seed(0)

# establecer top_k en 50
sample_output = model.generate(
    input_ids,
    do_sample=True,
    max_length=50,
    top_k=50
)

print("Resultado:\n" + 100 * '-')
print(tokenizer.decode(sample_output[0], skip_special_tokens=True))

¡No está mal en absoluto! El texto es probablemente el texto con un aspecto más humano hasta ahora. Sin embargo, una preocupación con el muestreo Top-K es que no adapta dinámicamente el número de palabras que se filtran de la distribución de probabilidad de la siguiente palabra P(w∣w1:t−1)P(w|w_{1:t-1})P(w∣w1:t−1​). Esto puede ser problemático ya que algunas palabras pueden ser muestreadas de una distribución muy afilada (distribución a la derecha en el gráfico anterior), mientras que otras de una distribución mucho más plana (distribución a la izquierda en el gráfico anterior).

En el paso t=1t=1t=1, Top-K elimina la posibilidad de muestrear (“people”,”big”,”house”,”cat”)(\text{“people”}, \text{“big”}, \text{“house”}, \text{“cat”})(“people”,”big”,”house”,”cat”), que parecen ser candidatos razonables. Por otro lado, en el paso t=2t=2t=2, el método incluye las palabras (“down”,”a”)(\text{“down”}, \text{“a”})(“down”,”a”) que parecen ser inadecuadas en el conjunto de palabras de muestra. Por lo tanto, limitar el conjunto de muestra a un tamaño fijo K podría poner en peligro que el modelo produzca disparates para distribuciones afiladas y limitar la creatividad del modelo para distribuciones planas. Esta intuición llevó a Ari Holtzman et al. (2019) a crear el muestreo de Top-p o núcleo.

Muestreo de Top-p (núcleo)

En lugar de muestrear solo de las K palabras más probables, en el muestreo de Top-p se eligen palabras del conjunto más pequeño posible cuya probabilidad acumulativa supera la probabilidad p. Luego, la masa de probabilidad se redistribuye entre este conjunto de palabras. De esta manera, el tamaño del conjunto de palabras (también conocido como el número de palabras en el conjunto) puede aumentar y disminuir dinámicamente de acuerdo con la distribución de probabilidad de la siguiente palabra. Bueno, eso fue muy largo de explicar, vamos a visualizarlo.

Con p=0.92p=0.92p=0.92, el muestreo de Top-p elige el número mínimo de palabras para superar juntas el p=92%p=92\%p=92% de la masa de probabilidad, definido como Vtop-pV_{\text{top-p}}Vtop-p​. En el primer ejemplo, esto incluía las 9 palabras más probables, mientras que solo tiene que elegir las 3 palabras principales en el segundo ejemplo para superar el 92%. ¡Bastante simple en realidad! Se puede observar que mantiene una amplia gama de palabras donde la siguiente palabra es menos predecible, por ejemplo, P(w∣”The”)P(w | \text{“The”})P(w∣”The”), y solo unas pocas palabras cuando la siguiente palabra parece más predecible, por ejemplo, P(w∣”The”,”car”)P(w | \text{“The”}, \text{“car”})P(w∣”The”,”car”).

¡Bien, es hora de probarlo en transformers! Activamos el muestreo de Top-p estableciendo 0 < top_p < 1:

# establece la semilla para reproducir los resultados. Siéntete libre de cambiar la semilla para obtener resultados diferentes
tf.random.set_seed(0)

# desactiva el muestreo de top_k y muestrea solo de las palabras más probables en un 92%
sample_output = model.generate(
    input_ids, 
    do_sample=True, 
    max_length=50, 
    top_p=0.92, 
    top_k=0
)

print("Resultado:\n" + 100 * '-')
print(tokenizer.decode(sample_output[0], skip_special_tokens=True))

Resultado:
----------------------------------------------------------------------------------------------------
Disfruto caminando con mi lindo perro. Nunca será lo mismo. Lo veo jugar.


Chicos, mi perro necesita un nombre. Especialmente si lo encuentran con alas.


¿Qué fue eso? Tuve mucho o

Genial, suena como si hubiera sido escrito por un humano. Bueno, tal vez no del todo todavía.

Aunque en teoría, Top-p parece más elegante que Top-K, ambos métodos funcionan bien en la práctica. Top-p también se puede usar en combinación con Top-K, lo cual puede evitar palabras clasificadas muy bajas al permitir una selección dinámica.

Finalmente, para obtener múltiples resultados muestreados de manera independiente, podemos establecer nuevamente el parámetro num_return_sequences > 1:

# establece la semilla para reproducir los resultados. Siéntete libre de cambiar la semilla para obtener resultados diferentes
tf.random.set_seed(0)

# establece top_k = 50, top_p = 0.95 y num_return_sequences = 3
sample_outputs = model.generate(
    input_ids,
    do_sample=True, 
    max_length=50, 
    top_k=50, 
    top_p=0.95, 
    num_return_sequences=3
)

print("Resultado:\n" + 100 * '-')
for i, sample_output in enumerate(sample_outputs):
  print("{}: {}".format(i, tokenizer.decode(sample_output, skip_special_tokens=True)))

Resultado:
----------------------------------------------------------------------------------------------------
0: Disfruto caminando con mi lindo perro. Es tan bueno tener la oportunidad de caminar con un perro. Pero tengo este problema con el perro y cómo siempre nos mira y siempre trata de hacerme ver que puedo hacer algo
1: Disfruto caminando con mi lindo perro, a ella le encanta viajar a diferentes lugares del planeta, ¡incluso en el desierto! El mundo no es lo suficientemente grande para que viajemos en autobús con nuestro amado cachorro, pero ahí es donde encuentro mi amor
2: Disfruto caminando con mi lindo perro y jugando con nuestros hijos", dijo David J. Smith, director de la Sociedad Humanitaria de los Estados Unidos.

"Así que como resultado, tengo más trabajo en mi tiempo", dijo.

¡Genial, ahora deberías tener todas las herramientas para permitir que tu modelo escriba tus historias con transformers!

Conclusión

Como métodos de decodificación ad-hoc, el muestreo top-p y top-K parecen producir texto más fluido que la búsqueda codiciosa y por haz en la generación de lenguaje sin restricciones. Recientemente, ha habido más evidencia de que las aparentes fallas de la búsqueda codiciosa y por haz, principalmente la generación de secuencias de palabras repetitivas, son causadas por el modelo (especialmente la forma en que se entrena el modelo), en lugar del método de decodificación, cf. Welleck et al. (2019). Además, como se demostró en Welleck et al. (2020), parece que el muestreo top-K y top-p también sufren de generar secuencias de palabras repetitivas.

En Welleck et al. (2019), los autores muestran que, según las evaluaciones humanas, la búsqueda por haz puede generar texto más fluido que el muestreo top-p, cuando se adapta el objetivo de entrenamiento del modelo.

La generación de lenguaje sin restricciones es un campo de investigación en rápida evolución y, como suele ser el caso, no existe un método de talla única aquí, por lo que uno debe ver qué funciona mejor en su caso de uso específico.

Lo bueno es que puedes probar todos los diferentes métodos de decodificación en transformers 🤗.

Esa fue una breve introducción sobre cómo usar diferentes métodos de decodificación en transformers y las tendencias recientes en la generación de lenguaje sin restricciones.

Se agradecen comentarios y preguntas en el repositorio de Github.

Para divertirse más generando historias, echa un vistazo a Writing with Transformers

Gracias a todos los que han contribuido a la publicación del blog: Alexander Rush, Julien Chaumand, Thomas Wolf, Victor Sanh, Sam Shleifer, Clément Delangue, Yacine Jernite, Oliver Åstrand y John de Wasseige.

Apéndice

Hay un par de parámetros adicionales para el método generate que no se mencionaron anteriormente. ¡Los explicaremos brevemente aquí!

  • min_length se puede usar para obligar al modelo a no producir un token EOS (= no terminar la oración) antes de que se alcance min_length. Esto se usa con bastante frecuencia en la resumenización, pero puede ser útil en general si el usuario desea tener salidas más largas.

  • repetition_penalty se puede usar para penalizar palabras que ya se generaron o pertenecen al contexto. Fue introducido por primera vez por Keskar et al. (2019) y también se utiliza en el objetivo de entrenamiento en Welleck et al. (2019). Puede ser bastante efectivo para evitar repeticiones, pero parece ser muy sensible a diferentes modelos y casos de uso, por ejemplo, ver esta discusión en Github.

  • attention_mask se puede usar para enmascarar los tokens con relleno

  • pad_token_id, bos_token_id, eos_token_id: Si el modelo no tiene esos tokens de forma predeterminada, el usuario puede elegir manualmente otros identificadores de token para representarlos.

Para obtener más información, consulte también la descripción de la función generate.

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

La manía de la IA ¿Se dirige hacia una burbuja a punto de estallar?

El mundo de la inteligencia artificial (IA) experimentó un gran aumento de interés por parte de los inversores de cap...

Aprendizaje Automático

NODO Árboles Neuronales Centrados en Tablas

En los últimos años, el Aprendizaje Automático ha explotado en popularidad, y los modelos de Aprendizaje Profundo Neu...

Inteligencia Artificial

Explorando el Procesamiento del Lenguaje Natural - Inicio de NLP (Paso #2)

Recientemente, como parte de un proceso de entrevista, se me pidió explorar dos preguntas y en el camino, aprendí alg...