Saltar al contenido

Usando modelos abiertos

En la última entrada estuvimos examinando qué son los modelos abiertos y qué ventajas aportan. Ahora vamos a remangarnos y a ver varias opciones para usar modelos abiertos desde nuestros desarrollos. Las analizaremos empezando por las más sencillas, e iremos viendo cómo implementar a nuestro viejo amigo, el chatbot cascarrabias, con cada una de ellas, usando Python.

Hospedaje externo

La opción más sencilla es usar un servicio de hospedaje externo, y hacer que nuestra aplicación se comunique usando un API de inferencia. Hay muchos proveedores que ofrecen este servicio, como AWS Bedrock o Beam, pero para nuestro ejemplo usaremos Groq, que disponibiliza varios modelos abiertos a través de una API compatible con la de OpenAI, y que en estos momentos ofrece el acceso gratuitamente (con límites).

Como suele ser habitual con cualquier nueva API externa que queramos usar, el primer paso es conseguir una clave de API. Para ello deberemos ir a la web de Groq, crearnos una cuenta, y luego ir a la sección API Keys y crear nuestra clave de API.

Para usar la API de Groq, lo más cómodo es usar su librería, que podemos instalar con pip:

pip install groq

El uso de la librería de Groq es extremadamente parecido a la de OpenAI. Al igual que esta, por defecto va a buscar nuestra clave de API en una variable de entorno, en este caso llamada GROQ_API_KEY. Una vez hecho eso, el código Python para tener nuestro chatbot cascarrabias sería:

from groq import Groq

mensajes = [{"role": "system", "content": "Eres un viejo gruñón y cascarrabias. Responde a todo con quejas y desgana, aunque termina ayudando en lo que se te pide."}]
client = Groq()
# La conversación continuará hasta que dejemos un mensaje vacío
while len(texto_usuario := input("¿Qué le quieres decir al chatbot cascarrabias?\n> ").strip())>0:

    # Añadimos la entrada a la lista de mensajes, con el rol "user".
    mensajes.append({"role": "user", "content": texto_usuario})

    # Llamamos al servicio chat completions para obtener la respuesta del modelo, enviando la lista completa de mensajes para poder continuar la conversación.
    respuesta = client.chat.completions.create(
        model="llama-3.1-8b-instant",
        messages=mensajes,
        stream=True)

    mensaje_respuesta = ""
    for trozo in respuesta:
        texto_trozo = trozo.choices[0].delta.content or ""
        mensaje_respuesta += texto_trozo
        print(texto_trozo, end="", flush=True)
    print("")

    # Añadimos la respuesta a la lista de mensajes, con rol "assistant"
    mensajes.append({"role": "assistant", "content": mensaje_respuesta})

Groq soporta múltiples modelos abiertos. En este caso he optado por usar Llama 3.1 8B, pero se puede usar cualquier otro con solo modificar en parámetro model en la llamada a chat.completions.create.

Ejecución local

Para la ejecución de los modelos en local, el abanico de posibilidades es enorme, ya que existen multitud de herramientas, librerías, lenguajes, formatos … cada uno con sus casos de uso, sus pros y sus contras, y constantemente aparecen posibilidades nuevas. Por ello, es imposible apuntar LA manera de hacerlo. Dicho esto voy a describir un par de formas de ejecutar localmente modelos.

Ollama

La primera forma será usando Ollama, que es una herramienta que nos permite muy fácilmente ejecutar modelos de lenguaje localmente. Ollama tiene muchas características que lo hacen una buena opción:

  • Fácil de instalar, y disponible para Linux, macOS y Windows.
  • Hace automáticamente uso de la gran mayoría de GPUs (si la tenemos, lo cual es muy recomendable) disponibles en el mercado, múltiples GPUs, o exclusivamente la CPU, sin necesidad de configuración adicional.
  • Fácil acceso a los modelos descargados a través de:
    • Chat directo por linea de comandos.
    • Servicios REST publicados localmente.
    • Librerías para Python y JavaScript.

Para empezar, deberemos descargar Ollama desde su página, e instalarlo. Una vez instalado, podemos hacer una verificación rápida accediendo con el navegador a http://localhost:11434/. Si todo está bien, deberemos ver lo siguiente:

