Comment prévoir les billets d'avion?

Bonjour à tous!

Ceci est le troisième article sur la façon dont je fais un petit service confortable, qui devrait en théorie aider à la planification des voyages. Dans cet article, je vais vous expliquer comment prévoir les tarifs aériens avec les données Clickhouse, Catboost et 1 To * à portée de main.

image

Pourquoi est-ce?


L'une des principales caractéristiques de cheapster.travel est la combinaison flexible d'itinéraires complexes (plus dans l' article précédent ). Afin de combiner «tout-avec-tout», un cache d'agrégateur est utilisé, dans lequel il n'y a pas toujours de tickets qui sont rarement recherchés, et ils manquent cruellement pour construire des itinéraires complexes. Ceux. billets chauds (bon marché) sur laquelle fonder la route complexe il , mais pas assez 1-2 segments des billets « normaux » (au prix régulier, et non pas sur la direction la plus populaire). C'est ce problème qui m'a amené à la nécessité de construire un modèle qui pourrait prédire les billets d'avion.

Formalisation des tâches


  • Vous devez être en mesure de prévoir les billets pour les vols directs (aller-retour uniquement)
  • Vous devez être en mesure de prévoir et de stocker régulièrement ces informations dans la base de données (scénario simple)
  • Besoin de pouvoir prédire "à la volée" (scénario complexe)
  • Tout cela se produit sur un matériel très limité - donc, un minimum de manipulation avec de grandes quantités de données

Comment faire?


Pour commencer, nous allons former le modèle: préparer le jeu de données, mettre en évidence le nombre maximum d'entités dans les colonnes, le télécharger sur tsv, le charger dans le DataFrame / Pool, analyser, sélectionner les paramètres ... Arrêtez, nous avons trop de données et elles ne tiennent pas en mémoire , - intercepter les erreurs suivantes:

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

Pour contourner cette limitation, il a fallu apprendre itérativement en petits morceaux, cela ressemble à ceci:

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) # <--        

Le résultat était un modèle avec RMSE ~ 100 - en général, j'aurais été satisfait d' un tel résultat, mais après une petite analyse et une "normalisation" des prédictions (les valeurs négatives et très différentes des valeurs min / max dans l'histoire sont amenées aux limites correspondantes des prix historiques) . Après cela, la métrique cible est ~ 80, compte tenu du fait que, selon mon expérience, il n'y a presque pas de logique et de bon sens dans la tarification des billets d'avion.

Fonctionnalités qui affectent le plus le prix:

image

Statistiques des fonctionnalités «Distance entre les villes»:

image

super, nous avons un modèle - il est maintenant temps de l'utiliser. Tout d'abord, ajoutez le modèle KX, cela se fait avec une simple configuration:

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>


Nous faisons un processus de prédiction régulier - il est assez facile de le faire en utilisant Apache Airflow.

Le DAG résultant ressemble à ceci
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
)



Pour la prédiction "à la volée" en utilisant le sql ordinaire:

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

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

Je veux remplacer le fait qu'une telle approche a été choisie, non seulement parce qu'elle est plus facile à mettre en œuvre - il y a encore des avantages:

  • Il n'est pas nécessaire de télécharger des données à l'extérieur du KH (cela signifie plus rapide et moins coûteux sur la charge sur le fer)
  • Pas besoin de faire des processus etl (plus facile = plus fiable)

Nous corrigeons légèrement l'API et le front-end et obtenons les prédictions tant attendues.

Ces prévisions s'intègrent également bien dans la section Historique des prix des billets d' avion : la

image

fonctionnalité est disponible sur cheapster.travel/history (elle s'ouvrira de manière tordue sur le mobile, uniquement sur de grands écrans).

C'est tout, tout une journée productive!

Articles précédents


Une tentative de résoudre le problème du choix des billets d'avion avant les vacances
Une tentative de résoudre le problème du choix des billets d'avion avant les vacances # 2

Une autre caractéristique intéressante


Combinateur d'itinéraires difficiles
Billets complexes (triangles)

PS
Important! Ne prenez pas ces prédictions comme quelque chose qui vous aide à choisir une date d'achat - le modèle peut ne pas prédire correctement, en outre, son adéquation n'a pas été vérifiée par moi ou quelqu'un d'autre (tout à vos risques et périls, sans garanties).

1 To * - c'est si vous téléchargez sur tsv, dans KX, cela prend un ordre de grandeur de moins.

UPD:

Principaux problèmes non évidents lors de l'utilisation des bundles Catboost - Clickhouse


  1. Les caractéristiques catégorielles de KH changent l'ordre et deviennent à la fin (et non dans l'ordre qui était pendant la formation);
  2. modelEvaluate renvoie null - vous devez vérifier si vous avez des valeurs nulles dans les entités, si vous devez les remplacer par nan
  3. Dans les nouvelles versions, il y a un moment non évident avec le format de configuration pour KX, décrit ici

All Articles