Modelos | Aprende Machine Learning https://aprendemachinelearning.com en Español Tue, 22 Oct 2024 20:43:33 +0000 en-US hourly 1 https://wordpress.org/?v=6.6.2 https://aprendemachinelearning.com/wp-content/uploads/2017/11/cropped-icon_red_neuronal-1-32x32.png Modelos | Aprende Machine Learning https://aprendemachinelearning.com 32 32 134671790 Prompt Engineering para Desarrolladores https://aprendemachinelearning.com/prompt-engineering-para-desarrolladores-python-llm/ https://aprendemachinelearning.com/prompt-engineering-para-desarrolladores-python-llm/#respond Tue, 02 Apr 2024 08:00:00 +0000 https://www.aprendemachinelearning.com/?p=8761 Aprende a escribir Prompts que funcionen y cómo obtener los mejores resultados de tu LLM en código Python.

The post Prompt Engineering para Desarrolladores first appeared on Aprende Machine Learning.

]]>
Utiliza el poder de los LLMs como parte de tus Aplicaciones

Ahora que ya cuentas con tu LLM en Local, como explicamos en el artículo “Instala un LLM en Local”, podemos encenderlo en modo Servidor y comenzar a jugar con él desde nuestro código python.

En este artículo usaremos una Jupyter Notebook que puedes ver y descargar desde GitHub y realizar las actividades de Prompt Engineering.

Vamos a comenzar explicando los conceptos más importantes a la hora de pedir tareas a un Gran Modelo del Lenguaje y veremos como iterar sobre diversos casos de uso para mejorar el resultado final. Por último plantearemos el código para crear un Chatbot que guíe al cliente en sus compras en un ecommerce.

Introducción

El término Prompt Engineer surgió cuando los primeros Grandes Modelos de Lenguaje cómo (GPT-2 en 2019, GPT-3 en 2020) comenzaban a aparecer y encerrar en su interior los misterios del lenguaje humano. Entonces hacer prompt Engineer trataba de “encontrar de forma artística” la mejor forma de obtener buenas respuestas de estos modelos. De hecho, la técnica muchas veces consistía en hackear al modelo, descubrir vulnerabilidades y fortalezas. De las diversas y a veces aleatorias fórmulas utilizadas por los usuarios de la comunidad, el Prompt Engineer gana fuerza como una tarea en sí misma (y no como un complemento) en donde el saber cómo realizar la petición al modelo tenía salidas precisas y concretas.

Los actuales grandes modelos (de 2024) tienen “billones” de parámetros y si bien tenemos algo más de comprensión sobre su comportamiento -sabemos que son modelos estadísticos- lo cierto es que aún no tenemos un mapa completo de cómo se comportan. Esto da lugar a que el Prompt Engineering (“cómo consultamos el LLM”) siga siendo una parte importante de nuestra tarea como científicos de datos o Ingenieros de datos.

Lo cierto es que ahora un LLM puede ser una pieza más del sistema, por lo que debemos poder fiarnos de que tendremos la respuesta apropiada (y en el formato buscado).

Modelo Fundacional vs Modelo de Instrucciones

Hagamos un mini repaso antes de empezar; hay dos tipos de LLMS, los “LLM Base” (fundacional) y los “LLM tuneados con Instrucciones” (en inglés Instruction Tuned LLM). Los primeros entrenados únicamente para predecir la siguiente palabra. Los tuneados en Instrucciones están entrenados sobre los Base; pero pueden seguir indicaciones, eso los vuelve mucho más útiles para poder llevar adelante una conversación. Además, al agregar el RLHF, es decir, un paso adicional luego de Tunearlos en donde mediante el feedback de personas humanas se mejora la redacción de respuestas penalizando o premiando al modelo. El RLHF también funciona como una capa de censura para ciertas palabras o frases no deseadas.

Estas LLMs que siguen instrucciones son ajustadas con el objetivo de ser “utiles, honestas e inofensivas” (en inglés Helpful, Honest, Harmless) intentan ser lo menos tóxicas posibles. De ahí la importancia de la limpieza del dataset inicial con el que fueron entrenadas las “LLM base”.

Ten esto en cuenta cuando descargues o elijas qué LLM utilizar. Para la mayoría de aplicaciones deberás seleccionar una version de LLM que sea de Instrucciones y no base. Por ejemplo para modelos Llama 2 encontrarás versiones “raw” o base, pero generalmente queremos utilizar las tuneadas en instrucciones. A veces se les denomina como “versión chat”.

Las dos reglas para lograr buenos Prompts

¿Qué es lo que tienes que hacer para lograr buenas respuestas con tu LLM?

Veamos los dos principios básicos:

  1. Escribir tareas claras y específicas.
  2. Darle al modelo “tiempo para pensar” (o reflexionar).

Requerimientos técnicos

Veamos como hacerlo con ejemplos en python; siguiendo una Jupyter Notebook.

Recuerda iniciar tu modelo en LM Studio, en mi caso, estoy utilizando laser-dolphin-mixtral en mi Mac.

Y tampoco olvides instalar el paquete de OpenAI ejecutando “pip install openai==1.13.3” en el ambiente de Python en el que te encuentres trabajando. Inicia en LM Studio el Servidor Local, para poder utilizar la API.

Empezamos con el Código!

Importamos las librerías que utilizaremos y creamos una función “get_completion” que nos facilitará la obtención del resultado del modelo. Como verás ponemos el valor de Temperatura a cero, eso nos proporciona menos “creatividad” por parte del modelo para poder reproducir resultados (de no hacerlo, podríamos obtener distintas respuestas cada vez que ejecutemos el mismo prompt). También notarás que definimos un rol de Sistema para lograr que el LLM nos responda siempre en español.

Recuerda: Si bien utilizamos el paquete de openai, estaremos haciendo las consultas a nuestro modelo local y gratuito

from openai import OpenAI

# Point to the local server
client = OpenAI(base_url="http://localhost:1234/v1", api_key="lm-studio")
modelo = "local-model" # si tienes la API de pago: gpt-4 , gpt-3.5, etc

def get_completion(prompt:str, model:str=modelo, temperature:float=0):
    messages = [{
            "role": "system",
            "content": "Eres un asistente en español y ayudas respondiendo con la mayor exactitud posible.",
        },
        {"role": "user", "content": prompt}]
    response = client.chat.completions.create(
        model=model,
        messages=messages,
        temperature=temperature
    )
    return response.choices[0].message.content

Volvamos al primer Principio “dar instrucciones claras y específicas”, esto mejorará las chances de que el modelo de respuestas erróneas o incorrectas. OJO, no confundir el dar respuestas cortas con “claras”. En la mayoría de casos, las respuestas largas proveen mejores explicaciones y detalle que respuestas cortas.

1. Prompts claros

Una buena ayuda para aclarar nuestros prompts es la de usar delimitadores como triple comillas o guiones. Veamos un ejemplo en donde en el prompt definimos un delimitador con el texto al que queremos que el LLM ponga atención:

text = f"""
La sonda Voyager 1 ha vuelto a dar señales de vida. Hace poco te contaba en un vídeo largo de mi canal que los ingenieros de la NASA \
habían perdido completamente la comunicación con la sonda Voyager 1, que lleva haciendo ciencia durante décadas. \
Tenemos buenas noticias y es que nos están llegando señales coherentes de esta sonda. El día 3 de este mes nos llegó una comunicación \
desde el espacio, que aun no siendo legible, tenía buena pinta, así que alguien de la NASA la decodificó y resulta que su contenido es importante. \
Se trata de un volcado completo del sistema de datos de vuelo, que recoge los datos de todos los Instrumentos y sensores que aún están \
funcionando, de las variables internas de la sonda y otros datos adicionales. \
Por supuesto esto ha dado esperanzas al equipo de la Voyager 1, que ahora se está planteando intentar recuperar la comunicación con \
la sonda de alguna forma.
"""

prompt = f"""
Debes resumir en muy pocas palabras el siguiente texto delimitado por triple comilla simple: ```{text}```
"""
response = get_completion(prompt)
print(response)
# SALIDA: un resumen del texto.

Otra táctica es solicitar al modelo salida estructurada. Por ejemplo salidas JSON o HTML.

prompt = f"""
Genera una lista con tres títulos inventados de libros sobre lo bueno que es volar y el nombre del autor.
Provee una salida en formato JSON con las siguientes claves: libro_id, titulo, autor, año.
"""
response = get_completion(prompt)
print(response)

La salida puede ser usada directamente como un diccionario python, esto puede ser muy útil para nuestras apps! Esta es la salida que obtuve:

{
  "libro_1": {
    "titulo": "Volar con alas de acero",
    "autor": "Juan Pérez",
    "año": 2023
  },
  "libro_2": {
    "titulo": "El secreto del vuelo",
    "autor": "María Gómez",
    "año": 2021
  },
  "libro_3": {
    "titulo": "Viajes en el cielo",
    "autor": "Carl<|im_start|> Mendoza",
    "año": 2022
  }
}

Los tiempos de Salida del modelo pueden variar, en mi caso en un ordenador sin GPU el tiempo de respuesta puede ser de 1 o dos minutos. Si cuentas con GPU el tiempo de respuesta podría ser de apenas segundos.

Si estás pensando en comprar una GPU… aquí te dejo algunos enlaces con precios de Amazon España:

La tercer táctica es pedir al modelo que revise si las condiciones son satisfactorias y si no se cumplen indicarlo con un mensaje explicito.

text_1 = f"""
Instrucciones para dar cuerda al reloj

Allá al fondo está la muerte, pero no tenga miedo. Sujete el reloj con una mano, tome con dos dedos la llave de la cuerda, remóntela suavemente. 
Ahora se abre otro plazo, los árboles despliegan sus hojas, las barcas corren regatas, el tiempo como un abanico se va llenando de sí mismo y de él brotan el aire, las brisas de la tierra, la sombra de una mujer, el perfume del pan.
¿Qué más quiere, qué más quiere? Atelo pronto a su muñeca, déjelo latir en libertad, imítelo anhelante. El miedo herrumbra las áncoras, cada cosa que pudo alcanzarse y fue olvidada va corroyendo las venas del reloj, gangrenando la fría sangre de sus rubíes.
Y allá en el fondo está la muerte si no corremos y llegamos antes y comprendemos que ya no importa.
"""

prompt = f"""
Te pasaré un texto delimitado por triple comillas. 
Si contiene una secuencia de instrucciones, re-escribe esas instrucciones siguiendo el siguiente formato:

Paso 1 - ...
Paso 2 - …
…
Paso N - …

Si el texto no contiene instrucciones, simplemente responde \"No hay instrucciones.\"

\"\"\"{text_1}\"\"\"
"""

response = get_completion(prompt)
print("Respuesta:")
print(response)

Tenemos como salida:

Respuesta:
 "Paso 1 - Sujete el reloj con una mano."
"Paso 2 - Tome con dos dedos la llave de la cuerda, remóntela suavemente."
"Paso 3 - Ahora se abre otro plazo, los árboles despliegan sus hojas, las barcas corren regatas, el tiempo como un abanico se va llenando de sí mismo y de él brotan el aire, las brisas de la tierra, la sombra de una mujer, el perfume del pan."
"Paso 4 - ¿Qué más quiere, qué más quiere? Atelo pronto a su muñeca, déjelo latir en libertad, imítelo anhelante."
"Paso 5 - El miedo herrumbra las áncoras, cada cosa que pudo alcanzarse y fue olvidada va corroyendo las venas del reloj, gangrenando la fría sangre de sus rubíes."
"Paso 6 - Y allá en el fondo está la muerte si no corremos y llegamos antes y comprendemos que ya no importa."

La cuarta técnica para prompts claros, será la de “Few shot prompting“. Esto consiste en proveer de ejemplos correctos del tipo de respuesta que buscamos antes de pedir una tarea.

prompt = f"""
Tu tarea es responder siguiendo el mismo estilo que ves a continuación.

<Juan>: Esa nube tiene forma de triángulo.

<Músico>: Me recuerda al disco de Pink Floyd.

<Juan>: Esa otra nube tiene forma de lengua.

<Músico>: Me recuerda al disco de los Rolling Stones.

<Juan>: Esa tiene forma de círculo.

<Músico>:"""
response = get_completion(prompt)
print(response)

Salida:

<Músico>: Ah, entonces me recuerda al disco de los Beatles.

2. Piensa antes de hablar…

El segundo principio es el de “Darle tiempo al modelo a pensar (reflexionar)

Si las respuestas del modelo son incorrectas ante una tarea compleja, puede ser debido a que se apresura en responder, eso a veces nos pasa a los humanos que respondemos “lo primero que se viene a la mente” y no siempre es correcto. Lo que podemos hacer es decirle al LLM que realice una “cadena de pensamiento” (Chain of thought) o que <<piense atentamente>> antes de responder.

La primera técnica para conseguirlo es darle una lista detallada de los pasos que debe seguir para responder.

text = "Juan era un niño aventurero, muy curioso y sobre todo soñador. Una tarde, después de haber estado jugando durante horas en el parque con sus amigos, \
    y después de haber cenado con Mamá María y Papá Jorge, Mamá decidió acompañar a Juan a su habitación, ya que era hora de ir a la cama. \
La habitación de Juan estaba decorada con pósteres de barcos y mapas antiguos, y su cama estaba rodeada de juguetes y libros de aventuras de piratas. \
    Mamá se acercó a Juan, le dio un fuerte abrazo y le deseó buenas noches. Al poco tiempo, Juan se quedó profundamente dormido."

prompt_2 = f"""
Tu tarea es realizar las siguientes acciones: 
1 - Resumir el texto delimitado con <> en una breve oración.
2 - Traducir el texto a Italiano.
3 - Listar los nombres de personas del texto.
4 - Crear un objecto JSON que contenga las claves: resumen_italiano, nombres.

Usa el siguiente formato:
Resumen: <resumen>
Traducción: <resumen traducido>
Nombres: <Lista de nombres encontrados>
Salida JSON: <Json con las claves resumen_italiano y nombres>

Texto: <{text}>
"""

response = get_completion(prompt_2)

print("\nRespuesta:")
print(response)

Salida (sólo copio la salida JSON, por ahorrar texto):

Respuesta:
Salida JSON: {"resumen_italiano": "Juan era un bambino avventuroso, molto curioso e soprattutto sognoioso. Dopo aver trascorso ore in giuoco nel parco con i suoi amici, dopo avere cenato con Mamma Maria e Papà Jorge, Mamma decise di accompagnare Juan alla sua camera, poiché era ora di andare a letto. La camera di Juan era decorata con poster di navi e mappe antichi, e la culla era circondata da giocattie e libri d'avventura pirata. Mamma si avvicò a Juan, gli diede un forte abrazo e le desiderò buone notti. Poco dopo, Juan si addormentò profondamente.", "nombres": ["Mamá María", "Papà Jorge", "Juan"]}

Técnica dos; Pedir al modelo que trabaje internamente la solución antes de apresurarse en dar una respuesta. Aquí le daremos un problema de matemáticas real, que tuvo mi hija de 10 años. Introduciré un error (a propósito) en la solución del estudiante, al usar el valor del IVA y el modelo deberá detectarlo y explicarnos la solución correcta.

prompt = f"""
Eres un profesor de matemáticas muy riguroso y tu tarea es determinar si el examen que hizo el estudiante es correcto o incorrecto.
Para resolver el problema deberás:
- Primero, trabaja en tu propia solución al problema y calcula el valor Total final. 
- Compara tu solución con la del estudiante y evalúa si la solución del estudiante es correcta o no. 
No decidas si el estudiante ha acertado o fallado hasta haber realizado las operaciones tu mismo.

Enunciado:
```
Al comprar un Televisor que valía $250 nos hacen una rebaja del 4%. 
Luego de rebajarlo tenemos que añadir el 20% de IVA.
¿Cuánto pagamos por el televisor?
``` 
Solución del estudiante:
```
1. Calculo el 4% de 250: (4 * 250) / 100 = (1000 / 100) = 10
2. Resto la rebaja: (250 - 10) = 240
3. Añado el 10% de IVA al valor rebajado: (10 * 240) / 100 = (2400/100) = 24
4. Sumo el Total: 240 + 24 = 264
Respuesta: pagamos $264 por el televisor.
```

Solución Real:
```
Desarrolla aqui tu solución con detalle. 
Determina si la solución del estudiante tiene fallo.
```
"""
response = get_completion(prompt)
print(response)

A mi personalmente me parece impresionante que el modelo pueda detectar el fallo!, veamos la salida:

Primero, calculamos el valor de rebaja: (250 * 4%) = 10. Luego, restamos la rebaja al precio original: 250 - 10 = 240. A continuación, añadimos el IVA del 20%: (240 * 20%) / 100 = 48. Finalmente, sumamos el valor de rebaja y el IVA al precio rebajado: 240 + 48 = 288.

La solución del estudiante tiene un error en la operación de cálculo del IVA. El IVA debería ser 240 * 20% / 100, lo cual da como resultado 48. Si se añade este valor al precio rebajado (240), se obtendría un total de $288.

La solución correcta es: "pagamos $288 por el televisor".

Advertencia, este modelo puede alucinar!

Tengamos en cuenta las alucinaciones! Este es un fenómeno que ¿sufren? los LLMS y hacen que se inventen datos, fechas y lugares de manera muy convincente. Hay que tener esto en cuenta al momento de obtener salidas, porque podríamos estar dandole a un cliente información falsa!.

Vemos un ejemplo en donde le pedimos al LLM que nos cuente sobre un producto que no existe, y sin embargo es capaz de darnos una descripción completa y convincente!!

prompt = "Cuentame como se usa el ultimo producto de Tubble y porque es tan valioso."
response = get_completion(prompt)
print(response)

Como verás, pedimos información sobre un producto que no existe y el Modelo es capaz de inventar una respuesta que parece real. Salida (acorto el texto, pero está completo en la Notebook):

El último producto de Tubble, llamado "Tubble-X", es un potente antioxidante que se utiliza en la industria farmacéutica y de cuidados personales. Tiene una gran cantidad de aplicaciones debido a sus propiedades antioxidantes, antiinflamatorias y anticancerígenas.

Una vez extraído, el Tubble-X se utiliza en una amplia gama de productos, desde medicamentos hasta cosméticos y alimentos. Algunos de los usos más comunes incluyen:

1. Medicinas: Se utiliza como ingrediente principal en la formulación de medicamentos para tratar diversas afecciones, como el cáncer, las enfermedades inflamatorias y las enfermedades cardíacas.
2. Cuidados personales: Se incorpora en productos cosméticos, como cremas y lociones, que ayudan a mantener la piel haya y sana.
3. Alimentación: ...

Segunda vuelta

Además de los dos principios, deberás saber que la tarea de Prompt Engineering es una tarea iterativa, es decir, empezamos con un prompt y hacemos la prueba. Seguramente no saldrá el mejor resultado a la primera por lo que debemos refinar nuestro prompt; hacer leves variaciones, cambiar ciertas palabras e ir viendo como responde el modelo.

Debemos analizar porqué no funciona el prompt y corregir. Por ejemplo si el prompt retorna una salida muy larga podemos probar de acotarla con “Escribe máximo 50 palabras”. Otra opción podría ser “utiliza como máximo 3 oraciones”.

Prompts para Resumir texto

Uno de los casos de uso comunes es el de usar LLM para resumir textos; podemos pedir que nos haga un resumen diario de las noticias del día o del Feed de Twitter o de nuestras redes sociales.

Vemos un ejemplo. Primero podemos pedirle que lo haga “en como máximo 30 palabras”.

prod_review = """
Se mantiene a unas temperaturas ridículas a 0.975-1v @ 2800mhz. Los tres ventiladores a 35-37% no se oyen en absoluto y es más que suficiente para refrigerarla.
El consumo a máximo rendimiento no pasa de 210W.
Tan solo aplícale un undervolt con la curvatura y listo.
Sin duda, 4070 super es lo mejor que Nvidia ha fabricado. Nunca había probado este ensamblador MSI ventus. De momento muy contento con la compra, puedo garantizar que es de altísima calidad.
Mi anterior 3070 gybabyte el núcleo a 80°c y las memorias a 100-105. I cluso cambíandole los thermalpads... En fin, estoy muy feliz con esta MSI 4070.
"""

prompt = f"""
Tu tarea es generar un breve resumen de una reseña de un producto de un ecommerce.

Resume la reseña que viene a continuación, delimitada por triple comilla, en máximo de 30 palabras.

Reseña: ```{prod_review}```
"""

response = get_completion(prompt)
print(response)

Salida:

"Muy potente en temperaturas bajas y consumo bajo a 2800mhz, ventiladores silenciosos y undervolt para mejorar. Nvidia 4070 de MSI es una excelente adquisición."

Podemos refinarlo pidiendo que se centre en algún aspecto en particular de la review.

prompt = f"""
Tu tarea es generar un breve resumen de una reseña de un producto de un ecommerce para dar Feedback al departamento de Ventas.

Resume la reseña que viene a continuación, delimitada por triple comilla, en máximo de 30 palabras y centrate en el consumo eléctrico.

Reseña: ```{prod_review}```
"""

response = get_completion(prompt)
print(response)

Salida:

La tarjeta 4070 de MSI mantiene la temperatura a niveles razonables y el consumo máximo es de 210W. El usuario está muy contento con la compra, ya que su anterior 3070 tenía problemas de temperatura.

Otra opción es en vez de pedirle de “resumir” decirle que “extraiga” el contenido. Pruébalo!

Tareas de NLP con tu LLM

Ahora veremos de usar al LLM para que realice tareas que antes realizábamos con modelos específicos de NLP. Es fabuloso que el LLM pueda resolver estas tareas como si “estuviera razonando” y entendiendo realmente el lenguaje Natural!

Veremos ejemplos en los que usamos al LLM para obtener el análisis de sentimiento, detección de Entidades y clasificación de textos/tópicos.

Análisis de Sentimiento

Primero, le pedimos análisis de sentimiento:

monitor_review = """
El monitor LG 29WP500-B UltraWide impresiona con su relación de aspecto 21:9 y su panel IPS de alta resolución (2560x1080). 
El uso de FreeSync marca una clara diferencia en términos de compatibilidad con tarjetas gráficas y garantiza una visualización fluida. 
En comparación con mi antiguo monitor Eizo, el monitor LG ofrece una fidelidad de color superior con una cobertura sRGB de más del 99%. 
Con dos puertos HDMI y la posibilidad de inclinarlo, también es avanzado en términos de conectividad y facilidad de uso. 
Este monitor es una opción de actualización que merece la pena para disfrutar de una experiencia visual impresionante.
"""

prompt = f"""
Cuál es el sentimiento de la reseña hecha al producto
que viene a continuación, delimitada por triple comilla?

Reseña: '''{monitor_review}'''
"""
response = get_completion(prompt)
print(response)
# SALIDA: El sentimiento de la reseña es positivo.

También podemos intentar detectar emociones, intentar entender si un cliente está enojado nos puede servir por si debemos actuar u ofrecerle un cupón de descuento antes de que abandone su suscripción…

prompt = f"""
Contesta si el escritor de la siguiente reseña está expresando ira, furia o enojo.
La reseña está delimitada por triple comillas.
Da tu respuesta en una sóla palabra como "si" o "no".

Reseña: '''{monitor_review}'''
"""
response = get_completion(prompt)
print(response)

