پاسخ کوتاه: Concept Drift چیست؟ تغییری در رابطه آماری بین ورودی‌ها و خروجی هدف در گذر زمان که باعث می‌شود مدلی که روی داده‌های گذشته خوب بوده، بعد از مدتی دقت خود را از دست بدهد. این افت معمولا به خاطر تغییر رفتار کاربران یا بازار، فصل‌ها و رویدادها، تغییر در نحوه جمع آوری داده یا حتی تاثیر خود تصمیم‌های مدل رخ می‌دهد. راه حل، پایش مستمر، تشخیص به موقع و به روزرسانی کنترل شده مدل است؛ از بازآموزی دوره‌ای و پنجره زمانی تا یادگیری آنلاین و بازکالیبراسیون آستانه‌ها.

Concept Drift دقیقا چیست؟

در یادگیری ماشین ما فرض می‌کنیم رابطه بین ویژگی‌ها و برچسب هدف پایدار است. Concept Drift یعنی این رابطه تغییر کرده است؛ برای مثال احتمال برنده شدن یک آگهی با همان ویژگی‌ها امروز با ماه قبل فرق دارد. از نظر آماری، سه الگوی مهم وجود دارد:

  • تغییر توزیع برچسب (Prior shift): نسبت رخداد کلاس‌ها عوض می‌شود؛ مثلا نرخ تقلب کم یا زیاد می‌شود.
  • تغییر توزیع ویژگی‌ها (Covariate shift): ورودی‌ها عوض می‌شوند؛ مثلا منبع ترافیک جدید، الگوی جستجو یا دستگاه‌های کاربران تغییر می‌کند.
  • تغییر خود مفهوم (Concept shift): نگاشت بین X و y عوض می‌شود؛ یعنی برای همان ورودی‌ها نتیجه مورد انتظار دیگر همان قبلی نیست. این شدیدترین حالت است.

داده در دنیای واقعی ایستا نیست. هر جا داده تازه می‌آید یا محیط تصمیم تغییر می‌کند، خطر درفت وجود دارد.

چرا مدل بعد از مدتی ضعیف می‌شود؟

  • تغییر رفتار کاربران و بازار: گرایش‌ها، سلیقه‌ها یا الگوی مصرف تغییر می‌کند.
  • فصلی بودن و شوک‌ها: تعطیلات، کمپین‌ها، رویدادها یا بحران‌ها الگوها را جابجا می‌کنند.
  • تغییر در جمع آوری داده: نسخه جدید اپ، تگ گذاری متفاوت، خطای حسگر یا پلتفرم تبلیغاتی جدید.
  • حلقه‌های بازخوردی مدل: خود تصمیم‌های مدل داده آینده را سوگیر می‌کند (مثلا فقط کاربرانی که پیشنهاد را دیده‌اند رفتار متفاوتی نشان می‌دهند).
  • تاخیر در برچسب‌ها: وقتی y با تاخیر می‌رسد (اعتبارسنجی وام، بازگشت کالا)، مدل مدت‌ها بدون بازخورد درست کار می‌کند.
  • تغییر قوانین یا سیاست‌ها: مقررات ضدتقلب، محدودیت‌های حریم خصوصی یا قیمت گذاری جدید.
  • جابجایی دامنه کاربرد: مدل را در بازاری دیگر یا روی بخش کاربری تازه استفاده می‌کنیم.

چطور درفت را تشخیص دهیم؟

هیچ تک معیار جادویی وجود ندارد. باید همزمان عملکرد مدل، توزیع داده و کیفیت ورودی‌ها را پایش کنید.

۱) پایش عملکرد مدل

  • برای مسائل طبقه بندی: AUC، LogLoss و به خصوص کالیبراسیون (Brier score، ECE) را نسبت به خطوط مبنا رصد کنید.
  • برای رگرسیون: MAE/MAPE در پنجره‌های زمانی کوتاه؛ میانگین خطای باقیمانده نباید منحرف شود.
  • برای انتخاب آستانه: نرخ تبدیل، دقت در کلاس مهم، نرخ هشدار نادرست و نرخ کشف را با بازه اطمینان دنبال کنید.

۲) پایش تغییرات داده (بدون نیاز به برچسب)

  • آزمون‌های آماری روی ویژگی‌ها: KS برای پیوسته‌ها، کای دو برای گسسته‌ها، مقایسه با توزیع مرجع.
  • شاخص پایداری جمعیت (PSI): اختلاف بکت‌های توزیعی بین بازه مرجع و جاری؛ به عنوان راهنمای سرانگشتی، PSI کوچک نشانه ثبات و PSI بزرگ نشانه هشدار است.
  • کشف درفت جریانی: الگوریتم‌های DDM، EDDM یا ADWIN در داده‌های آنلاین تغییر را اعلان می‌کنند.

