كيف تتنبأ بالطيران؟

تحية للجميع!

هذه هي المقالة الثالثة حول كيفية تقديم خدمة صغيرة ومريحة ، والتي من الناحية النظرية يجب أن تساعد في تخطيط السفر. في هذه المقالة ، سأتحدث عن كيفية التنبؤ بالطيران مع بيانات Clickhouse و Catboost و 1 تيرابايت * في متناول اليد.

صورة

لما هذا؟


واحدة من الميزات الرئيسية لـ cheapster.travel هي المزيج المرن للطرق المعقدة (المزيد في المقالة السابقة ). من أجل الجمع بين "الكل مع الكل" ، يتم استخدام ذاكرة تخزين مؤقت مجمعة ، حيث لا توجد دائمًا تذاكر يتم البحث عنها نادرًا ، ويفتقرون إلى حد كبير لبناء مسارات معقدة. أولئك. تذاكر ساخنة (رخيصة) يعتمد عليها مسار معقد هناك ، ولكن ليس هناك ما يكفي من مقطعين أو اثنين من التذاكر "العادية" (بالسعر العادي ، وليس على الاتجاه الأكثر شيوعًا). كانت هذه المشكلة هي التي دفعتني إلى الحاجة إلى بناء نموذج يمكنه التنبؤ بتذاكر الطيران.

إضفاء الطابع الرسمي على المهام


  • يجب أن تكون قادرًا على توقع تذاكر الرحلات المباشرة (ذهابًا وإيابًا فقط)
  • يجب أن تكون قادرًا على التنبؤ بهذا وتخزينه بانتظام في قاعدة البيانات (سيناريو بسيط)
  • الحاجة إلى القدرة على التنبؤ "بسرعة" (السيناريو المعقد)
  • كل هذا يحدث على أجهزة محدودة للغاية - لذا فإن الحد الأدنى من التلاعب بكميات كبيرة من البيانات

كيف افعلها؟


بادئ ذي بدء ، سنقوم بتدريب النموذج: إعداد مجموعة البيانات ، وتحديد الحد الأقصى لعدد الميزات في الأعمدة ، وتحميلها إلى tsv ، وتحميلها في DataFrame / Pool ، والتحليل ، وتحديد المعلمات ... توقف ، لدينا الكثير من البيانات ولا تتناسب مع الذاكرة ، - التقاط الأخطاء التالية:

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

للتغلب على هذا القيد ، كان من الضروري التعلم بشكل متكرر في قطع صغيرة ، يبدو كما يلي:

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

كانت النتيجة نموذجًا مع RMSE ~ 100 - بشكل عام ، كنت سأكون سعيدًا بمثل هذه النتيجة ، ولكن بعد القليل من التحليل و "تطبيع" التنبؤات (يتم نقل القيم والقيم التي تختلف اختلافًا كبيرًا عن القيم الدنيا / القصوى في التاريخ إلى الحدود المقابلة للأسعار التاريخية) . بعد ذلك ، يكون المقياس المستهدف ~ 80 ، مع الأخذ في الاعتبار حقيقة أنه في تجربتي ، لا يوجد منطق تقريبًا وحس عام في تسعير تذاكر الطيران.

الميزات التي تؤثر على السعر أكثر:

صورة

إحصائيات ميزات "المسافة بين المدن":

صورة

رائع ، لدينا نموذج - حان الوقت الآن لاستخدامه. بادئ ذي بدء ، أضف نموذج KX ، يتم ذلك باستخدام تكوين بسيط:

التكوين
<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>


نجري عملية تنبؤ منتظمة - من السهل القيام بذلك باستخدام Apache Airflow.

يبدو DAG الناتج مثل هذا
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
)



للتنبؤ "سريع" باستخدام sql العادية:

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

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

أريد أن أستبدل حقيقة أن هذا النهج قد تم اختياره ، ليس فقط لأنه أسهل في التنفيذ - لا تزال هناك إيجابيات:

  • ليست هناك حاجة لتحميل البيانات إلى الخارج من KH (وهذا يعني أسرع وأقل تكلفة على الحمل على المكواة)
  • لا حاجة للقيام بعمليات ETL (أسهل = أكثر موثوقية)

نحن نصحح قليلاً واجهة برمجة التطبيقات والواجهة الأمامية ونحصل على التوقعات التي طال انتظارها.

تتناسب هذه التوقعات أيضًا بشكل جيد مع قسم سجل أسعار تذاكر الطيران :

صورة

تتوفر الوظيفة على cheapster.travel/history ( ستفتح بشكل ملتوي على الهاتف المحمول ، فقط الشاشات الكبيرة).

هذا كل شيء ، كل يوم منتج!

المقالات السابقة


محاولة لحل مشكلة اختيار تذاكر الطيران قبل الإجازة
محاولة لحل مشكلة اختيار تذاكر الطيران قبل الإجازة رقم 2

ميزة أخرى مثيرة للاهتمام


الجمع بين الطرق الصعبة
تذاكر معقدة (مثلثات)

PS
هام! لا تأخذ هذه التوقعات كشيء يساعدك على اختيار تاريخ الشراء - قد يتنبأ النموذج بشكل غير صحيح ، علاوة على ذلك ، لم يتم التحقق من كفاءته من قبلي أو من قبل أي شخص آخر (كل ذلك على مسؤوليتك ومخاطرك ، بدون ضمانات).

1 تيرابايت * - هذا إذا قمت بالتحميل إلى tsv ، في KX يستغرق الأمر حجمًا أقل.

تحديث:

أهم المشكلات غير الواضحة عند استخدام حزم Catboost - Clickhouse


  1. السمات الفئوية في KH تغير الترتيب وتصبح في النهاية (وليس بالترتيب الذي كان أثناء التدريب) ؛
  2. modelEvaluate تقوم بإرجاع null - تحتاج إلى التحقق مما إذا كان لديك قيم فارغة في الميزات ، إذا كنت بحاجة إلى استبدالها بـ nan
  3. في الإصدارات الجديدة ، هناك لحظة غير واضحة مع تنسيق التهيئة لـ KX ، الموصوف هنا

All Articles