Extracción de entidades

prompt = f"""
Identifica los siguientes elementos del texto de la reseña:
- Producto comprado por el autor
- Compañía del producto

La reseña está delimitada por triple comillas.
La respuesta deberá ser en un objeto JSON con "Item" y "Marca" como claves. 
Si no se encuentra la información, usa "desconocido" como valor.
Haz tu respuesta lo más corta posible.
  
Reseña: '''{monitor_review}'''
"""
response = get_completion(prompt)
print(response)
#SALIDA:  { "Item": "Monitor LG 29WP500-B", "Marca": "LG" }

¿De qué trata el texto?

Podemos hacer que el LLM detecte temas, es decir, sobre qué trata un texto. Podemos pedirle una lista libre (a su criterio) o acotarlo a una lista de temas que le damos nosotros. Veamos un ejemplo utilizando un extracto de texto de la lista de correos sobre IA de Unai Martinez:

story = """
Una vez hice un garabato de muchos círculos y a lápiz en la mesa de al lado de mi compañero de pupitre. No sé si recuerdas esas mesas verdes que habitaban nuestras aulas de manera continuada año tras año.
He de decir que este año cumpliré 50 tacos, es decir, te estoy hablando del año 1984 aproximadamente, tenía 10 años.

Ni siquiera sé por qué lo hice. Se me ocurrió y le planté un garabato enorme sin que él se diese cuenta.
Se ve que aquel día mi cabeza andaba sola en formato ameba y no se me ocurrió otra cosa que aportar a la humanidad.
Yo era un crío formal, he de decirlo.

Tuve la mala suerte de que el profe vio el tachón en la mesa al rato y dijo todo serio que si nadie asumía la culpa el finde no nos íbamos de excursión.
Joder que mala suerte, era finde de excursión. Eran las míticas excursiones que te ponías nervioso.
Ya sabes, camping, colegueo, cotilleo del pelo que si a fulanito le gusta menganita pero no te chives … y a los 5 minutos ya lo sabía toda la clase.
Estas son las cosas que todos recordamos.
 
Bien, el asunto es que en ese momento no dije nada. Callado como un muerto.
Me fui a casa a comer y a meditar.
Por la tarde, a la vuelta, tenía que darle solución.
 
Realmente no había opción, lo tenía que confesar. Tenía que enfrentarme a mis miedos y asumir mi culpa.
He de decir, que en ningún momento se me pasó por la cabeza fallar a mis compañeros. La idea de que por mi culpa se quedaran sin excursión no asomó en ningún momento como posible opción.
Tampoco contemplé la posibilidad de que el profe fuese de farol. Probablemente fuera así, pero en aquél entonces tenía las preocupaciones de un niño de 10 años.
"""
# Extracto del texto "Lecciones de negocio de un crío de 10 años" de Unai Martinez

prompt = f"""
Determina cinco tópicos que se comentan en el siguiente texto delimitado por triple comillas.

Cada tema será definido por una o dos palabras.

El formato de tu respuesta debe ser una lista separada por comas.

Texto: '''{story}'''
"""
response = get_completion(prompt)
print(response)
# SALIDA: "Garabato", "Mesas verdes", "Años 80", "Excursiones" y "Confesar".

Tienes algunos ejemplos y usos adicionales en la Notebook!

LLM para traducción y corrección de textos

Los grandes modelos están entrenados con Millones y Millones de textos sacados de internet en distintos idiomas; por lo cual, sin haberlo hecho con ese propósito, el modelo aprendió a entender y traducir entre una variedad de lenguas.

Veamos ejemplos de traducción:

prompt = f"""
Translate the following English text to Spanish: \ 
```Hi, I would like to order a coffee```
"""
response = get_completion(prompt)
print(response)
#SALIDA: "Hola, me gustaría pedir un café"

También podemos pedir que detecte un idioma:

prompt = f"""
Dime en que idioma esta el texto: 
```Combien coûte le lampadaire?```
"""
response = get_completion(prompt)
print(response)
# SALIDA: Este texto está en francés (idioma de Francia)

Corrección de Textos

En este ejemplo vamos a pasarle un listado de oraciones con error y probar si el modelo es capaz de detecta errores de sintaxis o gramática. Si eres un profesor, esto podría ser útil para corregir textos antes de procesarlos (o limpieza de datos en un pipeline de ML).

text = [
  "La niña esta jugando con la perro.",  # error
  "Juan tiene un ordenador.", # ok
  "Ba a acer un largo día.",  # error
  "Hayamos sido tan felices juntos.",  # error
  "El coche es rojo.", # ok
  "Vamos a porbar si hay error de deletreo."  # error deletreo
]
for t in text:
    prompt = f"""
    Corrige ls siguiente oración delimitada por triple comilla simple y reescribe la versión revisada sin error. Si no encuentras error, escribe: "No hay error".
    Oración: ```{t}```"""
    response = get_completion(prompt)
    print(f"Respuesta para '{t}':")
    print(response)
    print("- " * 10)

Tengamos en cuenta que el modelo también puede fallar y no siempre detectar los errores. Puedes ver las salidas en la Notebook.

Crea un Chatbot para ecommerce con tu LLM

En nuestro último ejercicio vamos a crear un chatbot para que conteste a los clientes de una empresa ya existente. Será un ejemplo básico, pero verás lo potente que resulta. Sobre todo, no tener que programar paso a paso todo el chatbot, si no, que es simplemente darle las instrucciones en lenguaje natural al LLM.

Roles y memoria!

Antes de empezar debemos saber sobre la memoria de un LLM y sobre los roles.

Sobre los roles, tenemos 3 roles principales que gestionamos cuando enviamos los prompts al modelo:

  • System: se define 1 solo mensaje de sistema. Este mensaje define “quién es el LLM”, su identidad y cómo debe contestar.
  • User: aquí veremos TODOS los mensajes que va escribiendo el usuario que utiliza el LLM y sus consultas
  • Assistant: serán las respuestas que provee el LLM a las peticiones del usuario.

El Modelo de lenguaje no tiene memoria, esto quiere decir que si le pedimos algo al modelo, debemos tener en cuenta que el modelo no recuerda las respuesta anteriores. Esto es un inconveniente porque para poder tener una conversación resultará muy útil que el modelo recuerde todo lo que se habló previamente. Si en el primer mensaje le decimos al Asistente nuestro nombre y luego le preguntamos “¿Quién soy?” el modelo no tendrá ni idea!

Para resolver esto, es que debemos almacenar el historial de mensajes e ir enviándolo al modelo constantemente.

Para nuestro ejemplo, modificamos levemente la función de “get_completion()” y esta vez enviaremos el historial completo como una lista de mensajes python.

Para nuestro ejemplo, mantendremos todos los mensajes! Así que cuidado… si la conversación se extiende podrías llenar tu memoria… pero en próximos artículos del blog veremos estrategias para limitar la memoria del chat.

def get_completion_from_messages(messages, model=modelo, temperature=0):
    response = client.chat.completions.create(
        model=model,
        messages=messages,
        temperature=temperature,
    )
    msg = response.choices[0].message
    return msg.content

Antes de comenzar con el bot, hagamos un ejemplo enviando mensajes y limitando el historial para comprobar que el LLM no recuerda…

messages =  [
    {'role':'system', 'content':'Eres un robot amistoso. Responde brevemente lo que se te pide.'},
    {'role':'user', 'content':'Hola, mi nombre es Juan.'}  
    ]
response = get_completion_from_messages(messages, temperature=1)
print(response)

Aquí comprobaremos que en su salida no sabe nuestro nombre:

messages =  [
    {'role':'system', 'content':'Eres un robot amistoso. Responde brevemente lo que se te pide.'},
    {'role':'user', 'content':'Sí, ¿puedes decir cúal es mi nombre?'}  
    ]
response = get_completion_from_messages(messages, temperature=1)
print(response)

Sin embargo, si le pasamos el historial completo, el modelo puede recordar que mi nombre es Juan 🙂

messages =  [
    {'role':'system', 'content':'Eres un robot amistoso. Responde brevemente lo que se te pide.'},
    {'role':'user', 'content':'Hola, mi nombre es Juan.'},
    {'role':'assistant', 'content': "¡Hola, Juan! Me encantaría saber qué tipo de información puedo proporcionarme para ayudarte? ¿Puedes darme más detalles sobre tu consulta?"},
    {'role':'user', 'content':'Sí, ¿puedes decir cúal es mi nombre?'}
    ]
response = get_completion_from_messages(messages, temperature=1)
print(response)

Bien, ya sabemos cómo debemos hacer para que el chatbot conserve nuestro historial y pueda seguir una conversación.

Un Bot que sirve Pizzas

Vamos a crear un listado con todos los mensajes que se van produciendo y los vamos a incorporar al chat. En el primer mensaje de sistema, le indicaremos al LLM quién es, la información con la que cuenta, en este caso serán las pizzas y productos que ofrece y sus precios.

context = [ {'role':'system', 'content':"""
Eres PedidosBot, un servicio automatizado que recoge pedidos de pizza de un restaurante.
Primero saludas al cliente, luego recibes su orden
Y luego preguntas si el pedido es para enviar a domicilio o si lo vienen a buscar.
Esperas a tener la orden completa, luego haces un resumen y calculas el precio total final.
Si es envío a domicilio deberás pedir al cliente su dirección.
Responde siendo breve y amistoso pero formal.
El menú incluye:
Pizza pepperoni  12.95 \
Pizza de Jamón y Queso   10.95 \
Pizza con huevo   11.95 \
Patatas fritas 4.50 \
Ensalada griega 7.25 \
Extras: \
extra de Queso 2.00, \
Setas 1.50 \
Bebidas: \
Zumo de Naranja 3.00 \
sprite 3.00 \
Agua 5.00 \
"""} ]

# Creamos un loop de consultas al bot, para salir escribe "salir" o un mensaje vacío.
# NOTA: Si estás en VSCODE la caja de texto aparece en la parte de arriba de la pantalla.
texto_usuario = "inicio"
context.append({'role':'assistant', 'content': "Bienvenido al PizaBot Service, ¿En que puedo ayudarle?"})

while texto_usuario != "salir" or texto_usuario == "":
    texto_usuario = input("CHAT: ")
    if texto_usuario == "salir" or texto_usuario == "":
        continue

    print(texto_usuario)
    context.append(
        {'role':'user', 'content':texto_usuario}
    )

    response = get_completion_from_messages(context, temperature=1)
    print(response)

    context.append(
        {'role':'assistant', 'content': response})

print("FIN DE CHAT")

Con este código de muy pocas líneas podemos hacer que el bot trabaje para nosotros!; aquí copio la salida que obtuve:

Qué Pizzas tienen?
 El menú de pizzas incluye las siguientes opciones:
1. Pizza pepperoni - 12.95
2. Pizza de Jamón y Queso - 10.95
3. Pizza con huevo - 11.95
Por favor, indique el número de la pizza que desea para más detalles.

Quiero una pizza de Jamón y Queso. ¿Tienen ensaladas?
 Además de las pizzas mencionadas, también tenemos ensaladitas! 
1. Ensalada mixta - 7.25
2. Ensalada Griega - 7.25
Puedes ordenar ambas. ¿Desea algo más?

Si, quiero agregar una Ensalada Griega.
 Ideal! ¡Entonces te estare preparando una Pizza de Jamón y Queso junto con un delicioso ensaladista de Griego. ¿Deseas algo más?

Nada más. Cúanto es el precio final?
 ¡Así! Así es la cuenta: Pizza de Jamón y Queso (10.95) + Ensaladilla Griega (7.25) = $18.20. ¡Vaya a ser un alimento delicioso!


FIN DE CHAT

Conclusiones

En este artículo podemos aprender a usar el LLM como una pieza de nuestras aplicaciones. Podemos contar con él como un motor de respuestas generales, o específicas para el caso de uso que abordemos.

Además si aprovechamos la capacidad de que responda en formatos como JSON, XML o HTML, podemos incorporar sus salidas para alimentar otras APIs, u otras funciones de nuestro código Python.

Los LLMs son muy potentes y resulta muy cómodo poder utilizar uno en Local y hacer este tipo de pruebas!

Espero que el artículo te resulte útil, cuentas con el código completo en la Jupyter notebook dentro de mi cuenta de Github.

En el próximo artículo veremos más ejemplos de uso del LLM y nuevas librerías python que surgieron para facilitarnos la vida!

Hasta la próxima.

Suscripción al Blog

Recibe los próximos artículos sobre Machine Learning, estrategias, teoría y código Python en tu casilla de correo!

NOTA: algunos usuarios reportaron que el email de confirmación y/o posteriores a la suscripción entraron en su carpeta de SPAM. Te sugiero que revises los “correos no deseados” y/o que agendes la dirección de remitente en tus contactos.

The post Prompt Engineering para Desarrolladores first appeared on Aprende Machine Learning.

]]>
https://aprendemachinelearning.com/prompt-engineering-para-desarrolladores-python-llm/feed/ 0 8761
Instalar un Modelo de Lenguaje en tu ordenador https://aprendemachinelearning.com/instalar-un-modelo-de-lenguaje-en-tu-ordenador-llm-local-llama/ https://aprendemachinelearning.com/instalar-un-modelo-de-lenguaje-en-tu-ordenador-llm-local-llama/#respond Wed, 13 Mar 2024 11:22:22 +0000 https://www.aprendemachinelearning.com/?p=8759 Instala un Gran Modelo de Lenguaje (LLM) en tu propio ordenador y obtén tu propio ChatGPT ilimitado y gratuito!

The post Instalar un Modelo de Lenguaje en tu ordenador first appeared on Aprende Machine Learning.

]]>
Puedes instalar Llama 2, Claude, Mistral, Falcon, StableLM ó cualquier otro LLM en tu computadora para ejecutar proyectos en Local. Tu propio ChatGPT privado. En este artículo te explicaremos cómo hacerlo paso a paso.

En pocos minutos podrás tener instalado un Gran Modelo de Lenguaje en tu ordenador y podrás chatear con él, pedir que escriba tus correos, sugerir ideas, consultas legales y hasta aprovecharlo como un servidor en local y que provea de valor a tus aplicaciones. Todo gracias al software libre LM Studio.

Los grandes modelos de Lenguaje (LLMs) se convirtieron en un asistente indispensable para trabajar, para resolver dudas, para programar y hasta para reemplazar al buscador. Hay quienes lo utilizan a diario y lo cuentan como una herramienta indispensable como un lápiz, el Excel o StackOverflow.

Seguramente conozcas ChatGPT que se popularizó a finales de 2022 y tomó gran relevancia con sus modelo GPT4 ya en 2023. A partir de ese momento surgieron muchos otros modelos GPT como Llama de Meta, Claude, Mistral, Gemini de Google ó Falcon. Muchos de ellos Open Source y/o con licencias de uso comercial.

Algunos también ofrecen la posibilidad de uso en la nube para probarlos, pero también tenemos la opción de descargarlos desde HuggingFace y correrlos en local.

Ventajas de tener un LLM en local

¿Por qué querríamos ejecutar un LLM en local?

  • Primero que nada, para experimentar libremente! es genial poder tener un LLM en tu ordenador y jugar a diario con él 24hs, sin necesidad de conexión a internet.
  • Todas las consultas que hagas serán tuyas, privadas, no compartirás datos en la nube ni con terceros.
  • Gratuito!, no tienes un límite de tokens ó consultas diarias. Puedes hacer uso extensivo. Si utilizas el servicio de OpenAI, sabes que es de Pago y hay servicios en la nube como Azure o AWS que ofrecen modelos pero que también deberás pagar por uso.
  • Para tus propios proyectos: puedes empezar a crear apps que hagan uso de tu LLM local por ejemplo para que retorne archivos JSON con análisis de sentimiento, resumen, clasificación, (tareas de PLN) y finalmente “hacerte millonario”
Preguntamos al modelo cómo hacer una pizza (incluso escribiendo mal “piza”, nos entiende)

¿Qué Hardware necesito para correr un LLM en local?

Requerimientos:

Disco

Los Grandes Modelos de Lenguaje son modelos con Miles de Millones (Billion en inglés) de parámetros; esto hace que sean muy pesados. Los modelos actuales publicados por Google, META, etc suelen lanzar versiones Small, Medium y Large de 7B, 40B y 70B respectivamente. Los tamaños de esos modelos son de decenas de Gigabytes, lo cual no sería gran problema, porque la mayoría de ordenadores cuentan con discos duros de Terabytes. El problema está en la memoria RAM.

Memoria RAM

Como recordarás; las redes neuronales son capas de neuronas que se interconectan para formar una matriz que “realiza multiplicaciones” por lo tanto esos tensores deben estar cargados en Memoria para poder operar. Los ordenadores actuales suelen contar RAM de 8GB, 16GB o con 32Gb, por lo que si un modelo es de 40 GB no podremos cargarlo completo en memoria.

Por suerte la comunidad OpenSource ha salido con diversas estrategias para poder reducir el tamaño de los modelos LLM sin afectar sus resultados (ó haciendo muy levemente).

Por ejemplo, un truco reside en utilizar menor precisión en el valor de los pesos de la red. Es decir, si por ejemplo una neurona artificial tiene un valor 3,141516 lo podríamos truncar a 3,141 y con ello reducimos el espacio que ocupan sus decimales en las 7 mil millones de neuronas “a la mitad”. Un modelo de 40Gygas ahora ocupa 20Gygas. Hay otras técnicas como cargar parcialmente la red en memoria RAM y Disco.

Velocidad: CPU y GPU

¿Y en cuanto a la velocidad? ¿Necesito una GPU?

Obviamente si contamos con GPU los tiempos de respuesta serán veloces. Con un modelo mediano, buena RAM y GPU podemos escribir un prompt y al apretar la tecla “Intro” veremos de inmediato cómo se va escribiendo la respuesta a 15 tokens por segundo.

Si usamos un CPU con procesador potente y un modelo pequeño, puede que tengamos una velocidad de decente, deberemos tener mayor paciencia para leer la respuesta completa. Las pruebas que he hecho funcionaron bastante bien.

Tarjeta gráfica GPU Nvidia RTX 4090

GPU o no GPU, esa es la cuestión

No es necesario contar con GPU para hacer pruebas caseras en los modelos pequeños de 7B Parámetros con tu CPU normal debería alcanzar. Si estás pensando en comprar un ordenador con GPU, te dejo estos enlaces (afiliado) de Amazon España con las opciones que he encontrado disponibles (precios marzo 2024) y con los que podrás ejecutar modelos medianos de unos 40B:

Una breve comparación de precios a tener en cuenta (Marzo 2024):

  • Si utilizas la API de OpenAI con GPT 4-turbo, te costará €40.- por millón de Tokens de entrada y salida.
  • La API de Mistral en su modelo más potente (70B) costará €27.- por Millón de Tokens de entrada y salida.
  • Si utilizas Llama 2 en la nube con Azure te costará €1000.- al mes si lo tienes encendido 8hs/20 días (ilimitado de tokens).
  • Si te compras un ordenador con GPU por €2400.- podrás utilizarlo con tokens ilimitados las 24 horas del día!

NOTA: Recuerda que tienes que sumar el coste de electricidad. Las tarjetas NVIDIA pueden consumir más de 200W, por lo que dependiendo de tu país, día de la semana y hora del día puedes tener un coste adicional elevado. Evalúa bien el uso que le darás a tu tarjeta y si te conviene comprar una o utilizar un servicio en la nube.
NOTA2: Ten en cuenta que mi recomendación no es para ambientes productivos con una alta demanda.

Instalar LMStudio en tu ordenador

Vamos a ello! Utilizaremos LM Studio, un software gratuito que podemos descargar desde aquí. Esta es su página web.

Sitio web de LM Studio

Elige la versión para Windows, Mac ó Linux, descarga y luego instala en tu ordenador.

Este programa nos permitirá elegir modelos compatibles con nuestro ordenador y luego ejecutarlos.

Una vez instalado, lo abrimos y vemos la barra de buscador que nos posibilita buscar modelos dentro de toda la base de HuggingFace.

Usamos el buscador para encontrar el modelo llama

Descarga tu LLM favorito!

Los modelos que te recomiendo para empezar, dependiendo de tu ordenador son:

Si tienes CPU y 8G RAM

Si tienes CPU y +16G RAM

Si tienes GPU y 16G RAM (ó Mac con M1/M2)

Si tienes GPU y 32G RAM

Puedes usar el buscador, filtrar modelos e instalar todos los que quieras, probarlos y quedarte con los mejores. Si quieres instalar un modelo demasiado grande para tu equipo, leerás un advertencia: “Likely too large for this machine” en rojo.

Podrás encontrar algunos modelos específicos para programación como “CodeLlama“. Si lo descargas podrás pedirle que te ayude con el código, a crear funciones, hacer Code Review u optimización, y hasta a debuguear tu código para encontrar errores.

Tu propio Chat (privado!)

Veamos un ejemplo; aquí descargo el modelo “laser dolphin mixtral” en mi Mac. Una vez descargado, podemos ir a la opción de CHAT para probarlo (el tercer ícono de la izquierda, comenzando de arriba).

Debes elegir el modelo, en la caja de selección central. Tarda unos segundos en cargar y ya lo puedes usar. Además verás que puedes crear diversos chats con conversaciones a tu antojo.

La opción de Chat

Aquí le pido una traducción al Francés y lo hace sin problemas. Además en la barra inferior contabiliza el uso de tokens y la velocidad. En el panel de la derecha podemos cambiar ajustes del modelo si fuera necesario.

Ponemos a prueba la lógica del modelo. ¿Qué pasa si suelto dos globos con helio?
Pedimos al modelo LLM que escriba código Python.

Modo Servidor – a jugar se ha dicho!

Y llegamos a lo interesante: poder utilizar los modelos como si fueran tu propio API, sin tener que pagarle a ningún proveedor 🙂 y manteniendo la privacidad.

Si vas a la opción “Local Server” (el cuarto ícono de la izquierda) y presionas el botón verde “Start Server”, habrás arrancado el modelo para ser consumido desde Python u otro lenguaje de programación. Podemos ir al Visual Studio Code (o cualquier editor de texto) y crear un archivo con unas pocas líneas de código y conectarlo con nuestras Apps…

Utilizamos la opción de Servidor local.

Veamos un ejemplo de uso en código Python. Para ello nos aseguramos de tener instalado en nuestro ambiente de desarrollo la librería de OpenAI. Es curioso porque utilizamos el paquete de OpenAI pero NO utilizaremos el modelo de pago de OpenAI, si no el modelo que hayamos instalado en local!. La librería de openai nos sirve de Wrapper (interface) para conectar cualquier modelo LLM. Se podría decir que es “un hack”… (ó una trampa mortal… broma). Instalamos la librería ejecutando en la terminal:

pip install openai

Y ahora copia y pega el siguiente código en un notebook Jupyter ó en un archivo Python (“test.py”) y luego ejecútale para consultar cuántos días llueve en Paris al año:

from openai import OpenAI

client = OpenAI(base_url="http://localhost:1234/v1", api_key="not-needed")

