Genera resultados de PaliGemma con Keras

Los modelos de PaliGemma tienen capacidades multimodales, lo que te permite generar resultados con datos de entrada de texto e imágenes. Puedes usar datos de imágenes con estos modelos para proporcionar contexto adicional para tus solicitudes o usar el modelo para analizar el contenido de las imágenes. En este instructivo, se muestra cómo usar PaliGemma con Keras para analizar imágenes y responder preguntas sobre ellas.

Contenido de este notebook

En este notebook, se usa PaliGemma con Keras y se muestra cómo hacer lo siguiente:

  • Instala Keras y las dependencias requeridas
  • Descarga PaliGemmaCausalLM, una variante de PaliGemma entrenada previamente para el modelado de lenguaje visual causal, y úsala para crear un modelo.
  • Prueba la capacidad del modelo para inferir información sobre las imágenes proporcionadas

Antes de comenzar

Antes de comenzar a usar este notebook, debes familiarizarte con el código de Python y con el modo en que se entrenan los modelos de lenguaje extensos (LLM). No es necesario que estés familiarizado con Keras, pero el conocimiento básico sobre Keras es útil cuando lees el código de ejemplo.

Configuración

En las siguientes secciones, se explican los pasos preliminares para que un notebook use un modelo de PaliGemma, incluido el acceso al modelo, la obtención de una clave de API y la configuración del entorno de ejecución del notebook.

Obtén acceso a PaliGemma

Antes de usar PaliGemma por primera vez, debes solicitar acceso al modelo a través de Kaggle. Para ello, completa los siguientes pasos:

  1. Accede a Kaggle o crea una cuenta nueva si aún no tienes una.
  2. Ve a la tarjeta de modelo de PaliGemma y haz clic en Solicitar acceso.
  3. Completa el formulario de consentimiento y acepta los Términos y Condiciones.

Configura tu clave de API

Para usar PaliGemma, debes proporcionar tu nombre de usuario y una clave de API de Kaggle.

Para generar una clave de API de Kaggle, abre la página Configuración en Kaggle y haz clic en Crear token nuevo. Esto activa la descarga de un archivo kaggle.json que contiene tus credenciales de API.

Luego, en Colab, selecciona Secrets (🔑) en el panel izquierdo y agrega tu nombre de usuario y clave de API de Kaggle. Almacena tu nombre de usuario con el nombre KAGGLE_USERNAME y tu clave de API con el nombre KAGGLE_KEY.

Selecciona el entorno de ejecución

Para completar este instructivo, deberás tener un entorno de ejecución de Colab con recursos suficientes para ejecutar el modelo de PaliGemma. En este caso, puedes usar una GPU T4:

  1. En la esquina superior derecha de la ventana de Colab, haz clic en el menú desplegable ▾ (Opciones de conexión adicionales).
  2. Selecciona Cambiar el tipo de entorno de ejecución.
  3. En Acelerador de hardware, selecciona GPU T4.

Configure las variables de entorno

Configura las variables de entorno para KAGGLE_USERNAME, KAGGLE_KEY y KERAS_BACKEND.

import os
from google.colab import userdata

# Set up environmental variables
os.environ["KAGGLE_USERNAME"] = userdata.get('KAGGLE_USERNAME')
os.environ["KAGGLE_KEY"] = userdata.get('KAGGLE_KEY')
os.environ["KERAS_BACKEND"] = "jax"

Instala Keras

Ejecuta la siguiente celda para instalar Keras.

pip install -U -q keras-nlp keras-hub kagglehub

Importa dependencias y configura Keras

Instala las dependencias necesarias para este notebook y configura el backend de Keras. También configurarás Keras para que use bfloat16, de modo que el framework use menos memoria.

import keras
import keras_hub
import numpy as np
import PIL
import requests
import io
import matplotlib
import re
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from PIL import Image

keras.config.set_floatx("bfloat16")

Carga el modelo

Ahora que configuraste todo, puedes descargar el modelo previamente entrenado y crear algunos métodos de utilidad para ayudar a que genere sus respuestas. En este paso, descargarás un modelo con PaliGemmaCausalLM desde Keras Hub. Esta clase te ayuda a administrar y ejecutar la estructura del modelo de lenguaje visual causal de PaliGemma. Un modelo de lenguaje visual causal predice el siguiente token según los tokens anteriores. Keras Hub proporciona implementaciones de muchas arquitecturas de modelos populares.

Crea el modelo con el método from_preset y, luego, imprime su resumen. Este proceso tardará alrededor de un minuto en completarse.

paligemma = keras_hub.models.PaliGemmaCausalLM.from_preset("kaggle://keras/paligemma2/keras/pali_gemma2_mix_3b_224")
paligemma.summary()

Crea métodos de utilidad