Para descargar un modelo de la lista de modelos soportados por Ollama abriremos un terminal y usaremos el comando ollama pull seguido del nombre del modelo deseado. Por ejemplo, vamos a descargar Phi 3.5, que es un modelo ligero desarrollado por Microsoft, de 3 mil millones de parámetros:

C:\>ollama pull phi3.5
pulling manifest
pulling b5374915da53... 100%████████████████████████████████████████████████████▏ 2.2 GB
pulling c608dc615584... 100%████████████████████████████████████████████████████▏  149 B
pulling c5f9f560a704... 100%████████████████████████████████████████████████████▏ 1.1 KB
pulling 45ac4c662a77... 100%████████████████████████████████████████████████████▏   60 B
pulling 0611867ea67b... 100%████████████████████████████████████████████████████▏  409 B
verifying sha256 digest
writing manifest
success

Para comprobar la lista de modelos que tenemos descargados, usaremos el comando list:

C:\>ollama list
NAME            ID              SIZE    MODIFIED
phi3.5:latest   61819fb370a3    2.2 GB  About a minute ago

Una vez tenemos un modelo descargado, la forma más directa de interactuar con él es mediante el comando run:

C:\>ollama run phi3.5
>>> Hola. ¿Quién eres?
¡Hola! Soy Phi, tu asistente virtual desarrollado para ayudarte con tareas o preguntas que puedas tener en línea. No dudes en preguntarme cualquier cosa relacionada a información u operaciones de texto: ¿qué necesitas hoy?

>>> Send a message (/? for help)

La interfaz de Ollama a través de la consola es muy espartana, aunque tiene un buen número de opciones. Sin embargo, lo interesante es que el modelo, una vez descargado y con Ollama corriendo, está accesible para que otras aplicaciones o herramientas hagan uso de él. Por ejemplo, podemos instalar Open WebUI para tener una interfaz web completa usando nuestro modelo local.

Y por supuesto, también podemos usar nuestro modelo local desde nuestras propias aplicaciones. Como no podía ser de otra manera, a modo de ejemplo vamos a adaptar al chatbot cascarrabias para que corra usando nuestro modelo recién descargado.

Empezaremos por descargar la librería de Ollama para Python:

pip install ollama

En este caso, como no llamamos a ningún servicio externo, no necesitaremos ninguna clave de API. Este sería el código del chatbot cascarrabias usando phi 3.5 a través de Ollama:

import ollama

mensajes = [{"role": "system", "content": "Eres un viejo gruñón y cascarrabias. Responde a todo con quejas y desgana, aunque termina ayudando en lo que se te pide."}]

# La conversación continuará hasta que dejemos un mensaje vacío
while len(texto_usuario := input("¿Qué le quieres decir al chatbot cascarrabias?\n> ").strip())>0:

    # Añadimos la entrada a la lista de mensajes, con el rol "user".
    mensajes.append({"role": "user", "content": texto_usuario})

    # Llamamos al servicio chat para obtener la respuesta del modelo, enviando la lista completa de mensajes para poder continuar la conversación.
    respuesta = ollama.chat(
        model="phi3.5:latest",
        messages=mensajes,
        stream=True)

    mensaje_respuesta = ""
    for trozo in respuesta:
        mensaje_respuesta += trozo["message"]["content"]
        print(trozo["message"]["content"], end="", flush=True)
    print("")

    # Añadimos la respuesta a la lista de mensajes, con rol "assistant"
    mensajes.append({"role": "assistant", "content": mensaje_respuesta})

Hugging Face Transformers

Hugging Face es una plataforma dedicada a la inteligencia. Tienen un enorme repositorio de modelos y juegos de datos, muchos de ellos subidos por los miembros de la comunidad. También publican su propia librería en Python para el manejo de modelos de IA, Transformers, que es la que vamos a usar ahora.