completion = client.chat.completions.create(
  model="local-model", # this field is currently unused
  messages=[
    {"role": "system", "content": "Eres un asistente en español y ayudas con respuestas breves."},
    {"role": "user", "content": "Buenos días, ¿cuantos días llueve en Paris al año?"}
  ],
  temperature=0.7,
)

msg = completion.choices[0].message

print(msg.content)

Al cabo de unos segundos (depende tu Ordenador/RAM) obtenemos una respuesta similar a:

En promedio, París experimenta entre 150 y 160 días de lluvia al año. Esto varía cada año y depende del clima general.

Con esto puedes hacer distintos prompts para poner a prueba tu LLM. Recuerda que el modelo no fue entrenado en Español, sin embargo es capaz de escribir y responder la mayoría de veces correctamente.

Le puedes pedir que te de recetas, que te ayude a planear un viaje, a escribir un libro, poesía, consultar cómo tratar un dolor de estómago (no recomendado) o a en qué acciones de la bolsa invertir tu dinero (menos recomendado).

También le puedes pedir que escriba código python, que genere datasets artificiales en pandas ó que te ayude a conseguir un trabajo con una buena descripción de perfil para tu LinkedIn.

DISCLAIMER: recuerda que el modelo tiene una capacidad limitada y puede dar respuestas falsas o erróneas, no te fíes al 100% de sus respuestas. Por otra parte ten cuidado/sé responsable de preguntar por asuntos delicados o ilegales. Las LLM sufren de una anomalía llamada “alucinaciones” y pueden dar respuetas que suenen muy convincentes pero que sean completamente equivocadas.

En próximos artículos veremos cómo utilizar Prompt Engineering y a realizar tareas de clasificación, soporte técnico, conversar con tus PDF o bases de datos, a usar LangChain y hasta la creación de Agentes!.

Puedes conocer más acerca de la arquitectura GPT en este artículo sobre Transformers

Conclusiones

Espero que estés tan emocionado como yo de poder instalar tu propio modelo LLM en local y poder jugar con él. Debo advertirte que puede llegar a resultar adictivo tener un asistente disponible las 24 horas sólo para ti! Puedes pasarte horas y horas charlando y consultando información. De alguna manera también te pone a prueba a ti… ¿Qué es lo que te interesa saber? ¿Cómo te puede ayudar? ¿Puedes confiar en sus afirmaciones?

En el artículo aprendemos los requerimientos básicos que tenemos para poder ejecutar un LLM en tu ordenador y aprovechamos el software LM Studio que nos facilita la descarga de modelos y su ejecución. Además podemos ejecutar el modo “Servidor Local” que nos permite utilizar el modelo LLM como un componente más de nuestras aplicaciones!

En los próximos artículos podremos realizar nuestros primeros pasos en Prompt Engineering y empezar a sacarle partido a nuestro LLM.

Enlaces de Interés

Suscripción al Blog

Recibe los próximos artículos sobre Machine Learning, estrategias, teoría y código Python en tu casilla de correo!

NOTA: algunos usuarios reportaron que el email de confirmación y/o posteriores a la suscripción entraron en su carpeta de SPAM. Te sugiero que revises los “correos no deseados” y/o que agendes la dirección de remitente en tus contactos.

The post Instalar un Modelo de Lenguaje en tu ordenador first appeared on Aprende Machine Learning.

]]>
https://aprendemachinelearning.com/instalar-un-modelo-de-lenguaje-en-tu-ordenador-llm-local-llama/feed/ 0 8759
Generación de Texto en Español con GPT-2 https://aprendemachinelearning.com/generacion-de-texto-en-espanol-con-gpt-2/ https://aprendemachinelearning.com/generacion-de-texto-en-espanol-con-gpt-2/#comments Tue, 13 Dec 2022 09:00:00 +0000 https://www.aprendemachinelearning.com/?p=7869 Crearemos nuestra propia IA de generación de texto basada en los diálogos y entrevistas de Ibai Llanos publicados en Youtube. Usaremos un modelo pre-entrenado GPT-2 en castellano disponible desde HuggingFace y haremos el fine-tuning con Pytorch para que aprenda el estilo de escritura deseado.

The post Generación de Texto en Español con GPT-2 first appeared on Aprende Machine Learning.

]]>
Crea tu propio bot-influencer, basado en Ibai Llanos, en Python ¿Qué puede salir mal?

Crearemos nuestra propia IA de generación de texto basada en los diálogos y entrevistas de Ibai Llanos publicados en Youtube. Usaremos un modelo pre-entrenado GPT-2 en castellano disponible desde HuggingFace y haremos el fine-tuning con Pytorch para que aprenda el estilo de escritura deseado.

En este artículo comentaremos brevemente el modelo GPT-2 y crearemos un entorno en Python desde donde poder entrenar y generar texto!

¿Qué son los modelos GPT?

GPT significa “Generative Pre-Training” y es un modelo de Machine Learning creado por OpenAI para la generación de texto. El modelo de Procesamiento del Lenguaje Natural, es un caso particular de Transformers. GPT propone el pre-entrenamiento de un enorme corpus de texto para luego -opcionalmente- realizar el fine-tuning.

El fine-tuning es el proceso de realizar un “ajuste fino” de los parámetros ó capas de la red neuronal, en nuestro caso con un dataset adicional para guiar al modelo a obtener las salidas deseadas.

¿Entonces es aprendizaje no supervisado? Sí; se considera que es aprendizaje no supervisado porque estamos pasando al modelo enormes cantidades de texto, que el modelo organizará automáticamente y le pedimos que “prediga la siguiente palabra” usando como contexto todos los tokens previos (con posicionamiento!). El modelo ajusta sin intervención humana los embeddings y los vectores de Atención. Algunos autores lo consideran aprendizaje “semi-supervisado” porque consideran como “etiqueta de salida” el token a predecir.

Ejemplo: Si tenemos la oración “Buenos días amigos”, el modelo usará “Buenos días” para predecir como etiqueta de salida “amigos”.

Este modelo puede usarse directamente como modelo generativo luego de la etapa de aprendizaje no supervisado (sin hacer fine-tuning).

Al partir de este modelo en crudo y realizar un fine-tuning a nuestro antojo, podemos crear distintos modelos específicos: de tipo Question/Answering, resumen de textos, clasificación, análisis de sentimiento, etc.

Eso es lo que haremos en el ejercicio de hoy: descargar el modelo GPT y realizar el fine-tuning!

¿Cómo es la arquitectura de GPT-2?

GPT es un modelo Transformer. Utiliza sólo la rama “Tansformer-Decoder” a diferencia de modelos como BERT que utilizan la rama Encoder. De esta manera se elimina la Atención cruzada, pues ya no es necesaria y mantiene la “Masked Self-Attention”.

 Entre sus características:

  • El Transformer Decoder utiliza Masked Self-Attention. Sólo utiliza los tokens precedentes de la oración para calcular la atención del token final.
  • GPT es un modelo con posicionamiento absoluto de embeddings.
  • GPT fue entrenado con “Causal Language Modelling” y es poderoso para predecir el “siguiente token” de la oración. Esto le permite generar texto coherente, imitando al lenguaje de los humanos.
  • GPT-2 fue entrenado con el texto de 8 millones de páginas web que acumulan más de 40GB.
  • GPT-2 tiene 1500 millones de parámetros en su versión Extra-Large.
  • El tamaño de vocabulario es de 50.257 tokens.
  • Existen 4 modelos de distinto tamaño de GPT-2 según la cantidad de decoders y la dimensionalidad máxima.
Desde la versión GPT-2 Small de unos 500MB (117Millones de parámetros) hasta el Extra large que ocupa más de 6.5GB.

Como vemos, la versión pequeña tiene un tamaño aún manejable para entrenar en un ordenador “normal”. Es la versión del modelo que utilizaremos en el ejercicio.

Zero shot Learning

Una ventaja que se consigue al entrenar al modelo con millones de textos de conocimiento general (en contraposición a utilizar textos sobre un sólo tema) es que el modelo consigue habilidades “zero shot”, es decir, logra realizar satisfactoriamente algunas tareas para las que no ha sido entrenado específicamente. Por ejemplo, GPT-2 puede traducir textos de inglés a francés sin haber sido entrenado para ello. También consigue responder a preguntas ó generar código en Java.

¿Por qué usar GPT-2?

Puede que sepas de la existencia de GPT-3 y hasta puede que hayas escuchado hablar sobre el recientemente lanzado “ChatGPT” que algunos denominan como GPT-3.5 ó GPT-4. Entonces, ¿porqué vamos a usar al viejo GPT-2 en este ejercicio?

La respuesta rápida es porque GPT-2 es libre!, su código fue liberado y tenemos acceso al repositorio y a su implementación desde HuggingFace. Existen muchos modelos libres tuneado de GPT-2 y publicados que podemos usar. Si bien cuenta con un tamaño de parámetros bastante grande, GPT-2 puede ser reentrenado en nuestro propio ordenador.

En cuanto a resultados, GPT-2 fue unos de los mejores de su época (Feb 2019), batiendo records y con valores -en algunos casos- similares a los del humano:

En cambio GPT-3 aún no ha sido liberado, ni su código ni su red pre-entrenada, además de que tiene un tamaño inmensamente mayor a su hermano pequeño, haciendo casi imposible que lo podamos instalar ó usar en nuestra computadora de casa ó trabajo.

Es cierto que puedes utilizar GPT-3 mediante la API de pago de OpenAI y también se puede utilizar ChatGPT de modo experimental desde su web. Te animo a que lo hagas, pero no dejes de aprender a utilizar GPT-2 que será de gran ayuda para comprender como ajustar uno de estos modelos de lenguaje para tus propios fines.

¿Qué tiene que ver HuggingFace en todo esto?

HuggingFace se ha convertido en el gran repositorio de referencia de modelos pre-entrenados. Es un sitio web en donde cualquier persona ó insitutición pueden subir sus modelos entrenados para compartirlos.

HuggingFace ofrece una librería python llamada transformers que permite descargar modelos preentrenados de NLP (GPT, BERT, BART,ELECTRA, …), utilizarlos, hacer el fine tuning, reentrenar.

En el ejercicio que haremos instalaremos la librería de HuggingFace para acceder a los modelos de GPT.

Modelo pre-entrenado en Español

Dentro de HuggingFace podemos buscar modelos para NLP y también para Visión Artificial, cómo el de Stable Diffusion, para crear imágenes, como se explica en un anterior post del blog!).

Y podemos encontrar Modelos con distintos fines. En nuestro caso, estamos interesados en utilizar un modelo en Español.

Usaremos el modelo llamado “flax-community/gpt-2-spanish“, puedes ver su ficha aquí, y desde ya, agradecemos enormemente al equipo que lo ha creado y compartido gratuitamente. Ocupa unos 500MB.

Un detalle, que verás en el código: realmente cargaremos una red pre-entrenada con los pesos y el embeddings PERO también usaremos el tokenizador! (es decir, cargaremos 2 elementos del repositorio de HuggingFace, no sólo el modelo).

El proyecto Python: “Tu propio bot influencer”

En otros artículos de NLP de este tipo, utilizan textos de Shakespeare porque es un escritor reconocido, respetado y porque no tiene derechos de autor. Nosotros utilizaremos textos de Ibai Llanos generados a partir de transcripciones generadas automáticamente por Whisper de sus videos de Youtube. Ibai es un reconocido Streamer español de Twitch. ¿Porqué Ibai? Para hacer divertido el ejercicio! Para que sea en castellano, con jerga actual 😀

El proyecto consiste en tomar un modelo GPT-2 pre-entrenado en castellano y realizar el fine-tuning con nuestro propio dataset de texto. Como resultado obtendremos un modelo que será capaz de crear textos “con la manera de hablar” de Ibai.

Aquí puedes encontrar la Jupyter notebook completa en mi repo de Github con el ejercicio que realizaremos. En total son unas 100 líneas de código.

El Dataset educacional: Diálogos de Ibai

Banner del Canal de Ibai en Youtube 2022

El dataset es una selección totalmente arbitraría de videos de Youtube de Ibai con entrevistas y charlas de sus streams en Twitch. En algunos videos juega videojuegos en vivo, entrevista cantantes, futbolistas ó realiza compras de productos usados que le llaman la atención.

Utilicé un notebook de Google Colab con Whisper que es un modelo de machine learning lanzado hace pocos meses (en 2022) que realiza la transcripción automática de Audio a Texto. Usaremos como entradas esos textos. Disclaimer: Pueden contener errores de mala transcripción y también es posible que hubiera palabras que el modelo no comprenda del español.

El archivo de texto que utilizaremos como Dataset con fines educativos, lo puedes encontrar aquí.

Creación del entorno Python con Anaconda

Si tienes instalado Anaconda, puedes crear un nuevo Environment python para este proyecto. Si no, instala anaconda siguiendo esta guía, ó utiliza cualquier manejador de ambientes python de tu agrado.

También puedes ejecutar el código una notebook en la nube con Google Colab y aprovechar el uso de GPU gratuito. En este artículo te cuento sobre cómo usar Colab.

En este ejercicio utilizaremos la librería Pytorch para entrenar la red neuronal. Te recomiendo ir a la web oficial de Pytorch para obtener la versión que necesitas en tu ordenador, porque puede variar la instalación si usas Windows, Linux ó Mac y si tienes o no GPU.

Ejecuta las siguientes líneas en tu terminal:

conda create -n gpt2 python=3.9 -y
# Activa el nuevo ambiente con: 'conda activate gpt2'
conda install numpy tqdm transformers -y
# si tienes GPU instala Pytorch con:
conda install pytorch torchvision torchaudio pytorch-cuda=11.7 -c pytorch -c nvidia
# si no tienes GPU, instala con:
conda install pytorch torchvision torchaudio cpuonly -c pytorch

Importamos las librerías

Ahora pasamos a un notebook o una IDE Python y empezamos importando las librerías python que utilizaremos, incluyendo transformers de HuggingFace:

import os
import time
import datetime
import numpy as np
import random
from tqdm import tqdm
import torch
from torch.utils.data import Dataset, DataLoader, random_split, RandomSampler
from transformers import AutoTokenizer, AutoModelForCausalLM
from transformers import AdamW, get_linear_schedule_with_warmup

Uso de CPU ó GPU

Haremos una distinción; si vamos a utilizar GPU para entrenar ó CPU, definiendo una variable llamada device. Nótese que también alteramos el tamaño que usaremos de batch. En el caso de GPU, podemos utilizar valores 2 ó 3 según el tamaño de memoria RAM que tenga la tarjeta gráfica.

if torch.cuda.is_available():
    print("Usar GPU")
    device = torch.device("cuda")
    batch_size = 3
else:
    print("usar CPU")
    device = torch.device("cpu")
    batch_size = 1

Cargamos el Modelo de HuggingFace

La primera vez que ejecutemos esta celda, tomará unos minutos en descargar los 500MB del modelo y el tokenizador en Español desde HuggingFace, pero luego ya se utilizará esa copia desde el disco, siendo una ejecución inmediata.

Para este ejercicio estamos creando un “token especial” (de control) que llamaremos “ibai” con el que luego indicaremos al modelo que queremos obtener una salida de este tipo.

# Load the GPT tokenizer.
tokenizer = AutoTokenizer.from_pretrained("flax-community/gpt-2-spanish", bos_token='<|startoftext|>', eos_token='<|endoftext|>', pad_token='<|pad|>')
model = AutoModelForCausalLM.from_pretrained("flax-community/gpt-2-spanish")

control_code = "ibai"

special_tokens_dict = {
         "additional_special_tokens": ['f"<|{control_code}|>"'],
}
num_added_toks = tokenizer.add_special_tokens(special_tokens_dict)
model.resize_token_embeddings(len(tokenizer))
unk_tok_emb = model.transformer.wte.weight.data[tokenizer.unk_token_id, :]
for i in range(num_added_toks):
        model.transformer.wte.weight.data[-(i+1), :] = unk_tok_emb

Cargamos el Dataset “Ibai_textos.txt”

Creamos una clase python que hereda de Dataset que recibe el archivo txt que contiene los textos para fine-tuning.

class GPT2Dataset(Dataset):
  def __init__(self, control_code, tokenizer, archivo_texto, max_length=768):
    self.tokenizer = tokenizer
    self.input_ids = []
    self.attn_masks = []
    print('loading text...')
    sentences = open(archivo_texto, 'r', encoding="utf-8").read().lower().split('n')
    print('qty:',len(sentences))
    for row in tqdm(sentences):
      encodings_dict = tokenizer('<|startoftext|>'+ f"<|{control_code}|>" + row + '<|endoftext|>', truncation=True, max_length=max_length, padding="max_length")
      self.input_ids.append(torch.tensor(encodings_dict['input_ids']))
      self.attn_masks.append(torch.tensor(encodings_dict['attention_mask']))
    
  def __len__(self):
    return len(self.input_ids)
  def __getitem__(self, idx):
    return self.input_ids[idx], self.attn_masks[idx]

Instanciamos la clase, pasando el nombre de archivo “ibai_textos.txt” a utilizar

dataset = GPT2Dataset(control_code, tokenizer, archivo_texto="ibai_textos.txt", max_length=768)
# Split into training and validation sets
train_size = int(0.99 * len(dataset))
val_size = len(dataset) - train_size
train_dataset, val_dataset = random_split(dataset, [train_size, val_size])
print('{:>5,} training samples'.format(train_size))
print('{:>5,} validation samples'.format(val_size))
train_dataloader = DataLoader(
            train_dataset,  # The training samples.
            sampler = RandomSampler(train_dataset), # Select batches randomly
            batch_size = batch_size # Trains with this batch size.
        )

Entrenamos haciendo el Fine-Tuning

Realizando entre 1 y 3 epochs debería ser suficiente para que el modelo quede tuneado.

epochs = 1
learning_rate = 5e-4
warmup_steps = 1e2
epsilon = 1e-8
optimizer = AdamW(model.parameters(), lr = learning_rate, eps = epsilon)
total_steps = len(train_dataloader) * epochs
scheduler = get_linear_schedule_with_warmup(optimizer, num_warmup_steps = warmup_steps, num_training_steps = total_steps)
def format_time(elapsed):
    return str(datetime.timedelta(seconds=int(round((elapsed)))))

Ahora si, a entrenar el modelo durante cerca de 2 horas si tenemos GPU ó durante un día entero en CPU.

El código es bastante estándar en PyTorch para entreno de redes neuronales profundas; un loop principal por epoch donde procesamos por batches las líneas de texto del dataset y hacemos backpropagation.

total_t0 = time.time()
model = model.to(device)
for epoch_i in range(0, epochs):
    print('======== Epoch {:} / {:} ========'.format(epoch_i + 1, epochs))
    print('Training...')
    t0 = time.time()
    total_train_loss = 0
    model.train()
    for step, batch in enumerate(train_dataloader):
        b_input_ids = batch[0].to(device)
        b_labels = batch[0].to(device)
        b_masks = batch[1].to(device)
        model.zero_grad()
        outputs = model(  b_input_ids, labels=b_labels, 
                          attention_mask = b_masks, token_type_ids=None )
        loss = outputs[0]
        batch_loss = loss.item()
        total_train_loss += batch_loss
        # Get sample every x batches.
        if step % sample_every == 0 and not step == 0:
            elapsed = format_time(time.time() - t0)
            print('  Batch {:>5,}  of  {:>5,}. Loss: {:>5,}.   Elapsed: {:}.'.format(step, len(train_dataloader), batch_loss, elapsed))
        loss.backward()
        optimizer.step()
        scheduler.step()
    # Calculate the average loss over all of the batches.
    avg_train_loss = total_train_loss / len(train_dataloader)
    # Measure how long this epoch took.
    training_time = format_time(time.time() - t0)
    print("")
    print("  Average training loss: {0:.2f}".format(avg_train_loss))
    print("  Training epoch took: {:}".format(training_time))
    t0 = time.time()
    total_eval_loss = 0
    nb_eval_steps = 0
print("Training complete!")
print("Total training took {:} (h:mm:ss)".format(format_time(time.time()-total_t0)))

Guardar el modelo, para uso futuro

El tiempo de entreno varía según tu ordenador, memoria RAM y si tienes o no placa de video con GPU.

Luego de varias horas de entreno, mejor guardar el modelo para no tener que reentrenar cada vez y reutilizar el modelo que hicimos. Para guardar hacemos:

output_dir = './model_gpt_ibai/'
if not os.path.exists(output_dir):
    os.makedirs(output_dir)
model_to_save = model.module if hasattr(model, 'module') else model
model_to_save.save_pretrained(output_dir)
tokenizer.save_pretrained(output_dir)

En la notebook con el ejercicio verás también una celda con el código de ejemplo para cargar tu modelo ya entrenado.

Crear Texto al estilo influencer

Generamos 3 salidas preguntando ¿Qué es el fútbol? con máximo de 300 caracteres. Puedes variar estos parámetros para obtener más párrafos y con un máximo de 764 letras.

Fijate que agregamos al prompt el token de control de inicio (startoftext) pero también nuestro token de control que llamamos “ibai”.

model.eval()
prompt = "<|startoftext|>" + "<|ibai|>" + "¿ qué es el fútbol ?"
generated = torch.tensor(tokenizer.encode(prompt)).unsqueeze(0)
generated = generated.to(device)
sample_outputs = model.generate(
                                generated, 
                                num_return_sequences=3,
                                max_length = 300,
                                do_sample=True, 
                                top_k=50, 
                                top_p=0.95
                                )
for i, sample_output in enumerate(sample_outputs):
  print("{}: {}nn".format(i, tokenizer.decode(sample_output, skip_special_tokens=True)))

Y obtenemos como salidas:

  • <|ibai|>¿ qué es el fútbol? porque yo creo que son los mejores momentos de la vida del mundo. eh no se. eh y eso es lo que me llama la atención. eh ¡gracias! gracias.
  • <|ibai|>¿ qué es el fútbol? fútbol es la suma de sus defectos, del mal que uno puede poner en uno, de un mal que tú quieres que la gente haga como si nada, es decir, yo soy el contrario, yo soy el mejor, soy el peor. es decir, que tú eres el mejor y yo soy el peor. ¡joder, yo soy del mejor! ¡pero, bueno, ya está! ¿ tú eres el que está ahí para que me lo diga? ¿ me crees que estás bueno? no, no.
  • <|ibai|>¿ qué es el fútbol? para empezar, el que se mueve por el interés de los futbolistas es increíble. ¿ de quién eres? en el mejor sentido, y además de lo que eres. te he dicho que ya sé que a ti te gusta eso de hacer las cosas, pero eres muy, muy, muy, muy, muy, muy especial, ¿ no? un niño. te lo voy a decir un poco de corazón.

Ahora ya puedes jugar y probar tú mismo con distintas salidas!