Para ayudarte a generar respuestas de tu modelo, crea dos métodos de utilidad:

  • crop_and_resize: Es un método auxiliar para read_img. Este método recorta y cambia el tamaño de la imagen al tamaño pasado para que el tamaño de la imagen final cambie sin sesgar las proporciones de la imagen.
  • read_img: Es un método auxiliar para read_img_from_url. Este método es el que realmente abre la imagen, cambia su tamaño para que se ajuste a las restricciones del modelo y la coloca en un array que el modelo puede interpretar.
  • read_img_from_url: Recibe una imagen a través de una URL válida. Necesitas este método para pasar la imagen al modelo.

Usarás read_img_from_url en el siguiente paso de este notebook.

def crop_and_resize(image, target_size):
    width, height = image.size
    source_size = min(image.size)
    left = width // 2 - source_size // 2
    top = height // 2 - source_size // 2
    right, bottom = left + source_size, top + source_size
    return image.resize(target_size, box=(left, top, right, bottom))

def read_image(url, target_size):
    contents = io.BytesIO(requests.get(url).content)
    image = PIL.Image.open(contents)
    image = crop_and_resize(image, target_size)
    image = np.array(image)
    # Remove alpha channel if necessary.
    if image.shape[2] == 4:
        image = image[:, :, :3]
    return image

def parse_bbox_and_labels(detokenized_output: str):
  matches = re.finditer(
      '<loc(?P<y0>\d\d\d\d)><loc(?P<x0>\d\d\d\d)><loc(?P<y1>\d\d\d\d)><loc(?P<x1>\d\d\d\d)>'
      ' (?P<label>.+?)( ;|$)',
      detokenized_output,
  )
  labels, boxes = [], []
  fmt = lambda x: float(x) / 1024.0
  for m in matches:
    d = m.groupdict()
    boxes.append([fmt(d['y0']), fmt(d['x0']), fmt(d['y1']), fmt(d['x1'])])
    labels.append(d['label'])
  return np.array(boxes), np.array(labels)

def display_boxes(image, boxes, labels, target_image_size):
  h, l = target_size
  fig, ax = plt.subplots()
  ax.imshow(image)
  for i in range(boxes.shape[0]):
      y, x, y2, x2 = (boxes[i]*h)
      width = x2 - x
      height = y2 - y
      # Create a Rectangle patch
      rect = patches.Rectangle((x, y),
                               width,
                               height,
                               linewidth=1,
                               edgecolor='r',
                               facecolor='none')
      # Add label
      plt.text(x, y, labels[i], color='red', fontsize=12)
      # Add the patch to the Axes
      ax.add_patch(rect)

  plt.show()

def display_segment_output(image, bounding_box, segment_mask, target_image_size):
    # Initialize a full mask with the target size
    full_mask = np.zeros(target_image_size, dtype=np.uint8)
    target_width, target_height = target_image_size

    for bbox, mask in zip(bounding_box, segment_mask):
        y1, x1, y2, x2 = bbox
        x1 = int(x1 * target_width)
        y1 = int(y1 * target_height)
        x2 = int(x2 * target_width)
        y2 = int(y2 * target_height)

        # Ensure mask is 2D before converting to Image
        if mask.ndim == 3:
            mask = mask.squeeze(axis=-1)
        mask = Image.fromarray(mask)
        mask = mask.resize((x2 - x1, y2 - y1), resample=Image.NEAREST)
        mask = np.array(mask)
        binary_mask = (mask > 0.5).astype(np.uint8)


        # Place the binary mask onto the full mask
        full_mask[y1:y2, x1:x2] = np.maximum(full_mask[y1:y2, x1:x2], binary_mask)
    cmap = plt.get_cmap('jet')
    colored_mask = cmap(full_mask / 1.0)
    colored_mask = (colored_mask[:, :, :3] * 255).astype(np.uint8)
    if isinstance(image, Image.Image):
        image = np.array(image)
    blended_image = image.copy()
    mask_indices = full_mask > 0
    alpha = 0.5

    for c in range(3):
        blended_image[:, :, c] = np.where(mask_indices,
                                          (1 - alpha) * image[:, :, c] + alpha * colored_mask[:, :, c],
                                          image[:, :, c])

    fig, ax = plt.subplots()
    ax.imshow(blended_image)
    plt.show()

Generar salida

Después de cargar el modelo y crear métodos de utilidad, puedes solicitarle datos de imagen y texto para que genere respuestas. Los modelos de PaliGemma se entrenan con una sintaxis de instrucciones específica para tareas específicas, como answer, caption y detect. Para obtener más información sobre la sintaxis de la tarea de instrucción de PaliGemma, consulta Instrucciones del sistema y de la instrucción de PaliGemma.

Prepara una imagen para usarla en una instrucción de generación con el siguiente código para cargar una imagen de prueba en un objeto:

target_size = (224, 224)
image_url = 'https://storage.googleapis.com/keras-cv/models/paligemma/cow_beach_1.png'
cow_image = read_image(image_url, target_size)
matplotlib.pyplot.imshow(cow_image)

Responde en un idioma específico

En el siguiente código de ejemplo, se muestra cómo solicitarle al modelo de PaliGemma información sobre un objeto que aparece en una imagen proporcionada. En este ejemplo, se usa la sintaxis answer {lang} y se muestran preguntas adicionales en otros idiomas:

prompt = 'answer en where is the cow standing?\n'
# prompt = 'svar no hvor står kuen?\n'
# prompt = 'answer fr quelle couleur est le ciel?\n'
# prompt = 'responda pt qual a cor do animal?\n'

output = paligemma.generate(
    inputs={
        "images": cow_image,
        "prompts": prompt,
    }
)
print(output)

Usa la instrucción detect

En el siguiente ejemplo de código, se usa la sintaxis de la instrucción detect para ubicar un objeto en la imagen proporcionada. El código usa las funciones parse_bbox_and_labels() y display_boxes() definidas anteriormente para interpretar el resultado del modelo y mostrar los cuadros delimitados generados.

prompt = 'detect cow\n'
output = paligemma.generate(
    inputs={
        "images": cow_image,
        "prompts": prompt,
    }
)
boxes, labels = parse_bbox_and_labels(output)
display_boxes(cow_image, boxes, labels, target_size)

Usa la instrucción segment

En el siguiente ejemplo de código, se usa la sintaxis de la instrucción segment para ubicar el área de una imagen que ocupa un objeto. Usa la biblioteca big_vision de Google para interpretar el resultado del modelo y generar una máscara para el objeto segmentado.

Antes de comenzar, instala la biblioteca de big_vision y sus dependencias, como se muestra en este ejemplo de código:

import os
import sys

# TPUs with
if "COLAB_TPU_ADDR" in os.environ:
  raise "It seems you are using Colab with remote TPUs which is not supported."

# Fetch big_vision repository if python doesn't know about it and install
# dependencies needed for this notebook.
if not os.path.exists("big_vision_repo"):
  !git clone --quiet --branch=main --depth=1 \
     https://github.com/google-research/big_vision big_vision_repo

# Append big_vision code to python import path
if "big_vision_repo" not in sys.path:
  sys.path.append("big_vision_repo")


# Install missing dependencies. Assume jax~=0.4.25 with GPU available.
!pip3 install -q "overrides" "ml_collections" "einops~=0.7" "sentencepiece"

Para este ejemplo de segmentación, carga y prepara una imagen diferente que incluya un gato.

cat = read_image('https://big-vision-paligemma.hf.space/file=examples/barsik.jpg', target_size)
matplotlib.pyplot.imshow(cat)

Esta es una función para ayudar a analizar el resultado del segmento de PaliGemma.

import  big_vision.evaluators.proj.paligemma.transfers.segmentation as segeval
reconstruct_masks = segeval.get_reconstruct_masks('oi')
def parse_segments(detokenized_output: str) -> tuple[np.ndarray, np.ndarray]:
  matches = re.finditer(
      '<loc(?P<y0>\d\d\d\d)><loc(?P<x0>\d\d\d\d)><loc(?P<y1>\d\d\d\d)><loc(?P<x1>\d\d\d\d)>'
      + ''.join(f'<seg(?P<s{i}>\d\d\d)>' for i in range(16)),
      detokenized_output,
  )
  boxes, segs = [], []
  fmt_box = lambda x: float(x) / 1024.0
  for m in matches:
    d = m.groupdict()
    boxes.append([fmt_box(d['y0']), fmt_box(d['x0']), fmt_box(d['y1']), fmt_box(d['x1'])])
    segs.append([int(d[f's{i}']) for i in range(16)])
  return np.array(boxes), np.array(reconstruct_masks(np.array(segs)))

Consulta a PaliGemma para segmentar el gato en la imagen

prompt = 'segment cat\n'
output = paligemma.generate(
    inputs={
        "images": cat,
        "prompts": prompt,
    }
)

Visualiza la máscara generada desde PaliGemma

bboxes, seg_masks = parse_segments(output)
display_segment_output(cat, bboxes, seg_masks, target_size)

Mensajes por lotes

Puedes proporcionar más de un comando de instrucción dentro de un solo mensaje como un lote de instrucciones. En el siguiente ejemplo, se muestra cómo estructurar el texto de la instrucción para proporcionar varias instrucciones.

prompts = [
    'answer en where is the cow standing?\n',
    'answer en what color is the cow?\n',
    'describe en\n',
    'detect cow\n',
    'segment cow\n',
]
images = [cow_image, cow_image, cow_image, cow_image, cow_image]
outputs = paligemma.generate(
    inputs={
        "images": images,
        "prompts": prompts,
    }
)
for output in outputs:
    print(output)