¿Cómo pronosticar el pasaje aéreo?

¡Hola a todos!

Este es el tercer artículo sobre cómo hago un servicio pequeño y cómodo, que en teoría debería ayudar con la planificación del viaje. En este artículo, hablaré sobre cómo predecir el pasaje aéreo con los datos de Clickhouse, Catboost y 1TB * disponibles.

imagen

¿Para qué sirve?


Una de las principales características de cheapster.travel es la combinación flexible de rutas complejas (más en el artículo anterior ). Para combinar "todo con todos", se utiliza una memoria caché de agregador, en la que no siempre hay tickets que rara vez se buscan, y son muy escasos para construir rutas complejas. Aquellos. boletos calientes (baratos) en los que basar rutas complejas allí , pero no suficientes 1-2 segmentos de los boletos "normales" (al precio regular, no en la dirección más popular). Fue este problema lo que me llevó a la necesidad de construir un modelo que pudiera predecir el pasaje aéreo.

Formalización de tareas


  • Debe poder predecir boletos para vuelos directos (solo ida y vuelta)
  • Debe poder predecir y almacenar esto regularmente en la base de datos (escenario simple)
  • Necesita poder predecir "sobre la marcha" (escenario complejo)
  • Todo esto sucede en un hardware muy limitado, por lo tanto, un mínimo de manipulación con grandes cantidades de datos.

¿Cómo hacerlo?


Para comenzar, entrenaremos el modelo: prepare el conjunto de datos, seleccione el número máximo de características en las columnas, cárguelo en tsv, cárguelo en el DataFrame / Pool, analice, seleccione los parámetros ... Detener, tenemos demasiados datos y no caben en la memoria , - detecte los siguientes errores:

MemoryError: Unable to allocate array with shape (38, 288224989) and data type float64
OSError: [Errno 12] Cannot allocate memory

Para sortear esta limitación, era necesario aprender iterativamente en pequeños pedazos, se ve así:

model = CatBoostRegressor(cat_features=cat_features,
          iterations=100,
          learning_rate=.5,
          depth=10,
          l2_leaf_reg=9,
          one_hot_max_size=5000)

for df in tqdm(pd.read_csv('history.tsv', sep='\t', 
                           na_values=['\\N'], 
                           chunksize=2_000_000)):
    ...
     model.fit(X=df[df.columns[:-1]][:train_size].values,
                  y=df['price'][:train_size].values,
                  eval_set=eval_pool,
                  verbose=False,
                  plot=False,
                  init_model=model) # <--        

El resultado fue un modelo con RMSE ~ 100; en general, me hubiera gustado tal resultado, pero después de un pequeño análisis y "normalización" de las predicciones (los valores negativos y los valores que difieren mucho de los valores mínimos / máximos en la historia se llevan a los límites correspondientes de los precios históricos) . Después de eso, la métrica objetivo es ~ 80, teniendo en cuenta el hecho de que, en mi experiencia, casi no hay lógica ni sentido común en el precio de los boletos aéreos.

Funciones que más afectan el precio:

imagen

Estadísticas para funciones "Distancia entre ciudades":

imagen

Genial, tenemos un modelo, ahora es el momento de usarlo. En primer lugar, agregue el modelo KX, esto se hace con una configuración simple:

Config
<models>
    <model>
        <!-- Model type. Now catboost only. -->
        <type>catboost</type>
        <!-- Model name. -->
        <name>price</name>
        <!-- Path to trained model. -->
        <path>/opt/models/price_iter_model_2.bin</path>
        <!-- Update interval. -->
        <lifetime>0</lifetime>
    </model>
</models>


Hacemos un proceso de predicción regular: es bastante fácil hacerlo con Apache Airflow.

El DAG resultante se ve así
image
DAGa ( Airflow):

SimpleHttpOperator
insert_ow_in_tmp = SimpleHttpOperator(
    task_id='insert_ow_in_tmp',
    http_conn_id='clickhouse_http',
    endpoint=dll_endpoint,
    method='POST',
    data=sql_templates.INSERT_OW_PREDICTIONS_IN_TMP,
    pool='clickhouse_select',
    dag=dag
)



Para la predicción "sobre la marcha" usando sql ordinario:

select origin, destination, date,
         modelEvaluate('price', *)  predicted_price -- ,   
from log.history

+--------+-------------+------------+-----------------+
| origin | destination | date       | predicted_price |
+--------+-------------+------------+-----------------+
| VKO    | DEB         | 2020-03-20 | 3234.43244      |
+--------+-------------+------------+-----------------+
--* ,   

Quiero reemplazar el hecho de que se haya elegido un enfoque de este tipo, no solo porque es más fácil de implementar, todavía hay ventajas:

  • No es necesario cargar datos al exterior del KH (esto significa más rápido y menos costoso en la carga en la plancha)
  • No es necesario realizar procesos etl (más fácil = más confiable)

Corregimos ligeramente la API y el front end y obtenemos las predicciones tan esperadas.

Estas predicciones también encajan bien en la sección Historial de precios de boletos aéreos : la

imagen

funcionalidad está disponible en cheapster.travel/history (se abrirá torcidamente en el móvil, solo en pantallas grandes).

¡Eso es todo, todo un día productivo!

Artículos anteriores


Un intento de resolver el problema de elegir boletos aéreos antes de las vacaciones
Un intento de resolver el problema de elegir boletos aéreos antes de las vacaciones # 2

Otra característica interesante.


Combinador de rutas difíciles
Entradas complejas (triángulos)

PS
¡Importante! No tome estas predicciones como algo que lo ayude a elegir una fecha de compra: el modelo puede no predecir correctamente, además, ni yo ni nadie más ha verificado su idoneidad (todo bajo su propio riesgo y riesgo, sin garantías).

1TB *: esto es si sube a tsv, en KX se necesita un orden de magnitud menor.

UPD:

Principales problemas no obvios al usar paquetes Catboost - Clickhouse


  1. Las características categóricas en KH cambian el orden y se vuelven al final (y no en el orden que estaba durante el entrenamiento);
  2. modelEvaluate devuelve nulo: debe verificar si tiene valores nulos en las características, si necesita reemplazarlos con nan
  3. En las nuevas versiones hay un momento no obvio con el formato de configuración para KX, descrito aquí

All Articles