۳) کیفیت و شِمای داده

  • ولیدیشن شِما: نوع، رنج، نال شدن‌های ناگهانی، توقف جریان رویداد.
  • رانش ویژگی‌های مشتق شده: اگر یک فیچر ناگهان ثابت یا بسیار کم‌تنوع شود.

وقتی برچسب‌ها دیر می‌رسند

از نشانگرهای جایگزین استفاده کنید: توزیع نمره مدل، نرخ عبور از آستانه، همبستگی نمره با سیگنال‌های قابل مشاهده. در داده جریانی، ارزیابی پیشازمانی (prequential) و پنجره‌های لغزان کمک می‌کند.

انواع درفت با مثال‌های قابل تصور

  • Prior shift: نرخ تقلب در تعطیلات افزایش می‌یابد؛ حتی با همان الگوی ویژگی‌ها، نسبت y=1 بیشتر می‌شود.
  • Covariate shift: موتور جستجو الگوریتم نمایش را تغییر می‌دهد و توزیع کوئری‌ها عوض می‌شود؛ مدل CTR شما ورودی متفاوتی می‌بیند.
  • Concept shift: خریداران نسبت به قیمت حساس‌تر شده‌اند؛ همان ترکیب ویژگی‌ها حالا به خرید منجر نمی‌شود. یا در اعتبارسنجی، معیارهای ریسک به دلیل مقررات تازه تغییر کرده‌اند.

راهکارهای مقابله و بازیابی

  • بازکالیبراسیون و تنظیم آستانه: وقتی نمره‌ها از نظر رتبه‌بندی خوبند اما احتمال‌ها یا نرخ کشف منحرف شده، کالیبراسیون دوباره یا تغییر آستانه سریع‌ترین ترمیم است.
  • بازآموزی مبتنی بر پنجره زمانی: روی آخرین N روز/هفته بازآموزی کنید تا مدل به الگوی جاری حساس باشد.
  • وزن دهی زمانی: داده‌های تازه وزن بیشتر بگیرند تا گذشته دور تاثیر کمتری داشته باشد.
  • یادگیری آنلاین/افزایشی: مدل‌های قابل partial_fit (مثل SGD، Naive Bayes) یا کتابخانه‌های جریانی را برای به روز رسانی تدریجی به کار ببرید.
  • چندمدلی و Champion–Challenger: مدل فعلی در تولید و مدل‌های چالشگر در سایه اجرا شوند؛ در صورت برتری، ترفیع کنترل شده انجام دهید.
  • نمونه گیری فعال: اگر برچسب‌ها گران یا دیررس‌اند، برای نواحی پرعدم قطعیت سریعتر برچسب جمع کنید.
  • مهندسی ویژگی مقاوم: از فیچرهایی با پایداری بیشتر استفاده کنید، منابع پرنویز یا ناپایدار را کاهش دهید.
  • حاکمیت داده و نسخ گذاری: داده، کد و پیکربندی‌ها را نسخه‌گذاری کنید تا بدانید دقیقا چه چیزی باعث تغییر شد و قابل بازگشت باشد.

مثال کوتاه با پایتون: پایش درفت و بازآموزی

هدف: نشان دهیم با تغییر توزیع و مفهوم، عملکرد افت می‌کند؛ سپس با تشخیص درفت و بازآموزی، بخشی از عملکرد برمی‌گردد. این کد آموزشی است و برای تولید نیاز به سخت‌گیری بیشتری دارد.

import numpy as np
import pandas as pd
from scipy.stats import ks_2samp
from sklearn.linear_model import SGDClassifier
from sklearn.metrics import roc_auc_score
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import make_pipeline
np.random.seed(42)

# داده مرجع (گذشته)
n0 = 5000
x1_old = np.random.normal(0.0, 1.0, n0)
x2_old = np.random.normal(0.0, 1.0, n0)
noise0 = np.random.normal(0, 0.5, n0)
y_old = ((x1_old + 0.5 * x2_old + noise0) > 0).astype(int)
X_old = np.c_[x1_old, x2_old]

# داده جدید (حال) با درفت: میانگین x1 تغییر کرده و نقش x2 معکوس شده
n1 = 3000
x1_new = np.random.normal(0.6, 1.0, n1)     # covariate shift
x2_new = np.random.normal(0.0, 1.0, n1)
noise1 = np.random.normal(0, 0.5, n1)
y_new = ((0.5 * x1_new - 1.0 * x2_new + noise1) > 0).astype(int)  # concept shift
X_new = np.c_[x1_new, x2_new]

# آموزش روی گذشته
clf = make_pipeline(StandardScaler(), SGDClassifier(loss="log_loss", random_state=42))
clf.fit(X_old, y_old)

