عنوان
در مقالهٔ اول فهمیدیم یک شبکهٔ عصبی چطور از اشتباههایش یاد میگیره. در مقالهٔ دوم دیدیم وقتی داده ترتیب دارد، مسیر معماریها از MLP و RNN به LSTM، Attention و Transformer میرسد.
حالا یک سؤال تازه داریم.
فرض کن برای پیشبینی مصرف برق یک مدل ساختهایم. برای پیشبینی ترافیک، فروش فروشگاه، دمای هوا و قیمت یک دارایی هم مدلهای جداگانهای میسازیم. هر بار باید داده جمع کنیم، معماری انتخاب کنیم، مدل را از اول آموزش بدهیم و امیدوار باشیم دادهٔ همان مسئله برای یادگیری کافی باشد.
اما آیا میشود یک مدل قبل از رسیدن به مسئلهٔ ما، تعداد بسیار زیادی سری زمانی از دنیا دیده باشد؟ مدلی که مفهوم روند، فصل، شوک، نوسان، بازگشت و تکرار را فقط از یک سهم یا یک فروشگاه یاد نگرفته باشد؛ بلکه از مجموعهای بزرگ و متنوع از سریها تجربه گرفته باشد و بعد، با دیدن سری تازهٔ ما، بدون آموزش دوباره یک پیشبینی قابلقبول تولید کند؟
ایدهٔ Foundation Model برای سری زمانی از همینجا شروع میشود.
TimesFM یکی از جدیترین پاسخهای اولیه به این سؤال است: یک Transformer از نوع decoder-only که گوگل آن را روی حجم بزرگی از سریهای زمانی پیشآموزش داده تا بتواند روی دادهای که قبلاً ندیده، بهصورت zero-shot پیشبینی انجام دهد.
من به چنین مدلی میگویم «مدل دنیادیده». نه چون همهچیز را میداند؛ چون پیش از ملاقات با مسئلهٔ ما، شکلهای بسیار متنوعی از رفتار زمان را دیده است.
برای فهم Foundation Model، اول باید فرق سه نوع نگاه به مسئله را ببینیم.
در مدلهای محلی، برای هر سری زمانی یک مدل جدا میسازیم:
فروشگاه A → مدل A
فروشگاه B → مدل B
فروشگاه C → مدل C
ARIMA و Exponential Smoothing معمولاً در چنین قالبی استفاده میشوند. مدلِ فروشگاه A فقط تاریخچهٔ همان فروشگاه را میبیند.
این روش میتواند روی یک سری مشخص خوب باشد، اما دانشی که مدل A پیدا کرده خودبهخود به مدل B منتقل نمیشود. اگر هر سری کوتاه یا پرنویز باشد، دادهٔ زیادی هم برای یادگیری نداریم.
قدم بعدی این بود که یک مدل را روی چند سری با هم آموزش بدهیم:
فروشگاههای A, B, C, ...
↓
یک مدل مشترک
مدل global میتواند الگوهای مشترک میان سریها را یاد بگیرد. مثلاً فروش پایان هفته شاید در بسیاری از شعب دیده شود.
اما این مدل هنوز معمولاً برای همان مجموعهداده و همان مسئله ساخته شده است. یک مدل فروش فروشگاهها الزاماً قرار نیست بدون آموزش روی ترافیک شهری یا مصرف برق خوب عمل کند.
Foundation Model تلاش میکند مرحلهٔ آموزش عمومی را از مسئلهٔ نهایی جدا کند:
سریهای فراوان از دامنهها و فرکانسهای مختلف
↓
Pretraining
↓
یک مدل عمومی
↓
استفاده روی مسئلههای جدید
مدل قرار نیست نام تکتک سریهایی را که دیده حفظ کند. هدف این است که ساختارهای قابلانتقال را یاد بگیرد؛ چیزهایی مثل:
بزرگبودن بهتنهایی یک مدل را Foundation Model نمیکند.
سه ویژگی کنار هم مهماند:
پس یک Transformer بسیار بزرگ که فقط روی تاریخچهٔ یک سهم آموزش دیده، Foundation Model نیست. در مقابل، مدلی کوچکتر که روی دامنههای بسیار متنوع آموزش دیده و روی سریهای ندیده انتقال پیدا میکند، به فلسفهٔ Foundation Model نزدیکتر است.
در آموزش عادی میگوییم:
این دادهٔ مشخص را بگیر
برای همین مسئله یک مدل بساز
در pretraining میگوییم:
این جهان بزرگ از دادهها را ببین
یک دانش عمومی یاد بگیر
بعداً آن دانش را روی مسئلههای مختلف مصرف میکنیم
پیشآموزش یک هدف جادویی جدا ندارد. در TimesFM، مدل بارها گذشتهٔ یک سری را میبیند و باید آیندهٔ آن را پیشبینی کند. تفاوت اصلی در مقیاس و تنوع دادههاست.
مسئلهٔ عمومی پیشبینی
C طول گذشتهای است که مدل میبیند و H طول آیندهای است که باید پیشبینی کند.
این تمرین روی تعداد زیادی سری تکرار میشود. اگر مدل در این مسیر فقط یک الگوی خاص را حفظ کند، روی سریهای دیگر شکست میخورد. برای کمکردن loss در یک مجموعهٔ بسیار متنوع، مجبور میشود الگوهای عمومیتری پیدا کند.
این سه اصطلاح را باید از هم جدا کنیم.
مدل وزنهایش را تغییر نمیدهد. فقط گذشتهٔ سری جدید را میگیرد و آینده را پیشبینی میکند:
سری جدید → مدل ازپیشآموزشدیده → پیشبینی
مدل ممکن است هرگز همین dataset یا همین دارایی را در pretraining ندیده باشد.
این اصطلاح در سری زمانی گاهی به این معنی استفاده میشود که مقدار کمی context یا دادهٔ مسئلهٔ جدید در اختیار مدل است. بسته به مقاله، ممکن است وزنها تغییر کنند یا نکنند؛ پس باید تعریف هر پژوهش را جدا خواند.
در fine-tuning، وزنهای مدل یا بخشی از آنها با دادهٔ مسئلهٔ جدید بهروزرسانی میشوند:
مدل عمومی + دادهٔ مالی
↓
آموزش تکمیلی
↓
مدل سازگارشده با امور مالی
در زبان، با وجود پیچیدگی، واحدهایی مثل کلمه و subword داریم. مدل میتواند یک vocabulary بسازد. اما سری زمانی vocabulary مشخصی ندارد.
عدد 100 در دو سری ممکن است معنای کاملاً متفاوتی داشته باشد:
دمای 100 درجه
قیمت 100 دلار
فروش 100 واحد
ضربان 100 بار در دقیقه
چالشهای مهمتر:
پس کپیکردن مستقیم یک LLM و ریختن عددها داخل آن، مسئله را حل نمیکند. باید تعریف کنیم «توکن» در سری زمانی چیست، سریها چطور هممقیاس میشوند و مدل با horizonهای مختلف چگونه کار میکند.
TimesFM مخفف Time Series Foundation Model و محصول Google Research است. مقالهٔ اصلی آن با عنوان A Decoder-Only Foundation Model for Time-Series Forecasting در ICML 2024 منتشر شد.
ایدهٔ اصلیاش آشناست:
همانطور که یک مدل زبانی با دیدن توکنهای قبلی توکن بعدی را میسازد، یک مدل سری زمانی هم میتواند با دیدن patchهای گذشته patch آینده را پیشبینی کند.
اما کلمهای در کار نیست. ورودی مجموعهای از عددهاست و باید آنها را به چیزی شبیه token تبدیل کنیم.
تا زمان نوشتن این مقاله، نسلهای جدیدتری از TimesFM منتشر شدهاند. بااینحال برای اینکه بحث معماری دقیق و قابلبازتولید بماند، تمرکز این نوشته روی نخستین checkpoint عمومی مدل است:
google/timesfm-1.0-200m-pytorch
بنابراین هرجا در این مقاله از TimesFM حرف میزنیم، منظور اصلی نسخهٔ 1.0 با حدود ۲۰۰ میلیون پارامتر است؛ مگر اینکه صریحاً نسخهٔ دیگری نام برده شود.
نسخهٔ مقالهٔ پژوهشی و checkpoint نهایی عمومی در بعضی عددهای گزارششده تفاوت دارند. نسخههای اولیهٔ متن پژوهشی آزمایش مدلی حدود ۲۲۵ میلیون پارامتر روی حدود یک میلیارد time point را شرح میدهند؛ انتشار عمومی و گزارش نهایی Google Research از checkpoint حدود ۲۰۰ میلیون پارامتری و corpus گسترشیافتهٔ ۱۰۰ میلیارد time point حرف میزنند. در این نوشته، مشخصات checkpoint عمومی معیار عملی ماست.
در Transformer اصلی ترجمه، Encoder ابتدا کل ورودی را پردازش میکرد و Decoder با کمک Cross-Attention از آن برای ساخت خروجی استفاده میکرد. TimesFM این دو مسیر جدا را ندارد؛ همهچیز داخل یک stack از blockهای decoder-style اتفاق میافتد.
هر patch فقط خودش و patchهای قبل از خودش را میبیند:
patch 1 → میتواند patch 1 را ببیند
patch 2 → میتواند patchهای 1 و 2 را ببیند
patch 3 → میتواند patchهای 1 تا 3 را ببیند
این محدودیت با یک causal mask ساخته میشود. اگر patch اول هنگام آموزش patchهای بعدی را میدید، جواب آینده را از قبل در اختیار داشت و عملاً تقلب میکرد.
اما این به معنی ازدسترفتن گذشته نیست. نمایش patch سوم، هر سه patch را دیده و برای پیشبینی بخش بعدی استفاده میشود. فقط اجازه ندادهایم اطلاعات از آینده به گذشته نشت کند.
مزیت این انتخاب این است که معماری یکپارچهتر میشود، جهت حرکت اطلاعات با جهت زمان هماهنگ میماند و میتوان از چند جایگاه یک سری برای ساخت هدفهای آموزشی استفاده کرد. مدل همچنین میتواند contextهایی با طولهای متفاوت دریافت کند.
در مقابل، پیشبینی افقهای بلند معمولاً مرحلهبهمرحله انجام میشود. بنابراین خطای یک forecast patch میتواند وارد مرحلهٔ بعد شود و روی خطاهای بعدی اثر بگذارد. همچنین Encoder–Decoder برای بعضی مسئلههای چندورودی یا اطلاعات جانبیِ آینده ساختار طبیعیتری دارد.
پس Decoder-only همیشه از Encoder–Decoder بهتر نیست؛ برای مسئلهٔ TimesFM، یعنی «گذشتهٔ یک سری را ببین و ادامهاش را بساز»، انتخاب ساده و طبیعیتری است.
اگر هر time point را یک token بگیریم، توالی خیلی طولانی میشود. TimesFM سری را به تکههای پیوسته و بدون overlap تقسیم میکند.
در نسخهٔ 1.0:
input_patch_len = 32
فرض کن ۹۶ روز داده داریم:
روزهای 1 تا 32 → patch₁
روزهای 33 تا 64 → patch₂
روزهای 65 تا 96 → patch₃
پس ۹۶ عدد به سه token تبدیل میشوند.
تعداد patchها
مثلاً اگر context برابر ۶۴ نقطه و P = 32 باشد، ورودی فقط دو patch دارد:
مثال یک context با ۶۴ نقطه
پس حتی یک مدل بزرگ هم در این تنظیم خاص، از ۶۴ مقدار خام فقط دو token زمانی میسازد. اندازهٔ patch مستقیماً تعیین میکند Attention با چه دقت زمانیای روابط را ببیند.
نه. Transformer انتظار دارد هر token یک بردار با اندازهٔ ثابت D باشد. پس هر patch ابتدا وارد یک Residual Block میشود.
32 raw values
↓
Input Residual Block
↓
1280-dimensional token
این Residual Block در اصل یک MLP با skip connection است؛ همان چیزی که در دو مقالهٔ قبلی یاد گرفتیم.
تبدیل patch به token
PEⱼ اطلاعات موقعیت patch است. بدون آن، مدل نمیفهمد کدام patch قدیمیتر و کدام تازهتر است.
در checkpoint عمومی TimesFM 1.0:
model_dimension = D = 1280
number_of_layers = 20
number_of_attention_heads = 16
پس هر patch بعد از ورودی، به یک بردار ۱۲۸۰بعدی تبدیل میشود و از ۲۰ Transformer block میگذرد.
هر block اصلی دو بخش آشنا دارد:
Multi-Head Causal Self-Attention
↓
Feed-Forward Network
بهاضافهٔ residual connection و normalization.
Attention تعیین میکند هر patch برای ساختن نمایش خودش، از کدام patchهای قبلی اطلاعات بگیرد. Feed-Forward Network بعد از آن، نمایش هر patch را جداگانه پردازش میکند.
خروجی causal در جایگاه j
بهدلیل causal mask، outputⱼ نمیتواند از patchهای بعد از j اطلاعات بگیرد. پس میتوانیم از آن برای پیشبینی آیندهٔ همان نقطه استفاده کنیم، بدون اینکه leakage رخ دهد.
در یک LLM، هر token معمولاً توکن بعدی را پیشبینی میکند. اما اگر TimesFM هر بار فقط یک عدد آینده بسازد، پیشبینی افق بلند به گامهای autoregressive بسیار زیادی نیاز دارد و خطاها روی هم جمع میشوند.
TimesFM بهجای یک نقطه، یک output patch میسازد:
output_patch_len = 128
خروجی Transformer وارد Output Residual Block میشود و به ۱۲۸ مقدار آینده نگاشت میشود:
تبدیل نمایش پنهان به پیشبینی
این یعنی input patch میتواند ۳۲ نقطه باشد، اما output patch برابر ۱۲۸ نقطه باشد. برابرنبودن این دو یک انتخاب عمدی معماری است.
فرض کن یک سری ۵۱۲نقطهای داریم و patch ورودی ۳۲ و خروجی ۱۲۸ است.
مدل در یک توالی آموزشی چند سؤال میگیرد:
نقاط 1 تا 32 → نقاط 33 تا 160 را پیشبینی کن
نقاط 1 تا 64 → نقاط 65 تا 192 را پیشبینی کن
نقاط 1 تا 96 → نقاط 97 تا 224 را پیشبینی کن
...
بهخاطر causal attention، خروجی هر patch فقط گذشتهٔ مجاز خودش را دیده است. بنابراین از یک سری، چند هدف آموزشی بهطور همزمان ساخته میشود.
Loss پیشآموزش بهشکل ساده
Backpropagation از تمام این lossها عبور میکند و Input Residual Block، Transformerها و Output Residual Block را با هم آموزش میدهد.
مدل اولین patch آینده را میسازد. سپس پیشبینی تولیدشده به انتهای context اضافه میشود و مدل patch بعدی را تولید میکند:
context واقعی
↓
forecast 1
↓
context واقعی + forecast 1
↓
forecast 2
Autoregressive patch decoding
این هنوز autoregressive است، اما بهجای تولید یک نقطه در هر گام، یک بلوک بزرگ تولید میشود. در نتیجه برای افق ۵۱۲، با output patch برابر ۱۲۸ فقط چهار مرحله لازم داریم.
طبق گزارش انتشار عمومی Google Research، corpus نهایی حدود ۱۰۰ میلیارد time point واقعی داشته است. بخش مهمی از داده از Google Trends و Wikipedia Pageviews آمده و مجموعههای عمومی دیگر هم به آن اضافه شدهاند. نسخهٔ پژوهشی اولیه همچنین استفاده از M4، Traffic و Electricity را شرح میدهد.
این تنوع از دو جهت مهم است:
دادهٔ مصنوعی هم میتواند الگوهای پایهای مثل روند و فصل را با کنترل بیشتر به مدل نشان بدهد؛ دادهٔ واقعی بینظمی و پیچیدگی دنیای واقعی را اضافه میکند.
اما «۱۰۰ میلیارد نقطه» را نباید با «۱۰۰ میلیارد مثال مستقل و باکیفیت» یکی گرفت. نقاط مجاور یک سری به هم وابستهاند، بعضی دامنهها سهم بیشتری دارند و حجم زیاد همیشه به معنی پوشش متوازن همهٔ دنیا نیست.
یک سری ممکن است اطراف 0.01 و دیگری اطراف 100000 حرکت کند. اگر مقدار خام را بدون ملاحظه وارد مدل کنیم، مقیاس میتواند بر یادگیری غلبه کند.
TimesFM در مسیر inference داده را بر اساس context هر سری نرمال میکند و پس از پیشبینی، خروجی را به مقیاس اصلی برمیگرداند. فلسفهٔ این کار مهم است:
مدل بهجای حفظ سطح عددی هر سری،
روی شکل تغییرات و ساختار زمانی تمرکز کند.
این نرمالسازی به انتقال میان دامنهها کمک میکند، ولی اطلاعات وابسته به سطح مطلق را هم ممکن است کمرنگ کند. هیچ preprocessing خنثی نیست؛ هرکدام یک فرض وارد مدل میکند.
نسخهٔ عمومی TimesFM یک نشانگر دستهای فرکانس هم میپذیرد. این ورودی به مدل سرنخ میدهد که سری تقریباً در چه مقیاس زمانی قرار دارد.
در API نسخهٔ 1.0 معمولاً سه گروه دیده میشود:
0 → فرکانس بالا، تا حدود روزانه
1 → فرکانس متوسط، مانند هفتگی یا ماهانه
2 → فرکانس پایینتر
این indicator خودِ timestamp کامل نیست. فقط یک دستهبندی کلی از granularity است. در نوتبوک بیتکوین روزانه از freq = 0 استفاده میکنیم.
وقتی TimesFM را روی بیتکوین اجرا میکنیم و وزنهایش را تغییر نمیدهیم، کار zero-shot انجام دادهایم:
آخرین 512 قیمت بیتکوین
↓
TimesFM frozen
↓
پیشبینی 64 روز آینده
مدل از خود context بیتکوین استفاده میکند، اما روی آن training انجام نمیدهد. این با «بدون اطلاعات» فرق دارد. مدل دادهٔ گذشتهٔ همان سری را میبیند؛ چیزی که نمیگیرد gradient و بهروزرسانی وزن روی dataset جدید است.
Foundation Model میتواند هزینهٔ شروع یک پروژه را کم کند:
TimesFM در ارزیابیهای مقالهٔ اصلی توانست در تعدادی از benchmarkهای ندیده به مدلهای supervised نزدیک شود یا از بعضی آنها جلو بزند؛ درحالیکه آن مدلهای supervised برای dataset هدف جداگانه آموزش دیده بودند.
این نتیجه مهم است، اما معنایش این نیست که TimesFM در همهٔ datasetها بهترین مدل است. ادعای درستتر این است:
یک مدل واحد و ازپیشآموزشدیده میتواند بدون آموزش روی dataset هدف، در دامنههای متنوع baseline رقابتی تولید کند.
دنیادیدگی با دانای کل بودن فرق دارد.
TimesFM نمیتواند:
در مالی، نسبت signal به noise پایین است، توزیع داده تغییر میکند و رقابت باعث محوشدن الگوهای قابلسوءاستفاده میشود. مدلی که روی ترافیک، فروش و جستوجو عالی است، لزوماً رفتار قیمت دارایی را بدون adaptation کامل نمیفهمد.
سه مسئله مهم داریم.
ترکیب corpus عمومی با دادهٔ مالی یکی نیست. الگوی بازدید صفحه یا مصرف برق میتواند فصلپذیر و نسبتاً پایدار باشد؛ قیمت بازار تحت شوک، انتظارات و تغییر رژیم است.
اگر کل مدل ۲۰۰میلیونی را روی دادهٔ محدود چند سهم fine-tune کنیم، خطر overfitting بالاست.
اگر مدل را خیلی به S&P100 تخصصی کنیم، شاید روی همان سهام بهتر شود اما روی سهم ندیده از S&P500 بدتر عمل کند. یعنی چیزی به دست آوردهایم و چیزی از دست دادهایم.
برای اجرای دقیق نسخهٔ 1.0 باید package قدیمی را عمداً pin کنیم، چون package فعلی TimesFM نسلهای جدیدتری دارد.
%pip install -q "timesfm==1.3.0" torch huggingface_hub
بعد checkpoint رسمی PyTorch را بارگذاری میکنیم:
import timesfm
model = timesfm.TimesFm(
context_len=512,
horizon_len=64,
input_patch_len=32,
output_patch_len=128,
num_layers=20,
model_dims=1280,
backend="cpu",
)
model.load_from_checkpoint(
repo_id="google/timesfm-1.0-200m-pytorch"
)
و برای یک سری روزانه:
point_forecast, quantile_forecast = model.forecast(
inputs=[context_values],
freq=[0],
)
forecast = point_forecast[0][:64]
مدل روی CPU هم قابل اجراست، اما بارگذاری آن سبک نیست. checkpoint PyTorch حدود ۸۱۴ مگابایت است و مستندات رسمی نسخهٔ قدیمی برای dependencyها حافظهٔ زیادی توصیه میکنند. GPU سرعت را بهتر میکند، ولی برای یک آزمایش zero-shot کوچک الزام معماری نیست.
نوتبوک کامل تست بیتکوین کنار همین مقاله قرار دارد. در آن، دادهٔ روزانهٔ BTC-USD دانلود میشود، آخرین ۵۱۲ روز به مدل داده میشود، ۶۴ روز آینده پیشبینی و سپس MAE و RMSE همراه نمودار محاسبه میشوند.
یک نمودار زیبا کافی نیست. حداقل باید این اصول را رعایت کنیم:
ممکن است مدلی RMSE بهتری داشته باشد ولی هیچ مزیت معاملاتی پایدار ایجاد نکند. forecasting خوب و strategy سودآور یک چیز نیستند.
Patching و نرمالسازی باعث میشوند سریهای متفاوت به نمایش مشترکتری تبدیل شوند. مدل بهجای وابستگی کامل به واحد اندازهگیری، روی شکل رفتار تمرکز میکند.
مدل causal بهطور طبیعی از گذشته برای آینده استفاده میکند و میتواند با history lengthهای مختلف کار کند.
پیشبینی ۱۲۸ نقطه در هر مرحله تعداد گامهای autoregressive را کم میکند و برای horizon بلند مفید است.
وقتی ۳۲ نقطه یک token میشوند، Attention دیگر مستقیماً میان تکتک time pointها عمل نمیکند. MLP ورودی باید اطلاعات مهم داخل patch را در بردار ۱۲۸۰بعدی حفظ کند.
نسخهٔ 1.0 اساساً گذشتهٔ یک سری را برای پیشبینی آیندهٔ همان سری استفاده میکند. رابطهٔ همزمان چند سری، حجم، نرخ بهره یا متغیرهای بیرونی مستقیماً در backbone اصلی آن مدل نمیشود.
نسخهٔ 1.0 عمدتاً برای point forecasting عرضه شد. در کاربردهای حساس، دانستن دامنهٔ عدمقطعیت گاهی از یک عدد مرکزی مهمتر است.
مدل عمومی biasهای corpus خودش را دارد. دیدن دادهٔ زیاد، شکاف دامنه را حذف نمیکند؛ فقط نقطهٔ شروع را بهتر میکند.
بهتر است Foundation Model را «پایه» ببینیم، نه جواب نهایی.
سه حالت منطقی داریم:
اگر zero-shot کافی است:
مدل frozen را مستقیم استفاده کن
اگر دادهٔ مناسب و سختافزار زیاد داری:
full fine-tuning را بررسی کن
اگر داده یا منابع محدود است:
PEFT، LoRA یا Adapter را بررسی کن
Foundation Model در سری زمانی یعنی مدلی که قبل از مسئلهٔ ما، روی مجموعهای بزرگ و متنوع از توالیها پیشآموزش دیده و قرار است دانش زمانیاش را به دادهٔ تازه منتقل کند.
TimesFM این فلسفه را با چند تصمیم معماری پیاده میکند:
سری خام
↓
تقسیم به patchهای 32 نقطهای
↓
تبدیل هر patch به token با MLP residual
↓
20 لایهٔ Transformer با causal self-attention
↓
Output Residual Block
↓
پیشبینی patch آینده
دلیل جذابیتش فقط بزرگی مدل نیست. ارزش اصلی در این است که یک مدل واحد، بعد از پیشآموزش گسترده، میتواند روی سری ندیده بدون آموزش دوباره پیشبینی تولید کند.
اما دنیادیدهبودن به معنی همهچیزدانبودن نیست. TimesFM یک prior قدرتمند و یک baseline عمومی به ما میدهد، ولی کیفیت نهایی همچنان باید روی دادهٔ واقعی، با split زمانی، rolling backtest و baselineهای ساده سنجیده شود.
این مقاله با روایت و مثالهای تألیفی نوشته شده و مشخصات فنی آن بر منابع اولیهٔ زیر تکیه دارد: