Comparación de modelos de clasificación con datos de GLOBE#
Probaremos y compararemos diferentes modelos de clasificación utilizando datos de GLOBE.
# Instalar los paquetes necesarios
!pip install -q geemap geopandas
import ee
import geemap
import geopandas as gpd
import pandas as pd
from IPython.display import display, Image
# Autenticar Google Earth Engine
ee.Authenticate()
# Cambia "emerge-lessons" por el ID de tu proyecto si es diferente
ee.Initialize(project="emerge-lessons")
# Definir las clases de cobertura terrestre
muc_level1_map = {
"0": "Bosque cerrado",
"1": "Bosque abierto",
"2": "Matorral o espesura",
"3": "Matorral enano o espesura enana",
"4": "Vegetación herbácea",
"5": "Terreno sin vegetación",
"6": "Humedal",
"7": "Aguas abiertas",
"8": "Terreno cultivado",
"9": "Zona urbana"
}
custom_palette = [
"#004b23",
"#2d6a4f",
"#52b788",
"#95d5b2",
"#d8f3dc",
"#d4a373",
"#40E0D0",
"#1E90FF",
"#8B4513",
"#808080"
]
def prepare_data():
"""
Carga los datos de GLOBE, prepara las variables y obtiene las
representaciones satelitales.
"""
print("Cargando y preparando los datos de referencia...")
# Cargar y limpiar los datos tabulares
url = (
"https://github.com/geo-di-lab/emerge-lessons/"
"raw/refs/heads/main/docs/data/globe_land_cover.zip"
)
gdf = gpd.read_file(url).dropna(
subset=["MucCode", "geometry"]
)
# Dar formato a las etiquetas
gdf["Level1Code"] = gdf["MucCode"].astype(str).str[1]
gdf["label"] = gdf["Level1Code"].astype(int)
gdf["MucDescription"] = gdf["Level1Code"].map(
muc_level1_map
)
gdf_subset = gdf[
["label", "Level1Code", "MucDescription", "geometry"]
]
fc = geemap.geopandas_to_ee(gdf_subset)
# Definir la región de estudio: Florida
fl_url = (
"https://github.com/geo-di-lab/emerge-lessons/"
"raw/refs/heads/main/docs/data/florida_boundary.geojson"
)
region = geemap.gdf_to_ee(
gpd.read_file(fl_url)[["geometry"]]
).geometry()
local_fc = fc.filterBounds(region)
# Cargar las representaciones satelitales
print(
"Obteniendo las representaciones satelitales anuales "
"V1 de AlphaEarth..."
)
embeddings = (
ee.ImageCollection(
"GOOGLE/SATELLITE_EMBEDDING/V1/ANNUAL"
)
.filterDate("2024-01-01", "2025-01-01")
.mosaic()
.clip(region)
)
return local_fc, embeddings, region
# Ejecutar la preparación
local_fc, embeddings, region = prepare_data()
def extract_and_split(
embeddings_img,
point_features,
split_ratio=0.8
):
"""
Extrae las variables de las imágenes y divide los datos en
conjuntos de entrenamiento y prueba.
"""
print(
"Extrayendo las variables. Este proceso puede tardar "
"un momento..."
)
sampled_data = embeddings_img.sampleRegions(
collection=point_features,
properties=["label"],
scale=10,
tileScale=4
)
# Aleatorizar y dividir los datos
with_random = sampled_data.randomColumn("random")
train_set = with_random.filter(
ee.Filter.lt("random", split_ratio)
)
test_set = with_random.filter(
ee.Filter.gte("random", split_ratio)
)
return train_set, test_set, embeddings_img.bandNames()
# Crear los conjuntos de entrenamiento y prueba
training, testing, band_names = extract_and_split(
embeddings,
local_fc
)
print(
f"Bandas extraídas: {band_names.length().getInfo()} "
"variables disponibles por punto."
)
Clasificador de bosque aleatorio#
El bosque aleatorio es un modelo de ensamble. Construye varios árboles de decisión, 50 en este caso, y combina sus predicciones para obtener un resultado más estable.
print("Entrenando el clasificador de bosque aleatorio...")
# Inicializar y entrenar el modelo
rf_classifier = ee.Classifier.smileRandomForest(50).train(
features=training,
classProperty="label",
inputProperties=band_names
)
# Validar el modelo con los datos de prueba
rf_validated = testing.classify(rf_classifier)
# Calcular y mostrar la exactitud
rf_confusion_matrix = rf_validated.errorMatrix(
"label",
"classification"
)
rf_accuracy = rf_confusion_matrix.accuracy().getInfo()
print(
"Exactitud general del bosque aleatorio: "
f"{rf_accuracy:.4f}"
)
CART: árboles de clasificación y regresión#
CART es un único árbol de decisión. Examina las bandas de las representaciones satelitales y encuentra los mejores puntos para dividir los datos mediante umbrales sencillos. Por ejemplo: “Si la banda 1 es mayor que 0.5, corresponde a un bosque”.
Los árboles de decisión individuales son rápidos y fáciles de interpretar, pero pueden presentar sobreajuste. Esto significa que podrían memorizar los datos de entrenamiento en lugar de aprender patrones generales.
print("Entrenando el clasificador CART...")
# Inicializar y entrenar el modelo
cart_classifier = ee.Classifier.smileCart().train(
features=training,
classProperty="label",
inputProperties=band_names
)
# Validar el modelo
cart_validated = testing.classify(cart_classifier)
# Calcular y mostrar la exactitud
cart_confusion_matrix = cart_validated.errorMatrix(
"label",
"classification"
)
cart_accuracy = cart_confusion_matrix.accuracy().getInfo()
print(
f"Exactitud general de CART: {cart_accuracy:.4f}"
)
Máquina de vectores de soporte (SVM)#
Una máquina de vectores de soporte encuentra el límite o la línea óptima, llamada hiperplano, que separa distintas clases dentro del espacio multidimensional de las representaciones satelitales. El modelo intenta maximizar la distancia entre las clases para que las predicciones sean lo más claras posible.
Las máquinas de vectores de soporte pueden ser muy eficaces para identificar límites complejos, pero su entrenamiento puede requerir más recursos y ser más lento con conjuntos de datos satelitales muy grandes que el de los modelos basados en árboles.
print(
"Entrenando el clasificador de máquina de vectores "
"de soporte..."
)
# Inicializar y entrenar el modelo
svm_classifier = ee.Classifier.libsvm().train(
features=training,
classProperty="label",
inputProperties=band_names
)
# Validar el modelo con los datos de prueba
svm_validated = testing.classify(svm_classifier)
# Calcular y mostrar la exactitud
svm_confusion_matrix = svm_validated.errorMatrix(
"label",
"classification"
)
svm_accuracy = svm_confusion_matrix.accuracy().getInfo()
print(
f"Exactitud general de SVM: {svm_accuracy:.4f}"
)
Árboles potenciados por gradiente#
Al igual que el bosque aleatorio, la potenciación por gradiente es un método de ensamble que utiliza varios árboles de decisión. Sin embargo, mientras que el bosque aleatorio construye todos sus árboles de manera independiente, la potenciación por gradiente los construye de forma secuencial.
Cada árbol nuevo examina los errores cometidos por los árboles anteriores e intenta corregirlos. Esto suele producir una mayor exactitud general, pero el entrenamiento tarda más y el modelo puede presentar sobreajuste si no se ajusta cuidadosamente.
print(
"Entrenando el clasificador de árboles potenciados "
"por gradiente..."
)
# Inicializar y entrenar el modelo
# Se utilizan 50 árboles para facilitar la comparación con
# el bosque aleatorio
gbt_classifier = (
ee.Classifier.smileGradientTreeBoost(50).train(
features=training,
classProperty="label",
inputProperties=band_names
)
)
# Validar el modelo
gbt_validated = testing.classify(gbt_classifier)
# Calcular y mostrar la exactitud
gbt_confusion_matrix = gbt_validated.errorMatrix(
"label",
"classification"
)
gbt_accuracy = gbt_confusion_matrix.accuracy().getInfo()
print(
"Exactitud general de los árboles potenciados por "
f"gradiente: {gbt_accuracy:.4f}"
)
Cómo recorrer varios modelos#
Podemos utilizar el siguiente código para entrenar varios modelos rápidamente y comparar sus resultados.
# Definir un diccionario con los modelos que se evaluarán
models_to_test = {
"Bosque aleatorio (50 árboles)": (
ee.Classifier.smileRandomForest(50)
),
"Árboles potenciados por gradiente (50)": (
ee.Classifier.smileGradientTreeBoost(50)
),
"Máquina de vectores de soporte": (
ee.Classifier.libsvm()
),
"CART (árbol de decisión)": (
ee.Classifier.smileCart()
)
}
# Crear diccionarios vacíos para almacenar los resultados
model_accuracies = {}
trained_models = {}
# Recorrer cada modelo
for model_name, classifier in models_to_test.items():
print(f"Entrenando {model_name}...")
# Entrenar
trained_clf = classifier.train(
features=training,
classProperty="label",
inputProperties=band_names
)
# Validar
validated = testing.classify(trained_clf)
acc = (
validated
.errorMatrix("label", "classification")
.accuracy()
.getInfo()
)
# Almacenar los resultados
model_accuracies[model_name] = acc
trained_models[model_name] = trained_clf
# Crear una tabla comparativa
df_results = pd.DataFrame(
list(model_accuracies.items()),
columns=["Modelo", "Exactitud general"]
).sort_values(
by="Exactitud general",
ascending=False
).reset_index(drop=True)
print("\nComparación final de los modelos")
display(df_results)