PatchTSMixer هو أسلوب خفيف الوزن لنمذجة السلاسل الزمنية يعتمد على بنية MLP-Mixer. تم اقتراحه في TSMixer: نموذج MLP-Mixer خفيف الوزن للتنبؤ بالسلاسل الزمنية متعددة المتغيرات بواسطة مؤلفي أبحاث IBM Vijay Ekambaram، وArindam Jati، وNam Nguyen، وPhanwadee Sinthong، وJayant Kalagnanam.
للمشاركة الذهنية الفعالة وتعزيز المصادر المفتوحة – تتعاون IBM Research مع فريق HuggingFace لإصدار هذا النموذج في مكتبة Transformers.
في تطبيق Hugging Face، نوفر إمكانات PatchTSMixer لتسهيل المزج الخفيف عبر التصحيحات والقنوات والميزات المخفية من أجل نمذجة سلاسل زمنية متعددة المتغيرات بشكل فعال. كما أنه يدعم آليات الانتباه المختلفة بدءًا من الانتباه البسيط إلى كتل الانتباه الذاتي الأكثر تعقيدًا والتي يمكن تخصيصها وفقًا لذلك. يمكن تدريب النموذج مسبقًا واستخدامه لاحقًا في العديد من المهام النهائية مثل التنبؤ والتصنيف والانحدار.
PatchTSMixer يتفوق على أحدث نماذج MLP وTransformer في التنبؤ بهامش كبير يتراوح بين 8-60%. كما أنه يتفوق على أحدث المعايير القوية لنماذج Patch-Transformer (بنسبة 1-2%) مع انخفاض كبير في الذاكرة ووقت التشغيل (2-3X). لمزيد من التفاصيل، راجع الورقة.
في هذه المدونة، سنعرض أمثلة للبدء في استخدام PatchTSMixer. سنوضح أولاً القدرة على التنبؤ PatchTSMixer في مجموعة بيانات الكهرباء. سنقوم بعد ذلك بإظهار قدرة تعلم النقل الخاصة بـ PatchTSMixer باستخدام النموذج المدرب على الكهرباء للقيام بالتنبؤ الصفري على ETTH2 dataset.
نظرة عامة سريعة على PatchTSMixer
تخطي هذا القسم إذا كنت معتادا على PatchTSMixer!
PatchTSMixer يقسم سلسلة زمنية متعددة المدخلات إلى سلسلة من التصحيحات أو النوافذ. وبعد ذلك، يقوم بتمرير السلسلة إلى طبقة التضمين، والتي تولد موترًا متعدد الأبعاد.
يتم بعد ذلك تمرير الموتر متعدد الأبعاد إلى PatchTSMixer العمود الفقري، والذي يتكون من سلسلة من طبقات MLP Mixer. تتعلم كل طبقة MLP Mixer الارتباطات بين التصحيحات وداخل التصحيحات وبين القنوات من خلال سلسلة من عمليات التقليب وعمليات MLP.
PatchTSMixer يستخدم أيضًا الاتصالات المتبقية والاهتمامات المسورة لتحديد أولويات الميزات المهمة.
ومن ثم، فإن تسلسل طبقات MLP Mixer ينشئ ما يلي PatchTSMixer العمود الفقري.
PatchTSMixer لديه تصميم معياري لدعم التدريب المسبق للسلاسل الزمنية المقنعة بسلاسة بالإضافة إلى التنبؤ المباشر بالسلاسل الزمنية.
تثبيت
يتطلب هذا العرض التوضيحي معانقة الوجه Transformers للنموذج وIBM tsfm حزمة للمعالجة المسبقة للبيانات المساعدة. يمكن تثبيت كلاهما باتباع الخطوات أدناه.
- قم بتثبيت مستودع نموذج مؤسسة IBM Time Series Foundation
tsfm.
pip install git+https://github.com/IBM/tsfm.git
- تثبيت معانقة الوجه
Transformers
pip install transformers
- اختبره باستخدام الأوامر التالية في ملف
pythonصالة.
from transformers import PatchTSMixerConfig
from tsfm_public.toolkit.dataset import ForecastDFDataset
الجزء الأول: التنبؤ بمجموعة بيانات الكهرباء
هنا ندرب أ PatchTSMixer نموذج مباشرة على مجموعة بيانات الكهرباء، وتقييم أدائها.
import os
import random
from transformers import (
EarlyStoppingCallback,
PatchTSMixerConfig,
PatchTSMixerForPrediction,
Trainer,
TrainingArguments,
)
import numpy as np
import pandas as pd
import torch
from tsfm_public.toolkit.dataset import ForecastDFDataset
from tsfm_public.toolkit.time_series_preprocessor import TimeSeriesPreprocessor
from tsfm_public.toolkit.util import select_by_index
تعيين البذور
from transformers import set_seed
set_seed(42)
تحميل وإعداد مجموعات البيانات
في الخلية التالية، يرجى ضبط المعلمات التالية لتناسب طلبك:
dataset_path: المسار إلى ملف .csv المحلي، أو عنوان الويب إلى ملف CSV للبيانات محل الاهتمام. يتم تحميل البيانات مع الباندا، لذلك يتم دعم أي شيء بواسطة
pd.read_csvمدعوم: (https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_csv.html).timestamp_column: اسم العمود الذي يحتوي على معلومات الطابع الزمني، استخدمNoneإذا لم يكن هناك مثل هذا العمود.id_columns: قائمة بأسماء الأعمدة تحدد معرفات السلاسل الزمنية المختلفة. في حالة عدم وجود عمود معرف، استخدم[].forecast_columns: قائمة الأعمدة المراد نمذجتها.context_length: كمية البيانات التاريخية المستخدمة كمدخلات للنموذج. نوافذ إدخال بيانات السلاسل الزمنية بطول يساوي
context_lengthسيتم استخراجها من إطار بيانات الإدخال. في حالة مجموعة بيانات السلاسل الزمنية المتعددة، سيتم إنشاء نوافذ السياق بحيث يتم تضمينها ضمن سلسلة زمنية واحدة (أي معرف واحد).forecast_horizon: عدد الطوابع الزمنية للتنبؤ بها في المستقبل.train_start_index,train_end_index: مؤشرات البداية والنهاية في البيانات المحملة والتي تحدد بيانات التدريب.valid_start_index,valid_end_index: مؤشرات البداية والنهاية في البيانات المحملة والتي تحدد بيانات التحقق من الصحة.test_start_index,test_end_index: مؤشرات البداية والنهاية في البيانات المحملة والتي تحدد بيانات الاختبار.num_workers: عدد العاملين في وحدة المعالجة المركزية في أداة تحميل بيانات PyTorch.batch_size: حجم الدفعة. يتم تحميل البيانات أولاً في إطار بيانات Pandas وتقسيمها إلى أجزاء التدريب والتحقق من الصحة والاختبار. ثم يتم تحويل إطارات بيانات Pandas إلى مجموعة بيانات PyTorch المناسبة المطلوبة للتدريب.
dataset_path = "~/Downloads/ECL.csv"
timestamp_column = "date"
id_columns = []
context_length = 512
forecast_horizon = 96
num_workers = 16
batch_size = 64
data = pd.read_csv(
dataset_path,
parse_dates=[timestamp_column],
)
forecast_columns = list(data.columns[1:])
num_train = int(len(data) * 0.7)
num_test = int(len(data) * 0.2)
num_valid = len(data) - num_train - num_test
border1s = [
0,
num_train - context_length,
len(data) - num_test - context_length,
]
border2s = [num_train, num_train + num_valid, len(data)]
train_start_index = border1s[0]
train_end_index = border2s[0]
valid_start_index = border1s[1]
valid_end_index = border2s[1]
test_start_index = border1s[2]
test_end_index = border2s[2]
train_data = select_by_index(
data,
id_columns=id_columns,
start_index=train_start_index,
end_index=train_end_index,
)
valid_data = select_by_index(
data,
id_columns=id_columns,
start_index=valid_start_index,
end_index=valid_end_index,
)
test_data = select_by_index(
data,
id_columns=id_columns,
start_index=test_start_index,
end_index=test_end_index,
)
time_series_processor = TimeSeriesPreprocessor(
context_length=context_length,
timestamp_column=timestamp_column,
id_columns=id_columns,
input_columns=forecast_columns,
output_columns=forecast_columns,
scaling=True,
)
time_series_processor.train(train_data)
train_dataset = ForecastDFDataset(
time_series_processor.preprocess(train_data),
id_columns=id_columns,
timestamp_column="date",
input_columns=forecast_columns,
output_columns=forecast_columns,
context_length=context_length,
prediction_length=forecast_horizon,
)
valid_dataset = ForecastDFDataset(
time_series_processor.preprocess(valid_data),
id_columns=id_columns,
timestamp_column="date",
input_columns=forecast_columns,
output_columns=forecast_columns,
context_length=context_length,
prediction_length=forecast_horizon,
)
test_dataset = ForecastDFDataset(
time_series_processor.preprocess(test_data),
id_columns=id_columns,
timestamp_column="date",
input_columns=forecast_columns,
output_columns=forecast_columns,
context_length=context_length,
prediction_length=forecast_horizon,
)
قم بتكوين نموذج PatchTSMixer
بعد ذلك، نقوم بإنشاء نموذج PatchTSMixer الذي تمت تهيئته عشوائيًا باستخدام التكوين. تتحكم الإعدادات أدناه في المعلمات الفائقة المختلفة المتعلقة بالبنية.
num_input_channels: عدد قنوات الإدخال (أو الأبعاد) في بيانات السلاسل الزمنية. يتم تعيين هذا تلقائيًا إلى رقم أعمدة التنبؤ.context_length: كما هو موضح أعلاه، كمية البيانات التاريخية المستخدمة كمدخل للنموذج.prediction_length: هذا هو نفس الأفق المتوقع كما هو موضح أعلاه.patch_length: طول التصحيح لPatchTSMixerنموذج. يوصى باختيار قيمة تقسم بالتساويcontext_length.patch_stride: الخطوة المستخدمة عند استخراج التصحيحات من نافذة السياق.d_model: بُعد الميزة المخفية للنموذج.num_layers: عدد طبقات النموذج.dropout: احتمالية التسرب لجميع الطبقات المتصلة بالكامل في المشفر.head_dropout: احتمالية التسرب المستخدمة في رأس النموذج.mode: وضع التشغيل PatchTSMixer. “common_channel”https://huggingface.co/”mix_channel”. تعمل القناة المشتركة في الوضع المستقل للقناة. للتدريب المسبق، استخدم “common_channel”.scaling: القياس القياسي لكل أرملة. القيمة الموصى بها: “الأمراض المنقولة جنسيا”.
للحصول على تفاصيل كاملة عن المعلمات، نشير إلى الوثائق.
نوصي بضبط القيم الموجودة في الخلية التالية فقط.
patch_length = 8
config = PatchTSMixerConfig(
context_length=context_length,
prediction_length=forecast_horizon,
patch_length=patch_length,
num_input_channels=len(forecast_columns),
patch_stride=patch_length,
d_model=16,
num_layers=8,
expansion_factor=2,
dropout=0.2,
head_dropout=0.2,
mode="common_channel",
scaling="std",
)
model = PatchTSMixerForPrediction(config)
نموذج القطار
بعد ذلك، يمكننا الاستفادة من فئة Hugging Face Trainer لتدريب النموذج بناءً على استراتيجية التنبؤ المباشر. نحدد أولاً وسيطات التدريب التي تسرد المعلمات الفائقة المختلفة المتعلقة بالتدريب مثل عدد العصور ومعدل التعلم وما إلى ذلك.
training_args = TrainingArguments(
output_dir="./checkpoint/patchtsmixer/electricity/pretrain/output/",
overwrite_output_dir=True,
learning_rate=0.001,
num_train_epochs=100,
do_eval=True,
evaluation_strategy="epoch",
per_device_train_batch_size=batch_size,
per_device_eval_batch_size=batch_size,
dataloader_num_workers=num_workers,
report_to="tensorboard",
save_strategy="epoch",
logging_strategy="epoch",
save_total_limit=3,
logging_dir="./checkpoint/patchtsmixer/electricity/pretrain/logs/",
load_best_model_at_end=True,
metric_for_best_model="eval_loss",
greater_is_better=False,
label_names=["future_values"],
)
early_stopping_callback = EarlyStoppingCallback(
early_stopping_patience=10,
early_stopping_threshold=0.0001,
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=valid_dataset,
callbacks=[early_stopping_callback],
)
trainer.train()
>>> | Epoch | Training Loss | Validation Loss |
|-------|---------------|------------------|
| 1 | 0.247100 | 0.141067 |
| 2 | 0.168600 | 0.127757 |
| 3 | 0.156500 | 0.122327 |
...
تقييم النموذج في مجموعة الاختبار
لاحظ أن خسارة التدريب والتقييم الخاصة بـ PatchTSMixer هي خسارة متوسط الخطأ التربيعي (MSE). ومن ثم، فإننا لا نحسب بشكل منفصل مقياس MSE في أي من تجارب التقييم التالية.
results = trainer.evaluate(test_dataset)
print("Test result:")
print(results)
>>> Test result:
'eval_loss': 0.12884521484375, 'eval_runtime': 5.7532, 'eval_samples_per_second': 897.763, 'eval_steps_per_second': 3.65, 'epoch': 35.0
حصلنا على درجة MSE تبلغ 0.128 وهي نتيجة SOTA في بيانات الكهرباء.
حفظ النموذج
save_dir = "patchtsmixer/electricity/model/pretrain/"
os.makedirs(save_dir, exist_ok=True)
trainer.save_model(save_dir)
الجزء 2: نقل التعلم من الكهرباء إلى ETTh2
في هذا القسم، سوف نقوم بإظهار القدرة على نقل التعلم لل PatchTSMixer نموذج. نحن نستخدم النموذج الذي تم تدريبه مسبقًا على مجموعة بيانات الكهرباء لإجراء تنبؤات صفرية على ETTh2 dataset.
من خلال نقل التعلم، نعني أننا نقوم أولاً بتدريب النموذج مسبقًا لمهمة التنبؤ على source مجموعة البيانات (وهو ما فعلناه أعلاه في ملف Electricity مجموعة البيانات). بعد ذلك، سوف نستخدم النموذج المُدرب مسبقًا للتنبؤ بالصفر على أ target dataset. ونعني بالطلقة الصفرية أننا نختبر الأداء في target المجال دون أي تدريب إضافي. نأمل أن يكون النموذج قد اكتسب المعرفة الكافية من التدريب المسبق والتي يمكن نقلها إلى مجموعة بيانات مختلفة.
بعد ذلك، سنقوم بإجراء فحص خطي و(بعد ذلك) ضبط النموذج المُدرب مسبقًا على train تقسيم البيانات المستهدفة، وسوف يتم التحقق من صحة أداء التنبؤ على test تقسيم البيانات المستهدفة في هذا المثال، مجموعة البيانات المصدر هي مجموعة بيانات الكهرباء ومجموعة البيانات المستهدفة هي ETTh2.
نقل التعلم على بيانات ETTh2
جميع التقييمات على test جزء من ETTh2 البيانات: الخطوة 1: التقييم المباشر للنموذج المُدرب مسبقًا بالكهرباء. هذا هو الأداء الصفري.
الخطوة 2: التقييم بعد إجراء الفحص الخطي.
الخطوة 3: التقييم بعد إجراء الضبط الدقيق الكامل.
قم بتحميل مجموعة بيانات ETTh2
أدناه، نقوم بتحميل ETTh2 مجموعة البيانات باعتبارها إطار بيانات Pandas. بعد ذلك، نقوم بإنشاء 3 أقسام للتدريب والتحقق والاختبار. ثم نقوم بالاستفادة من TimeSeriesPreprocessor فئة لإعداد كل تقسيم للنموذج.
dataset = "ETTh2"
dataset_path = f"https://raw.githubusercontent.com/zhouhaoyi/ETDataset/main/ETT-small/dataset.csv"
timestamp_column = "date"
id_columns = []
forecast_columns = ["HUFL", "HULL", "MUFL", "MULL", "LUFL", "LULL", "OT"]
train_start_index = None
train_end_index = 12 * 30 * 24
valid_start_index = 12 * 30 * 24 - context_length
valid_end_index = 12 * 30 * 24 + 4 * 30 * 24
test_start_index = 12 * 30 * 24 + 4 * 30 * 24 - context_length
test_end_index = 12 * 30 * 24 + 8 * 30 * 24
data = pd.read_csv(
dataset_path,
parse_dates=[timestamp_column],
)
train_data = select_by_index(
data,
id_columns=id_columns,
start_index=train_start_index,
end_index=train_end_index,
)
valid_data = select_by_index(
data,
id_columns=id_columns,
start_index=valid_start_index,
end_index=valid_end_index,
)
test_data = select_by_index(
data,
id_columns=id_columns,
start_index=test_start_index,
end_index=test_end_index,
)
time_series_processor = TimeSeriesPreprocessor(
context_length=context_length
timestamp_column=timestamp_column,
id_columns=id_columns,
input_columns=forecast_columns,
output_columns=forecast_columns,
scaling=True,
)
time_series_processor.train(train_data)
>>> TimeSeriesPreprocessor
"context_length": 512,
"feature_extractor_type": "TimeSeriesPreprocessor",
"id_columns": [],
...
train_dataset = ForecastDFDataset(
time_series_processor.preprocess(train_data),
id_columns=id_columns,
input_columns=forecast_columns,
output_columns=forecast_columns,
context_length=context_length,
prediction_length=forecast_horizon,
)
valid_dataset = ForecastDFDataset(
time_series_processor.preprocess(valid_data),
id_columns=id_columns,
input_columns=forecast_columns,
output_columns=forecast_columns,
context_length=context_length,
prediction_length=forecast_horizon,
)
test_dataset = ForecastDFDataset(
time_series_processor.preprocess(test_data),
id_columns=id_columns,
input_columns=forecast_columns,
output_columns=forecast_columns,
context_length=context_length,
prediction_length=forecast_horizon,
)
التنبؤ الصفري على ETTh2
نظرًا لأننا سنقوم باختبار أداء التنبؤ خارج الصندوق، فإننا نقوم بتحميل النموذج الذي قمنا بتدريبه مسبقًا أعلاه.
from transformers import PatchTSMixerForPrediction
finetune_forecast_model = PatchTSMixerForPrediction.from_pretrained(
"patchtsmixer/electricity/model/pretrain/"
)
finetune_forecast_args = TrainingArguments(
output_dir="./checkpoint/patchtsmixer/transfer/finetune/output/",
overwrite_output_dir=True,
learning_rate=0.0001,
num_train_epochs=100,
do_eval=True,
evaluation_strategy="epoch",
per_device_train_batch_size=batch_size,
per_device_eval_batch_size=batch_size,
dataloader_num_workers=num_workers,
report_to="tensorboard",
save_strategy="epoch",
logging_strategy="epoch",
save_total_limit=3,
logging_dir="./checkpoint/patchtsmixer/transfer/finetune/logs/",
load_best_model_at_end=True,
metric_for_best_model="eval_loss",
greater_is_better=False,
)
early_stopping_callback = EarlyStoppingCallback(
early_stopping_patience=5,
early_stopping_threshold=0.001,
)
finetune_forecast_trainer = Trainer(
model=finetune_forecast_model,
args=finetune_forecast_args,
train_dataset=train_dataset,
eval_dataset=valid_dataset,
callbacks=[early_stopping_callback],
)
print("\n\nDoing zero-shot forecasting on target data")
result = finetune_forecast_trainer.evaluate(test_dataset)
print("Target data zero-shot forecasting result:")
print(result)
>>> Doing zero-shot forecasting on target data
Target data zero-shot forecasting result:
'eval_loss': 0.3038313388824463, 'eval_runtime': 1.8364, 'eval_samples_per_second': 1516.562, 'eval_steps_per_second': 5.99
كما ترون، حصلنا على خطأ متوسط مربع (MSE) قدره 0.3 صفر وهو قريب من النتيجة الحديثة.
بعد ذلك، دعونا نرى كيف يمكننا القيام بذلك عن طريق إجراء فحص خطي، والذي يتضمن تدريب مصنف خطي فوق نموذج مجمد تم تدريبه مسبقًا. غالبًا ما يتم إجراء الفحص الخطي لاختبار أداء ميزات النموذج المُدرب مسبقًا.
التحقيق الخطي على ETTh2
يمكننا إجراء فحص خطي سريع على train جزء من البيانات المستهدفة لمعرفة أي ممكن test تحسين الأداء.
for param in finetune_forecast_trainer.model.model.parameters():
param.requires_grad = False
print("\n\nLinear probing on the target data")
finetune_forecast_trainer.train()
print("Evaluating")
result = finetune_forecast_trainer.evaluate(test_dataset)
print("Target data head/linear probing result:")
print(result)
>>> Linear probing on the target data
| Epoch | Training Loss | Validation Loss |
|-------|---------------|------------------|
| 1 | 0.447000 | 0.216436 |
| 2 | 0.438600 | 0.215667 |
| 3 | 0.429400 | 0.215104 |
...
Evaluating
Target data head/linear probing result:
'eval_loss': 0.27119266986846924, 'eval_runtime': 1.7621, 'eval_samples_per_second': 1580.478, 'eval_steps_per_second': 6.242, 'epoch': 13.0
كما يتبين، من خلال تدريب طبقة خطية بسيطة فوق العمود الفقري المتجمد، انخفض MSE من 0.3 إلى 0.271 محققًا أحدث النتائج.
save_dir = f"patchtsmixer/electricity/model/transfer/dataset/model/linear_probe/"
os.makedirs(save_dir, exist_ok=True)
finetune_forecast_trainer.save_model(save_dir)
save_dir = f"patchtsmixer/electricity/model/transfer/dataset/preprocessor/"
os.makedirs(save_dir, exist_ok=True)
time_series_processor.save_pretrained(save_dir)
>>> ['patchtsmixer/electricity/model/transfer/ETTh2/preprocessor/preprocessor_config.json']
أخيرًا، دعونا نرى ما إذا كنا سنحصل على المزيد من التحسينات من خلال إجراء ضبط كامل للنموذج في مجموعة البيانات المستهدفة.
ضبط كامل على ETTh2
يمكننا إجراء ضبط كامل للنموذج (بدلاً من فحص الطبقة الخطية الأخيرة كما هو موضح أعلاه) على train جزء من البيانات المستهدفة لمعرفة ممكن test تحسين الأداء. يبدو الكود مشابهًا لمهمة الفحص الخطي أعلاه، باستثناء أننا لا نقوم بتجميد أي معلمات.
finetune_forecast_model = PatchTSMixerForPrediction.from_pretrained(
"patchtsmixer/electricity/model/pretrain/"
)
finetune_forecast_trainer = Trainer(
model=finetune_forecast_model,
args=finetune_forecast_args,
train_dataset=train_dataset,
eval_dataset=valid_dataset,
callbacks=[early_stopping_callback],
)
print("\n\nFinetuning on the target data")
finetune_forecast_trainer.train()
print("Evaluating")
result = finetune_forecast_trainer.evaluate(test_dataset)
print("Target data full finetune result:")
print(result)
>>> Finetuning on the target data
| Epoch | Training Loss | Validation Loss |
|-------|---------------|-----------------|
| 1 | 0.432900 | 0.215200 |
| 2 | 0.416700 | 0.210919 |
| 3 | 0.401400 | 0.209932 |
...
Evaluating
Target data full finetune result:
'eval_loss': 0.2734043300151825, 'eval_runtime': 1.5853, 'eval_samples_per_second': 1756.725, 'eval_steps_per_second': 6.939, 'epoch': 9.0
في هذه الحالة، لا يوجد تحسن كبير من خلال إجراء الضبط الدقيق الكامل. دعونا نحفظ النموذج على أي حال.
save_dir = f"patchtsmixer/electricity/model/transfer/dataset/model/fine_tuning/"
os.makedirs(save_dir, exist_ok=True)
finetune_forecast_trainer.save_model(save_dir)
ملخص
في هذه المدونة، قدمنا دليلًا خطوة بخطوة حول الاستفادة من PatchTSMixer في المهام المتعلقة بالتنبؤ ونقل التعلم. نعتزم تسهيل التكامل السلس لنموذج PatchTSMixer HF لحالات استخدام التنبؤ الخاصة بك. نحن على ثقة من أن هذا المحتوى يمثل مصدرًا مفيدًا لتسريع عملية اعتماد PatchTSMixer. شكرًا لك على متابعة مدونتنا، ونأمل أن تجد هذه المعلومات مفيدة لمشاريعك.