Modelo de visión y lenguaje para observaciones de GLOBE#
Los modelos de visión y lenguaje, o VLM por sus siglas en inglés, son modelos de inteligencia artificial que pueden “ver” imágenes y “comprender” lenguaje al mismo tiempo. Esto los hace muy útiles para la ciencia participativa, en la que las personas voluntarias recopilan fotografías y los equipos de investigación necesitan analizar rápidamente miles de ellas.
En esta lección utilizaremos BLIP-2, desarrollado por Junnan Li, Dongxu Li, Silvio Savarese y Steven Hoi, para describir automáticamente fotografías de hábitats de mosquitos recopiladas mediante NASA GLOBE Observer.
# Instalar las bibliotecas necesarias
!pip install -q transformers torch pillow requests geopandas matplotlib
!pip install -q accelerate
# Importar las bibliotecas
import geopandas as gpd
import requests
from PIL import Image
from io import BytesIO
import random
import matplotlib.pyplot as plt
from transformers import AutoModelForCausalLM, AutoTokenizer, Blip2Processor, Blip2ForConditionalGeneration
import torch
A continuación, cargaremos imágenes de hábitats de mosquitos, es decir, fuentes de agua, a partir de observaciones de NASA GLOBE. Para obtener más información, consulta la lección Introducción a los datos de GLOBE.
# Obtener los enlaces de las fotografías incluidas en las observaciones de GLOBE
mosquito = gpd.read_file(
"https://github.com/geo-di-lab/emerge-lessons/raw/refs/heads/main/docs/data/globe_mosquito.zip"
)
urls = mosquito.dropna(
subset=["WaterSourcePhotoUrls"]
)["WaterSourcePhotoUrls"]
# Obtener los tipos de fuentes de agua que se usarán como etiquetas
# Los nombres de los campos se mantienen en inglés porque pertenecen al conjunto de datos
data = mosquito.dropna(
subset=["WaterSourcePhotoUrls"]
)[["WaterSourcePhotoUrls", "WaterSourceType"]].copy()
print(f"\nTotal de observaciones con fotografías: {len(data)}")
print(
f"\nTipos de fuentes de agua:\n"
f"{data['WaterSourceType'].value_counts()}"
)
# Seleccionar muestras aleatorias para la demostración
random.seed(42)
sample_data = data.sample(n=5).reset_index(drop=True)
print("\nSe seleccionaron 5 imágenes aleatorias para el análisis")
Ahora cargaremos el modelo BLIP-2 y los datos asociados. El siguiente bloque de código puede tardar varios minutos en terminar de cargarse.
BLIP-2 fue entrenado principalmente con contenido en inglés, por lo que las descripciones generadas pueden aparecer en inglés aunque el resto de la lección esté en español.
# Cargar el modelo BLIP-2
model_name = "Salesforce/blip2-opt-2.7b"
processor = Blip2Processor.from_pretrained(model_name)
model = Blip2ForConditionalGeneration.from_pretrained(
model_name,
torch_dtype=torch.float16,
device_map="auto"
)
print("Modelo BLIP-2 cargado")
def generate_image_summary(image_url):
"""
Recibe la URL de una imagen y genera una oración que la describe.
"""
try:
# Si hay varias URL separadas por punto y coma, usar la primera
if ";" in image_url:
image_url = image_url.split(";")[0].strip()
# Descargar la imagen
response = requests.get(image_url, timeout=10)
response.raise_for_status()
image = Image.open(BytesIO(response.content)).convert("RGB")
# Generar una descripción con BLIP-2
# El mensaje se conserva en inglés porque este modelo funciona mejor así
prompt = "a photo of"
# Procesar los datos de entrada
inputs = processor(
image,
prompt,
return_tensors="pt"
).to(model.device)
# Generar la descripción
outputs = model.generate(
**inputs,
max_new_tokens=50,
num_beams=5
)
summary = processor.decode(
outputs[0],
skip_special_tokens=True
).strip()
return image, summary
except Exception as e:
print(f"Error al procesar la imagen: {str(e)}")
return None, f"Error: {str(e)}"
# Mostrar un resumen de los resultados junto con las imágenes
fig, axes = plt.subplots(
5,
2,
figsize=(14, 18),
gridspec_kw={"width_ratios": [2, 3]}
)
for idx, row in sample_data.iterrows():
url = row["WaterSourcePhotoUrls"]
water_type = row["WaterSourceType"]
print(f"Procesando imagen {idx + 1}/5...")
# Obtener la imagen y la descripción generada
image, summary = generate_image_summary(url)
if image is not None:
# Columna izquierda: mostrar la imagen
axes[idx, 0].imshow(image)
axes[idx, 0].axis("off")
axes[idx, 0].set_title(
f"Imagen {idx + 1}",
fontsize=12,
fontweight="bold"
)
# Columna derecha: mostrar las descripciones como texto
axes[idx, 1].axis("off")
caption_text = f"Anotación de GLOBE:\n{water_type}\n\n"
caption_text += f"Descripción generada por IA:\n{summary}"
axes[idx, 1].text(
0.05,
0.5,
caption_text,
fontsize=10,
verticalalignment="center",
wrap=True,
bbox=dict(
boxstyle="round",
facecolor="lightblue",
alpha=0.3
)
)
else:
# Manejar los casos en los que no se pueda cargar la imagen
axes[idx, 0].text(
0.5,
0.5,
"No se pudo cargar la imagen",
ha="center",
va="center"
)
axes[idx, 0].axis("off")
axes[idx, 1].text(
0.5,
0.5,
summary,
ha="center",
va="center",
wrap=True,
color="red"
)
axes[idx, 1].axis("off")
plt.suptitle(
"Análisis de hábitats de mosquitos: anotaciones de GLOBE y descripciones de IA",
fontsize=14,
fontweight="bold",
y=0.995
)
plt.tight_layout()
plt.savefig(
"mosquito_results_summary.png",
dpi=150,
bbox_inches="tight"
)
plt.show()
Podemos observar que las descripciones generadas suelen representar correctamente el contenido de las imágenes, aunque algunos elementos pueden identificarse de manera incorrecta, como un hidrante o un bote de basura.
Este flujo de trabajo puede ser muy útil para procesar muchas imágenes a la vez e identificar características que aparecen repetidamente. Por ejemplo, se pueden buscar términos clave dentro de las descripciones generadas.
Utilizamos BLIP-2 en este ejemplo porque es más rápido y ligero que otros modelos. Existen muchos otros modelos de visión y lenguaje que pueden utilizarse para encontrar patrones en imágenes de ciencia participativa.