Aquí te dejo algunos ejemplos divertidos que conseguí (pinto en negrita el comienzo del prompt enviado al modelo):

  • <|ibai|>para mi la vida es una cosa en la que te tienes que esforzar por lo que te gusta, porque quieres algo. cuando me pongo de acuerdo en algo me siento preparado para el momento. el ego que te llevas es la presión de no ser perfecto. y claro, si quieres, no quieres ser tan bueno, eres tú.
  • <|ibai|>la música que me gusta escuchar, claro. hay muchas personas que no nos conocen de nada, se nota. es un tío muy, muy, muy directo y creo que a lo mejor es un poco directo, de hecho, hay mucho ego en su actitud. la gente en general está bastante influenciada por él.
  • <|ibai|>un día todos deberíamos tener una vida, que es el futuro, una vida en paz con uno mismo, con la sociedad, y eso no es tan complicado como parece. y te digo lo de
  • <|ibai|>la felicidad es cuando hay armonía, que el mundo entero tiene su armonía. bueno, amigos, es que estamos unidos, a mí la música me relaja. bueno, es que no quiero dejar de escucharme ni de escuchar. y la música, de hecho, no es mi música, es mi vida.
  • <|ibai|>si voy a un restaurante, voy a un restaurante de argentina. me voy a un restaurante argentino. ¡ah, la verdad que me lo estoy pasando bien!
  • <|ibai|>la navidad es muy importante, porque es la época que vivimos. ¿ no crees que la navidad sería algo diferente de como la vivimos nosotros? en vez de algo muy tradicional, de un poco de juerga y de hacer una noche loca. no sé si la navidad es de las fechas en las que más fiesta hay. de verdad, no sé si es de las fechas en las que más fiesta hay o más fiesta no hay.
  • <|ibai|>en el próximo mes voy a empezar el segundo año. me llevo la bici para el club. de momento, voy a aprender a convivir con mis seguidores. y de hecho, hoy estoy hablando de eso.
  • <|ibai|>la inteligencia artificial, la realidad aumentada, ¿ qué pasa, tío? en este mundo hay gente que intenta crear un juego de magia que le pueda pasar un poquito de mal. bueno, que sí, que le pasa con las personas.
  • <|ibai|>la inteligencia artificial se está dando en todos los ámbitos. se está dando en todos los ámbitos, es cierto. en general, es un mundo donde la inteligencia artificial y el cerebro humano son los dos primeros motores.
  • <|ibai|>¿ qué es la inteligencia artificial? inteligencia artificial, es la de verdad. si la inteligencia artificial es más potente, es más fácil trabajar con ella. y es más difícil tener más inteligencia. porque la inteligencia artificial es la de verdad.
  • <|ibai|>yo sé mucho sobre el tema, pero me hace un poco de gracia. y también quiero que vosotros tengáis una gran audiencia, que leéis un libro, porque yo creo que eso es una idea que está muy bien. y es que si a tu amiga le pasa lo mismo que a ti, se va al final. por eso te pido que se ponga a grabar el libro, porque yo creo que eso, como el libro ya está hecho, le va a quedar espectacular.
  • <|ibai|>el amor es el camino, y no te vas a quedar ahí, a las 9. 40 am. el amor es un sentimiento que debe de ser muy fuerte en tu vida. a ver, yo creo que en la vida hay un tipo de personas que te hacen sentir una persona especial en tu vida. y el amor, que es la otra persona, también lo es.
Imagen generada por el autor con StableDiffusion

Resumen

En estos días estamos viendo cómo ChatGPT está siendo trending topic por ser el modelo GPT más poderoso y versátil de OpenAI, con capacidad de responder a cualquier pregunta, traducir idiomas, dar definiciones, crear poesía, historias y realizar snippets de código python.

En este artículo te acercamos un poco más a conocer qué son los modelos GPT que están revolucionando el campo del NLP mediante un ejercicio práctico.

Ya conoces un poco más sobre la librería transformers de HuggingFace, sobre los distintos modelos que puedes descargar en tu ordenador y personalizar. Como siempre, esto es sólo la punta del iceberg, te invito a que sigas investigando y aprendiendo más sobre todo ello y me dejes tus comentarios al respecto.

Nos vemos en el próximo post!

Puedes descargar la notebook con el ejercicio completo y el archivo con los textos de Ibai.

Otros Enlaces de interés

Suscripción al Blog

Recibe los próximos artículos sobre Machine Learning, estrategias, teoría y código Python en tu casilla de correo!

NOTA: algunos usuarios reportaron que el email de confirmación y/o posteriores a la suscripción entraron en su carpeta de SPAM. Te sugiero que revises y recomiendo que agregues nuestro remitente info @ aprendemachinelearning.com a tus contactos para evitar problemas. Gracias!

El libro del Blog

Si te gustan los contenidos del blog y quieres darme tu apoyo, puedes comprar el libro en papel, ó en digital (también lo puede descargar gratis!).

The post Generación de Texto en Español con GPT-2 first appeared on Aprende Machine Learning.

]]>
https://aprendemachinelearning.com/generacion-de-texto-en-espanol-con-gpt-2/feed/ 4 7869
¿Cómo funcionan los Transformers? en Español https://aprendemachinelearning.com/como-funcionan-los-transformers-espanol-nlp-gpt-bert/ https://aprendemachinelearning.com/como-funcionan-los-transformers-espanol-nlp-gpt-bert/#comments Tue, 08 Nov 2022 08:54:00 +0000 https://www.aprendemachinelearning.com/?p=7771 El Machine Learning cambió para siempre con la llegada en 2017 de un paper llamado Attention is all you need en donde se presentaba una nueva arquitectura para NLP: Los Transformers

The post ¿Cómo funcionan los Transformers? en Español first appeared on Aprende Machine Learning.

]]>
Imagen creada por el Autor utilizando el modelo de text-to-img StableDiffusion

Los Transformers aparecieron como una novedosa arquitectura de Deep Learning para NLP en un paper de 2017 “Attention is all you need” que presentaba unos ingeniosos métodos para poder realizar traducción de un idioma a otro superando a las redes seq-2-seq LSTM de aquel entonces. Pero lo que no sabíamos es que este “nuevo modelo” podría ser utilizado en más campos como el de Visión Artificial, Redes Generativas, Aprendizaje por Refuerzo, Time Series y en todos ellos batir todos los records! Su impacto es tan grande que se han transformado en la nueva piedra angular del Machine Learning.

En este artículo repasaremos las piezas fundamentales que componen al Transformer y cómo una a una colaboran para conseguir tan buenos resultados. Los Transformers y su mecanismo de atención posibilitaron la aparición de los grandes modelos generadores de texto GPT2, GPT3 y BERT que ahora podían ser entrenados aprovechando el paralelismo que se alcanza mediante el uso de GPUs.

Agenda

  • ¿Qué son los transformers?
  • Arquitectura
    • General
    • Embeddings
    • Positional Encoding
    • Encoder
      • Mecanismo de Atención
      • Add & Normalisation Layer
      • Feedforward Network
    • Decoder
    • Salida del Modelo
  • Aplicaciones de los Transformers
    • BERT
    • GPT-2
    • GPT-3
  • Resumen

¿Qué son los transformers en Machine Learning?

En el paper original de 2017 “Attention is all you need” aparece el diagrama con la novedosa arquitectura del Transformer, que todos deberíamos tatuarnos en un brazo. Esta arquitectura surge como una solución a problemas de aprendizaje supervisado en Procesamiento del Lenguaje Natural, obteniendo grandes ventajas frente a los modelos utilizados en ese entonces. El transformer permitía realizar la traducción de un idioma a otro con la gran ventaja de poder entrenar al modelo en paralelo; lo que aumentaba drásticamente la velocidad y reducción del coste; y utilizando como potenciador el mecanismo de atención, que hasta ese momento no había sido explotado del todo. Veremos que en su arquitectura utiliza diversas piezas ya existentes pero que no estaban combinadas de esta manera. Además el nombre de “Todo lo que necesitas es Atención” es a la vez un tributo a los Beatles y una “bofetada” a los modelos NLP centrados en Redes Recurrentes que en ese entonces estaban intentando combinarlos con atención. De esta sutil forma les estaban diciendo… “tiren esas redes recurrentes a la basura”, porque el mecanismo de atención NO es un complemento… es EL protagonista!

All you need is Love Attention

The Beatles

Con el tiempo, esta arquitectura resultó ser flexible y se pudo utilizar para tareas más allá del NLP, además de para la generación de texto, clasificación, resumen de contenidos también pudo traspasar esa frontera y ser aplicado en Visión Artificial, Generación de Audio, Predicción de Series Temporales y Aprendizaje por Refuerzo.

La Arquitectura Encoder-Decoder del Transformer

Veamos primero la gran foto de los Transformers.

Visión General

Estás viendo el dibujo que deberías de llevar en tu próxima camiseta:

La Arquitectura de los Transformers

La primera impresión puede ser algo intimidante, pero vayamos poco a poco. Si pensamos en el modelo como una caja negra, sería simplemente:

Entrada -> Transformer -> Salida

Vemos que con una entrada de texto “Hola” obtenemos la salida “Hello”.

Si hacemos un poco de zoom en esa caja, veremos dos componentes principales: Encoders y Decoders.

La entrada pasará por una serie de Encoders que se encadenan uno tras otro y luego envían su salida a otra serie de Decoders hasta emitir la Salida final. En el paper original, se utilizan 6 encoders y 6 decoders. Notaremos un recuadro llamado “Target” donde estamos pasando el Output Deseado al modelo para entrenar; pero obviamente que no lo pasaremos al momento de la inferencia; al menos no completo (más adelante en el artículo).

Observamos ahora con mayor detalle cómo está compuesto un Encoder y un Decoder y veremos que son bastante parecidos por dentro.

En su interior tanto Encoder como decoder cuentan con un componente de Atención (o dos) y una red neuronal “normal” como salidas.

Para comenzar a evaluar con mayor detalle las partes del Transformer, primero deberemos generar un Embedding de la entrada y luego entrar al Encoder. Así que veamos eso primero.

Embeddings

El uso de embeddings existía en NLP desde antes de los Transformers, puede que estés familiarizado con modelos como el word-2-vec y puede que ya hayas visto las proyecciones creadas con miles de palabras donde los embeddings logran agrupar palabras de manera automática en un espacio multidimensional. Entonces conceptos como “hombre y mujer”, distanciado de “perro y gato” veremos nombres de países juntos y profesiones ó deportes también agrupados en ese espacio. Primero convertimos las palabras a tokens, es decir a un valor numérico asociado, porque recordemos que las redes neuronales únicamente pueden procesar números y no cadenas de texto.

Ejemplo de Word Embeddings y una proyección en 3D.

Entonces convertimos una palabra a un número, ese número a un vector de embeddings de 512 dimensiones (podría ser de otro valor, pero el propuesto en el paper fue este).

Entonces:

  1. Palabra -> Token(*)
  2. Token -> Vector n-dimensional

(*) NOTA: una palabra podría convertirse en más de un token

La parte novedosa que introduce el paper de los transformers, es el “Positional Encoding” a esos embeddings…

Positional Encoding

Una vez que tenemos los embeddings, estaremos todos de acuerdo que el orden en que pasemos las palabras al modelo, será importante. De hecho podríamos alterar totalmente el significado de una oración si mezclamos el orden de sus palabras o hasta podría carecer totalmente de significado.

La casa es verde != verde casa la es

Entonces necesitamos pasar al modelo los tokens pudiendo especificar de alguna manera su posición.

Aquí, de paso comentaremos dos novedades de los Transformers: la solución a este posicionamiento pero también esto permite el poder enviar en simultáneo TODOS los tokens al modelo, algo que anteriormente no se podía hacer, por lo que se pasaba “palabra por palabra” una después de la otra, resultando en una menor velocidad.

Para resolver el posicionamiento, se agrega una valor secuencial al embedding que se asume que la red podrá interpretar. Si aparece el token “perro” como segunda palabra ó como décima, mantendrá en esencia el mismo vector (calculado anteriormente en el embedding) pero con un ligero valor adicional que lo distinguirá y que será el que le de la pista a la red neuronal de interpretar su posición en la oración.

En el paper se propone una función sinusoidal sobre el vector de 512 dimensiones del embedding. Entonces ese vector de embeddings del token “perro” será distinto si la palabra aparece primera, segunda, tercera.

Y ahora sí, podemos dar entrada al Encoder de los Transformers.

Encoder

El Encoder está compuesto por la capa de “Self attention” y esta conectada a una red feed forward. Entre las entradas y salidas se aplica la Skip Connection (ó ResNet) y un Norm Layer.

El mecanismo de Atención, es una de las implementaciones novedosas que propone el paper. Veremos que logra procesar en paralelo, manteniendo la ventaja de entreno mediante GPU.

El Encoder tiene como objetivo procesar la secuencia de los tokens de entrada y ser a su vez la entrada del mecanismo de atención del Decoder. No olvides que realmente se encadenan varios Encoders con varios Decoders (no se utilizan sólo uno en la práctica).

El -dichoso- mecanismo de Atención

Primero que nada, ¿Para qué queremos un mecanismo de Atención? El mecanismo de atención nos ayuda a crear y dar fuerza a las relaciones entre palabras en una oración. Si tenemos el siguiente enunciado:

El perro estaba en el salón, durmiendo tranquilo.

Nosotros comprendemos fácilmente que la palabra “tranquilo” se refiere al perro y no al “salón”. Pero a la red neuronal que comienza “en blanco”, sin saber nada de las estructuras del lenguaje (español, pero podría ser cualquier idioma), y que además ve la misma frase como valores numéricos:

5 186 233 7 5 1433 567 721

NOTA: no olvides que cada token, por ej. el nº5 corresponde a un embedding de n-dimensiones de 512 valores. Por simplificar usamos el 5 como reemplazo de “El”, por no escribir sus 512 componentes.

¿Cómo podrá entender que ese último token “721” está afectando a la segunda palabra “186”?
Por eso surgen los mecanismos de atención; para lograr dar más -o menos- importancia a una palabra en relación a las otras de la oración.

La solución que presentan los Transformers en cuanto a la atención, es la construcción de un “Diccionario Blando” (Soft Dictionary). ¿Qué es esto de un diccionario blando?
En programación, es probable que conozcas el uso de diccionarios de tipo “clave-valor” un típico “hashmap” en donde ante una entrada, obtengo un valor dict[“perro”]=0.78.
Para el “diccionario de atenciones” podría ser que si pedimos la atención de “tranquilo vs perro” de la oración, nos devuelva un 1 indicando mucha atención, pero si le pedimos “tranquilo vs salón” nos devuelva un -1.

Pero… eso no es todo. Nuestro Diccionario es “Suave/Blando”, esto quiere decir que no se va a saber “de memoria” el resultado de una clave. Si alteramos un poco la oración, el diccionario tradicional fallaría:

El perro estaba durmiendo en un salón tranquilo.

Ahora la clave de atención para “tranquilo vs salón” deberá pasar de -1 a ser cercana a 1.

Para poder calcular la atención, se utilizan 3 matrices llamadas “Query-Key-Value” que van a operar siguiendo una fórmula que nos devuelve un “score” o puntaje de atención.

  • En la matriz Q de Query tendremos los tokens (su embedding) que estamos evaluando.
  • En la matriz K de Key tendremos los tokens nuevamente, como claves del diccionario.
  • En la matriz V de Value tendremos todos los tokens (su embedding) “de salida”.

El resultado de la atención será aplicar la siguiente fórmula:

Si nos olvidamos del Softmax y el “multihead” (se definirá a continuación), podemos simplificar la fórmula diciendo que:
La atención será multiplicación matricial Q por la transpuesta de K; a eso le llamamos “factor”; y ese factor multiplicado por V.

¿Y eso qué significa? ¿Por qué estamos operando de esa manera? Si recuerdas, tanto Q como K son los valores de los Embeddings, es decir, cada token es un vector n-dimensional. Al hacer el producto vectorial obtenemos matemáticamente la “Similitud” entre los vectores. Entonces si el embedding estuviera funcionando bien, la similitud entre “nieve” y “blanco” deberían estar más cercanas que “nieve” y “negro”. Entonces cuando dos palabras sean similares, tendremos un valor positivo y mayor que si son opuestos, donde obtendríamos un valor negativo (dirección contraria). Este factor se multiplica por la matriz de Valor conformando el Score final de atención para cada relación entre pares de <<palabras>> que estamos evaluando.

Como estamos trabajando con matrices, seguimos aprovechando la capacidad de calcular todo a la vez y poder hacerlo acelerado por GPU.

Más de una atención: Multi-Head Attention

…hay más en el paper, porque el tipo de atención que vamos a calcular se llama “Multi-head Attention“. ¿Qué son esas “Multi-cabezas”??? Lo que se hace es que en vez de calcular la atención de todos los tokens de la oración una vez con las 512 dimensiones (provenientes del embedding), subdividiremos esos valores en grupos y de cada uno, calcular su atención. En el paper proponen “8 heads” con lo que entonces calcularemos “8 atenciones de a 64 valores del embeddings” por cada token! Esto a mi modo de entender es algo bastante arbitrario pero por arte de “magia matemática” funciona… Luego de calcular esas 8 cabezas, (esas 8 atenciones) haremos un promedio de los valores de cada atención entre tokens.

Si volvemos al ejemplo para la clave de “tranquilo vs perro” calcularemos 8 atenciones y al promediarlas deberíamos obtener un valor cercano a 1 (para la 1er oración).

Cuando terminemos de entrenar el modelo Transformer completo, podríamos intentar analizar y entender esas matrices de atención creadas y tratar de comprenderlas. Algunos estudios muestran que la “multi-atención” logra representar relaciones como la de “artículo-sustantivo” ó “adjetivo-sustantivo”, “verbo sustantivo” lo cual me sigue pareciendo algo increíble.

3 tipos de atención: Propia, Cruzada y Enmascarada

Prometo que esto ya es lo último que nos queda comprender sobre los mecanismos de atención… A todo lo anterior, hay que sumar que tenemos 3 tipos distintos de atención

  1. Self Attention
  2. Cross Attention
  3. Masked Attention

Su comportamiento es igual que al descripto anteriormente pero con alguna particularidad:

Self Attention se refiere que crearemos los valores y las matrices de Q-K-V a partir de las propias entradas de los tokens de entrada.

En la Atención Cruzada, vemos cómo utilizamos como entradas en el Decoder los valores obtenidos en el Encoder. Esto es lo que hará que con sólo el valor (Value) del Output pueda modelar la salida de atención buscada, esto es importante porque al final es lo que condicionará mayormente la “traducción” que está haciendo internamente el Decoder!

Y la llamada “Masked attention” se refiere a que enmascaramos parte triangular superior de la matriz de atención al calcularla para no caer en “data-leakage”, es decir, para no “adelantar información futura” que el Output no podría tener en su momento. Esto puede resultar confuso, intentaré aclararlo un poco más. En el Encoder, cuando tenemos los valores de entrada y hacemos “self-attention” dijimos que calculamos la atención de “todos contra todos” porque todos los tokens de entrada son conocidos desde el principio. Si recordamos la frase anterior:

El perro estaba en el salón, durmiendo tranquilo.

Aquí podemos calcular tanto la atención para la clave “perro-tranquilo” y también la de “tranquilo-perro”

Sin embargo -si suponemos que estamos traduciendo al inglés- en el output tendremos

“The dog was in the living room, sleeping peacefully”

PERO hay un detalle; para poder generar el output al momento de la inferencia, generaremos de a una palabra por vez, entonces iremos produciendo el siguiente output:

T1 – The
T2 – The dog
T3 – The dog was
T4 – The dog was in …

Esto quiere decir que vamos generando los tokens de a uno a la vez por lo que al principio no podríamos conocer la relación entre “dog-peacefully” pues esta relación aún no existe!

Entonces diferenciemos esto:

-> Al momento de entrenar el modelo pasamos el output deseado COMPLETO a las matrices de QKV de “Masked Attention” para entrenar en paralelo; al estar enmascarado, es como si estuviésemos simulando la secuencia “The, The dog, The dog was…”

-> Al momento de inferencia REALMENTE tendremos uno a uno los tokens como una secuencia temporal, realmente, iremos agregando de a una palabra del output a la cadena de salida a la vez.

Al enmascarar la matriz Q*K en su diagonal superior, prevenimos obtener valores “futuros” en la relación de atención entre sus tokens.

Short residual skip Connections y Layer Normalization

Al utilizar Skip Connections permitimos mantener el valor de origen del input a través de las deep neural networks evitando que sus pesos se desvanezcan con el paso del tiempo. Esta técnica fue utilizada en las famosas ResNets para clasificación de imágenes y se convirtieron en bloques de construcción para redes profundas. También es sumamente importante la Normalización en RRNN. Previene que el rango de valores entre capas cambie “demasiado bruscamente” logrando hacer que el modelo entrene más rápido y con mayor capacidad de generalización.

Feed Forward Network

La salida final del Encoder la dará una Red Neuronal “normal”, también llamada MLP ó capa densa. Se agregarán dos capas con Dropout y una función de activación no lineal.

Decoder

Ahora que conocemos los componentes del Encoder, podemos ver que con esos mismos bloques podemos crear el Decoder.

Al momento de entrenar, estaremos pasando el Input “hola amigos” y Output “hello friends” (su traducción) al mismo tiempo al modelo.

Tradicionalmente, usamos la salida únicamente para “validar” el modelo y ajustar los pesos de la red neuronal (durante el backpropagation). Sin embargo en el Transformer estamos usando la salida “hello friends” como parte del aprendizaje que realiza el modelo.

Entonces, el output “hello friends” es también la “entrada” del decoder hacia embeddings, posicionamiento y finalmente ingreso a la Masked Self Attention que comentamos antes (para los valores de Q,K,V).

De aquí, y pasando por la Skip Connection y Normalización (en la gráfica “Add & Norm) entramos al segundo mecanismo de Atención Cruzada que contiene para las matrices Query y Key” la salida del Encoder y como Value la salida de la Masked Attention.

Nuevamente Suma y Normalización, entrada a la Feed Forward del Decoder.

RECORDAR: “N encoder y N decoders”

No olvidemos que si bien estamos viendo en mayor detalle “un encoder” y “un decoder”, la arquitectura completa del Transformer implica la creación de (en el paper original) 6 encoders encadenados que enlazan con otros 6 decoders.

Salida final del Modelo

La salida final del modelo pasa por una última capa Lineal y aplicar Softmax. El Softmax, por si no lo recuerdas nos dará un valor de entre 0 y 1 para cada una de las posibles palabras (tokens) de salida. Entonces si nuestro “lenguaje” total es de 50.000 posibles tokens (incluyendo signos de puntuación, admiración, interrogación), encontraremos a uno de ellos con valor más alto, que será la predicción.

Si yo te dijera “en casa de herrero cuchillo de …” y vos tuvieras que predecir la próxima palabra, de entre todas las posibles en el castellano seguramente elegirías “palo” con probabilidad 0,999. Ahí acabas de aplicar Softmax intuitivamente en tu cabeza, y descartaste al resto de 49.999 palabras restantes.

Repaso de la arquitectura

Repasemos los puntos fuertes de la arquitectura de los Transformers:

  • La arquitectura de Transformers utiliza Encoders y Decoders
  • El Transformer permite entrenar en paralelo y aprovechar el GPU
  • Utiliza un mecanismo de atención que cruza en memoria “todos contra todos los tokens” y obtiene un score. Los modelos anteriores como LSTM no podían memorizar textos largos ni correr en paralelo.
  • El mecanismo de atención puede ser de Self Attention en el Encoder, Cross Attention ó Masked Attention en el Decoder. Su funcionamiento es igual en todos los casos, pero cambian los vectores que utilizamos como entradas para Q,K,V.
  • Se utiliza El Input pero también la Salida (el Output del dataset) para entrenar al modelo.

