الوحدة 6 — التمهيد الأسّي وProphet
بعد الحائط الإحصائيّ الكلاسيكيّ (SARIMA)، ننتقل إلى عائلتين شائعتين جدًّا في المصانع والشركات: التمهيد الأسّي بصيغته Holt-Winters، وProphet الذي طرحه فيسبوك في 2017 وأصبح خيارًا صناعيًا واسعًا. هذه الوحدة تشرح الاثنين، وتُثبّت المقارنة، وتُضيء بصراحة على حدود Prophet التي كثيرًا ما تُخفى في العروض التسويقيّة.
Holt-Winters: التمهيد الأسّي الثلاثيّ
فكرة التمهيد الأسّي بسيطة وأنيقة: كلّ تنبّؤ هو متوسّط موزون للماضي، مع أوزان تتناقص أسّيًّا كلّما ابتعدنا في الزمن. النس خة الأولى (Simple Exponential Smoothing) تُتابع مستوى ثابتًا. Holt يُضيف مكوّنة اتّجاه. أمّا Holt-Winters فيُضيف موسميّة، ويأتي في نسختين جمعيّة وضربيّة تُذكّرنا بتفكيك الوحدة 2.
الصيغ الثلاث (نسخة جمعيّة):
المستوى يتتبّع القيمة الحاليّة، والاتّجاه يتتبّع الميل، والموسميّة تتتبّع النمط الدوريّ. المعاملات تُقدَّر تلقائيًا بتقليل مجموع مربّعات الأخطاء.
from statsmodels.tsa.holtwinters import ExponentialSmoothing
modele_hw = ExponentialSmoothing(
train,
trend="add",
seasonal="add",
seasonal_periods=7,
initialization_method="estimated",
).fit()
prev_hw = modele_hw.forecast(steps=28)
mae_hw = (prev_hw - test).abs().mean()
print(f"MAE Holt-Winters (7) = {mae_hw:.1f}")
ملاحظات عمليّة. أوّلًا، اختيار seasonal="add" أو "mul" يعكس نفس القرار من الوحدة 2 (جمعيّ إن كانت التذبذبات ثابتة الحجم، ضربيّ إن كانت تتضخّم مع الاتّجاه). ثانيًا، Holt-Winters يُدير موسميّة واحدة؛ للأسبوعيّة + السنويّة نحتاج إلى امتداد ETSModel أو Prophet. ثالثًا، أداء Holt-Winters كثيرًا ما يقارب SARIMA على السلاسل الأسبوعيّة البسيطة، بميزة سرعة تدريب مذهلة.
Prophet: التفكيك المعمَّم لأغراض الأعمال
Prophet ليس نموذجًا إحصائيًا كلاسيكيًا بل نموذج انحدار توليديّ مضاف يفكّك السلسلة إلى أربع مكوّنات صريحة:
حيث اتّجاه (قابل للانحناء عند نقاط كسر تُكتشف تلقائيًا)، و موسميّات دوريّة تُمثَّل بمتسلسلات فورييه (لذا يمكن الجمع بين أسبوعيّة وسنويّة بلا صعوبة)، و أثر الأعياد المُصرَّح بها، و ضجيج.
import pandas as pd
from prophet import Prophet
# Prophet يتوقّع أعمدة "ds" و"y".
d = train.reset_index().rename(columns={"date": "ds", "ventes_totales": "y"})
# نُصرّح الأعياد الوطنيّة التي أُثبت أثرها على المبيعات.
feries = pd.DataFrame({
"holiday": "aid",
"ds": pd.to_datetime(["2024-04-10", "2025-03-31", "2026-03-20", "2027-03-09"]),
"lower_window": -1,
"upper_window": 2,
})
modele_p = Prophet(
yearly_seasonality=True,
weekly_seasonality=True,
daily_seasonality=False,
seasonality_mode="multiplicative",
holidays=feries,
changepoint_prior_scale=0.05, # مرونة الاتّجاه: 0.05 افتراضيّ محافظ
)
modele_p.fit(d)
futur = modele_p.make_future_dataframe(periods=28, freq="D")
prev_p = modele_p.predict(futur).tail(28)
mae_p = (prev_p["yhat"].values - test.values).mean()
print(f"MAE Prophet = {abs(mae_p):.1f}")
يُنتج predict عمودًا yhat (التنبّؤ) وعمودَي yhat_lower وyhat_upper (فاصلا ثقة افتراضيّان 80%، قابلان للضبط). قوّة Prophet الحقيقيّة تكمن في سهولة إدماج الأعياد، وفي الرسوم التشخيصيّة الجاهزة التي تعرض المكوّنات (modele_p.plot_components(prev_p)).
نقاط الكسر: قوّة وخطر
Prophet يحدّد تلقائيًا نقاطًا في السلسلة يتغيّر عندها ميل الاتّجاه. الوسيط changepoint_prior_scale (افتراضيّ 0.05) يتحكّم في مدى حرّيّة هذه النقاط: كلّما كبر، اتّبعت السلسلة تغيّرات حادّة، وكلّما صغر، بقي الاتّجاه محافظًا. على سلسلة صيدليّاتنا، رفع القيمة إلى 0.5 يُنتج إفراطًا في التخصيص على تذبذبات ماضية غير قابلة للتكرار؛ خفضها إلى 0.01 يُبطئ الاستجابة لتغيير حقيقيّ (فتح فروع جديدة). قيمة افتراضيّة معتدلة، ثمّ ضبط على تحقّق متدرّج (وحدة 9)، هو المسار الصحيح.