حوزه پیشبینی سریهای زمانی اخیراً با پیشرفتهای زیادی در زمینه مدلهای بنیادی پیشبینی همراه بوده است.این حرکت با انتشار مدل TimeGPT در اکتبر ۲۰۲۳ آغاز شد، یکی از اولین مدلهای بنیادی که قادر به پیشبینی بدون آموزش اولیه (zero-shot) و تشخیص ناهنجاری است.پس از آن، تلاشهای زیادی برای تطبیق مدلهای بزرگ زبان (LLM) برای پیشبینی، مانند PromptCast و LLMTime صورت گرفت.به دنبال آن، شاهد مدلهای بنیادی متنباز بیشتری شدیم، مانند Lag-LLaMA برای پیشبینی احتمالی بدون آموزش اولیه و Time-LLM که مدلهای زبان موجود را برای پیشبینی سریهای زمانی بازطراحی میکند.
حالا، در مارس ۲۰۲۴، شرکت آمازون نیز با معرفی Chronos وارد این عرصه شده است.در مقاله آنها با عنوان «کرونوس: یادگیری زبان سریهای زمانی»، نویسندگان چارچوبی را برای پیشبینی احتمالی بدون آموزش اولیه (zero-shot) پیشنهاد میکنند که از معماریهای موجود مدلهای زبان مبتنی بر ترانسفورمر استفاده میکند. این چارچوب میتواند با حداقل تغییرات، مدلهای زبان موجود را برای وظایف پیشبینی تطبیق دهد.
در این مقاله، ما به بررسی عملکرد داخلی کرونوس میپردازیم؛ اینکه چگونه آموزش داده شده و از چه تکنیکهایی برای افزایش حجم داده جهت پیش-آموزش مدلهای بزرگ استفاده شده است. سپس کرونوس را در یک آزمایش کوچک با استفاده از پایتون به کار میگیریم تا عملکرد آن را به صورت عملی مشاهده کنیم.
انگیزه پشت کرونوس با یک سوال ساده شروع شد:
آیا مدلهای زبان خوب نباید برای پیشبینی سریهای زمانی هم کارآمد باشند؟
این اولین باری نیست که سعی میکنیم فناوریهای پردازش زبان طبیعی (NLP) را با پیشبینی سریهای زمانی تطبیق دهیم. به هر حال، در هر دو زمینه، مدلها تلاش میکنند تا یک توالی از دادهها را برای پیشبینی توکن بعدی یاد بگیرند، خواه این توکن یک کلمه باشد یا یک مقدار واقعی. با این حال، در NLP، تعداد کلمات برای انتخاب محدود است، در حالی که در سریهای زمانی، این تعداد از نظر فنی نامحدود است؛ سری شما میتواند به طور نامحدود افزایش یا کاهش یابد. بنابراین، استفاده مستقیم از مدلهای زبان بزرگ (LLM) برای پیشبینی منطقی نیست و باید آنها را برای چنین وظیفهای تطبیق داد. در همین راستا، کرونوس یک چارچوبی است که مدلهای LLM موجود را برای پیشبینی سریهای زمانی تطبیق میدهد. این چارچوب به محققان اجازه داد تا خانوادهای از مدلها را برای پیشبینی بدون نیاز به آموزش اولیه (zero-shot) پیشآموزش دهند.
کرونوس از سه مرحله اصلی برای تطبیق مدلهای زبان بزرگ (LLM) برای پیشبینی استفاده میکند.
همانطور که در شکل بالا مشاهده میکنید، کرونوس ابتدا دادهها را مقیاسبندی میکند و پیش از تولید توکنهای زمینه (context tokens)، آنها را کمیسازی (quantization) میکند (به واحدهای مجزا تبدیل میکند). سپس از این توکنها میتوان برای آموزش یک مدل زبان موجود استفاده کرد که توکن زمینه بعدی را پیشبینی کند. این پیشبینی سپس برای به دست آوردن پیشبینی واقعی، کمیزدایی (dequantized) و بدون مقیاس (unscaled) درمیآید.
بیایید نگاه دقیقتری به هر مرحله داشته باشیم.
از آنجایی که مدلهای زبان بزرگ (LLM) با توکنهایی از یک دایره واژگان محدود کار میکنند، نیاز داریم راهی برای نگاشت مقادیر سریهای زمانی به مجموعه محدودی از توکنها بیابیم.
برای این منظور، نویسندگان پیشنهاد میکنند مقادیر سری زمانی را ابتدا مقیاسبندی (scale) کرده و سپس آنها را به تعداد ثابتی از دسته (bin) کمیسازی (quantize) کنیم.
تکنیکهای زیادی برای مقیاسبندی دادهها وجود دارد، مانند مقیاسبندی حداقل-حداکثر (min-max)، مقیاسبندی میانگین (mean) و مقیاسبندی استاندارد (standard). در اینجا، نویسندگان از مقیاسبندی میانگین استفاده کردهاند که از فرمول ساده زیر پیروی میکند:
که در آن m روی 0 تنظیم شده است و s میانگین مقادیر مطلق سری است.
همانطور که در شکل بالا مشاهده میکنید، مقیاسبندی دادهها، شکل آن را در طول زمان حفظ میکند، اما مقادیر را به مجموعه کوچکتری محدود میکنیم. به این ترتیب، نمایش این مقادیر به عنوان یک مجموعه ثابت از توکنها برای آموزش LLM آسانتر خواهد بود.
پس از مقیاسبندی دادهها، کمیسازی (quantization) اعمال میشود. این یک مرحله حیاتی است، زیرا سری مقیاسبندیشده هنوز مقادیر حقیقی دارد و LLM نمیتواند این نوع داده را مدیریت کند.
کمیسازی فرآیندی است که مقادیر پیوسته و نامحدود را به مجموعهای از مقادیر گسسته و محدود نگاشت میکند. اینگونه است که میتوانیم از دادههای سریهای زمانی توکنهای زمینه (context token) ایجاد کنیم تا آنها را به LLM ها تغذیه کنیم.
در چارچوب کرونوس، محققان از باینبندی (binning) استفاده میکنند که شامل ایجاد دستههایی (bin) است که در آن کوچکترین مقدار به اولین دسته تعلق دارد و بزرگترین مقدار سری به آخرین دسته اختصاص داده میشود. سپس، هر لبه دسته به طور یکنواخت در کل محدوده مقادیر سری پخش میشود و هر مقدار به یک دسته اختصاص مییابد.
در شکل بالا، نمونهای از کمیسازی با استفاده از باینبندی بر اساس صدک (percentile) را مشاهده میکنیم. ما ۱۰۰ دسته تعریف میکنیم، یک دسته برای هر صدک، و مقادیر مقیاسبندیشده را به دسته مربوطه اختصاص میدهیم.
به این ترتیب، اکنون مجموعهای از توکنهای ثابت داریم، زیرا با باینبندی بر اساس صدک، توکن تنها میتواند مقادیر ۱ تا ۱۰۰ را داشته باشد.
بنابراین، ما با موفقیت یک سری زمانی را از مقادیر حقیقی نامحدود به مجموعه ثابتی از توکنها تبدیل کردهایم، که همان چیزی است که مدلهای زبان بزرگ (LLM) انتظار دارند.
توجه داشته باشید که در کرونوس، رمزگذاری ویژگیهای زمانی رایج مانند روز هفته، روز سال و غیره وجود ندارد. در عوض، کرونوس سری را به عنوان یک توالی ساده از توکنها در نظر میگیرد.
پس از توکنیزهکردن سری زمانی، میتوان آن را برای آموزش به LLM فرستاد. در اینجا، نویسندگان از تابع اتلاف (loss function) آنتروپی متقاطع دستهبندیشده (categorical cross-entropy) به عنوان تابع اتلاف برای آموزش مدل استفاده میکنند.
این بدان معناست که مدل یاد میگیرد دستههای (bin) نزدیک به هم را با هم مرتبط کند. به عبارت دیگر، آنها در حال یادگیری یک توزیع (distribution) هستند.
این کار به مدل اجازه میدهد تا توزیعهای دلخواه را بیاموزد و به طور بالقوه تعمیمپذیری بهتری روی دادههای نادیده شده برای پیشبینی بدون نیاز به آموزش اولیه (zero-shot) داشته باشد.
این رویکرد همچنین با رویکردهای معمولی متفاوت است. به طور معمول، مدلها نوعی توزیع را برای پیشبینیهای احتمالی اعمال میکنند، مانند توزیع گاوسی (Gaussian) یا توزیع تی-استیودنت (Student’s-t) (که دومی در Lag-LLaMA استفاده میشود).
پس از آموزش مدل، میتواند پیشبینی را انجام دهد.
ما به سادگی توکنهای زمینه را تغذیه میکنیم، که مدل از آنها برای تولید توالیای از توکنهای آتی استفاده میکند.
سپس آن توکنها کمیزدایی میشوند که منجر به پیشبینیهای مقیاسشده میشود.
در انتها تبدیل برعکس است، که دادهها را به مقیاس اصلی برمیگرداند و سپس پیشبینیهای نهایی را به دست میآوریم.
حالا که فهمیدیم کرونوس چگونه LLM ها را برای پیشبینی سریهای زمانی تطبیق میدهد، چالش بزرگی برای ایجاد مدلهای بنیادی پیشبینی با عملکرد بالا وجود دارد: دسترسی به داده.
برای این منظور، محققان دو روش افزایش داده را پیشنهاد میکنند: TSMix و KernelSynth.
مدلهای بنیادی به مجموعه دادههای بزرگ و متنوعی متکی هستند که این برای زبان طبیعی امکانپذیر است، ولی برای دادههای سریهای زمانی به راحتی قابل دسترسی نیستند.
بنابراین، محققان برای افزایش و متنوعسازی مجموعه داده آموزشی خود از دو تکنیک افزایش داده استفاده کردند.
ترکیب سریهای زمانی TSMix
اولین روش TSMix است که مخفف Time Series Mixup میباشد.
ایده پشت TSMix بسیار ساده است. این روش به طور تصادفی زیرمجموعههایی از سریهای زمانی موجود با طول مشخص را نمونهبرداری میکند، آنها را مقیاسبندی میکند و ترکیب محدب (convex combination) آنها را میگیرد.
در شکل بالا، مشاهده میکنیم که چگونه سریها نمونهبرداری و سپس با سریهای دیگر با استفاده از وزنهای مختلف ترکیب میشوند تا سری دیگری برای آموزش ایجاد شود.
تولید سریهای زمانی مصنوعی KernelSynth
برای گسترش بیشتر مجموعه داده آموزشی خود، نویسندگان همچنین KernelSynth را پیشنهاد میکنند، روشی برای تولید سریهای زمانی مصنوعی با استفاده از فرآیندهای گاوسی، همانطور که در زیر نشان داده شده است.
با KernelSynth، مجموعهای از کرنلها وجود دارد که رفتار کلی سریهای زمانی را تعریف میکنند: خطی، دورهای و غیره. در شکل بالا، مشاهده میکنیم که هسته خطی با استفاده از ضرب با خود ترکیب میشود، که منجر به شکل درجه دوم میشود. سپس، این دوباره با هسته دورهای با استفاده از جمع ترکیب میشود و یک الگوی فصلی اضافه میکند. از آنجا، هسته به یک فرآیند گاوسی تغذیه میشود که وظیفه تولید نمونههایی را در یک توزیع خاص بر عهده دارد.
حالا که فهمیدیم کرونوس چگونه کار میکند و از چه تکنیکهای افزایش داده برای پیشآموزش مدلها استفاده شده است، بیایید در یک پروژه کوچک پیشبینی با استفاده از پایتون از آنها استفاده کنیم.
با استفاده از چارچوب کرونوس، محققان از قبل پنج مدل را پیشآموزش دادهاند که به صورت عمومی برای پیشبینی بدون نیاز به آموزش اولیه (zero-shot) در دسترس هستند.
این مدلها با معماری T5 که توسط گوگل پیشنهاد شده است، آموزش داده شدهاند و در اندازههای مختلف در دسترس هستند:
وزنهای مدلها همگی روی HuggingFace در دسترس هستند.
برای این آزمایش کوچک، از chronos-t5-large استفاده خواهیم کرد و آن را روی مجموعه داده ماهانه M3 آزمایش میکنیم. این مجموعه شامل 1428 سری زمانی منحصر به فرد، همگی با فرکانس ماهانه، از صنایع مختلف است. این مجموعه در اینجا در دسترس است.
عملکرد کرونوس را همچنین با عملکرد MLP و N-BEATS مقایسه خواهیم کرد. توجه داشته باشید که M3 یک مجموعه داده نسبتاً کوچک است و روشهای یادگیری عمیق عملکرد چندان خوبی ندارند، اما این مدلها معمولاً روی مجموعههای داده کوچک عملکرد خوبی دارند.
ابتدا باید Chronos را نصب کنیم
pip install git+https://github.com/amazon-science/chronos-forecasting.git
سپس بقیه کتابخانه ها را نصب میکنیم.
import time from datasetsforecast.m3 import M3 import torch from chronos import ChronosPipeline
پس از آن، می توانیم مجموعه داده را بارگذاری کنیم. برای این کار کتابخانه datasetsforecast
، یک رابط پایتون برای دانلود مجموعه داده به ما می دهد.
Y_df, *_ = M3.load(directory='./', group='Monthly')
حالا می توانیم Chronos را مقداردهی اولیه کنیم. یک ابجکت ازChronosPipeline
میسازیم و مدل از پیش آموزش دیده ای را که می خواهیم مشخص می کنیم.
pipeline = ChronosPipeline.from_pretrained(
"amazon/chronos-t5-large",
device_map="cuda",
torch_dtype=torch.bfloat16,
)
حالا برای پیش بینی آمادهایم!
برای این کار، روی تمام سریهای موجود در مجموعه داده M3 حلقه میزنیم و ۱۲ مقدار آخر را به عنوان مجموعه تست برای ارزیابی پیشبینیها نگه میداریم.
کرونوس یک torch.tensor
به عنوان ورودی انتظار دارد، بنابراین باید سری زمانی pandas.Series
خود را به یک آرایه numpy.array
و سپس به یک تنسور تبدیل کنیم.
تمام مقادیر به عنوان توکنهای زمینه به مدل داده میشوند.
سپس متد predict
را در پایپلاین فراخوانی میکنیم.
برای به دست آوردن پیشبینیهای واقعی، باید مشخص کنیم که به کدام چارک (quantile) علاقهمند هستیم، زیرا کرونوس پیشبینیهای احتمالی را تولید میکند. در این مورد، ما فقط به میانه (median) علاقهمند هستیم. با این حال، برای یک بازه اطمینان ۸۰ درصد، میتوانید لیست زیر از چارکها را پاس دهید: [۰.۱، ۰.۵، ۰.۹]. اطمینان حاصل کنید که همیشه چارک ۰.۵ را در نظر بگیرید، زیرا این مقدار نشاندهنده میانه است.
horizon = 12 batch_size = 12 actual = [] chronos_large_preds = [] start = time.time() all_timeseries = [ torch.tensor(sub_df["y"].values[:-horizon]) for _, sub_df in Y_df.groupby("unique_id") ] for i in tqdm(range(0, len(all_timeseries), batch_size)): batch_context = all_timeseries[i : i + batch_size] forecast = pipeline.predict(batch_context, horizon) predictions = np.quantile(forecast.numpy(), 0.5, axis=1) chronos_large_preds.append(predictions) chronos_large_preds = np.concatenate(chronos_large_preds) chronos_large_duration = time.time() - start print(chronos_large_duration)
در این مرحله، پیشبینیهایی از کرونوس داریم. پس بیایید عملکرد آن را با سایر مدلها مانند MLP و N-BEATS مقایسه کنیم.
حالا بیایید یک مدل MLP و N-BEATS آموزش دهیم. در اینجا، پیکربندی پیشفرض را حفظ کرده و از اندازهی ورودی معادل سه برابر افق (horizon) استفاده میکنیم.
from neuralforecast.models import MLP, NBEATS from neuralforecast.losses.pytorch import HuberLoss from neuralforecast.core import NeuralForecast horizon = 12 val_size = 12 test_size = 12 # Fit an MLP mlp = MLP(h=horizon, input_size=3*horizon, loss=HuberLoss()) nf = NeuralForecast(models=[mlp], freq='M') mlp_forecasts_df = nf.cross_validation(df=Y_df, val_size=val_size, test_size=test_size, n_windows=None, verbose=True) # Fit N-BEATS nbeats = NBEATS(h=horizon, input_size=3*horizon, loss=HuberLoss()) nf = NeuralForecast(models=[nbeats], freq='M') nbeats_forecasts_df = nf.cross_validation(df=Y_df, val_size=val_size, test_size=test_size, n_windows=None, verbose=True)
اکنون که پیشبینیهایی از همه مدلهای خود داریم، بیایید عملکرد آنها را ارزیابی کنیم.
بیایید پیشبینیهای هر مدل را در برابر مقادیر واقعی ترسیم کنیم.
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(14,12)) for i, ax in enumerate(axes.flatten()): id = f"M{i+1}" mlp_to_plot = mlp_forecasts_df[mlp_forecasts_df['unique_id'] == id] nbeats_to_plot = nbeats_forecasts_df[nbeats_forecasts_df['unique_id'] == id] ax.plot(mlp_to_plot['ds'], mlp_to_plot['y'], label='actual') ax.plot(mlp_to_plot['ds'], mlp_to_plot['MLP'], ls='--', label='MLP') ax.plot(nbeats_to_plot['ds'], nbeats_to_plot['NBEATS'], ls=':', label='N-BEATS') ax.plot(nbeats_to_plot['ds'], preds[0+12*i:12+12*i], ls='-.', label='Chronos') ax.legend()
در شکل بالا، پیشبینیهای هر مدل برای چهار سری اول مجموعه داده ما را مشاهده میکنیم. باز هم میبینیم که مدلهای یادگیری عمیق با کمبود داده دست و پنجه نرم میکنند.
با این حال، از آنجایی که کرونوس یک مدل یادگیری عمیق است، میخواستم مقایسه را در حوزه یادگیری عمیق نگه دارم.
حالا بیایید میانگین خطای مطلق (MAE) و میانگین تقارنی خطای مطلق درصدی (sMAPE) را برای هر مدل محاسبه کنیم.
برای این کار، از کتابخانه utilsforecast
استفاده میکنیم که با بسیاری از توابع رایج مورد استفاده در پروژههای سریهای زمانی برای ارزیابی مدل، ترسیم و پیش پردازش داده همراه است.
from utilsforecast.losses import mae, smape from utilsforecast.evaluation import evaluate forecast_df = mlp_forecasts_df forecast_df["NBEATS"] = nbeats_forecasts_df["NBEATS"] forecast_df["Chronos-Tiny"] = chronos_pred_df["chronos_tiny_pred"] forecast_df["Chronos-Large"] = chronos_pred_df["chronos_large_pred"] evaluation = evaluate( forecast_df, metrics=[mae, smape], models=["MLP", "NBEATS", "Chronos-Tiny", "Chronos-Large"], target_col="y", ) time = pd.DataFrame( { "metric": ["Time"], "MLP": [mlp_duration], "NBEATS": [nbeats_duration], "Chronos-Tiny": [chronos_tiny_duration], "Chronos-Large": [chronos_large_duration], } ) avg_metrics = ( evaluation.drop(columns="unique_id").groupby("metric").mean().reset_index() ) avg_metrics = pd.concat([avg_metrics, time]).reset_index(drop=True) avg_metrics
در جدول بالا میبینیم که N-BEATS مدل برتر است، زیرا کمترین MAE و sMAPE را به دست میآورد.
مهم است که توجه داشته باشید که N-BEATS و MLP نسبت به کرونوس به عملکرد بهتری در کسری از زمان دست مییابند.
برای این آزمایش، مدل chronos-large تقریباً سه برابر زمان بیشتری را برای انجام پیشبینی بدون نیاز به آموزش اولیه (zero-shot) روی مجموعه داده M3 صرف کرد، در مقایسه با برازش و پیشبینی با N-BEATS. از طرف دیگر، chronos-tiny زمان استنتاج (inference) سریعتری را به دست میآورد، اما این به قیمت عملکرد پایینتر تمام میشود.
همچنین این آزمایش تحت تأثیر محدودیت دسترسی من به قدرت محاسباتی قرار دارد.
ما میتوانیم این آزمایش را با موارد زیر قویتر کنیم:
برای یک بنچمارک جامعتر از Chronos، به اینجا مراجعه کنید: [مقاله بررسی جامع Chronos]
با آزمایش Lag-LLaMA و Time-LLM ، میتوانم بگویم که استفاده از Chronos بسیار سادهتر و سریعتر است.
من معتقدم محققان کار بزرگی در ایجاد یک مدل بنیادی که واقعاً قابل استفاده باشد انجام دادهاند. اگرچه آزمایش من ۲۰ دقیقه طول کشید، اما همچنان بسیار سریعتر از Lag-LLaMA و Time-LLM است.
با این حال، ما هنوز یک مدل بنیادی نداریم که عملکردی به طور مداوم بهتر از مدلهای خاص داده داشته باشد.
اگرچه مدلهای بنیادی به پیشبینی بدون نیاز به آموزش اولیه (zero-shot) اجازه میدهند که از نظر فنی خط لوله پیشبینی را ساده میکند، اما آنها به قدرت محاسباتی زیادی نیاز دارند، اجرای آنها زمان زیادی میبرد و در نهایت عملکردی بهتر از مدلهای ساده ندارند.
مدلهای پیشبینی بنیادی هنوز در مراحل اولیه توسعه هستند، اما برای من، مدلهای خاص داده هنوز برنده هستند.
با تشکر از اینکه خواندید. امیدوارم چیز جدیدی یاد گرفته باشید!