Aplicaciones de los Transformers

Los Transformers se convirtieron en el modelo “de facto” para todo tipo de tareas de NLP, incluyendo Traducción de idiomas (como vimos), clasificación de texto, resumen de textos y Question-Answering. Sólo basta con modificar los datasets que utilizamos para entrenar al modelo y la “salida final del modelo”, manteniendo al resto de arquitectura.

A raíz de poder entrenar mediante GPU, reducción de tiempo y dinero, surgieron varios modelos para NLP que fueron entrenados con datasets cada vez más grandes, con la creencia de que cuantas más palabras, más acertado sería el modelo, llevándolos al límite. Ahora, parece que hemos alcanzado un tope de acuracy, en el cual no vale la pena seguir extendiendo el vocabulario.

Vemos 3 de estos grandes modelos de NLP y sus características

BERT – 2018

BERT (Bidirectional Encoder Representations from Transformers) aparece en 2018, desarrollado por Google y utiliza sólo la parte del Encoder de los Transformers. Este modelo fue entrenado con todos los artículos de la Wikipedia en Inglés que contiene más de 2500 millones de palabras. Esto permitió generar un modelo “pro-entrenado” en inglés muy poderoso que podía ser utilizado para múltiples tareas de NLP. Además, al ser Open Source, permitió la colaboración y extensión por parte de la comunidad científica. El poder utilizar un modelo pre-entrenado tan grande, preciso y potente, permite justamente “reutilizar” ese conocimiento sin necesidad de tener que entrenar nuevamente un modelo de NLP, con el coste y tiempo (e impacto medioambiental) que puede conllevar.

Además BERT contenía algunas novedades, por ejemplo, en vez de utilizar un “Embeddings único y estático”, implementó un mecanismo en donde la misma palabra (token) podría devolver un vector distinto (de “embeddings”) de acuerdo al contexto de la oración de esa palabra.

Cuando salió BERT, batió muchos de los records existentes en datasets como SQuAD, GLUE y MultiNLI.

GPT-2 – 2019

GPT es un modelo de generación de texto creado por Open AI en 2019 y que contiene 1500 millones de parámetros en la configuración de su red neuronal profunda.

Al ser entrenado para generar “siguiente palabra”, pasa a convertirse en un problema de tipo “no supervisado”. Su re-entreno fue realizado usando BooksCorpus, un dataset que contiene más de 7,000 libros de ficción no publicados de diversos géneros.

Este modelo generó polémica en su momento debido a que empezaba a crear textos similares a lo que podía escribir una persona, con lo cual antes de ser lanzado, se temió por su mal uso para generar contenido falso en internet. Había sido anunciado en febrero y recién fue accesible al público, de manera parcial en Agosto. Los modelos de tipo GPT utilizan únicamente la parte de “Decoder” del Transformer.

Ejercicio Práctico: Crea tu propio chatbot con GPT-2 en Español!

GPT-3 – 2020

Lanzado en 2020, la red de GPT-3 tiene 175.000 millones de parámetros. Entrenado con textos de internet que contienen más de 500.000 millones de tokens.

En GPT-3, la generación de textos por la IA puede alcanzar un nivel literario que pasa inadvertido por los humanos, el modelo es capaz de mantener la coherencia en textos largos y debido a su gran conocimiento del mundo, crear un contexto y párrafos muy reales.

Otra de las novedades que trajo GPT3 es que lograba realizar tareas “inesperadas” de manera correcta, sin haber sido entrenado para ello. Por ejemplo, puede realizar Question-Answer, crear diálogos ó hasta escribir código en Python, Java o traducir idiomas. A este tipo de aprendizaje “no buscado” le llamamos “One Shot Learning”.

El Codigo de GPT-3 aún no ha sido liberado, hasta la fecha.

Más allá del NLP

Al comportarse tan bien para tareas de NLP, se comenzó a utilizar esta arquitectura adaptada para Clasificación de imágenes en Computer Vision y también logró superar en muchos casos a las Redes Convolucionales! A este nuevo modelo se le conoce como Vision Transformer o “Vit”.

También podemos utilizar Transformers para pronóstico de Series Temporales (Time Series). Este caso de uso tiene mucha lógica si lo pensamos, porque al final es muy parecido a “predecir la próxima palabra”, pero en su lugar “predecir la próxima venta”, ó stock…

Los Transformers están siendo utilizados en modelos generativos de música o como parte de los modelos de difusión text-to-image como Dall-E2 y Stable Diffusion.

Por último, los Transformers también están siendo utilizados en Aprendizaje por Refuerzo, donde también tienen sentido, pues la fórmula principal de este tipo de problemas también contiene una variable temporal/secuencial.

Y en todos estos campos, recién está empezando la revolución! No digo que será el Transformer quien reemplace al resto de Arquitecturas y modelos de Machine Learning existentes, pero está siendo un buen motivo para cuestionarlos, replantearlos y mejorar!

Resumen

Los Transformers aparecieron en un paper de 2017 implementando un eficiente mecanismo de atención como punto clave para resolver problemas de traducción en el campo del Procesamiento del Lenguaje Natural. Como bien sabemos, el lenguaje escrito conlleva implícitamente un orden temporal, secuencial que hasta aquel entonces era una de las barreras para no poder crear modelos extensos, pues impedía el aprovechamiento de GPU. La nueva arquitectura rompía esa barrera utilizando unos sencillos trucos: utilizar todos los token al mismo tiempo (en vez de uno en uno) y enmascarar la multiplicación matricial para impedir el data leakage en el caso del decoder.

Además, resolvió el Posicionamiento de los token y aprovechó técnicas ya existentes como el uso de Skip Connections y Normalization Layers.

Todo ello posibilitó la creación de los grandes modelos actuales, BERT, GPT y la aparición de muchísimos modelos disponibles “pre-entrenados” que pueden ser descargados y reutilizados para fine-tuning por cualquier persona.

Como si esto fuera poco, los Transformers tienen la flexibilidad suficiente para traspasar el área de NLP y contribuir al resto de áreas del Machine Learning obteniendo resultados impresionantes.

Habrá que agradecer a Ashish VaswaniNoam ShazeerNiki ParmarJakob UszkoreitLlion JonesAidan N. GomezLukasz KaiserIllia Polosukhin por su obra maestra.

Material Extra

Enlaces a más info sobre Transformers!

Suscripción al Blog

Recibe los próximos artículos sobre Machine Learning, estrategias, teoría y código Python en tu casilla de correo!

NOTA: algunos usuarios reportaron que el email de confirmación y/o posteriores a la suscripción entraron en su carpeta de SPAM. Te sugiero que revises y recomiendo que agregues nuestro remitente info @ aprendemachinelearning.com a tus contactos para evitar problemas. Gracias!

El libro del Blog

Si te gustan los contenidos del blog y quieres darme tu apoyo, puedes comprar el libro en papel, ó en digital (también lo puede descargar gratis!).

The post ¿Cómo funcionan los Transformers? en Español first appeared on Aprende Machine Learning.

]]>
https://aprendemachinelearning.com/como-funcionan-los-transformers-espanol-nlp-gpt-bert/feed/ 1 7771
Crea imágenes increíbles con Inteligencia Artificial en tu ordenador https://aprendemachinelearning.com/crea-imagenes-stable-diffusion-con-inteligencia-artificial-en-tu-ordenador/ https://aprendemachinelearning.com/crea-imagenes-stable-diffusion-con-inteligencia-artificial-en-tu-ordenador/#respond Thu, 06 Oct 2022 08:00:00 +0000 https://www.aprendemachinelearning.com/?p=7681 El modelo de Machine Learning llamado Stable Diffusion es Open Source y permite generar cualquier imagen a partir de un texto, por más loca que sea, desde el sofá de tu casa! Estamos viviendo unos días realmente emocionantes en el campo de la inteligencia artificial, en apenas meses, hemos pasado de tener modelos enormes y […]

The post Crea imágenes increíbles con Inteligencia Artificial en tu ordenador first appeared on Aprende Machine Learning.

]]>
El modelo de Machine Learning llamado Stable Diffusion es Open Source y permite generar cualquier imagen a partir de un texto, por más loca que sea, desde el sofá de tu casa!

Estamos viviendo unos días realmente emocionantes en el campo de la inteligencia artificial, en apenas meses, hemos pasado de tener modelos enormes y de pago en manos de unas pocas corporaciones a poder desplegar un modelo en tu propio ordenador y lograr los mismos -increíbles- resultados de manera gratuita. Es decir, ahora mismo, está al alcance de prácticamente cualquier persona la capacidad de utilizar esta potentísima herramienta y crear imágenes en segundos (ó minutos) y a coste cero.

En este artículo les comentaré qué es Stable Diffusion y por qué es un hito en la historia de la Inteligencia Artificial, veremos cómo funciona y tienes la oportunidad de probarlo en la nube o de instalarlo en tu propio ordenador sea Windows, Linux ó Mac, con o sin placa GPU.

Reseña de los acontecimientos

  • 2015: Paper que propone los Diffusion Models.
  • 2018 -2019 Text to Image Synthesis – usando GANS se generan imágenes de 64×64 pixels, utiliza muchos recursos y baja calidad de resultados.
  • Enero 2021: Open AI anuncia Dall-E, genera imágenes interesantes, pequeñas, baja resolución, lentas.
  • Febrero 2021: CLIP de Open AI (Contrastive Language-Image Pretraining), un codificador dual de lenguaje-imagen muy potente.
  • Julio 2021: Image Text Contrastive Learning Mejora sobre las Gans “image-text-label” space.
  • Marzo 2022: GLIDE: esta red es una mejora sobre Dall-E, tambien de openAI pero usando DIFFUSION model.
  • Abril 2022: Dall-E 2 de Open AI, un modelo muy bueno de generación de imágenes. Código cerrado, acceso por pedido y de pago.
  • Mayo 2022: Imagen de Google.
  • Agosto de 2022: Lanzamiento de Stable Diffusion 1.4 de Stability AI al público. Open Source, de bajos recursos, para poder ejecutar en cualquier ordenador.

¿Qué es Stable Diffusion?

Stable Diffusion es el nombre de un nuevo modelo de Machine Learning de Texto-a-Imagen creado por Stability Ai, Comp Vis y LAION. Entrenado con +5 mil millones de imágenes del dataset Laion-5B en tamaño 512 por 512 pixeles. Su código fue liberado al público el 22 de Agosto de 2022 y en un archivo de 4GB con los pesos entrenados de una red neuronal que podemos descargar desde HuggingFace, tienes el poder de crear imágenes muy diversas a partir de una entrada de texto.

Stable Diffusion es también una gran revolución en nuestra sociedad porque trae consigo diversas polémicas; al ofrecer esta herramienta a un amplio público, permite generar imágenes de fantasía de paisajes, personas, productos… ¿cómo afecta esto a los derechos de autor? Qué pasa con las imágenes inadecuadas u ofensivas? Qué pasa con el sesgo de género? Puede suplantar a un diseñador gráfico? Hay un abanico enorme de incógnitas sobre cómo será utilizada esta herramienta y la disrupción que supone. A mí personalmente me impresiona por el progreso tecnológico, por lo potente que es, los magnificos resultados que puede alcanzar y todo lo positivo que puede acarrear.

¿Por qué tanto revuelo? ¿Es como una gran Base de datos de imágenes? – ¡No!

Es cierto que fue entrenada con más de 5 mil millones de imágenes. Entonces podemos pensar: “Si el modelo vio 100.000 imágenes de caballos, aprenderá a dibujar caballos. Si vio 100.000 imágenes de la luna, sabrá pintar la luna. Y si aprendió de miles de imágenes de astronautas, sabrá pintar astronautas“. Pero si le pedimos que pinte “un astronauta a caballo en la luna” ¿qué pasa? La respuesta es que el modelo que jamás había visto una imagen así, es capaz de generar cientos de variantes de imágenes que cumplen con lo solicitado… esto ya empieza a ser increíble. Podemos pensar: “Bueno, estará haciendo un collage, usando un caballo que ya vio, un astronauta (que ya vió) y la luna y hacer una composición“. Y no; no es eso lo que hace, ahí se vuelve interesante: el modelo de ML parte de un “lienzo en blanco” (en realidad es una imagen llena de ruido) y a partir de ellos empieza a generar la imagen, iterando y refinando su objetivo, pero trabajando a nivel de pixel (por lo cual no está haciendo copy-paste). Si creyéramos que es una gran base de datos, les aseguro que no caben las 5.500.000.000 de imágenes en 4 Gygabytes -que son los pesos del modelo de la red- pues estaría almacenando cada imagen (de 512x512px) en menos de 1 Byte, algo imposible.

¿Cómo funciona Stable Diffusion?

Veamos cómo funciona Stable Diffusion!

Stable Diffusion está basado en otro modelo llamado “Latent Diffusion” que proviene de modelos de difusión de ML que están entrenados para “eliminar el ruido de “imágenes sucias” paso a paso”. Esto quiere decir que al modelo le entrenamos con fotos donde ensuciamos ciertos pixeles, con manchas, desenfoque (blur) o distorsiones que agregamos a propósito y le pedimos como salida la imagen correcta (la imagen original sin ruido). Entonces, la red neuronal del modelo aprende a “quitar el ruido”, es decir, transformar esas manchas (ruido) en la imagen original.

Los modelos de difusión lograron resultados muy buenos como generadores de imágenes aunque su contra es que como trabajan a nivel de pixel requieren de mucha memoria RAM y toman tiempo para crear imágenes de alta definición.

La mejora introducida por los modelos “Latent Diffusion” es que el modelo es entrenado para generar “representaciones de imágenes latentes” (comprimidas). Sus tres componente principales son:

  1. Autoencoder (VAE)
  2. U-Net
  3. Text-Encoder

1-Autoencoder (VAE)

El modelo VAE tiene dos partes, un codificador y un decodificador. En codificador es usado para convertir la imagen en una representación latente de baja dimensión, que servirá como entrada a la “U-Net”. El decodificador por el contrario, transforma la representación latente nuevamente en una imagen.

Durante el entrenamiento de difusión latente, se usa el codificador para obtener las representaciones latentes de las imágenes para el proceso de difusión directa, se aplica más y más ruido en cada paso. Durante la inferencia, se realiza el proceso inverso de difusión donde “expande los latentes” para convertirlos nuevamente en imágenes con el decodificador VAE. Para la inferencia sólo necesitamos el decodificador.

Ejemplo de arquitectura de una red Autoencoder VAE del artículo “VAE

2-U-Net

La U-Net tiene una mitad de camino “de contracción” y otra mitad de “expansión“, ambos compuestos por bloques ResNet (para soportar redes profundas sin desvanecer el aprendizaje). La primera mitad de la U-Net reduce la imagen a una representación de baja resolución (similar a un encoder) y la segunda parte intentará generar la imagen original en alta resolución (similar a un decoder). La salida de la U-Net predice el “ruido residual” que puede ser usado para calcular la representación “sin ruido” de la imagen.

Para prevenir que la U-Net pierda información importante durante el downsampling, se agregan conexiones de “atajo corto” (skip connections) entre los dos caminos: encoder y decoder. Además la U-Net de stable diffusion puede condicionar su salida respecto de los text-embeddings de las capas de cross-attention. Las capas de “Atención Cruzada” se agregan tanto en las partes de codificación y decodificación de la U-Net, entre los bloques ResNet. A eso se le llama Difusión guiada ó Difusión condicionada.

Ejemplo de Arquitectura de una U-Net, se llama así por su forma de “U”.

3-Text-Encoder

El Text-Encoder es el responsable de transformar el mensaje de entrada por ejemplo “Ilustración de Taylor Swift con un pingüino bailando en la ciudad” en un espacio de embeddings que puede ser comprendido por la U-Net. Se utiliza un encoder de tipo Transformers que mapea la secuencia de palabras de entrada en una secuencia latente del embedding de textos.

Stable Diffusion no entrena al Text-Encoder durante la etapa de entrenamiento del modelo si no que utiliza un encoder ya entrenado de CLIP.

Ejemplo de arquitectura de Clip, un modelo text-encoder
Ilustración de Taylor Swift con un Pingüino bailando en la ciudad, creada por el Autor.

Resumen de la arquitectura de Stable Difussion

El modelo al completo, como lo muestra la web oficial de Stable Diffusion es así:

Gráfica de arquitectura de Stable Diffusion. Fuente: web oficial de Stable Diffusion

Al momento de entrenar, la red tiene como entrada una imagen y un texto asociado. La red convertirá la imagen “en ruido” por completo y luego la intentará reconstruir. No olvidemos que es un problema de Aprendizaje supervisado, por lo cual, contamos con el dataset completo, con F(x)=Y desde el inicio.

  1. A la izquierda, en rojo “Pixel Space” tenemos la “x” inicial que entrará en el Encoder de la VAE.
  2. En verde, Espacio Latente, Arriba el Proceso de Difusión, lleva “z” a “zT” agregando ruido a la imagen
  3. En verde, Espacio Latente, Abajo, de derecha a izquierda, entra “zT” a la U-Net e intentará reconvertirla en “z”.
  4. Conditioning, a la derecha, utiliza el modelo CLIP con el texto asociado a la imagen y dirige la salida de la U-Net.
  5. Por último, luego de iterar varias veces la U-Net y obtener una “z buena” (que es la imagen en estado latente), la decodificamos a pixeles utilizando el Decoder de la VAE (en el Pixel Space) y obtendremos una imagen similar a la “x” inicial.

Esta es la arquitectura para entrenar al modelo. Si vas a utilizar la red una vez entrenada, realmente realizaremos el “camino de inferencia“, veamos:

Al hacer la Inferencia, creamos una imagen:

Al momento de hacer la inferencia crearemos una imágen desde ruido! Por eso, el primer paso, es crear una imagen de 512×512 completamente de pixeles aleatorios!

Veamos la gráfica de inferencia que nos propone Stability.ai

Flujo de Inferencia explicada en HuggingFace sobre Stable Diffusion

Entonces, generamos la imagen de ruido y a partir de ella, la pasaremos a la U-Net que junto con el texto de entrada irá condicionando la salida, una y otra vez, intentará “quitar el ruido” para volver a una imagen original inexistente…

¿Te das cuenta? estamos engañando a la red neuronal, para que genere un gráfico que nunca antes existió…

La pobre Red Neuronal, es como si fuera un escultor con un cincel al que le damos un bloque de piedra enorme y le decimos “Quiero a Taylor Swift con un pingüino, hazlo!“.

Entonces, en cada iteración, creará desde el ruido, una imagen

Partimos de una imagen aleatoria completamente con ruido y tras 25 iteraciones la red de Stable Diffusion será capaz de generar una bonita ilustración.

Pero… ¿Qué imágenes puedes crear con Stable Diffusion?

Veamos algunos ejemplos de imágenes creadas por Stable Diffusion para ver si te convenzo de que esto es realmente algo grande… y luego ya puedes decidir si quieres probarlo y hasta instalarlo en tu propio equipo.

Aquí algunas imágenes encontradas en diversos canales:

En Lexica, que por cierto, te recomiendo visitar su web, pues tiene imágenes junto a los prompts para generarlas.

En Instagram

Imágenes encontradas en Reddit

Imágenes encontradas en Twitter

¡Quiero usar Stable Diffusion! ¿Cómo hago?

Puedes pagar por el servicio, ejecutar en la nube ó instalarlo en tu propia computadora.

1-Probarlo gratis, lo primero! (pagar luego…)

Desde la web de los creadores puedes dar tus primeros pasos. Tienes que registrarte y obtienes unos créditos gratuitos, luego que se acaben, tendrás que pagar. Debes entrar en https://beta.dreamstudio.ai/dream

Página de Bienvenida al Dream Studio de Stable Diffusion

Veremos en la parte de abajo, centro la caja de texto donde podemos ingresar el “prompt” con lo que queremos dibujar. Sobre la derecha los parámetros de configuración, que comentaremos luego, pero lo básico es que puedes elegir el tamaño de imagen y cantidad de imágenes a generar.

Ten en cuenta que tienes unos créditos (gratuitos) limitados para utilizar, por lo que debes estar atento a lo que vas consumiendo.

2-Instalar StableDiffusion en tu Computadora

Podemos instalar Stable Difussion en Windows y en Linux con “Instaladores automáticos” siguiendo las instrucciones del repositorio de Automatic1111. Para Windows hay otro instalador aqui .

Puedes instalar en ordenadores Mac (y aprovechar las GPUS de los chips M1 y M2) desde el repositorio de InvokeAI siguiendo las instrucciones para Macintosh.

Si te atreves a instalarlo de manera un poco más “manual”, puedes aventurarte a seguir las instrucciones del Repositorio Oficial de Stable Diffusion. No es difícil, básicamente, si tienes instalado Anaconda en tu ordenador, es clonar el repo y crear el environment de python siguiendo los pasos.

Un paso Clave: descargar el modelo de la red de HuggingFace

Casi todos los modos de instalar que vimos anteriormente, necesitan de un paso manual que es el de obtener y descargar el modelo desde la web de HuggingFace que ocupa 4.27 Gygabytes. Para ello, debes registrarte en HuggingFace e ir a la página del modelo. Deberás aceptar las condiciones de uso, y luego podrás descargar el último modelo, al momento de escribir este artículo es el archivo sd-v1-4.ckpt. Una vez descargado, lo deberás copiar en la carpeta models/ldm/stable-diffusion-1/ y renombrar el archivo como model.ckpt.

Eso es todo! Voilá, crea todas las imágenes que quieras! Tienes el mundo en tus manos!

Tiempos de “Rendering”

Si tienes una tarjeta gráfica con GPU, en mi caso la Nvidia RTX3080 tarda 5 segundos en crear una imágen de 512x512px. Si no tienes tarjeta puedes crear imágenes usando CPU pero tardarán unos 6 minutos (en un ordenador del año 2015 Core i5 y 8GB de memoria). En ordenadores Macbook con chip M2 tarda aproximadamente 1 minuto por imagen.

3-Usar StableDiffusion gratis y con GPU desde la nube de Google Colab

Otra opción para utilizar este genial modelo de forma gratuita es utilizar las notebooks de Google Colab y activar la opción de GPU. Existen varias notebooks compartidas que puedes utilizar como template con la instalación, aquí te recomiendo esta notebook y un hilo en Twitter en español, que te ayuda a seguir los pasos.

¿Cómo Funcionan los Transformers?

Entendiendo los parámetros de entrada de Stable Diffusion