La librería Transformers hace mucho más que inferencia. Podemos usarla para interactuar con los modelos de Hugging Face, entrenarlos, afinarlos, etc. Para poder utilizarla, necesitaremos tener instalada y configurada una librería de aprendizaje profundo (deep learning), típicamente PyTorch, aunque es compatible con otras. Dependiendo de nuestra máquina y el sistema operativo que usemos, configurar adecuadamente PyTorch para que haga uso eficiente de nuestro hardware puede no ser sencillo, por lo que, si lo único que queremos hacer es inferencia, la opción más sencilla es usar Ollama. Si por el contrario queremos ir preparando el camino para poder hacer un uso más profundo, como por ejemplo poder entrenar o afinar nuestros modelos, sigamos adelante. En cualquier caso, no vamos a entrar en esta entrada más en detalle sobre la instalación de PyTorch.

Para empezar, lo primero es ir a la página de Hugging Face y darse de alta. Una vez hecho eso, ir a la sección de profile > Access Tokens, y crear un nuevo token de lectura:

Vamos a usar el modelo gemma-2-2b-it, que es la versión afinada para instrucciones (instruction tuned) del modelo Gemma 2 2B, un modelo abierto entrenado por Google. Google permite a cualquiera descargarse este modelo, pero requiere que para hacerlo el usuario esté logado y acepte las condiciones. Una vez hecho esto Hugging Face nos permitirá descargarlo, y para poder hacerlo directamente desde la librería Transformers usaremos el token de lectura como forma de identificarnos.

Como último paso, instalaremos la librería en nuestro entorno:

pip install transformers

Y una vez hecho esto, ya podemos implementar el chatbot cascarrabias usando el modelo:

from transformers import pipeline

# Gemma 2 no soporta mensajes de sistema
mensajes = [{"role": "user", "content": "Eres un viejo gruñón y cascarrabias. Responde a todo con quejas y desgana, aunque termina ayudando en lo que se te pide."},
            {"role": "assistant", "content": "OK."}]

pipe = pipeline(
    task="text-generation",
    model="google/gemma-2-2b-it",
    device="cuda",  # "mps" para Mac, "cpu" si no tienes GPU
    token="HF_TOKEN", 
)

# La conversación continuará hasta que dejemos un mensaje vacío
while len(texto_usuario := input("¿Qué le quieres decir al chatbot cascarrabias?\n> ").strip())>0:
    # Añadimos la entrada a la lista de mensajes, con el rol "user".
    mensajes.append({"role": "user", "content": texto_usuario})

    # Generamos la respuesta del modelo usando pipe, enviando la lista completa de mensajes para poder continuar la conversación.
    mensaje_respuesta = pipe(mensajes, max_new_tokens=256)[0]["generated_text"][-1]["content"]

    # Añadimos la respuesta a la lista de mensajes, con rol "assistant"
    print(mensaje_respuesta)
    mensajes.append({"role": "assistant", "content": mensaje_respuesta})

Un par de comentarios sobre la invocación a pipeline():

  • Si identificamos el modelo (parámetro model) unicamente usando su nombre, la librería se encargará de descargarlo, si no se había descargado previamente, en la primera ejecución.
  • Para esa descarga, al requerir el modelo gemma que hayamos aceptado las condiciones para permitir la descarga, será necesaria autenticación, la cual facilitamos con nuestro token de lectura en el parámetro token.
  • Si no queremos tener que poner el token en el código, podemos descargar el modelo previamente y luego simplemente usarlo de la siguiente manera:
    • Ejecutar git clone https://huggingface.co/google/gemma-2-2b-it, y cuando nos pida autenticación introducir nuestro nombre de usuario de Hugging Face, y el token de lectura.
    • Alternativamente, podemos descargar los archivos del modelo directamente desde la página de Hugging Face. Si lo hacemos así, no es necesario crear token de lectura.
    • En el parámetro model, indicar la ruta del directorio donde hemos descargado el modelo.
    • Eliminar el parámetro token.
  • El parámetro device deberemos ajustarlo en función del hardware que vayamos a usar para la inferencia, que será cuda para GPUs en PC, mps para usar chips de Mac (que llevan GPU integrada), o cpu si no disponemos de GPU.

Como siempre, todo el código de los ejemplos que hemos estado viendo está disponible en GitHub.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *