En los últimos años se ha visto un aumento de la inversión en modelos de base tabular comerciales y de código abierto construidos en torno al aprendizaje en contexto (ICL). En 2025, por ejemplo, el gigante del software SAP lanzó el conjunto de modelos SAP-RPT-1, dirigido a tareas centradas en ERP en áreas como la planificación financiera, el procesamiento de pedidos de ventas y adquisiciones y la gestión de la cadena de suministro. A diferencia del aprendizaje automático supervisado tradicional, donde los modelos se entrenan y ajustan para tareas específicas, ICL permite que un único modelo genéricamente preentrenado se adapte sobre la marcha utilizando cantidades relativamente pequeñas de datos específicos de la tarea proporcionados en la carga útil del contexto, que actúa como una especie de conjunto de entrenamiento efímero.
Si bien el cambio a ICL elimina la necesidad de un (re)entrenamiento costoso de modelos tabulares específicos de tareas, introduce una importante compensación entre precisión y latencia en el momento de la inferencia, especialmente para modelos alojados centralmente como SAP-RPT-1. Por un lado, el tiempo necesario para enviar la carga útil del contexto al servidor del modelo y para que el modelo interprete y aprenda de esa carga útil contribuye directamente a la latencia general de la respuesta. Las cargas útiles más pequeñas pueden reducir la latencia. Por otro lado, es posible que el modelo necesite inferir esquemas y distribuciones de datos complejos a partir de datos contextuales heterogéneos que potencialmente contienen valores atípicos, valores faltantes y patrones de cola larga. Las predicciones precisas suelen depender de cargas útiles de contexto grandes y bien seleccionadas. En la práctica, esto significa encontrar formas de destilar la carga útil del contexto para reducir el tiempo de respuesta sin degradar el rendimiento predictivo del modelo. Las compensaciones secundarias involucran factores como el rendimiento del servicio del modelo, la estabilidad de la respuesta y el costo monetario del uso del modelo. Todos estos desafíos hacen que la optimización de la carga útil del contexto sea una preocupación arquitectónica central en los flujos de trabajo basados en ICL.
En las siguientes secciones, examinaremos con más detalle las compensaciones en tiempo de inferencia que implican los modelos básicos tabulares basados en ICL, describiremos estrategias prácticas para optimizar las cargas útiles de contexto y demostraremos el uso del prefiltrado de contexto basado en KNN como técnica de optimización de cargas útiles con un ejemplo de extremo a extremo en Python.
Compensaciones entre inferencia y tiempo
Un enfoque eficaz para analizar las compensaciones entre inferencia y tiempo de los modelos de base tabular basados en ICL es aplicar el marco del llamado “triángulo de hierro” analizado en este artículo anterior. Allí, mostramos cómo los clientes y usuarios de sistemas de IA deben navegar las tensiones inherentes entre la calidad de la respuesta, el costo de la inferencia y la latencia, que es un análogo en el tiempo de inferencia de la clásica “triple restricción” del tiempo de diseño en la gestión de proyectos. Fundamentalmente, mejorar cualquiera de estas dimensiones normalmente ejerce presión sobre las demás: las respuestas de mayor calidad tienden a ser más intensivas en términos computacionales, lo que aumenta tanto la latencia como el costo; reducir la latencia a menudo requiere sacrificar la calidad o pagar más por hardware más rápido; y reducir costos generalmente significa aceptar respuestas de IA más lentas o de menor calidad.
Encontramos esta misma tensión triangular en el contexto de los modelos de base tabular basados en ICL. La principal desventaja es la necesidad de equilibrar la calidad de la respuesta (medida en términos de precisión, recuperación, etc.) con la latencia. Consideremos un sistema de detección de fraude en tiempo real implementado en cajeros automáticos: tanto la precisión como la velocidad son críticas, pero empujan al sistema en diferentes direcciones cuando se trata de construir la carga útil del contexto. Las cargas útiles más grandes y ricas brindan al modelo de IA más ejemplos a partir de los cuales inferir el esquema subyacente, reconocer patrones raros y de cola larga y, por lo tanto, ofrecer predicciones de mayor calidad. Al mismo tiempo, cada fila o característica adicional aumenta el volumen de datos que se deben enviar al servidor modelo e interpretar durante la inferencia, lo que puede introducir una sobrecarga mensurable en el tiempo de respuesta de un extremo a otro. En aplicaciones en tiempo real, incluso un pequeño aumento en el tamaño de la carga útil puede degradar notablemente la capacidad de respuesta del sistema y, en última instancia, dañar la experiencia del usuario.
Además, en la práctica surgen una serie de compensaciones secundarias relacionadas. Una carga útil de contexto más grande no sólo ralentiza la inferencia sino que también consume más tokens. En la facturación basada en tokens, esto crea una tensión entre la latencia de respuesta y el costo monetario del uso del modelo para los clientes, lo que se vuelve especialmente destacado para los modelos alojados centralmente como SAP-RPT-1. Una carga útil más grande puede aumentar el tiempo de procesamiento por solicitud, creando una compensación entre latencia y rendimiento que puede obligar al equipo de desarrollo del sistema de IA a tomar decisiones difíciles de escala. También existe una posible compensación entre calidad y estabilidad: aumentar el volumen y la variedad de los datos contextuales puede mejorar la precisión predictiva, pero puede reducir el determinismo al introducir ruido y hacer que los resultados sean más sensibles a pequeñas variaciones en los datos. Finalmente, métodos de selección de carga útil más sofisticados, como la recuperación basada en KNN, pueden mejorar la calidad de la predicción pero también aumentar el tiempo de construcción de la carga útil, lo que aumenta la latencia general.
Estrategias de optimización de la carga útil del contexto
En general, las estrategias para optimizar la carga útil del contexto abarcan dos dimensiones ortogonales: el método y el momento de optimización. El método de optimización determina cómo se selecciona exactamente la carga útil, es decir, las técnicas específicas de filtrado, agrupamiento o incrustación utilizadas para comprimir las filas en el contexto sin formato. El momento de optimización se refiere a cuándo y dónde se lleva a cabo la optimización, por ejemplo, si se calcula previamente fuera de línea o se deriva sobre la marcha en el momento de la inferencia, y si esto lo hace el cliente o el servicio modelo. Elegir un momento particular para construir la carga útil optimizada puede tener consecuencias importantes para la latencia de inferencia y la mantenibilidad. El método y el momento de optimización de la carga útil deben estar alineados con el alcance, el presupuesto, el umbral de latencia y los requisitos de calidad de un caso de uso de IA determinado.
Métodos de optimización
Podemos distinguir ampliamente entre métodos de optimización de carga útil independientes y conscientes de las tareas. Los métodos independientes de la tarea se basan en técnicas como el muestreo aleatorio y el muestreo basado en lo reciente, que no requieren conocimiento de la tarea de predicción específica ni de la estructura semántica de los datos. El muestreo aleatorio es fácil de implementar, rápido e imparcial, lo que lo convierte en una estrategia de referencia o alternativa útil. Sin embargo, puede descartar inadvertidamente filas que capturan patrones raros pero importantes, cruciales para el rendimiento del modelo. El muestreo basado en lo reciente supone que las marcas de tiempo se registran en los datos y recupera las filas más recientes, lo que puede ser valioso para distribuciones de datos con plazos determinados (por ejemplo, estacionales) o susceptibles a derivas temporales. Sin embargo, el muestreo basado en lo reciente ignora la estructura más amplia del conjunto de datos y puede sobreponderar el ruido a corto plazo. En general, los métodos independientes de la tarea ofrecen simplicidad y velocidad, pero brindan un control limitado sobre la representatividad y relevancia de la carga útil resultante.
Por el contrario, los métodos que reconocen tareas pueden incorporar información sobre la tarea de predicción, las filas de consulta y la distribución de datos subyacentes para seleccionar las filas más relevantes para la carga útil del contexto. Un enfoque común es el muestreo de K vecinos más cercanos (KNN), que identifica filas en los datos históricos que son similares a las filas de consulta. Esto puede producir datos contextuales muy relevantes y un sólido rendimiento empírico, pero requiere métricas de distancia (por ejemplo, coseno) y modelos auxiliares para vectorizar o incrustar los datos y, por lo tanto, puede resultar computacionalmente costoso a escala. Otra clase de técnicas utiliza algoritmos de agrupamiento (por ejemplo, K-means, agrupamiento jerárquico, DBSCAN) para extraer muestras representativas de los grupos pertenecientes a las filas de consulta. Esto puede garantizar una cobertura suficiente de diversos patrones en los datos y, al mismo tiempo, evitar la redundancia, aunque normalmente requiere el cálculo fuera de línea de los clústeres y un nuevo cálculo periódico para garantizar que los clústeres permanezcan actualizados.
También son posibles métodos más sofisticados que tienen en cuenta las tareas. Por ejemplo, el contexto sin formato y las filas de consulta se pueden incrustar en un espacio vectorial de baja dimensión, codificado en la solicitud y decodificado en la respuesta de la API del modelo básico; esto equivale a una forma de compresión con pérdidas que sacrifica cierta precisión por la latencia y los beneficios de costos de una carga útil más pequeña. Las técnicas de generación aumentada de recuperación (RAG) pueden enriquecer aún más la carga útil con una base específica de dominio para aumentar la relevancia de la respuesta.
En resumen, los métodos conscientes de las tareas generalmente producen cargas útiles de contexto de mayor calidad, pero conllevan una mayor sobrecarga computacional y de ingeniería.
Momentos de optimización
Una decisión clave relacionada con el momento es si algunos de los pasos de optimización de la carga útil se pueden calcular previamente fuera de línea (es decir, el “cuándo”). Por ejemplo, un conjunto de datos "dorado" curado se puede precalcular a partir de datos históricos, optimizarse para la densidad de información y enriquecerse con metadatos (por ejemplo, ID de clúster, hashtags, etc.). Se pueden seleccionar filas relevantes de este conjunto de datos más ágil y dorado para construir y enviar rápidamente la carga útil del contexto en el momento de la inferencia. Los conjuntos de datos dorados son adecuados para esquemas estables y tareas repetitivas (por ejemplo, autocompletado de pedidos de ventas comunes en el dominio ERP), pero su conservación y mantenimiento pueden generar una sobrecarga adicional para el equipo de desarrollo. Por el contrario, la optimización sobre la marcha deriva la carga útil en el momento de la inferencia en función de las filas de consulta actuales y los datos históricos disponibles. Este enfoque es más adaptable pero puede aumentar el costo de cómputo y la latencia de cada llamada de inferencia. La optimización sobre la marcha tampoco reduce necesariamente los gastos generales del equipo de desarrollo: los ahorros derivados de no mantener un conjunto de datos dorado pueden compensarse con el rápido esfuerzo de ingeniería necesario para optimizar dinámicamente la carga útil del contexto.
Otra decisión relacionada con el momento se refiere a si la optimización se produce en el lado del cliente o del servicio (es decir, el “dónde”). La optimización del lado del cliente brinda control total a la aplicación consumidora, lo que permite un preprocesamiento personalizado, almacenamiento en caché local y una depuración más sencilla. Pero también hace que cada cliente sea responsable de implementar y mantener su propia lógica de optimización, un esfuerzo que puede duplicarse entre aplicaciones y equipos. El procesamiento del lado del cliente también requiere suficientes recursos informáticos, lo que puede resultar difícil para las aplicaciones que se ejecutan en IoT o dispositivos perimetrales con recursos limitados. La optimización del lado del servicio, por el contrario, se beneficia de las economías de escala: con un uso suficiente entre los clientes, el proveedor de servicios de IA puede justificar algoritmos más sofisticados y hardware de gama alta que los que cualquier cliente implementaría por sí solo. El proveedor también puede aprovechar la experiencia profunda y específica del modelo y la visibilidad de cómo se desempeña el modelo en múltiples entornos de clientes (que se agravan con el tiempo) para desarrollar una estrategia más refinada y armonizada. El procesamiento del lado del servicio también simplifica la gobernanza, ya que las actualizaciones de software, los controles de privacidad, los registros de auditoría y las comprobaciones de cumplimiento se pueden aplicar de manera uniforme. Las desventajas incluyen una transparencia reducida para los clientes, una mayor carga en la infraestructura del proveedor y el costo continuo para el proveedor de servicios de IA por desarrollar y mantener la lógica de optimización.
Por supuesto, los flujos de trabajo de IA tabulares basados en ICL también pueden adoptar una estrategia híbrida que combine los puntos fuertes de diferentes opciones. Un patrón útil consiste en un filtrado aproximado del lado del cliente para reducir la carga útil a un tamaño manejable (por ejemplo, seleccionando los K vecinos más cercanos o aplicando alguna otra heurística simple), combinado con una poda detallada del lado del servicio utilizando señales compatibles con el modelo para refinar el contexto final antes de la inferencia. Los enfoques híbridos pueden lograr un buen equilibrio entre transparencia, flexibilidad, gobernanza y desempeño.
Demostración práctica: prefiltrado de contexto basado en KNN
En el siguiente ejemplo de código Python, usaremos el conjunto de datos Solar Flare y la versión de juegos del modelo SAP-RPT-1. Consulte este artículo para obtener una introducción a la API del modelo.
Configuración
Primero, instale los paquetes de terceros necesarios utilizando el archivo requisitos.txt:
pandas numpy solicita scikit-learn ucimlrepo
A continuación, cree un archivo llamado demo.py y agregue las siguientes declaraciones de importación:
importar pandas como pd importar numpy como np importar tiempo importar json solicitudes de importación importar sys importar sistema operativo desde fecha y hora importar fecha y hora desde sklearn.preprocesamiento importar LabelEncoder desde sklearn.metrics importar distancias_pares desde ucimlrepo importar fetch_ucirepo
Agregue estos parámetros de configuración:
EXPERIMENT_ORDER = [" without_prefiltering ", " with_prefiltering "] API_URL = "https://rpt.cloud.sap/api/predict" ACCESS_TOKEN_PATH = "access_token.json" # Archivo que contiene su token API con open(ACCESS_TOKEN_PATH, "r") como f: token = json.load(f)["access_token"] n_test_rows = 20 # Número de filas de consulta para usar mask_proportion = 0.3 # Proporción de valores de columna para enmascarar (simulando un escenario de predicción) max_masked_columns = 4 # Limitación del modelo de Playground random_seed = 3 # Garantizar la reproducibilidad rng = np.random.default_rng(random_seed) # Crear un generador de números aleatorios ctx_max_rows = 600 # Máximo de filas permitidas en la ventana de contexto
Agregue este código para habilitar el registro de salida:
class Tee(object): """Una camiseta de salida estándar simple: imprime en la consola y escribe en un archivo de registro.""" def __init__(self, logfile_path): self.terminal = sys.stdout self.log = open(logfile_path, "a", encoding="utf-8") def write(self, message): self.terminal.write(message) self.log.write(message) def flush(self): self.terminal.flush() self.log.flush() script_dir = os.path.dirname(os.path.abspath(__file__)) marca de tiempo = datetime.now().strftime("%Y%m%d_%H%M%S") log_filename = f"log_knn_seed{random_seed}_{"".join([x[0]para x en EXPERIMENT_ORDER])}_{timestamp}.log" log_path = os.path.join(script_dir, log_filename) sys.stdout = Tee(log_path) print(f"Registro habilitado. La salida se escribe en: {log_path}n")
A continuación, agregaremos funciones auxiliares para el diagnóstico, construiremos la carga útil del modelo SAP-RPT-1, llamaremos al modelo y exportaremos los resultados de la predicción a un archivo CSV.
Una función de ejemplo para calcular estadísticas de características del conjunto de datos:
def Compute_feature_stats(df, random_seed): """ Calcula la cardinalidad y la métrica de concentración de HHI para cada característica. Guarda los resultados en: feature_stats_knn_seed_.csv """ stats =[]para col en df.columns: if col == "id": continue cardinality = df[col].nunique() # El valor normalizado cuenta vc = df[col].value_counts(normalize=True) # Índice Herfindahl-Hirschman # HHI = 1.0 implica perfectamente concentrado (solo aparece un valor) # HHI = 0.01 implica una distribución muy uniforme # Un HHI más alto implica una mayor concentración de características hhi = float((vc ** 2).sum()) # Proporción de categoría dominante (participación del valor de característica más común) max_prop = float(vc.max()) stats.append({ "feature": col, "cardinality": cardinality, "hhi": hhi, "max_proportion": max_prop }) stats_df = pd.DataFrame(stats) timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") filename = f"feature_stats_knn_seed{random_seed}_{timestamp}.csv" stats_df.to_csv(filename, index=False) print(f"Estadísticas de funciones guardadas en {filename}n")
Funciones para construir la carga útil del modelo SAP-RPT-1 simulando un escenario de predicción y llamando de forma segura a la API del modelo:
def valores_fila_mascarilla(fila, columnas_máscara_permitidas, p, rng): fila = fila.copia() candidatos_mascarilla = [c para c en columnas_máscara_permitidas si rng.random() < p] para c en candidatos_mascarilla: fila[c] = "[PREDICTO]" return fila def build_payload(df, index_column="id"): return {"filas": df.to_dict(orient="records"), "index_column": index_column} def safe_call_rpt1(carga útil, token): encabezados = { "Tipo de contenido": "aplicación/json", "Autorización": f"Portador {token}" } intente: respuesta = solicitudes.post(API_URL, json=carga útil, encabezados=encabezados) intente: respuesta_json = respuesta.json() excepto ValueError: print("nRespuesta no JSON de RPT-1:") print(response.text) devuelve Falso, {"error": "Respuesta no JSON"} si "error" en respuesta_json: print("nRPT-1 API devolvió un error:") print(json.dumps(response_json, indent=2)) devuelve Falso, respuesta_json si "aiApiResponsePayload" no está en respuesta_json: print("nFalta aiApiResponsePayload:") print(json.dumps(response_json, indent=2)) devuelve Falso, respuesta_json carga útil = respuesta_json["aiApiResponsePayload"] si "predicciones" no están en la carga útil: print("nFaltan predicciones en aiApiResponsePayload:") print(json.dumps(response_json, indent=2)) devuelve Falso, respuesta_json devuelve Verdadero, respuesta_json excepto request.exceptions.RequestException como e: print("nLa solicitud HTTP falló:") print(str(e)) devuelve Falso, {"error": str(e)}
Funciones para el posprocesamiento de predicción:
def flatten_predictions(pred_list): flat = {} para entrada en pred_list: fila = {} para clave, valor en entrada.items(): if clave == "id": fila["id"] = str(valor) else: si isinstance(valor, lista) y len(valor) > 0: fila[clave] = valor[0].get("prediction") else: fila[clave] = Ninguna flat[fila["id"]] = retorno de fila pd.DataFrame(flat.values()).set_index("id") def evalua_accuracy(pred_df, true_df, masked_df): correcto = 0 total = 0 para idx en masked_df.index: para col en masked_df.columns: # No cuenta predicciones para columnas desenmascaradas if masked_df.loc[idx, col] == "[PREDICT]": total += 1 if pred_df.loc[idx, col] == true_df.loc[idx, col]: correcto += 1 return correcto, total, correcto / total si total > 0 else np.nan def export_predictions_dynamic(true_rows, masked_rows, pred_df, nombre de archivo): """ Exporte un CSV sin NaN donde: – las columnas enmascaradas obtienen predicciones del modelo – las columnas desenmascaradas mantienen sus valores verdaderos – pred_df está alineado con true_rows por id """ # Asegúrese de que pred_df esté indexado por id pred_df = pred_df.copy() pred_df.index = pred_df.index.astype(int) # Reindexe pred_df para que coincida con true_rows pred_df = pred_df.reindex(true_rows.index) # Comenzar con filas verdaderas fusionadas = true_rows.reset_index().copy() # Alinear máscara por id masked_by_id = masked_rows.copy() # Agregar columnas de predicción dinámicamente para col en pred_df.columns: pred_col = f"pred_{col}" # Comenzar con valores verdaderos fusionados[pred_col] = fusionado[col] # Sobrescribir solo donde máscara enmascarada = masked_by_id[col] == "[PREDICT]" fusionada.loc[mask.values, pred_col] = pred_df.loc[mask.values, col] # Guardar CSV fusionado.to_csv( filename, index=False, encoding="utf-8", quoting=1 ) print(f"Resultados guardados en {filename}n")
A continuación, cargue y prepare el conjunto de datos de Solar Flare:
solar_flare_data = fetch_ucirepo(id=89) df = pd.concat([solar_flare_data.data.features, solar_flare_data.data.targets], axis=1) df.columns = [ "zurich_class", "spot_size", "spot_dist", "actividad", "evolución", "prev24_fac", "hist_complex", "region_complex", "area", "area_largest_spot", "c_class", "m_class", "x_class", ] si "id" no está en df.columns: df["id"] = df.index.astype(str) # Convierte códigos numéricos en palabras para forzar un comportamiento categórico replacement_map = {"0": "zero", "1": "one", "2": "two", "3": "tres"} para col in df.columns: si col! = "id": df[col] = df[col].astype(str) df[col] = df[col].replace(replacement_map)
Guardar estadísticas de funciones:
Compute_feature_stats (df, semilla_aleatoria)
Ahora agregue código para simular el escenario de predicción. Primero, divida el conjunto de datos de Solar Flare en contexto y filas de consulta/prueba:
df_test_rows = df.sample(n=n_test_rows, random_state=random_seed).reset_index(drop=True) df_context_full = df.drop(df_test_rows.index).reset_index(drop=True)
Luego, enmascare aleatoriamente algunas columnas en las filas de consulta/prueba:
all_columns = [c para c en df.columns si c != "id"] permitida_mask_columns = rng.choice(all_columns, size=max_masked_columns, replace=False) df_test_rows_masked = df_test_rows.apply( lambda fila: mask_row_values(row, permitida_mask_columns, proporción_máscara, rng), eje=1 ) df_test_rows_masked["id"] = df_test_rows["id"]
Lógica de prefiltrado
Agregue el siguiente código para derivar un conjunto optimizado de filas de contexto (df_context_prefiltered) sobre la marcha utilizando el prefiltrado basado en KNN:
start_prefilter = time.time() n_test = df_test_rows.shape[0]Budget_per_row = max(1, (ctx_max_rows – n_test) // n_test) print(f"Contexto máximo de filas: {ctx_max_rows}") print(f"Número de filas de prueba: {n_test}") print(f"Presupuesto KNN por fila de prueba: {budget_per_row}n") # Codifique usando LabelEncoder (puede usar vectorizadores más sofisticados y modelos integrados en la práctica) encoders = {} df_context_enc = df_context_full.copy() df_test_enc = df_test_rows.copy() para col en df_context_full.columns: if col == "id": continuar le = LabelEncoder() df_context_enc[col] = le.fit_transform(df_context_full[col].astype(str)) df_test_enc[col] = le.transform(df_test_rows[col].astype(str)) codificadores[col] = le X_context = df_context_enc.drop(columns=["id"]).to_numpy() X_test = df_test_enc.drop(columns=["id"]).to_numpy() selected_indices =[]para x_test en X_test: dists = pairwise_distances([x_test], X_context)[0]más cercano = np.argsort(dists)[:budget_per_row] selected_indices.extend(más cercano) df_context_prefiltered = ( df_context_full.iloc[selected_indices] .drop_duplicates() .reset_index(drop=True) ) end_prefilter = time.time() prefilter_time = end_prefilter – start_prefilter print(f"Tiempo de prefiltrado: {prefilter_time:.3f} segundos") print( f"Filas prefiltradas: {len(df_context_prefiltered)} " f"({100 * len(df_context_prefiltered) / len(df_context_full):.2f}% del contexto completo)n" )
Ejecución de experimentos
Agregue las siguientes funciones para llamar al modelo con y sin optimización de contexto (es decir, prefiltrado basado en KNN).
def run_ without_prefiltering(): print("=== CASO 1: SIN PREFILTERING ===") start = time.time() df_context_ without_prefiltering = pd.concat( [df_context_full, df_test_rows_masked], ignore_index=True ) carga útil = build_payload(df_context_ without_prefiltering) éxito, respuesta = safe_call_rpt1(carga útil, token) end = time.time() inference_time = end – start print(f"Tiempo de inferencia del caso 1: {inference_time:.3f} segundos") acc = np.nan si tiene éxito: pred_df = flatten_predictions(response["aiApiResponsePayload"]["predictions"]) pred_df = pred_df.astype(str) true_rows = df_test_rows.set_index("id") masked_rows = df_test_rows_masked.set_index("id") correct, total, acc = evalua_accuracy(pred_df, true_rows, masked_rows) print(f"Precisión del caso 1: {correct}/{total} = {acc:.3f}n") # Utilice el asistente para la exportación sin NaN marca de tiempo = datetime.now().strftime("%Y%m%d_%H%M%S") nombre de archivo = f"results_knn_seed{random_seed}_c_{timestamp}.csv" export_predictions_dynamic(true_rows, masked_rows, pred_df, filename) else: print("Omitir evaluación de precisión.n") return inference_time, acc def run_with_prefiltering(): print("=== CASO 2: PREFILTERADO BASADO EN KNN ===") start = time.time() df_context_with_prefiltering = pd.concat( [df_context_prefiltered, df_test_rows_masked], ignore_index=True ) carga útil = build_payload(df_context_with_prefiltering) éxito, respuesta = safe_call_rpt1(carga útil, token) end = time.time() inference_time = end – start print(f"Tiempo de inferencia del caso 2 (llamada RPT-1): {inference_time:.3f} segundos") acc = np.nan si tiene éxito: pred_df = flatten_predictions(response["aiApiResponsePayload"]["predicciones"]) pred_df = pred_df.astype(str) true_rows = df_test_rows.set_index("id") masked_rows = df_test_rows_masked.set_index("id") correcto, total, acc = evalua_accuracy(pred_df, true_rows, masked_rows) print(f"Exactitud del caso 2: {correct}/{total} = {acc:.3f}n") # Utilice el asistente para la marca de tiempo de exportación sin NaN = datetime.now().strftime("%Y%m%d_%H%M%S") filename = f"results_knn_seed{random_seed}_t_{timestamp}.csv" export_predictions_dynamic(true_rows, masked_rows, pred_df, filename) else: print("Omitir evaluación de precisión.n") return tiempo_inferencia, acc
Finalmente, ejecute los experimentos e imprima/registre los resultados:
def run_experiments(order): resultados = {} para exp en orden: if exp == "sin_prefiltering": resultados["sin_prefiltering"] = run_ without_prefiltering() elif exp == "with_prefiltering": resultados["with_prefiltering"] = run_with_prefiltering() else: print(f"Tipo de experimento desconocido: {exp}") devuelve resultados print("=== EN EJECUCIÓN EXPERIMENTOS ===n") resultados = ejecutar_experimentos(EXPERIMENTO_ORDER) print("n=== RESULTADOS FINALES ===") imprimir(resultados)
Tenga en cuenta que la primera llamada a la API del modelo puede tardar considerablemente más porque el servicio necesita prepararse. Esto puede implicar cargar el modelo en la memoria, inicializar los núcleos de ejecución y establecer conexiones de red. Las llamadas posteriores reutilizan el estado inicializado y, por tanto, tienden a ejecutarse más rápido. Cambiar el orden de los experimentos cambiará cuál absorbe el costo inicial del calentamiento. Para ver esto en acción, intente cambiar el orden de los experimentos en el parámetro de configuración EXPERIMENT_ORDER (por ejemplo, ejecutar el experimento con prefiltrado antes que el que no lo tiene).
La envoltura
A medida que los modelos de base tabular basados en ICL se adopten más ampliamente, el foco de optimización pasará del entrenamiento tradicional de modelos supervisados a la construcción de carga útil contextual. Las características de calidad, costo y latencia de un sistema basado en ICL dependen menos de cómo se entrenó el modelo básico y mucho más de la eficacia con la que se aprovecha la carga útil del contexto en el momento de la inferencia. Es probable que este cambio impulse a las organizaciones hacia patrones repetibles y reutilizables para gestionar las cargas útiles del contexto. Así como la industria finalmente se estandarizó en torno a los almacenes de funciones, los canales de datos y las convenciones de ingeniería rápida, podemos esperar una consolidación similar de las mejores prácticas para el diseño de carga útil contextual. Con el tiempo, estos patrones podrían convertirse en parte del vocabulario compartido de los equipos de desarrollo que trabajan con modelos de base tabulares basados en ICL, elevando la optimización del contexto a una preocupación arquitectónica de primera clase.