Tanto en la versión web, la de instaladores, manual ó en la nube; contaremos con los mismos parámetros para configurar la red neuronal. Estos son:

  • Alto y Ancho de imagen: deben ser múltiplos de 64, tamaño mínimo de 256 y máximo de 1024px. Sin embargo la recomendación es utilizar 512×512 pues es el tamaño con el que se entrenó la red.
  • Steps: es la cantidad de iteraciones que realizará la U-Net durante la inferencia. Cuanto más iteramos, mayor “ruido” quitaremos de la imagen, es decir, quedará mejor definida. Pero también tardará más tiempo. Teniendo en cuenta el sampler que utilicemos, un valor de entre 25 y 50 estará bien.
  • CFG Scale: este es un valor curioso, pues determina el “grado de libertad” que damos a la propia red para ser creativa. El valor por defecto es 7.5. Si disminuimos el valor, se centrará más en nuestro Prompt. Si aumentamos el valor (más de 10) empezará a improvisar y a hacer dibujos más delirantes y más a su antojo.
  • Número de Imágenes: la cantidad de diversas imágenes que se crearán durante la inferencia. Cuantas más creamos más memoria RAM necesitaremos, tener en cuenta.
  • Sampler: será la función con la que se creará el “denoising” en la U-Net y tiene implicancias en la imagen que se generará. El Sampler más avanzado (de momento) es el DPM2 y necesita más steps para lograr buenos resultados, llevando más tiempo. Curiosamente, el sampler llamado Euler Ancestral es el más básico y logra muy buenas imágenes en unas 20 iteraciones (menor tiempo).
  • Seed ó Semilla: La semilla está relacionada con la imagen con ruido que generamos inicialmente desde donde la red empezará a dibujar. Con una misma semilla podremos replicar una imagen todas la veces que queramos para un mismo prompt. Si no asignamos un valor de semilla, se generará aleatoriamente, obteniendo siempre imágenes distintas para el mismo prompt.

El Prompt Engineering

Se le llama Prompt Engineering al arte de introducir textos que generen buenas imágenes. Lo cierto es que no es tan fácil como parece la creación de imágenes, es decir, la red siempre creará imágenes, pero para que destaquen realmente, hay que agregar las keywords adecuadas. Por suerte ya hay personas que descubrieron muchos de esos tweaks

Los truquillos en el Prompt

Varios exploradores recomiendan seguir una fórmula de tipo:

Tipo imagen – objeto – lugar – tiempo – estilo ó autor

Por ejemplo:

Pintura de un gato con gafas en un teatro, 1960, por Velazquez

Y esto mismo… pero en inglés, obtenemos:

Oil paint of a cat wearing glasses in a theatre, 1960, by Velazquez

Hay algunas palabras que se agregan al final, que son muy útiles, poner “trending on ArtStation”, “highly detailed”, “unreal engine 5”.

Aqui te dejo un enlace a un artículo maravilloso que muestra con ejemplo de muchas de las combinaciones.

imágenes generadas con Imágenes: “img2Img”

Además del txt2Img (que a partir de un texto, generemos una imagen), tenemos otra opción llamada img2img.

Con esta opción ingresamos una imagen creada por nosotros con el “paintbrush” u otra herramienta similar y la utilizaremos como imagen de inicio para generar una nueva imagen. Esto tiene mucha lógica si lo piensas, en vez de empezar con una imagen llena de ruido, le damos unas “guías” a la red neuronal para que pueda crear la imagen. Y los resultados son increíbles!

Imagen de partida para img2img
Imagen obtenida con Img2Img

Por si fuera poco, Inpainting y Outpainting

El Inpainting permite crear una máscara dentro de una imagen y que el modelo dibuje dentro, manteniendo el estilo pictórico y la coherencia.

También existe el llamado OutPainting, que nos permite “extender” una imagen, logrando obras increíbles, será mejor que lo veas!

Resumen y Conclusiones

A estas alturas, espero que estes tan emocionado como yo con esta nueva tecnología y esto es sólo el comienzo! Los modelos de Machine Learning de texto-a-imagen acaban de aterrizar y se perfeccionarán; uno de los puntos fuertes y gran acierto de Stable Diffusion es que al lanzarse a todo el público, logró captar a una gran comunidad de desarrolladores, artistas y curiosos que colaboran y que potencian sus capacidades aún más! Al momento de escribir el artículo, han pasado menos de 2 meses y aparecieron muchísimos proyectos relacionados. Además se comenta que está por aparecer la nueva versión del modelo de pesos entrenado 1.5 dentro de poco. Algunos usuarios hasta crearon videos mediante Stable Diffusion y otros empiezan a mezclar la red con las 3 dimensiones para crear objetos.

En próximos artículos veremos en mayor profundidad y en código Python el uso de redes VAE, U-Net y Transformers.

Hasta pronto!

Material Adicional:

Aquí comparto dos videos muy buenos sobre Arte con IA y otro sobre Stable Diffusion

Otros artículos relacionados de interés:

Suscripción al Blog

Recibe los próximos artículos sobre Machine Learning, estrategias, teoría y código Python en tu casilla de correo!

NOTA: algunos usuarios reportaron que el email de confirmación y/o posteriores a la suscripción entraron en su carpeta de SPAM. Te sugiero que revises y recomiendo que agregues nuestro remitente info @ aprendemachinelearning.com a tus contactos para evitar problemas. Gracias!

El libro del Blog

Si te gustan los contenidos del blog y quieres darme una mano, puedes comprar el libro en papel, ó en digital.

The post Crea imágenes increíbles con Inteligencia Artificial en tu ordenador first appeared on Aprende Machine Learning.

]]>
https://aprendemachinelearning.com/crea-imagenes-stable-diffusion-con-inteligencia-artificial-en-tu-ordenador/feed/ 0 7681
Sets de Entrenamiento, Test y Validación https://aprendemachinelearning.com/sets-de-entrenamiento-test-validacion-cruzada/ https://aprendemachinelearning.com/sets-de-entrenamiento-test-validacion-cruzada/#comments Tue, 03 Mar 2020 08:00:00 +0000 https://www.aprendemachinelearning.com/?p=7152 Vamos a comentar las diferencias entre los conjuntos de Entrenamiento, Validación y Test utilizados en Machine Learning ya que suele haber bastante confusión en para qué es cada uno y cómo utilizarlos adecuadamente. Intentaré hacerlo mediante un ejemplo práctico por eso de ser didácticos 🙂 Además veremos que tenemos distintas técnicas de hacer la validación […]

The post Sets de Entrenamiento, Test y Validación first appeared on Aprende Machine Learning.

]]>
Vamos a comentar las diferencias entre los conjuntos de Entrenamiento, Validación y Test utilizados en Machine Learning ya que suele haber bastante confusión en para qué es cada uno y cómo utilizarlos adecuadamente.

Intentaré hacerlo mediante un ejemplo práctico por eso de ser didácticos 🙂

Además veremos que tenemos distintas técnicas de hacer la validación del modelo y aplicarlas con Scikit Learn en Python.

Un nuevo Mundo

Al principio de los tiempos, sólo tenemos un conjunto Pangea que contiene todo nuestro dato disponible. Digamos que tenemos un archivo csv con 10.000 registros.

Para entrenar nuestro modelo de Machine Learning y poder saber si está funcionando bien, alguien dijo: Separemos el conjunto de datos inicial en 2: conjunto de entrenamiento (train) y conjunto de Pruebas (test). Por lo general se divide haciendo “80-20”. Y se toman muestras aleatorias -no en secuencia, si no, mezclado.

Para hacer el ejemplo sencillo, supongamos que queremos hacer clasificación usando un algoritmo supervisado, con lo cual tendremos:

  • X_train con 8.000 registros para entrenar
  • y_train con las “etiquetas” de los resultados esperados de X_train
  • X_test con 2.000 registros para test
  • y_test con las “etiquetas” de los resultados de X_test

Hágase el conjunto de Test

Lo interesante y a destacar de esto es que una vez los separamos en 8.000 registros para entrenar y 2.000 para probar, usaremos sólo esos 8.000 registros para alimentar al modelo al entrenarlo haciendo:

modelo.fit(X_train, y_train)

Luego de entrenar nuestro modelo y habiendo decidido como métrica de negocio el Accuracy (el % de aciertos) obtenemos un 75% sobre el set de entrenamiento (y asumimos que ese porcentaje nos sirve para nuestro objetivo de negocio).

Los 2.000 registros que separamos en X_test aún nunca han pasado por el modelo de ML. ¿Se entiende esto? porque eso es muy importante!!! Cuando usemos el set de test, haremos:

modelo.predict(X_test)

Como verás, no estamos usando fit()!!! sólo pasaremos los datos sin la columna de “y_test” que contiene las etiquetas. Además remarco que estamos haciendo predicción; me refiero a que el modelo NO se está entrenando ni <<incorporando conocimiento>>. El modelo se limita a “ver la entrada y escupir una salida”.

Cuando hacemos el predict() sobre el conjunto de test y obtenemos las predicciones, las podemos comprobar y contrastar con los valores reales almacenados en y_test y hallar así la métrica que usamos. Los resultados que nos puede dar serán:

  1. Si el accuracy en Test es <<cercano>> al de Entrenamiento (dijimos 75%) por ejemplo en este caso si estuviera entre 65 ú 85% quiere decir que nuestro modelo entrenado está generalizando bien y lo podemos dar por bueno (siempre y cuando estemos conformes con las métricas obtenidas).
  2. Si el Accuracy en Test es muy distinto al de Entrenamiento tanto por encima como por debajo, nos da un 99% ó un 25% (lejano al 75%) entonces es un indicador de que nuestro modelo no ha entrenado bien y no nos sirve. De hecho este podría ser un indicador de Overfitting.

Para evaluar mejor el segundo caso, es donde aparece el “conjunto de Validación”.

Al Séptimo día Dios creo el Cross-Validation

Si el conjunto de Train y Test nos está dando métricas muy distintas esto es que el modelo no nos sirve.

Para mejorar el modelo, podemos pensar en Tunear sus parámetros y volver a entrenar y probar, podemos intentar obtener más registros, cambiar el preprocesado de datos, limpieza, balanceo de clases, selección de features, generación de features… De hecho, podemos pensar que seleccionamos un mal modelo, y podemos intentar con distintos modelos: de árbol de decisión, redes neuronales, ensambles

La técnica de Validación Cruzada nos ayudará a medir el comportamiento el/los modelos que creamos y nos ayudará a encontrar un mejor modelo rápidamente.

Aclaremos antes de empezar: hasta ahora contamos con 2 conjuntos: el de Train y Test. El “set de validación” no es realmente un tercer set si no que “vive” dentro del conjunto de Train. Reitero: el set de validación no es un conjunto que apartemos de nuestro archivo csv original. El set de validación se utilizará durante iteraciones que haremos con el conjunto de entrenamiento.

Técnicas de Validación Cruzada

Entonces volvamos a tener las cosas claras: SOLO tenemos conjunto de Train y Test, ok?. El de Test seguirá tratándose como antes: lo apartamos y lo usaremos al final, una vez entrenemos el modelo.

Dentro del conjunto de Train, y siguiendo nuestro ejemplo inicial, tenemos 8.000 registros. La validación más común utilizada y que nos sirve para entender el concepto es “K-folds”, vamos a comentarla:

Cross-Validation: K-fold con 5 splits

Lo que hacemos normalmente al entrenar el modelo es pasarle los 8.000 registros y que haga el fit(). Con K-Folds -en este ejemplo de 5 splits- para entrenar, en vez de pasarle todos los registros directamente al modelo, haremos así:

  • Iterar 5 veces:
    1. Apartaremos 1/5 de muestras, es decir 1600.
    2. Entrenamos al modelo con el restante 4/5 de muestras = 6400.
    3. Mediremos el accuracy obtenido sobre las 1600 que habíamos apartado.
  • Esto quiere decir que hacemos 5 entrenamientos independientes.
  • El Accuracy final será el promedio de las 5 accuracies anteriores.
En amarillo las muestras para entrenar y en verde el conjunto de Validación.

Entonces fijémonos que estamos “ocultando” una quinta parte del conjunto de train durante cada iteración. Esto es similar a lo que explique antes, pero esta vez aplicado al momento de entrenamiento. Al cabo de esas 5 iteraciones, obtenemos 5 accuracies que deberían ser “similares” entre sí, esto sería un indicador de que el modelo está funcionando bien.

Ejemplo K-Folds en Python

Veamos en código python usando la librería de data science scikit-learn como podemos hacer el cross-validation con K-Folds:

from sklearn import datasets, metrics
from sklearn.model_selection import train_test_split
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import KFold
from sklearn.linear_model import LogisticRegression

iris = datasets.load_iris()

X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.2, random_state=0)

kf = KFold(n_splits=5)

clf = LogisticRegression()

clf.fit(X_train, y_train)

score = clf.score(X_train,y_train)

print("Metrica del modelo", score)

scores = cross_val_score(clf, X_train, y_train, cv=kf, scoring="accuracy")

print("Metricas cross_validation", scores)

print("Media de cross_validation", scores.mean())

preds = clf.predict(X_test)

score_pred = metrics.accuracy_score(y_test, preds)

print("Metrica en Test", score_pred)

En el ejemplo vemos los pasos descritos anteriormente:

  • Cargar el dataset
  • Dividir en Train y Test (en 80/20)
  • Creamos un modelo de Regresión Logística (podría ser otro) y lo entrenamos con los datos de Train
  • Hacemos Cross-Validation usando K-folds con 5 splits
  • Comparamos los resultados obtenidos en el modelo inicial, en el cross validation y vemos que son similares.
  • Finalmente hacemos predict sobre el Conjunto de Test y veremos que también obtenemos buen Accuracy

Más técnicas para Validación del modelo

Otras técnicas usadas y que nos provee sklearn para python son:

Stratified K-Fold

Statified K-fold es una variante mejorada de K-fold, que cuando hace los splits (las divisiones) del conjunto de train tiene en cuenta mantener equilibradas las clases. Esto es muy útil, porque imaginen que tenemos que clasificar en “SI/NO” y si una de las iteraciones del K-fold normal tuviera muestras con etiquetas sólo “SI” el modelo no podría aprender a generalizar y aprenderá para cualquier input a responder “SI”. Esto lo soluciona el Stratified K-fold.

Leave P Out

Leave P Out selecciona una cantidad P por ejemplo 100. Entonces se separarán de a 100 muestras contra las cuales validar y se iterará como se explico anteriormente. Si el valor P es pequeño, esto resultará en muchísimas iteraciones de entrenamiento con un alto coste computacional (y seguramente en tiempo). Si el valor P es muy grande, podría contener más muestras que las usadas para entrenamiento, lo cual sería absurdo. Usar esta técnica con algo de sentido común y manteniendo un equilibrio entre los scores y el tiempo de entreno.

ShuffleSplit

ShuffleSplit primero mezcla los datos y nos deja indicar la cantidad de splits (divisiones) es decir las iteraciones independientes que haremos y también indicar el tamaño del set de validación.

Instala tu ambiente de Desarrollo Python siguiendo esta guía paso a paso

Series Temporales: Atención al validar

Para problemas de Series temporales tenemos que prestar especial cuidado con los datos. Pues si pasamos al modelo “dato futuro” antes de tiempo estaríamos haciendo Data Leakage, esto es como si le hiciéramos spoiler al modelo y le contaremos el final de la película antes de que la vea. Esto causaría overfitting.

Para empezar al hacer el split inicial de datos estos deberán estar ordenados por fecha y no podemos mezclarlos.

Para ayudarnos con el cross-validation sklearn nos provee de TimeSeriesSplit.

TimeSeriesSplit

TimeSeriesSplit es una variante adaptada de K-folds que evita “la fuga” de datos. Para hacerlo va iterando los “folds” de a uno (usando una ventana de tiempo que se desplaza) y usando el “fold más reciente” cómo el set de validación. Se puede entender mejor viendo una animación:

En Amarillo las muestras para entrenar y en verde el conjunto de Validación.

Practicar con un ejercicio en Python de Series Temporales!

Pero entonces? Cuando uso Cross-Validation?

Es una buena práctica usar cross-validation en nuestros proyectos. De hecho usarlo nos ayudará a elegir el modelo correcto y nos da mayor seguridad y respaldo ante nuestra decisión.

PERO… (siempre hay un pero)

En casos en los que hacer 1 sólo entrenamiento “normal” tome muchísimo tiempo y recursos, podría ser nuestra perdición. Imaginen que hacer un k-folds de 10 implica hacer 10 entrenos -aunque un poco más pequeños-, pero que consumirían mucho tiempo y dinero.

Entonces en la medida de lo posible siempre usar validación cruzada. Y -vuelvo a reforzar el concepto- luego se probará el modelo contra el conjunto de Pruebas (test).

Para hacer tuneo de Hiper-parámetros como RandomSearch, GridSearch ó Tuneo Bayesiano es muy útil hacer Cross-Validation.

¿Si ya estoy “conforme” y quiero llevar el modelo a un entorno de Producción?

Supongamos que el entrenamiento haciendo Cross Validation y el predict() en Test nos están dando buenos accuracy (y similares) y estamos conformes con nuestro modelo. PUES si lo queremos usar en un entorno REAL y productivo, ANTES de publicarlo es recomendado que agreguemos el conjunto de test al modelo!!!, pues así estaremos aprovechando el 100% de nuestros datos. Espero que esto último también se entienda porque es super importante: lo que estoy diciendo es que si al final de todas nuestras iteraciones, pre procesado de dato, mejoras de modelo, ajuste de hiper-parámetros y comparando con el conjunto de test, estamos seguros que el modelo funciona correctamente, es entonces ahora, que usaremos las 10.000 muestras para entrenar al modelo, y ese modelo final, será el que publicamos en producción.

Es una última iteración que debería mejorar el modelo final aunque este no lo podemos contrastar contra nada… excepto con su comportamiento en el entorno real.

Si esta última iteración te causara dudas, no la hagas, excepto que tu problema sea de tipo Serie Temporal. En ese caso sí que es muy importante hacerlo o quedaremos con un modelo que no “es el más actual”.

Resumen, Conclusiones y por favor Que quede claro!

Lo más importante que quisiera que quede claro es que entonces tenemos 2 conjuntos: uno de Train y otro de Test. El “conjunto de validación” no existe como tal, si no, que “vive temporalmente” al momento de entrenar y nos ayuda a obtener al mejor modelo de entre los distintos que probaremos para conseguir nuestro objetivo. Esa técnica es lo que se llama Validación Cruzada ó en inglés cross-validation.

NOTA: en los ejemplos de la documentación de sklearn podremos ver que usan las palabras train y test. Pero conceptualmente se está refiriendo al conjunto de validación y no al de Test que usaremos al final. Esto es en parte el causante de tanta confusión con este tema.

Tener en cuenta el tamaño de split 80/20 es el usual pero puede ser distinto, y esta proporción puede cambiar sustancialmente las métricas obtenidas del modelo entrenado! Ojo con eso. El tamaño ideal dependerá del dominio de nuestro problema, deberemos pensar en una cantidad de muestras para test que nos aseguren que estamos el modelo creado está funcionando correctamente. Teniendo 10.000 registros puede que con testear 1000 filas ya estemos conformes ó que necesitemos 4000 para estar mega-seguros. Por supuesto debemos recordar que las filas que estemos “quitando” para testear, no las estamos usando al entrenar.

Otro factor: al hacer el experimento y tomar las muestras mezcladas, mantener la “semilla” ó no podremos reproducir el mismo experimento para comparar y ver si mejora o no. Este suele ser un parámetro llamado “random_state” y está bien que lo usemos para fijarlo.

Recomendaciones finales:

  • En principio separar Train y Test en una proporción de 80/20
  • Hacer Cross Validation siempre que podamos:
    • No usar K-folds. Usar Stratified-K-folds en su lugar.
    • La cantidad de “folds” dependerá del tamaño del dataset que tengamos, pero la cantidad usual es 5 (pues es similar al 80-20 que hacemos con train/test).
    • Para problemas de tipo time-series usar TimeSeriesSplit
  • Si el Accuracy (ó métrica que usamos) es similar en los conjuntos de Train (donde hicimos Cross Validation) y Test, podemos dar por bueno al modelo.

Recibe los artículos del Blog

Recibe los próximos artículos sobre Machine Learning, estrategias, teoría y código Python en tu casilla de correo!

NOTA: algunos usuarios reportaron que el email de confirmación y/o posteriores a la suscripción entraron en su carpeta de SPAM. Te sugiero que revises y recomiendo que agregues nuestro remitente a tus contactos para evitar problemas. Gracias!

Recursos Adicionales

Otros artículos interesantes en inglés:

El libro del Blog

Si te gustan los contenidos del blog y quieres darme una mano, puedes comprar el libro en papel, ó en digital.

The post Sets de Entrenamiento, Test y Validación first appeared on Aprende Machine Learning.

]]>
https://aprendemachinelearning.com/sets-de-entrenamiento-test-validacion-cruzada/feed/ 7 7152
Random Forest, el poder del Ensamble https://aprendemachinelearning.com/random-forest-el-poder-del-ensamble/ https://aprendemachinelearning.com/random-forest-el-poder-del-ensamble/#respond Mon, 17 Jun 2019 08:00:00 +0000 https://www.aprendemachinelearning.com/?p=6001 Si ya leíste el algoritmo de árbol de Decisión con Aprendizaje Automático, tu próximo paso es el de estudiar Random Forest. Comprende qué és y cómo funciona con un ejemplo práctico en Python. Podrás descargar el código de ejemplo en una Jupyter Notebook -como siempre-. Random Forest es un tipo de Ensamble en Machine Learning […]

The post Random Forest, el poder del Ensamble first appeared on Aprende Machine Learning.

]]>
Si ya leíste el algoritmo de árbol de Decisión con Aprendizaje Automático, tu próximo paso es el de estudiar Random Forest. Comprende qué és y cómo funciona con un ejemplo práctico en Python. Podrás descargar el código de ejemplo en una Jupyter Notebook -como siempre-.

Random Forest es un tipo de Ensamble en Machine Learning en donde combinaremos diversos árboles -ya veremos cómo y con qué características- y la salida de cada uno se contará como “un voto” y la opción más votada será la respuesta del <<Bosque Aleatorio>>.

Random Forest, al igual que el árbol e decisión, es un modelo de aprendizaje supervisado para clasificación (aunque también puede usarse para problemas de regresión).

¿Cómo surge Random Forest?

Uno de los problemas que aparecía con la creación de un árbol de decisión es que si le damos la profundidad suficiente, el árbol tiende a “memorizar” las soluciones en vez de generalizar el aprendizaje. Es decir, a padecer de overfitting. La solución para evitar esto es la de crear muchos árboles y que trabajen en conjunto. Veamos cómo.

Cómo funciona Random Forest?

Random Forest funciona así:

  • Seleccionamos k features (columnas) de las m totales (siendo k menor a m) y creamos un árbol de decisión con esas k características.
  • Creamos n árboles variando siempre la cantidad de k features y también podríamos variar la cantidad de muestras que pasamos a esos árboles (esto es conocido como “bootstrap sample”)
  • Tomamos cada uno de los n árboles y le pedimos que hagan una misma clasificación. Guardamos el resultado de cada árbol obteniendo n salidas.
  • Calculamos los votos obtenidos para cada “clase” seleccionada y consideraremos a la más votada como la clasificación final de nuestro “bosque”.

¿Por qué es aleatorio?

Contamos con una <<doble aleatoriedad>>: tanto en la selección del valor k de características para cada árbol como en la cantidad de muestras que usaremos para entrenar cada árbol creado.

Es curioso que para este algoritmo la aleatoriedad sea tan importante y de hecho es lo que lo “hace bueno”, pues le brinda flexibilidad suficiente como para poder obtener gran variedad de árboles y de muestras que en su conjunto aparentemente caótico, producen una salida concreta. Darwin estaría orgulloso 😉

Ventajas y Desventajas del uso de Random Forest