# عملکرد روی گذشته و حال
auc_old = roc_auc_score(y_old, clf.predict_proba(X_old)[:, 1])
auc_new = roc_auc_score(y_new, clf.predict_proba(X_new)[:, 1])
print(f"AUC گذشته: {auc_old:.3f} | AUC حال: {auc_new:.3f} (افت: {auc_old - auc_new:.3f})")

# آزمون KS برای x1 بین گذشته و حال
ks_stat, ks_p = ks_2samp(x1_old, x1_new)
print(f"KS x1: stat={ks_stat:.3f}, p-value={ks_p:.3e}")

# PSI ساده برای x1
def psi(expected, actual, bins=10):
    # بکت سازی بر اساس صدک های بازه مرجع
    qs = np.quantile(expected, np.linspace(0, 1, bins + 1))
    qs[0] -= 1e-9
    qs[-1] += 1e-9
    e_counts, _ = np.histogram(expected, bins=qs)
    a_counts, _ = np.histogram(actual, bins=qs)
    e_ratio = np.clip(e_counts / len(expected), 1e-6, None)
    a_ratio = np.clip(a_counts / len(actual), 1e-6, None)
    return float(np.sum((a_ratio - e_ratio) * np.log(a_ratio / e_ratio)))

psi_x1 = psi(x1_old, x1_new, bins=10)
print(f"PSI x1: {psi_x1:.3f}")

# تصمیم: اگر افت عملکرد یا شواهد آماری قوی باشد، بازآموزی روی پنجره اخیر
drift_alert = (auc_old - auc_new > 0.05) or (ks_p < 0.01) or (psi_x1 > 0.25)
print("هشدار درفت:", drift_alert)

if drift_alert:
    clf.fit(X_new, y_new)  # بازآموزی روی پنجره اخیر
    auc_new_retrained = roc_auc_score(y_new, clf.predict_proba(X_new)[:, 1])
    print(f"AUC پس از بازآموزی روی حال: {auc_new_retrained:.3f}")

چطور نتیجه را بررسی کنیم؟ اجرای کد باید نشان دهد AUC روی داده جدید افت کرده، آزمون KS یا PSI تغییر توزیع را گزارش می‌کند و با بازآموزی روی پنجره اخیر، AUC روی داده جاری بهتر می‌شود. در محیط واقعی به جای شبیه سازی، بازه مرجع را ماه‌های گذشته و بازه جاری را هفته یا روزهای تازه بگیرید.

خطاهای رایج و محدودیت‌ها

  • اتکا به یک معیار: فقط دقت یا فقط PSI؛ بهتر است ترکیبی از عملکرد، داده و کیفیت را ببینید.
  • بی توجهی به تاخیر برچسب: بدون y تازه، تکیه صرف بر دقت می‌تواند گمراه کننده باشد.
  • بازآموزی بی برنامه: آموزش بسیار مکرر می‌تواند مدل را ناپایدار کند و هزینه‌زا باشد.
  • نادیده گرفتن کالیبراسیون: حتی با AUC خوب، احتمال‌ها ممکن است بدکالیبره شوند و تصمیم مبتنی بر آستانه را خراب کنند.
  • پوشاندن درفت با مهندسی بیش از حد: افزودن فیچر ناپایدار مشکل را به آینده موکول می‌کند.
  • نشت زمانی در ارزیابی: هرگز داده آینده را در آموزش یا انتخاب ویژگی وارد نکنید؛ از اعتبارسنجی زمانی استفاده کنید.

چک لیست اجرایی برای تولید

  • تعریف خطوط مبنا: AUC/LogLoss/MAE، نرخ‌های اصلی کسب و کار و آستانه‌های هشدار.
  • پایش داده: KS/PSI برای ویژگی‌های کلیدی، نرخ نال، دامنه، شِما.
  • سیاست بازآموزی: دوره‌ای یا محرک محور (بر مبنای افت عملکرد/PSI/KS).
  • کالیبراسیون دوره‌ای: بررسی ECE و تنظیم آستانه‌ها متناسب با نرخ‌های جاری.
  • چندمدلی: نگه داشتن مدل چالشگر و ارزیابی افلاین/آنلاین پیش از جایگزینی.
  • نسخه گذاری و ردیابی: داده، کد، پارامترها و مدل‌ها را ثبت کنید.
  • اعتبارسنجی زمانی: تقسیم‌های زمان محور و بک تست روی چند فصل/رویداد.

گام بعدی چیست؟

اگر مدل در تولید دارید، از همین هفته یک داشبورد پایش مشترک برای عملکرد، توزیع ویژگی‌ها و کیفیت داده بسازید و آستانه‌های هشدار عملی تعریف کنید. سپس یک خط لوله بازآموزی ایمن با پنجره زمانی و کالیبراسیون دوره‌ای پیاده کنید. اگر هنوز در مرحله توسعه هستید، اعتبارسنجی زمانی و حساسیت مدل به سناریوهای درفت را جزو معیارهای پذیرش قرار دهید.