تفاوت اساسی ویدیو با تصویر ثابت در بعد زمان است؛ یک توالی فریم بدون درک ترتیب حرکات نمیتواند تفاوت باز کردن در را با بستن آن تشخیص دهد. بسیاری از توسعهدهندگان کار خود را با اجرای مدلهای تصویر روی تکتک فریمها آغاز میکنند، اما زمانی که تسک وابسته به حرکت باشد، این رویکرد به بنبست میرسد.
درک ویدیو و مدلسازی زمانی این چالش را با معماریهایی حل میکند که بعد زمان را مستقیماً وارد محاسبات میکنند؛ از کانوولوشنهای سهبعدی و ترفند تورم وزنها (Weight Inflation) گرفته تا ترنسفورمرهای مکانی-زمانی مدرن. این درس به شما کمک میکند مصالحهی دقیق بین هزینه پردازشی و دقت مدلها را بسنجید و خط لولهی نمونهبرداری و پردازش ویدیویی را در PyTorch بسازید.
برای اجرای کدهای پایه و پیادهسازی مدلهای سبک این درس به سختافزار گرانقیمت یا سرویسهای ابری نیاز ندارید و روی CPU یا شتابدهندههای سیستم شخصی قابل اجرا هستند؛ هرچند با توجه به محدودیتها و هزینههای دسترسی به منابع ابری پردازش گرافیکی (GPU) از ایران، انتخاب هوشمندانهی مدل و مدیریت فریمها اهمیت دوچندانی در کارهای واقعی دارد.
ویدیو یعنی توالی تصاویری که قوانین فیزیک آنها را به هم پیوند داده است. در مدلهای ویدیویی، زمان یا یک محور اضافه به حساب میآید (کانوولوشن سهبعدی)، یا توالیای است که روی آن اتنشن (Attention) اعمال میشود (ترنسفورمر)، یا ویژگیای که صرفاً یک بار استخراج و سپس تجمیع (Pool) میگردد (2D+pool).
اهداف یادگیری
- تمایز میان سه رویکرد اصلی مدلسازی ویدیو (2D+pool، کانوولوشن سهبعدی و ترنسفورمر مکانی-زمانی) و پیشبینی مصالحههای میان هزینه و دقت در آنها
- پیادهسازی نمونهبرداری فریم (Frame sampling)، تجمیع زمانی (Temporal pooling) و یک طبقهبند پایهی 2D+pool در PyTorch
- توضیح دلیل انتقالپذیری مناسب وزنهای ImageNet با کرنلهای سهبعدی متورمشده (Inflated) در I3D و تمایز کارکرد کانوولوشن فاکتورگیریشدهی (2+1)D
- تحلیل دیتاستها و معیارهای استاندارد بازشناسی کنش (Action recognition): شامل Kinetics-400/600، UCF101، Something-Something V2 و دقت top-1 در سطح کلیپ و ویدیو
صورت مسئله
یک ویدیوی 30 ثانیهای با نرخ 30 فریم بر ثانیه شامل 900 تصویر است. در نگاه اول و سادهانگارانه، طبقهبندی ویدیو همان طبقهبندی تصویر است که 900 بار تکرار و نتایجش با روشی تجمیع میشود. این روش وقتی کنش در تقریباً تمام فریمها دیده شود خوب جواب میدهد (ویدیوهای ورزشی، آشپزی، تمرینات بدنی)؛ اما وقتی ماهیت کنش با خودِ حرکت تعریف شود، کاملاً شکست میخورد: مثلاً عمل «هل دادن چیزی از چپ به راست» در تکتک فریمهای ساکن صرفاً شبیه دو جسم ثابت دیده میشود.
پرسش بنیادین در تمام معماریهای ویدیویی این است: ساختار زمانی در چه مرحلهای و چگونه مدلسازی میشود؟ پاسخ این پرسش تکلیف همهچیز را مشخص میکند — از هزینهی محاسباتی و استراتژی پیشآموزش گرفته تا امکان استفادهی مجدد از وزنهای ImageNet و انتخاب دیتاستهای مناسب آموزش مدل.
این درس را عمداً کوتاهتر از درسهای تصویر دوبعدی طراحی کردهایم؛ زیرا سازوکار اصلی پردازش تصویر را پیشتر چیدهاید و درک ویدیو بیشتر حول خط داستانی زمان میچرخد: نمونهبرداری، مدلسازی و تجمیع.
مفاهیم پایه
سه خانوادهی معماری
2D + pool
یک شبکهی عصبی پیچشی دوبعدی یا همان 2D CNN (مانند ResNet، EfficientNet یا ViT) بردارید و آن را مستقل روی تکتک فریمهای نمونهبرداریشده اجرا کنید. امبدینگهای حاصل از هر فریم را میانگین بگیرید (یا از max-pool یا attention-pool استفاده کنید) و بردار تجمیعشده را به یک طبقهبند بدهید.
مزایا:
- وزنهای پیشآموزشدیدهی ImageNet مستقیماً منتقل میشوند.
- سادهترین پیادهسازی را دارد.
- ارزان است: تعداد فریمها (T) ضرب در هزینهی استنتاج یک تصویر تکی.
معایب:
- توانایی مدلسازی حرکت را ندارد؛ کنش را صرفاً برآیند ویژگیهای ظاهری میبیند.
- تجمیع زمانی به ترتیب فریمها حساس نیست؛ رفتارهایی مثل «باز کردن در» و «بستن در» از دید مدل یکسان هستند.
کاربرد: تسکهای متکی بر ویژگیهای ظاهری، یادگیری انتقالی روی دیتاستهای ویدیویی کوچک و ساخت مدلهای اولیهی پایه (Baseline).
کانوولوشنهای سهبعدی
کرنلهای دوبعدی (H, W) را با کرنلهای سهبعدی (T, H, W) جایگزین کنید. شبکه همزمان روی ابعاد مکان و زمان کانوالو میکند. خانوادههای آغازین این روش: C3D، I3D و SlowFast.
ترفند مدل I3D: یک مدل دوبعدی پیشآموزشدیده روی ImageNet را برمیداریم و با کپی کردن هر کرنل دوبعدی در امتداد یک محور زمانی جدید، آن را باد (متورم یا Inflate) میکنیم. به این ترتیب، کانوولوشن دوبعدی 3x3 به کانوولوشن سهبعدی 3x3x3 تبدیل میشود. با این کار، مدل سهبعدی به جای آموزش از صفر، وزنهای پیشآموزشدیدهی قدرتمندی به دست میآورد.
مزایا:
- حرکت را مستقیماً مدلسازی میکند.
- متورمسازی در I3D یادگیری انتقالیِ بیدردسری فراهم میکند.
معایب:
- محاسبات (FLOPs) آن تقریباً 3 برابر اجرای فریمبهفریم شبکهی دوبعدی است؛ زیرا هر کرنل 3x3 به 3x3x3 تبدیل میشود.
- کرنلهای زمانی کوچکاند؛ ثبت حرکات در بازههای زمانی طولانی به ساختار هرمی یا رویکردهای دو جریانی (Dual-stream) نیاز دارد.
کاربرد: بازشناسی کنش در جایی که حرکت نقش سیگنال اصلی را دارد (مانند Something-Something V2 یا کلاسهای متکی بر حرکت در Kinetics).
ترنسفورمرهای مکانی-زمانی
ویدیو را به شبکهای از پچهای مکان-زمان توکنایز کنید و روی کل آنها مکانیزم اتنشن بزنید. مدلهایی مثل TimeSformer، ViViT، Video Swin و VideoMAE در این دستهاند.
الگوهای اتنشن کلیدی:
- یکپارچه (Joint) — یک اتنشن بزرگ روی کل ابعاد (t, h, w). با
T*H*Wرابطهی مربعی دارد؛ بسیار پرهزینه. - تفکیکشده (Divided) — دو اتنشن در هر بلوک: یکی روی زمان و دیگری روی مکان. مقیاسپذیری آن تقریباً خطی است.
- فاکتورگیریشده (Factorised) — اتنشن زمانی و مکانی بهصورت متناوب در بلوکهای متوالی پخش میشوند.
مزایا:
- دستیابی به بالاترین دقت روز (SOTA) در تمام بنچمارکهای مهم.
- امکان انتقال وزنها از ترنسفورمرهای تصویر (ViT) از طریق متورمسازی پچها.
- پشتیبانی از ویدیوهای طولانی به کمک اتنشن پراکنده (Sparse attention).
معایب:
- اشتهای پردازشی بالا.
- نیازمند انتخاب دقیق الگوی اتنشن است، وگرنه زمان اجرا در محیط اجرا (Runtime) بیرویه اوج میگیرد.
کاربرد: دیتاستهای بزرگ، درک ویدیو با دقت و وفاداری بالا و تسکهای چندوجهی ویدیو+متن.
نمونهبرداری فریم
یک کلیپ 10 ثانیهای با نرخ 30 فریم بر ثانیه شامل 300 فریم است؛ دادن همهی این 300 فریم به هر مدلی هدر دادن منابع است. استراتژیهای استاندارد:
- نمونهبرداری یکنواخت (Uniform sampling) — انتخاب T فریم با فواصل یکسان در طول کلیپ؛ پیشفرض روش 2D+pool.
- نمونهبرداری متراکم (Dense sampling) — یک پنجرهی T فریمی تصادفی و پیوسته در حافظه (Contiguous)؛ در کانوولوشنهای سهبعدی رایج است چون ثبت حرکت به فریمهای مجاور نیاز دارد.
- چندکلیپی (Multi-clip) — نمونهبرداری از چندین پنجرهی T فریمی از همان ویدیو، طبقهبندی هر کدام، و میانگینگیری از پیشبینیها در زمان آزمون.
مقدار T معمولاً 8، 16، 32 یا 64 انتخاب میشود. مقدار T بالاتر یعنی دریافت سیگنال زمانی بیشتر در ازای بار پردازشی بالاتر.
ارزیابی
در دو سطح انجام میشود:
- دقت در سطح کلیپ (Clip-level accuracy) — مدل یک کلیپ T فریمی را میبیند و top-k را گزارش میدهد.
- دقت در سطح ویدیو (Video-level accuracy) — میانگینگیری از پیشبینیهای سطح کلیپ روی چندین کلیپ از یک ویدیو؛ مقداری بالاتر و باثباتتر است.
همیشه هر دو را گزارش کنید. مدلی با دقت کلیپ 78% و ویدیوی 82% اتکای شدیدی به میانگینگیری در زمان آزمون دارد؛ مدلی با دقت 80% / 81% در سطح تکتک کلیپها مقاومتر است.
دیتاستهایی که خواهید دید
- Kinetics-400 / 600 / 700 — دیتاست کنش عمومی. 400 هزار کلیپ با پیوندهای یوتیوب (که خیلیهایشان اکنون از دسترس خارج شدهاند).
- Something-Something V2 — کنشهای تعریفشده با حرکت (مثل «جابهجا کردن جسم از چپ به راست»)؛ روش 2D+pool از پس حل آن برنمیآید.
- UCF-101 و HMDB-51 — قدیمیتر، کوچکتر و هنوز مرجع مقایسهها.
- AVA — مکانیابی مکانی-زمانی کنش (Action localisation)؛ بسیار سختتر از طبقهبندیِ صرف.
پیادهسازی
مرحلهی 1: نمونهبردار فریم
نمونهبردارهای یکنواخت و متراکم که روی لیستی از فریمها (یا یک تنسور ویدیویی) کار میکنند.
import numpy as np
def sample_uniform(num_frames_total, T):
if num_frames_total <= T:
return list(range(num_frames_total)) + [num_frames_total - 1] * (T - num_frames_total)
step = num_frames_total / T
return [int(i * step) for i in range(T)]
def sample_dense(num_frames_total, T, rng=None):
rng = rng or np.random.default_rng()
if num_frames_total <= T:
return list(range(num_frames_total)) + [num_frames_total - 1] * (T - num_frames_total)
start = int(rng.integers(0, num_frames_total - T + 1))
return list(range(start, start + T))
هر دو تابع، T ایندکس برمیگردانند که با استفاده از آنها تنسور ویدیو را برش (Slice) میزنید.
مرحلهی 2: یک مدل پایهی 2D+pool
اجرای ResNet-18 دوبعدی روی تکتک فریمها، تجمیع میانگین ویژگیها، و سپس طبقهبندی.
import torch
import torch.nn as nn
from torchvision.models import resnet18, ResNet18_Weights
class FramePool(nn.Module):
def __init__(self, num_classes=400, pretrained=True):
super().__init__()
weights = ResNet18_Weights.IMAGENET1K_V1 if pretrained else None
backbone = resnet18(weights=weights)
self.features = nn.Sequential(*(list(backbone.children())[:-1])) # global avg pool kept
self.head = nn.Linear(512, num_classes)
def forward(self, x):
# x: (N, T, 3, H, W)
N, T = x.shape[:2]
x = x.view(N * T, *x.shape[2:])
feats = self.features(x).view(N, T, -1)
pooled = feats.mean(dim=1)
return self.head(pooled)
model = FramePool(num_classes=10)
x = torch.randn(2, 8, 3, 224, 224)
print(f"output: {model(x).shape}")
print(f"params: {sum(p.numel() for p in model.parameters()):,}")
یازده میلیون پارامتر، پیشآموزشدیده روی ImageNet، اجرا روی هر فریم، میانگینگیری و در نهایت طبقهبندی. این مدل پایه در تسکهای مبتنی بر ظاهر اغلب با اختلافی کمتر از 5 تا 10 درصد در تعقیب مدلهای سهبعدی واقعی است — و حتی گاهی بهتر کار میکند، چون از یک ستون فقراتِ (Backbone) پیشآموزشدیدهی قویتر روی ImageNet بهره میبرد.
مرحلهی 3: کانوولوشن سهبعدی متورمشده به سبک I3D
تبدیل یک کانوولوشن دوبعدی ساده به سهبعدی از طریق تکرار وزنها در امتداد یک محور زمانی جدید.
def inflate_2d_to_3d(conv2d, time_kernel=3):
out_c, in_c, kh, kw = conv2d.weight.shape
weight_3d = conv2d.weight.data.unsqueeze(2) # (out, in, 1, kh, kw)
weight_3d = weight_3d.repeat(1, 1, time_kernel, 1, 1) / time_kernel
conv3d = nn.Conv3d(in_c, out_c, kernel_size=(time_kernel, kh, kw),
padding=(time_kernel // 2, conv2d.padding[0], conv2d.padding[1]),
stride=(1, conv2d.stride[0], conv2d.stride[1]),
bias=False)
conv3d.weight.data = weight_3d
return conv3d
conv2d = nn.Conv2d(3, 64, kernel_size=3, padding=1, bias=False)
conv3d = inflate_2d_to_3d(conv2d, time_kernel=3)
print(f"2D weight shape: {tuple(conv2d.weight.shape)}")
print(f"3D weight shape: {tuple(conv3d.weight.shape)}")
x = torch.randn(1, 3, 8, 56, 56)
print(f"3D output shape: {tuple(conv3d(x).shape)}")
تقسیم بر time_kernel دامنهی مقادیر اکتیویشن را تقریباً ثابت نگه میدارد؛ این مسئله برای به هم نخوردن آمارههای نرمالسازی دستهای (Batch normalization) در اولین گذر محاسباتی ضروری است.
مرحلهی 4: کانوولوشن فاکتورگیریشدهی (2+1)D
تجزیهی یک کانوولوشن سهبعدی به یک کانوولوشن دوبعدی (مکانی) و یک کانوولوشن 1 بعدی (زمانی). میدان دید (Receptive field) یکسان است، پارامترها کمتر میشوند و در برخی بنچمارکها دقت افزایش مییابد.
class Conv2Plus1D(nn.Module):
def __init__(self, in_c, out_c, kernel_size=3):
super().__init__()
mid_c = (in_c * out_c * kernel_size * kernel_size * kernel_size) \
// (in_c * kernel_size * kernel_size + out_c * kernel_size)
self.spatial = nn.Conv3d(in_c, mid_c, kernel_size=(1, kernel_size, kernel_size),
padding=(0, kernel_size // 2, kernel_size // 2), bias=False)
self.bn = nn.BatchNorm3d(mid_c)
self.act = nn.ReLU(inplace=True)
self.temporal = nn.Conv3d(mid_c, out_c, kernel_size=(kernel_size, 1, 1),
padding=(kernel_size // 2, 0, 0), bias=False)
def forward(self, x):
return self.temporal(self.act(self.bn(self.spatial(x))))
c = Conv2Plus1D(3, 64)
x = torch.randn(1, 3, 8, 56, 56)
print(f"(2+1)D output: {tuple(c(x).shape)}")
یک شبکهی کامل R(2+1)D دقیقاً شبیه ResNet-18 است، فقط به جای هر کانوولوشن 3x3 در آن از Conv2Plus1D استفاده شده است.
کاربرد عملی
دو کتابخانه نیازهای عملیاتی کار با ویدیو را در محیط پروداکشن برطرف میکنند:
torchvision.models.video— مدلهای R(2+1)D، MViT و Swin3D با وزنهای پیشآموزشدیدهی Kinetics؛ با همان رابط کاربری (API) مدلهای تصویری.pytorchvideo(متعلق به Meta) — مجموعهای از مدلها، لودرهای داده برای Kinetics ،SSv2 و AVA، به همراه تبدیلات استاندارد ویدیویی.
برای مدلهای ویدیویی زبان-تصویر (تولید کپشن برای ویدیو یا پرسشوپاسخ ویدیویی)، از transformers (مدلهای VideoMAE، VideoLLaMA و InternVideo) استفاده کنید.
تحویل پروژه
خروجیهای این درس:
outputs/prompt-video-architecture-picker.md— یک پرامپت برای انتخاب میان 2D+pool ،I3D ،(2+1)D یا ترنسفورمر، بر اساس برتری ظاهر یا حرکت، حجم دیتاست و بودجهی محاسباتی.outputs/skill-frame-sampler-auditor.md— مهارتی که نمونهبردار پایپلاین ویدیویی را وارسی میکند و باگهای رایج را هشدار میدهد: خطای یکیکمتر/یکیبیشتر در ایندکس (Off-by-one)، نمونهبرداری نامتوازن در زمانnum_frames < T، نبود برش بدون تغییر نسبت تصویر (Aspect-preserving crop) و موارد دیگر.
تمرینها
- (ساده) حجم محاسبات تقریبی (FLOPs) را برای FramePool با T=8 در برابر ResNet سهبعدی به سبک I3D با T=8 حساب کنید. توضیح دهید چرا روش 2D+pool حدوداً 3 تا 5 برابر سبکتر است.
- (متوسط) یک دیتاست ویدیویی ساختگی بسازید: توپهای تصادفی که در جهتهای دلخواه حرکت میکنند و بر اساس جهت حرکت برچسب خوردهاند («چپ به راست»، «راست به چپ»، «مورب به بالا»). مدل FramePool را روی آن آموزش دهید. نشان دهید دقت آن در حد حدس تصادفی باقی میماند و اثبات کنید ظاهرِ تنها برای تسکهای حرکتی ناکافی است.
- (سخت) یک شبکهی R(2+1)D-18 بسازید و تمام لایههای Conv2d در ResNet-18 را با
Conv2Plus1Dجایگزین کنید. وزنهای اولین کانوولوشن را بر اساس یک ResNet-18 پیشآموزشدیده روی ImageNet متورم کنید. آن را روی دیتاست حرکتی تمرین 2 آموزش دهید و عملکرد بهتری نسبت به FramePool به ثبت برسانید.
اصطلاحات کلیدی
| اصطلاح | در گفتگوها چه میگویند | معنای دقیق فنی |
|---|---|---|
| 2D + pool | «طبقهبند فریمبهفریم» | اجرای یک 2D CNN روی تمام فریمهای نمونهبرداریشده، میانگینگیری از ویژگیها در طول زمان و سپس طبقهبندی |
| کانوولوشن سهبعدی | «کرنل مکانی-زمانی» | کرنلی که روی (T, H, W) کانوالو میکند و ذاتاً توانایی مدلسازی حرکت را دارد |
| متورمسازی (Inflation) | «ارتقای وزنهای دوبعدی به سهبعدی» | مقداردهی اولیهی وزنهای کانوولوشن سهبعدی با تکرار وزنهای دوبعدی در امتداد محور زمانی جدید و سپس تقسیم بر kernel_T برای حفظ مقیاس اکتیویشن |
| (2+1)D | «کانوولوشن فاکتورگیریشده» | تجزیهی کانوولوشن سهبعدی به دوبعدی مکانی + 1 بعدی زمانی؛ کاهش پارامترها و افزودن تابع غیرخطی اضافی میان آنها |
| اتنشن تفکیکشده (Divided attention) | «اول زمان بعد مکان» | بلوک ترنسفورمر با دو بار اتنشن در هر لایه: یکی روی توکنهای یک فریم یکسان، دیگری روی توکنهای همان موقعیت در فریمهای مختلف |
| کلیپ (Clip) | «پنجرهی T فریمی» | یک زیرتوالی نمونهبرداریشده شامل T فریم؛ واحد ورودی استاندارد برای پردازش در مدل ویدیویی |
| دقت کلیپ در برابر ویدیو | «دو حالت ارزیابی» | کلیپ = یک بار نمونهبرداری به ازای هر ویدیو؛ ویدیو = میانگینگیری روی چند کلیپ نمونهبرداریشده از یک ویدیو |
| Kinetics | «همان ImageNet برای ویدیو» | دارای 400 تا 700 کلاس کنش، بیش از 300 هزار کلیپ یوتیوب و مجموعهی دادهی مرجع برای پیشآموزش ویدیو |
برای مطالعهی بیشتر
- I3D: Quo Vadis, Action Recognition (Carreira & Zisserman, 2017) — مقالهی معرفی متورمسازی و دیتاست Kinetics
- R(2+1)D: A Closer Look at Spatiotemporal Convolutions (Tran et al., 2018) — کانوولوشن فاکتورگیریشده که همچنان پایهای قدرتمند برای مقایسه است
- TimeSformer: Is Space-Time Attention All You Need? (Bertasius et al., 2021) — نخستین ترنسفورمر مقتدر در حوزهی ویدیو
- VideoMAE (Tong et al., 2022) — پیشآموزش ویدیویی با اتوانکودر پوشاندهشده (Masked autoencoder)؛ دستورالعمل پیشتاز کنونی در پیشآموزش مدلها