Vemos algunas de sus ventajas son:

  • funciona bien -aún- sin ajuste de hiperparámetros
  • funciona bien para problemas de clasificación y también de regresión.
  • al utilizar múltiples árboles se reduce considerablemente el riesgo de overfiting
  • se mantiene estable con nuevas muestras puesto que al utilizar cientos de árboles sigue prevaleciendo el promedio de sus votaciones.

Y sus desjeventajas:

  • en algunos datos de entrada “particulares” random forest también puede caer en overfitting
  • es mucho más “costo” de crear y ejecutar que “un sólo árbol” de decisión.
  • Puede requerir muchísimo tiempo de entrenamiento
  • OJO! Random Forest no funciona bien con datasets pequeños.
  • Es muy difícil poder interpretar los ¿cientos? de árboles creados en el bosque, si quisiéramos comprender y explicar a un cliente su comportamiento.

Vamos al Código Python

Continuaremos con el ejercicio propuesto en el artículo “desbalanceo de datos” en donde utilizamos el dataset de Kaggle con información de fraude en tarjetas de crédito. Cuenta con 284807 filas y 31 columnas de características. Nuestra salida será 0 si es un cliente “normal” o 1 si hizo uso fraudulento.

¿Llegas a ver la mínima linea roja que representa los casos de Fraude? son apenas 492 frente a más de 250.000 casos de uso normal.

Retomaremos el mejor caso que obtuvimos en el ejercicio anterior utilizando Regresión Logística y logrando un 98% de aciertos, pero recuerda también las métricas de F1, precisión y recall que eran las que realmente nos ayudaban a validar el modelo.

Requerimientos para hacer el ejercicio Random Forest

Necesitaremos tener instalado Python 3.6 en el sistema y como lo haremos en una Notebook Jupyter, recomiendo tener instalada la suite de Anaconda que simplificará todo.

¿Cómo instalar el ambiente de desarrollo Python con Anaconda?

Pues vamos con nuestro Bosque!

Creamos el modelo y lo entrenamos

Utilizaremos el modelo RandomForrestClassifier de SkLearn.

from sklearn.ensemble import RandomForestClassifier

# Crear el modelo con 100 arboles
model = RandomForestClassifier(n_estimators=100, 
                               bootstrap = True, verbose=2,
                               max_features = 'sqrt')
# a entrenar!
model.fit(X_train, y_train)

Luego de unos minutos obtendremos el modelo entrenado (en mi caso 1 minuto 30 segundos)

Los Hiperparámetros más importantes

Al momento de ajustar el modelo, debemos tener en cuenta los siguientes hiperparámetros. Estos nos ayudarán a que el bosque de mejores resultados para cada ejercicio. Recuerda que esto no se trata de “copiar y pegar”!

  • n_estimators: será la cantidad de árboles que generaremos.
  • max_features: la manera de seleccionar la cantidad máxima de features para cada árbol.
  • min_sample_leaf: número mínimo de elementos en las hojas para permitir un nuevo split (división) del nodo.
  • oob_score: es un método que emula el cross-validation en árboles y permite mejorar la precisión y evitar overfitting.
  • boostrap: para utilizar diversos tamaños de muestras para entrenar. Si se pone en falso, utilizará siempre el dataset completo.
  • n_jobs: si tienes multiples cores en tu CPU, puedes indicar cuantos puede usar el modelo al entrenar para acelerar el entrenamiento.

Evaluamos resultados

Veamos la matriz de confusión y las métricas sobre el conjunto de test!!! (no confundir con el de training!!!)

Vemos muy buenos resultados, clasificando con error apenas 11 + 28 muestras.

Aquí podemos destacar que para la clase “minoritaria”, es decir la que detecta los casos de fraude tenemos un buen valor de recall (de 0.80) lo cual es un buen indicador! y el F1-score macro avg es de 0.93. Logramos construir un modelo de Bosque aleatorio que a pesar de tener un conjunto de datos de entrada muy desigual, logra buenos resultados.

Comparamos con el Baseline

Si comparamos estos resultados con los del algoritmo de Regresión Logística, vemos que el Random Forest nos dio mejores clasificaciones, menos falsos positivos y mejores métricas en general.

Conclusiones

Avanzando en nuestro aprendizaje sobre diversos modelos que podemos aplicar a las problemáticas que nos enfrentamos, hoy sumamos a nuestro kit de herramientas el Random Forest, vemos que es un modelo sencillo, bastante rápido y si bien perdemos la interpretabilidad maravillosa que nos brindaba 1 sólo árbol de decisión, es el precio a pagar para evitar el overfitting y para ganar un clasificador más robusto.

Los algoritmos Tree-Based -en inglés- son muchos, todos parten de la idea principal de árbol de decisión y la mejoran con diferentes tipos de ensambles y técnicas. Tenemos que destacar a 2 modelos que según el caso logran superar a las mismísimas redes neuronales! son XGboost y LightGBM. Si te parecen interesantes puede que en el futuro escribamos sobre ellos.

Suscribete al blog

Recibe los próximos artículos sobre Machine Learning, estrategias, teoría y código Python en tu casilla de correo!

NOTA: algunos usuarios reportaron que el email de confirmación y/o posteriores a la suscripción entraron en su carpeta de SPAM. Te sugiero que revises y que agregues nuestro remitente a tus contactos para evitar problemas. Gracias!

Recursos y Adicionales

Puedes descargar la notebook para este ejercicio desde mi cuenta de GitHub:

Otros artículos sobre Random Forest en inglés:

The post Random Forest, el poder del Ensamble first appeared on Aprende Machine Learning.

]]>
https://aprendemachinelearning.com/random-forest-el-poder-del-ensamble/feed/ 0 6001
Clasificación con datos desbalanceados https://aprendemachinelearning.com/clasificacion-con-datos-desbalanceados/ https://aprendemachinelearning.com/clasificacion-con-datos-desbalanceados/#comments Thu, 16 May 2019 08:00:00 +0000 https://www.aprendemachinelearning.com/?p=6881 Contrarrestar problemas con clases desbalanceadas Estrategias para resolver desequilibrio de datos en Python con la librería imbalanced-learn. Tabla de contenidos: ¿Qué son las clases desequilibradas en un dataset? Métricas y Confusión Matrix Ejercicio con Python Estrategias Modelo sin modificar Penalización para compensar / Métricas Resampling y Muestras sintéticas subsampling oversamplig combinación Balanced Ensemble Empecemos! ¿Qué […]

The post Clasificación con datos desbalanceados first appeared on Aprende Machine Learning.

]]>
Contrarrestar problemas con clases desbalanceadas

Estrategias para resolver desequilibrio de datos en Python con la librería imbalanced-learn.

Tabla de contenidos:

  1. ¿Qué son las clases desequilibradas en un dataset?
  2. Métricas y Confusión Matrix
  3. Ejercicio con Python
  4. Estrategias
  5. Modelo sin modificar
  6. Penalización para compensar / Métricas
  7. Resampling y Muestras sintéticas
    1. subsampling
    2. oversamplig
    3. combinación
  8. Balanced Ensemble

Empecemos!

¿Qué son los problemas de clasificación de Clases desequilibradas? (imbalanced data)

En los problemas de clasificación en donde tenemos que etiquetar por ejemplo entre “spam” o “not spam” ó entre múltiples categorías (coche, barco, avión) solemos encontrar que en nuestro conjunto de datos de entrenamiento contamos con que alguna de las clases de muestra es una clase “minoritaria” es decir, de la cual tenemos muy poquitas muestras. Esto provoca un desbalanceo en los datos que utilizaremos para el entrenamiento de nuestra máquina.

Un caso evidente es en el área de Salud en donde solemos encontrar conjuntos de datos con miles de registros con pacientes “negativos” y unos pocos casos positivos es decir, que padecen la enfermedad que queremos clasificar.

Otros ejemplos suelen ser los de Detección de fraude donde tenemos muchas muestras de clientes “honestos” y pocos casos etiquetados como fraudulentos. Ó en un funnel de marketing, en donde por lo general tenemos un 2% de los datos de clientes que “compran” ó ejecutan algún tipo de acción (CTA) que queremos predecir.

¿Cómo nos afectan los datos desbalanceados?

Por lo general afecta a los algoritmos en su proceso de generalización de la información y perjudicando a las clases minoritarias. Esto suena bastante razonable: si a una red neuronal le damos 990 de fotos de gatitos y sólo 10 de perros, no podemos pretender que logre diferenciar una clase de otra. Lo más probable que la red se limite a responder siempre “tu foto es un gato” puesto que así tuvo un acierto del 99% en su fase de entrenamiento.

Métricas y Confusion Matrix

Como decía, si medimos la efectividad de nuestro modelo por la cantidad de aciertos que tuvo, sólo teniendo en cuenta a la clase mayoritaria podemos estar teniendo una falsa sensación de que el modelo funciona bien.

Para poder entender esto un poco mejor, utilizaremos la llamada “Confusión matrix” que nos ayudará a comprender las salidas de nuestra máquina:

Y de aqui salen nuevas métricas: precisión y recall

Veamos la Confusion matrix con el ejemplo de las predicciones de perro y gato.

Breve explicación de estás métricas:

La Accuracy del modelo es básicamente el numero total de predicciones correctas dividido por el número total de predicciones. En este caso da 99% cuando no hemos logrado identificar ningún perro.

La Precisión de una clase define cuan confiable es un modelo en responder si un punto pertenece a esa clase. Para la clase gato será del 99% sin embargo para la de perro será 0%.

El Recall de una clase expresa cuan bien puede el modelo detectar a esa clase. Para gatos será de 1 y para perros 0.

El F1 Score de una clase es dada por la media harmonía de precisión y recall (2 x precision x recall / (precision+recall)) digamos que combina precisión y recall en una sola métrica. En nuestro caso daría cero para perros!.

Tenemos cuatro casos posibles para cada clase:

  • Alta precision y alto recall: el modelo maneja perfectamente esa clase
  • Alta precision y bajo recall: el modelo no detecta la clase muy bien, pero cuando lo hace es altamente confiable.
  • Baja precisión y alto recall: La clase detecta bien la clase pero también incluye muestras de otras clases.
  • Baja precisión y bajo recall: El modelo no logra clasificar la clase correctamente.

Cuando tenemos un dataset con desequilibrio, suele ocurrir que obtenemos un alto valor de precisión en la clase Mayoritaria y un bajo recall en la clase Minoritaria

MUY importante que conozcas los conceptos de Train, test y validación cruzada.

Vamos al Ejercicio con Python!

Usaremos el set de datos Credit Card Fraut Detection de la web de Kaggle. Son 66 MB que al descomprimir ocuparán 150MB. Usaremos el archivo creditcard.csv. Este dataset consta de 285.000 filas con 31 columnas (features). Como la información es privada, no sabemos realmente que significan los features y están nombradas como V1, V2, V3, etc. excepto por las columnas Time y Amount (el importe de la transacción). Y nuestras clases son 0 y 1 correspondiendo con “transacción Normal” ó “Hubo Fraude”. Como podrán imaginar, el set de datos está muy desequilibrado y tendremos muy pocas muestras etiquetadas como fraude.

La notebook que acompaña este artículo puedes verla aquí en Github y en los recursos, al final del artículo.

También debo decir que no nos centraremos tanto en la elección del modelo ni en su configuración y tuneo si no que nos centraremos en aplicar las diversas estrategias para mejorar los resultados a pesar del desequilibrio de clases.

Requerimientos Técnicos

Necesitaremos tener Python 3.6 en el sistema y como lo haremos en una Notebook Jupyter, recomiendo tener instalada Anaconda.

¿Cómo instalar mi ambiente de desarrollo Python – Anaconda?

Instala la librería de Imbalanced Learn desde linea de comando con: (toda la documentación en la web oficial imblearn)

pip install -U imbalanced-learn

Veamos el dataset

Análisis exploratorio, para comprobar el desequilibrio entre las clases

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

from sklearn.metrics import confusion_matrix
from sklearn.metrics import classification_report
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.decomposition import PCA
from sklearn.tree import DecisionTreeClassifier

from pylab import rcParams

from imblearn.under_sampling import NearMiss
from imblearn.over_sampling import RandomOverSampler
from imblearn.combine import SMOTETomek
from imblearn.ensemble import BalancedBaggingClassifier

from collections import Counter

Luego de importar las librerías que usaremos, cargamos con pandas el dataframe y vemos las primeras filas:

df = pd.read_csv("creditcard.csv") # read in data downloaded to the local directory
df.head(n=5)

Veamos de cuantas filas tenemos y cuantas hay de cada clase:

print(df.shape)
print(pd.value_counts(df['Class'], sort = True))

(284807, 31)

0 284315
1 492
Name: Class, dtype: int64

Vemos que son 284.807 filas y solamente 492 son la clase minoritaria con los casos de fraude. Representan el 0,17% de las muestras.

count_classes = pd.value_counts(df['Class'], sort = True)
count_classes.plot(kind = 'bar', rot=0)
plt.xticks(range(2), LABELS)
plt.title("Frequency by observation number")
plt.xlabel("Class")
plt.ylabel("Number of Observations");
¿Llegas a ver la mínima linea roja que representa los casos de Fraude? son muy pocas muestras!

Estrategias para el manejo de Datos Desbalanceados:

Tenemos diversas estrategias para tratar de mejorar la situación. Las comentaremos brevemente y pasaremos a la acción (al código!) a continuación.

  1. Ajuste de Parámetros del modelo: Consiste en ajustar parametros ó metricas del propio algoritmo para intentar equilibrar a la clase minoritaria penalizando a la clase mayoritaria durante el entrenamiento. Ejemplos on ajuste de peso en árboles, también en logisticregression tenemos el parámetro class_weight= “balanced” que utilizaremos en este ejemplo. No todos los algoritmos tienen estas posibilidades. En redes neuronales por ejemplo podríamos ajustar la métrica de Loss para que penalice a las clases mayoritarias.
  2. Modificar el Dataset: podemos eliminar muestras de la clase mayoritaria para reducirlo e intentar equilibrar la situación. Tiene como “peligroso” que podemos prescindir de muestras importantes, que brindan información y por lo tanto empeorar el modelo. Entonces para seleccionar qué muestras eliminar, deberíamos seguir algún criterio. También podríamos agregar nuevas filas con los mismos valores de las clases minoritarias, por ejemplo cuadriplicar nuestras 492 filas. Pero esto no sirve demasiado y podemos llevar al modelo a caer en overfitting.
  3. Muestras artificiales: podemos intentar crear muestras sintéticas (no idénticas) utilizando diversos algoritmos que intentan seguir la tendencia del grupo minoritario. Según el método, podemos mejorar los resultados. Lo peligroso de crear muestras sintéticas es que podemos alterar la distribución “natural” de esa clase y confundir al modelo en su clasificación.
  4. Balanced Ensemble Methods: Utiliza las ventajas de hacer ensamble de métodos, es decir, entrenar diversos modelos y entre todos obtener el resultado final (por ejemplo “votando”) pero se asegura de tomar muestras de entrenamiento equilibradas.

Apliquemos estas técnicas de a una a nuestro código y veamos los resultados.

PERO… antes de empezar, ejecutaremos el modelo de Regresión Logística “desequilibrado”, para tener un “baseline”, es decir unas métricas contra las cuales podremos comparar y ver si mejoramos.

Probando el Modelo “a secas” -sin estrategias-

#definimos nuestras etiquetas y features
y = df['Class']
X = df.drop('Class', axis=1)
#dividimos en sets de entrenamiento y test
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.7)

#creamos una función que crea el modelo que usaremos cada vez
def run_model(X_train, X_test, y_train, y_test):
    clf_base = LogisticRegression(C=1.0,penalty='l2',random_state=1,solver="newton-cg")
    clf_base.fit(X_train, y_train)
    return clf_base

#ejecutamos el modelo "tal cual"
model = run_model(X_train, X_test, y_train, y_test)

#definimos funciona para mostrar los resultados
def mostrar_resultados(y_test, pred_y):
    conf_matrix = confusion_matrix(y_test, pred_y)
    plt.figure(figsize=(12, 12))
    sns.heatmap(conf_matrix, xticklabels=LABELS, yticklabels=LABELS, annot=True, fmt="d");
    plt.title("Confusion matrix")
    plt.ylabel('True class')
    plt.xlabel('Predicted class')
    plt.show()
    print (classification_report(y_test, pred_y))

pred_y = model.predict(X_test)
mostrar_resultados(y_test, pred_y)

Aqui vemos la confusion matrix y en la clase 2 (es lo que nos interesa detectar) vemos 51 fallos y 97 aciertos dando un recall de 0.66 y es el valor que queremos mejorar. También es interesante notar que en la columna de f1-score obtenemos muy buenos resultados PERO que realmente no nos deben engañar… pues están reflejando una realidad parcial. Lo cierto es que nuestro modelo no es capaz de detectar correctamente los casos de Fraude.

Estrategia: Penalización para compensar

Utilizaremos un parámetro adicional en el modelo de Regresión logística en donde indicamos weight = “balanced” y con esto el algoritmo se encargará de equilibrar a la clase minoritaria durante el entrenamiento. Veamos:

def run_model_balanced(X_train, X_test, y_train, y_test):
    clf = LogisticRegression(C=1.0,penalty='l2',random_state=1,solver="newton-cg",class_weight="balanced")
    clf.fit(X_train, y_train)
    return clf

model = run_model_balanced(X_train, X_test, y_train, y_test)
pred_y = model.predict(X_test)
mostrar_resultados(y_test, pred_y)

Ahora vemos una NOTABLE MEJORA! en la clase 2 -que indica si hubo fraude-, se han acertado 137 muestras y fallado en 11, dando un recall de 0.93 !! y sólo con agregar un parámetro al modelo 😉 También notemos que en la columna de f1-score parecería que hubieran “empeorado” los resultados… cuando realmente estamos mejorando la detección de casos fraudulentos. Es cierto que aumentan los Falsos Positivos y se han etiquetado 1890 muestras como Fraudulentas cuando no lo eran… pero ustedes piensen… ¿qué prefiere la compañía bancaria? ¿tener que revisar esos casos manualmente ó fallar en detectar los verdaderos casos de fraude?

Sigamos con más métodos:

Estrategia: Subsampling en la clase mayoritaria

Lo que haremos es utilizar un algoritmo para reducir la clase mayoritaria. Lo haremos usando un algoritmo que hace similar al k-nearest neighbor para ir seleccionando cuales eliminar. Fijemonos que reducimos bestialmente de 199.020 muestras de clase cero (la mayoría) y pasan a ser 688. y Con esas muestras entrenamos el modelo.

us = NearMiss(ratio=0.5, n_neighbors=3, version=2, random_state=1)
X_train_res, y_train_res = us.fit_sample(X_train, y_train)

print ("Distribution before resampling {}".format(Counter(y_train)))
print ("Distribution after resampling {}".format(Counter(y_train_res)))

model = run_model(X_train_res, X_test, y_train_res, y_test)
pred_y = model.predict(X_test)
mostrar_resultados(y_test, pred_y)

Distribution before resampling Counter({0: 199020, 1: 344})
Distribution after resampling Counter({0: 688, 1: 344})

También vemos que obtenemos muy buen resultado con recall de 0.93 aunque a costa de que aumentaran los falsos positivos.

Estrategia: Oversampling de la clase minoritaria

En este caso, crearemos muestras nuevas “sintéticas” de la clase minoritaria. Usando RandomOverSampler. Y vemos que pasamos de 344 muestras de fraudes a 99.510.

os =  RandomOverSampler(ratio=0.5)
X_train_res, y_train_res = os.fit_sample(X_train, y_train)

print ("Distribution before resampling {}".format(Counter(y_train)))
print ("Distribution labels after resampling {}".format(Counter(y_train_res)))

model = run_model(X_train_res, X_test, y_train_res, y_test)
pred_y = model.predict(X_test)
mostrar_resultados(y_test, pred_y)
Distribution before resampling Counter({0: 199020, 1: 344})
Distribution after resampling Counter({0: 199020, 1: 99510})

Tenemos un 0.89 de recall para la clase 2 y los Falsos positivos son 838. Nada mal.

Estrategia: Combinamos resampling con Smote-Tomek

Ahora probaremos una técnica muy usada que consiste en aplicar en simultáneo un algoritmo de subsampling y otro de oversampling a la vez al dataset. En este caso usaremos SMOTE para oversampling: busca puntos vecinos cercanos y agrega puntos “en linea recta” entre ellos. Y usaremos Tomek para undersampling que quita los de distinta clase que sean nearest neighbor y deja ver mejor el decisión boundary (la zona limítrofe de nuestras clases).

os_us = SMOTETomek(ratio=0.5)
X_train_res, y_train_res = os_us.fit_sample(X_train, y_train)

print ("Distribution before resampling {}".format(Counter(y_train)))
print ("Distribution after resampling {}".format(Counter(y_train_res)))

model = run_model(X_train_res, X_test, y_train_res, y_test)
pred_y = model.predict(X_test)
mostrar_resultados(y_test, pred_y)
Distribution labels before resampling Counter({0: 199020, 1: 344})
Distribution after resampling Counter({0: 198194, 1: 98684})

En este caso seguimos teniendo bastante buen recall 0.85 de la clase 2 y vemos que los Falsos positivos de la clase 1 son bastante pocos, 325 (de 85295 muestras).

Estrategia: Ensamble de Modelos con Balanceo

Para esta estrategia usaremos un Clasificador de Ensamble que utiliza Bagging y el modelo será un DecisionTree. Veamos como se comporta:

bbc = BalancedBaggingClassifier(base_estimator=DecisionTreeClassifier(),
                                sampling_strategy='auto',
                                replacement=False,
                                random_state=0)

#Train the classifier.
bbc.fit(X_train, y_train)
pred_y = bbc.predict(X_test)
mostrar_resultados(y_test, pred_y)

Tampoco está mal. Vemos siempre mejora con respecto al modelo inicial con un recall de 0.88 para los casos de fraude.

Resultados de las Estrategias

Veamos en una tabla, ordenada de mejor a peor los resultados obtenidos.

Vemos que en nuestro caso las estrategias de Penalización y Subsampling nos dan el mejor resultado, cada una con un recall de 0.93.

Pero quedémonos con esto: Con cualquiera de las técnicas que aplicamos MEJORAMOS el modelo inicial de Regresión logística, que lograba un 0.66 de recall para la clase de Fraude. Y no olvidemos que hay un tremendo desbalance de clases en el dataset!

IMPORTANTE: esto no quiere decir que siempre hay que aplicar Penalización ó NearMiss Subsampling!, dependerá del caso, del desbalanceo y del modelo (en este caso usamos regresión logística, pero podría ser otro!).

Conclusiones

Es muy frecuente encontrarnos con datasets con clases desbalanceadas, de hecho… lo más raro sería encontrar datasets bien equilibrados.

A lo largo de estos 2 años de vida del blog la pregunta más frecuente que he recibido creo que a sido “¿cómo hago cuando tengo pocas muestras de una clase?”. Mi primera respuesta y la de sentido común es “Sal a la calle y consigue más muestras!” pero la realidad es que no siempre es posible conseguir más datos de las clases minoritarias (como por ejemplo en Casos de Salud).

En el artículo de hoy vimos diversas estrategias a seguir para combatir esta problemática: eliminar muestras del set mayoritario, crear muestras sintéticas con algún criterio, ensamble y penalización.

Además revisamos la Matriz de Confusión y comprendimos que las métricas pueden ser engañosas… si miramos a nuestros aciertos únicamente, puede que pensemos que tenemos un buen clasificador, cuando realmente está fallando.

Súmate a los suscriptores del Blog

Recibe los próximos artículos sobre Machine Learning, estrategias, teoría y código Python en tu casilla de correo!

NOTA: muchos usuarios reportaron que el email de confirmación y/o posteriores a la suscripción entraron en su carpeta de SPAM. Te sugiero que revises y recomiendo que agregues nuestro remitente a tus contactos para evitar problemas. Gracias!

Recursos

Artículos en Inglés

The post Clasificación con datos desbalanceados first appeared on Aprende Machine Learning.

]]>
https://aprendemachinelearning.com/clasificacion-con-datos-desbalanceados/feed/ 20 6881
Interpretación de Modelos de Machine Learning https://aprendemachinelearning.com/interpretacion-de-modelos-de-machine-learning/ https://aprendemachinelearning.com/interpretacion-de-modelos-de-machine-learning/#comments Tue, 30 Apr 2019 08:00:00 +0000 https://www.aprendemachinelearning.com/?p=6814 Descifrar las decisiones tomadas por la máquina La interpretación de las decisiones tomadas por nuestros algoritmos de Machine Learning pasa a un plano muy importante: para comprender el modelo y mejorarlo, evitar “biases” (ó descubrirlos), para justificar nuestra confianza en el modelo y hasta legalmente pues es requerido por leyes como la GDPR -para decisiones […]

The post Interpretación de Modelos de Machine Learning first appeared on Aprende Machine Learning.

]]>
Descifrar las decisiones tomadas por la máquina

La interpretación de las decisiones tomadas por nuestros algoritmos de Machine Learning pasa a un plano muy importante: para comprender el modelo y mejorarlo, evitar “biases” (ó descubrirlos), para justificar nuestra confianza en el modelo y hasta legalmente pues es requerido por leyes como la GDPR -para decisiones delicadas como puede ser dar ó no un crédito a una persona-.

Si nuestro algoritmo tuviera que detectar enfermedades y suponiendo que logramos una tasa de aciertos del 90% ¿no te parecería lógico comprender cómo lo ha hecho? ¿es puro azar? ¿está teniendo en cuenta combinaciones de características que nosotros no contemplamos?

Si de pequeño eras curioso y querías sabes cómo funcionaban las cosas: relojes, autos, ó hasta el mismísimo ordenador… serás un poco como yo… y… no siempre nos convence el concepto de “caja negra”.

Abriendo la Caja negra

El concepto de caja negra a veces es muy beneficioso, en sistemas decimos “yo al método le tiro estos parámetros y me devuelve true ó false”. Genial, con eso nos basta. Podemos trabajar en equipos distribuidos, intercambiar interfaces y listo. Podemos confiar en otras librerías ó paquetes “sin saber cómo lo hacen” pero que nos resuelven problemas. Y las encajamos como piezas de un puzzle.

Los algoritmos de Machine Learning, hasta ahora funcionaban muy de ese modo. Es decir, podemos hacer una red neuronal de 10 capas con 80 neuronas cada una, dropout, recurrencia y que nos dé unas buenas clasificaciones. Pero ¿qué pasa por dentro? ¿cómo hizo? ¿es magia?… esas oscuras épocas de incertidumbre deben acabar y deberemos tomar control de porqué se hacen las cosas como se hacen.

¿Que hay dentro de la caja negra?

Explainable Machine Learning

Interpretar el Modelos en Machine Learning es la habilidad de explicar su funcionamiento ó presentarlo de manera comprensible al humano.

¿Por qué es importante interpretar los modelos?

Imaginemos que nuestro algoritmo decidirá a qué empleado le daremos un ascenso, dadas sus características e historia en la empresa. Y luego de entrenar el modelo vemos que “aparentemente da buenos resultados” pero… todas las elecciones para puestos gerenciales son siempre para hombres y ninguna mujer…. mmmm.. sospechoso, ¿no?

Ese modelo “aprendió” que durante los últimos 10 años, los cargos gerenciales de esa empresa siempre fueron para hombres. Si ese algoritmo pasa a producción, estará discriminando a las mujeres e impidiendo su ascenso.

Entonces ¿Cómo hacemos para interpretar el modelo?

Respuesta corta: con otro modelo que ayude a los humanos a interpretar los procesos.

Hay que decir que modelos como “1 árbol de decisión pequeño” ó clasificación lineal, pueden llegar a interpretarse por su gráfica y/o fórmula (repito: si son sencillos). Sin embargo un Random Forest ó las Redes Neuronales son complejas y prácticamente imposibles de comprender <<de un vistazo>>.

Los beneficios de la “interpretabilidad de los modelos “son:

  • Dar confiabilidad en los resultados.
  • Ayudar en el Debugging.
  • Informar a la Ingeniería de Características (Feature Engineer).
  • Detectar necesidad de colectar nuevas muestras.
  • Ayudar a una persona en la toma de decisiones.
  • Mayor seguridad/robustez en el modelo obtenido.

Técnicas de Interpretación de modelos

Del análisis de los modelos podemos obtener:

  • Características más importantes (features)
  • Para una predicción en particular del modelo, el efecto que tuvo en ella cada característica
  • Efecto de cada característica en el global de las predicciones del modelo

Veamos algunas de esas técnicas y qué librerías de Python nos brindan estas funcionalidades:

1- Permutation Importance

¿Cuales de las features piensa el modelo que son más importantes? ¿Qué Características tienen mayor impacto en las predicciones? Estos conceptos son conocidos como “Feature Importante” y “Permutation Importance” y nos sirven para calcular nuestras características de entrada al modelo. Nos sirve para poder ver cuando nuestro modelo está funcionando de manera contra-intuitiva y también para demostrar a terceros cuando el funcionamiento es correcto.

Para hacer Permutation Importante debemos primero entrenar un modelo y “encajarlo” (fit). Luego tomamos el set de validación y tomamos las features una por vez: por ejemplo, tomamos la primer columna de entrada y mezclamos todos sus valores entre sus filas (pero el resto de features se mantienen igual). Entonces hacemos predicción usando el mismo modelo entrenado y deberían empeorar los resultados. Si “desmejoran mucho” es que esa feature era muy importante. En cambio, si no afecta demasiado, tampoco variarán mucho las predicciones obtenidas y quiere decir que esa característica no es relevante. Y así lo hacemos con todas las características, desordenando de a una a la vez.

Podemos utilizar la librería ELI5 para Python para visualizar la Permutation Importance

2- Partial Dependence Plots (PDP)

Los PDPs muestran el efecto marginal de una o dos características que tienen sobre la predicción dictada por un modelo. Los PDPs muestran cómo afectan las distintas características a las predicciones. El PDP puede mostrar la relación entre nuestra variable de salida y una ó dos características de entrada.

Lo que hacemos en tomar de a una sola fila, e ir variando los valores de una sola de las features (que queremos investigar) contra un modelo YA entrenado. Entonces veremos en que intervalos esa característica afecta a los resultados del modelo.

Lo podemos hacer hasta con 2 variables a la vez usando “2D Partial Plots” y visualizarlo.

Para esto podemos utilizar la librería PDPBox

3-SHAP Values (en predicciones individuales)

SHAP viene de “Shapley Additive exPlanation” y está basado en la teoría de Juegos para explicar cómo cada uno de los jugadores que intervienen en un “juego colaborativo” contribuyen en el éxito de la partida. Con esto podemos comprender una predicción y como impacta cada feature. Podemos decir que la interpretabilidad que nos ofrecen los valores SHAP es de las mejores.

De manera muy sencilla -e incompleta- de cómo se calculan estos valores podemos imaginar a una grupo de desarrolladores, testers, arquitectos y managers (features) que trabajan en conjunto (“juegan”/colaboran) para crear un Sistema de Software y queremos saber cuánto contribuyó cada uno de ellos en su producción. Lo que haremos es ir intercalando a los participantes en diversos “orden de aparación” ABCD, ABDC, ADBC, etc. e ir midiendo la <<contribución marginal>> de cada participante cada vez. Con ello sacar el promedio de cada uno y tendremos los valores Shapley que nos indican cuánto contribuyo cada jugador a conseguir el resultado obtenido.

Supongamos que tenemos que explicar a una persona por qué se ha rechazado su solicitud de un crédito -esto es, una única predicción, y no “el accuracy global” del modelo- los valores SHAP nos muestran cuales características que alimentan al modelo <<empujan>> a la denegación (ó aceptación) esa petición en concreto.

Utilizamos la librería SHAP para python para obtener estos valores.

4- Usos avanzados de Shap (comprensión global)

Si recopilamos muchos valores Shap podremos tener una mejor comprensión del modelo en su conjunto. De allí aparecen las gráficas “Shap Summary Plot” y “Shap Dependence Contribution Plot”.

Shap Summary Plot

Calculando los Shap Values de cada muestra, podemos obtener esta Visualización que nos muestra cuales características son las más importantes y el rango de valores donde afecta al set de datos.

Shap Dependence Contribution Plot

Esta gráfica es similar a la de los PDPs (vistos en el punto 2) pero nos dan mucho mayor detalle.

No puedo dejar de mencionar a una gran librería para ML Interpretability llamada LIME (Local Interpretable Model Explanation) y que nos ofrece comprensión a humanos para modelos de NLP (destacando visualmente palabras en el texto) y para imágenes clasificadas por una CNN (mostrando las áreas en donde “mira” la red).
También mencionar otra Librería Python llamada Skater -es de Oracle- y aunque aún está en desarrollo, provee de buenas herramientas.

Conclusión

La importancia de la interpretabilidad de los modelos de Machine Learning es crucial para poder justificar y comprender las predicciones y/o resultados obtenidos y hasta legalmente. Es curioso que necesitemos “modelos que expliquen como funcionan los modelos” para poder “bajar” a entendimiento humano la complejidad de lo que ocurre en nuestras máquinas de aprendizaje. Finalmente, aplicando diversos métodos, Permutation Importance, los PDP y los Shap Values logramos obtener transparencia en nuestro desarrollo y un panorama claro sobre cómo funciona nuestro engranaje para obtener los resultados.

Suscribe al Blog

Recibe los nuevos artículos sobre Aprendizaje Automático, teoría y el código Python

NOTA: algunos usuarios reportaron que el email de confirmación a la suscripción entraron en la carpeta SPAM. Te sugiero que revises y recomiendo agregar el remitente a tus contactos. Gracias!

Recursos Adicionales

Te recomiendo sobre todo y para pasar al Código este curso completo en Kaggle: Machine Learning Explainability

Algunos artículos y videos sobre Interpretación de Modelos (en inglés)

The post Interpretación de Modelos de Machine Learning first appeared on Aprende Machine Learning.

]]>
https://aprendemachinelearning.com/interpretacion-de-modelos-de-machine-learning/feed/ 4 6814
12 Consejos útiles para aplicar Machine Learning https://aprendemachinelearning.com/12-consejos-utiles-para-aplicar-machine-learning/ https://aprendemachinelearning.com/12-consejos-utiles-para-aplicar-machine-learning/#comments Thu, 04 Apr 2019 13:30:00 +0000 https://www.aprendemachinelearning.com/?p=6743 Si vas por el buen camino hacia el aprendizaje del Machine Learning, la inteligencia artificial y la ciencia de datos, seguramente te hayas topado con trabas y obstáculos frecuentes. En este artículo repasaremos 12 útiles consejos para tener en cuenta a la hora de trabajar con los modelos del Aprendizaje Automático. Estos postulados surgen del […]

The post 12 Consejos útiles para aplicar Machine Learning first appeared on Aprende Machine Learning.

]]>
Si vas por el buen camino hacia el aprendizaje del Machine Learning, la inteligencia artificial y la ciencia de datos, seguramente te hayas topado con trabas y obstáculos frecuentes. En este artículo repasaremos 12 útiles consejos para tener en cuenta a la hora de trabajar con los modelos del Aprendizaje Automático. Estos postulados surgen del paper A Few Useful Things to Know about Machine Learning escrito en 2012 por Pedro Domingos.

No olvides seguir los 7 pasos del Machine Learning

Vamos al grano!

Con el objetivo de ilustrar mejor estos consejos, nos centraremos en la aplicación del Machine Learning de Clasificar, pero esto podría servir para otros usos.

Los 3 componentes del Aprendizaje Automático

Supongamos que tienes un problema al que crees que puedes aplicar ML. ¿Qué modelo usar? Deberá ser una combinación de estos 3 componentes: Representación, evaluación y optimización.

  • Representación: Un clasificador deberá poder ser representado en un lenguaje formal que entienda el ordenador. Deberemos elegir entre los diversos algoritmos que sirven para resolver el problema. A este conjunto de “clasificadores aptos” se les llamará “espacio de hipótesis del aprendiz”. Ej: SVM, Regresión Logística, K-nearest neighbor, árboles de decisión, Redes Neuronales, etc.
  • Evaluación: Se necesitará una función de evaluación para distinguir entre un buen clasificador ó uno malo. También es llamada función objetivo ó scoring function. Ejemplos son accuracy, likelihood, information gain, etc.
  • Optimización: necesitamos un método de búsqueda entre los clasificadores para mejorar el resultado de la Evaluación. Su elección será clave. EJ: Descenso por gradiente, mínimos cuadrados, etc.

Lo más importante es lograr la Generalización de la máquina

El objetivo fundamental del Machine Learning es lograr la generalización del conocimiento más allá de las muestras tomadas en el conjunto de entrenamiento. Es es porque por más que entrenemos con muchísimos datos, es poco probable que se vuelvan a dar esos mismos casos al testear nuestra máquina. Por más que usemos “un millón de fotos de gatitos” para entrenar, siempre habrá fotos nuevas ó distintas que nuestro modelo desconoce. Entonces la magia ocurrirá si nuestro algoritmo es capaz de generalizar lo aprendido durante el entrenamiento y puede detectar “al gatito millón uno”. Siempre es conveniente guardar una parte de nuestro set de datos para poder validar lo aprendido durante el entrenamiento, con muestras que la máquina nunca ha visto y comprobar que sigue dando un buen resultado.

Los datos solos, no alcanzan

Siendo la generalización el objetivo del ML trae como consecuencia que sólo con datos “no alcanza”, no importa con cuántos contemos.

¿Entonces cómo podemos pretender que nuestras máquinas aprendan algo? Afortunadamente, en este proceso de inducción que realizamos con los algoritmos logramos llegar a ciertos niveles que nos dan buenos resultados.

El machine learning no hace magia. Sin datos no funcionará. Lo que hace es “sacar más con poco“. Y claro, cuantos más datos mejor. Pero no lo son todo. Deberemos combinar “conocimiento” con los datos.

El Overfitting tiene muchas caras

Si al entrenar nuestro modelo obtenemos resultados <<demasiado buenos>> con 100% aciertos y en el set de test apenas alcanzamos un 50% (ó menos!) es muy probable que nos hayamos topado con en “el gran problema del ML”: el overfitting.

Pero debemos saber que podemos “caer” en el overfitting de diversas maneras, a veces sin darnos cuenta. Dos de sus caras son:

  • Bias (ó sesgo): es la tendencia a aprender -equivocadamente- algo falso.
  • Varianza: es la tendencia a aprender algo random no relacionado con la realidad.

A veces puede ocurrir que un algoritmo -aprendiz- que “parece menos potente”, obtenga mejor resultado que uno “super poderoso” que cae en overfitting. A tener en cuenta 😉

Es difícil evitar el Overfitting, se puede utilizar Regularización ó la validación cruzada (cross validation), u otras técnicas pero ninguna nos asegura evitarlo del todo. Muchas veces ocurre que al querer corregir la varianza, caemos en Bias… y viceversa. Lograr evitar ambos en simultáneo es “el desafío” que tenemos.

A veces falla “la intuición” con muchas dimensiones

Cuando tenemos muchas features, por ejemplo 100 ó más, puede que alguno de nuestros algoritmos de aprendizaje “se vuelva loco”… es decir, que no logre generalizar ó que lo haga mal. A esto se le llamó “la maldición de la dimensionalidad” (Bellman, 1961). Esto dependerá también de la distribución de los datos de entrada, pero para entenderlo: con k-nearest neighbor es <<fácil visualizar en 2 ó 3 dimensiones>> los clusters. Pero al aumentar dimensiones puede ocurrir que para el algoritmo todos los puntos sean vecinos cercanos unos de otros, devolviéndonos resultados aleatorios.

Debemos tener esto en cuenta y si fuera el caso utilizar algún algoritmo de reducción de dimensiones – PCA, t-SNE- para aplacar el problema.

Que se cumplan supuestos teóricos no nos garantiza nada

Al aprender ML podemos leer en papers, ó en cursos y artículos algunas afirmaciones teóricas que intentan ayudarnos y guiarnos. Bueno, como el ML es un fenómeno muy complejo y depende tanto de cada problema en particular, las dimensiones y los datos de entrada (su distribución), los casos positivos/negativos que tengamos de las muestras y de tantas otras variables, es posible que muchos de esos supuestos teóricos NO nos ayuden en nuestro “problema particular”.

Esto puede sonar muy decepcionante, y en parte lo es. Pero esto debemos saberlo para contrastar nuestros datos y no confiar en que “como a Fulanito le dio así, a mi también me funcionará”. Con sólo variar un parámetro, o un sólo dato de entrada de nuestro conjunto de entrenamiento, podemos obtener resultados completamente distintos. Estar alertas!!!

La clave está en la Ingeniería de Características (Feature Engineer)

Seguramente pasemos mucho más tiempo seleccionando los features, transformando, preprocesando que el tiempo dedicado a preparar/ejecutar el algoritmo de Machine Learning. Muchas veces el desafío será si tenemos pocas dimensiones ser creativos y poder generar nuevas y útiles características, ó en caso de tener muchas poder seleccionar cuales serán realmente valiosas y cuales descartar.

Deberemos ser cuidadosos: si tenemos muchas features, podemos evaluarlas individualmente y pensar que algunas no aportan demasiado valor. Sin embargo, esas mismas características puede que sean imprescindibles si las consideramos en combinación con otras. Eh ahí nuestro ingenio y mucha prueba y error.

Una gran herramienta es la Interpretación de Modelos por Importancia de Features, Los SHAP Values y más gráficas que comento en este nuevo artículo!

Más muestras superan a un algoritmo complejo

Conseguir más muestras para entrenamiento utilizando un algoritmo “simple”, puede ser mejor que un algoritmo complejo que “tarde tanto en ejecutar y no termina nunca”.

En ciencias de la computación, solíamos tener dos limitantes: tiempo y recursos cómo la memoria. Ahora, con el Machine Learning aparece una tercera: los datos de entrenamiento. Actualmente podemos encontrar cantidades masivas de datos y nuestro “cuello de botella” es el tiempo.

Si bien contar con “más y más datos” es bueno y hasta impulsa la creación de modelos (algoritmos) más complejos para aprovecharlos, se da una paradoja: en la práctica algoritmos “más simples” pueden obtener buenos resultados en tiempo razonable contra algoritmos complejos que tardan una eternidad.

Entonces el consejo es: al afrontar un problema, empecemos probando con los modelos más sencillos a más complejos (si hiciera falta!).

Ensamble de Modelos

Está bien aplicar un modelo para resolver el problema. Pero estudios han demostrado que hacer ensamble de modelos muchas veces mejora significativamente los resultados. Esto consiste en combinar más de un modelo (por ej. una red neuronal, K-nn, árboles, etc). Las tres técnicas más utilizadas son (ejemplo para clasificadores de “perros y gatos”):

  • Bagging: alimentamos diversos modelos haciendo resamplig de las muestras y finalmente hacemos una votación (voting) con la clasificación obtenida (Supongamos que dos de tres modelos dicen “gato” y uno dice “perro”, ganarían los felinos)
  • Boosting: en este caso, utilizamos un modelo “potenciandolo” a detectar únicamente perros y otro a detectar sólo gatitos.
  • Stacking: utilizaremos diversos modelos, apilados “uno detrás de otro”, es decir, la salida del primero puede ser la entrada (ó un feature) del siguiente modelo y así sucesivamente. Para una famosa competición de Netflix, el ganador había encadenado más de 100 modelos!!

Simplicidad no implica precisión

Hubo un postulado algo confuso que parecía decir que al aplicar un modelo simple obteníamos la mayor precisión (frente a uno complejo). Sin embargo no hay que confundir: la simpleza no implica mayor precisión, esto se puede comprobar fácilmente con el punto anterior, pues al hacer ensamble de stacking de modelos vemos claramente que no se deteriora el resultado y en todo caso lo puede mejorar. Entonces podemos optar por modelos simples que “ya cuentan” con ventajas intrínsecas -probablemente en tiempo y coste- pero no necesariamente por la precisión del resultado.

Un problema “Representable”, no implica que pueda resolverse con Aprendizaje Automático

Podemos tender a pensar que para cualquier problema que podamos representar podrá ser construida una máquina que lo resuelva. Sin embargo hay problemas que nunca podrán ser “aprendidos” por una máquina, pues por ejemplo no tenemos las muestras suficientes para que generalice. En esos casos, podemos fácilmente encontrar la representación pero no lograremos dar con una solución en ML.

La correlación no implica causa

Para alimentar nuestras máquinas de Aprendizaje Automático utilizamos datos que son “muestras observables” donde la variable predictiva no está bajo control del algoritmo (en contraposición a muestras experimentales). Por esto es que las correlaciones que encontremos pueden ser interpretadas como señales de “responsabilidad” de la causa del problema. La realidad es que no necesariamente esa correlación implica la causa si no que son una buena pista para ponernos a investigar las conexiones que llevan a esos resultados.

Recomendado: Aprende a hacer el Análisis Exploratorio de Datos con Pandas/Python

Conclusión

Este gran paper de Pedro Domingos nos abre un poco los ojos sobre diversas trampas en las que podemos caer al trabajar en Machine Learning. Hace unos -pocos- años surgieron papers sobre Interpretación de Modelos de ML que comento en mi nuevo artículo y que dan luz a poder maniobrar ante situaciones problemáticas.

Moraleja en tiempos de IA

Personalmente creo que la “gran enseñanza” que nos deja es que debemos estar atentos, no confiarnos ni de datos, ni de algoritmos, ni de soluciones mágicas. Debemos ser muy profesionales y científicos, mucha prueba y error, validación y comprobación de los resultados. Y una vez hecho esto… volver a comprobar!

Descarga el paper original desde aquí

Suscripción: Nuevos artículos

Recibe los nuevos artículos sobre Aprendizaje Automático, teoría y práctica Python en tu casilla de correo!

NOTA: algunos usuarios reportaron que el email de confirmación a la suscripción entraron en la carpeta SPAM. Te sugiero que revises y recomiendo agregar el remitente a tus contactos. Gracias!

The post 12 Consejos útiles para aplicar Machine Learning first appeared on Aprende Machine Learning.

]]>
https://aprendemachinelearning.com/12-consejos-utiles-para-aplicar-machine-learning/feed/ 5 6743