رحلة من البكسل إلى التوقعات: تصميم تحول بصري من الصفر
من البكسلات إلى التوقعات: بناء مُحوَّل لمعالجة الصور في عام 2020، سأل فريق من الباحثين في غوغل سؤالًا جريئًا: هل يمكننا الاستغناء تمامًا عن التحويلات التجميعية (Convolutions) وإنشاء نماذج صورية عالمية؟ كان جوابهم هو مُحوَّل الصور (Vision Transformer - ViT)، الذي أطلق عصرًا جديدًا في التعلم العميق. الخلفية والبديهة 1.1 من النماذج التكرارية إلى هيمنة المُحوَّلات (في معالجة اللغات الطبيعية) قبل عام 2017، كانت معالجة اللغات الطبيعية (NLP) تحكمها النماذج التكرارية مثل RNNs وLSTMs التي كانت تقف وراء العديد من التطبيقات مثل الترجمة الآلية ونمذجة اللغة. ولكن على الرغم من نجاحها المبكر، كانت لها قيود أساسية، حيث لم تتمكن من التعامل بفعالية مع البيانات الطويلة ولا من توازي التدريب. في عام 2017، حقق باحثو غوغل اختراقًا عندما نشروا ورقة بحثية بعنوان "الانتباه هو كل ما تحتاجه". اقترحوا إطارًا جديدًا يعتمد على الانتباه الذاتي (Self-Attention)، مما سمح لكل كلمة في الجملة بفهم سياق الكلمات الأخرى بشكل مباشر. هذا الإطار أثبت كفاءته في التعامل مع العلاقات البعيدة وأصبح أساسًا للعديد من النماذج المهمة مثل BERT وGPT وT5. 1.2 هل يمكن للانتباه استبدال التحويلات التجميعية؟ التحول إلى الصور في عام 2020، قدم Dosovitskiy وزملاؤه مُحوَّل الصور (ViT) في ورقتهم "الصورة تساوي 16×16 كلمة". اقترحوا فكرة جريئة: ماذا لو عاملنا الصورة مثل الجملة؟ بدلاً من الاعتماد على المرشحات التجميعية، قسموا الصور إلى مربعات صغيرة (مثل 16×16 بكسل) ثم أدخلوها إلى مُحوَّل قياسي. رغم أن النماذج الأولى من ViT كانت تتطلب مجموعات بيانات ضخمة للمنافسة مع CNNs، فقد أثبتت أن النماذج القائمة على الانتباه يمكن أن تعمل بشكل فعال في مجال الرؤية الحاسوبية. كيفية عمل مُحوَّلات الصور 2.1 تضمين المربعات (Patch Embedding) تُعد الصور شبكات ثنائية الأبعاد من البكسلات، بينما يتعامل المُحوَّلات مع التسلسلات أحادية الأبعاد. لمعالجة هذه المشكلة، يقسم ViT الصورة إلى مربعات صغيرة غير متداخلة (مثل 16×16 بكسل). يتم تسطيح كل مربع إلى متجه واحد بُعدي (1D vector) ويتم ربطه بعد ذلك في تضمين ثابت الحجم (fixed-size embedding). 2.2 ورقة الفصل الخاصة بالتصنيف (Class Token) والاضمنان الموضعي (Positional Embeddings) لجعل المُحوَّل يعمل بشكل صحيح مع تسلسلات الصور، يحتاج إلى مكونين إضافيين: ورقة الفصل الخاصة بالتصنيف ([CLS] token): تُضاف إلى بداية التسلسل وتتعلم لتجميع المعلومات من جميع المربعات. الاضمنان الموضعي (Positional Embeddings): يُضاف إلى كل متجه في التسلسل لتقديم إحساسatial بالترتيب المكاني. 2.3 الانتباه الذاتي متعدد الرؤوس (Multi-Head Self-Attention - MHSA) في قلب ViT يوجد آلية الانتباه الذاتي متعدد الرؤوس، وهي تسمح للمُحوَّل بفهم كيف ترتبط المربعات ببعضها البعض، بغض النظر عن المسافة المكانية بينها. بدلاً من استخدام دالة انتباه واحدة، يتم تقسيم الإدخال إلى عدة "رؤوس"، حيث تركز كل رأس على جوانب مختلفة من الإدخال. يتم حساب الانتباه عبر هذه الرؤوس بشكل متوازي، ثم يتم دمج النواتج وإسقاطها مرة أخرى إلى البُعد الأصلي للتضمين. 2.4 الكتلة المُحوَّلة (Transformer Block) تجمع الكتلة المُحوَّلة مكونات الانتباه الذاتي والشبكة العصبية المتعددة الطبقات (MLP) مع الاتصالات المتبقية (residual connections) لتحقيق الاستقرار. تتكون الكتلة من: تطبيع الطبقة (LayerNorm) قبل الانتباه. تطبيق الانتباه الذاتي متعدد الرؤوس على الإدخال المُعَّدَم. اتصال متبقٍ لإضافة خرج الانتباه إلى الإدخال الأصلي. تطبيع آخر، تليه شبكة MLP صغيرة. اتصال متبقٍ آخر لإضافة خرج MLP. 2.5 رأس التصنيف (Classification Head) بعد معالجة الإدخال عبر عدة طبقات مُحوَّلة، يحتاج النموذج إلى طريقة لإنتاج توقع نهائي. في ViT، يتم استخدام تضمين [CLS] كمُلخص للصورة بأكملها. يُمرر هذا المتجه عبر طبقة خطية بسيطة لتوليد التوقعات النهائية. تنفيذ ViT 3.1 تضمين المربعات (Patch Embedding) يتم تحويل المربعات الصورية إلى تسلسل من التضمينات باستخدام طبقة Conv2d مع حجم نواة ومعدل خطوة يساوي حجم المربع. يتم تسطيح النواتج وإعادة ترتيبها لتشكيل تسلسل جاهز للمُحوَّل. ```python class PatchEmbed(nn.Module): def init(self, img_size=224, patch_size=16, in_chans=3, embed_dim=768): super().init() self.img_size = img_size self.patch_size = patch_size self.num_patches = (img_size // patch_size) ** 2 self.proj = nn.Conv2d( in_chans, embed_dim, kernel_size=patch_size, stride=patch_size ) def forward(self, x): x = self.proj(x) x = x.flatten(2) x = x.transpose(1, 2) return x ``` 3.2 ورقة الفصل الخاصة بالتصنيف (Class Token) والاضمنان الموضعي (Positional Embeddings) يتم تعريف ورقة الفصل الخاصة بالتصنيف (CLS token) والاضمنان الموضعي (positional embeddings) كمعلمات قابلة للتعلم. يتم إضافة ورقة الفصل الخاصة إلى بداية التسلسل وإضافة الاضمنان الموضعي إلى كل متجه. ```python class ViTEmbed(nn.Module): def init(self, num_patches, embed_dim): super().init() self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim)) self.pos_embed = nn.Parameter(torch.zeros(1, num_patches + 1, embed_dim)) def forward(self, x): batch_size = x.shape[0] cls_tokens = self.cls_token.expand(batch_size, -1, -1) x = torch.cat((cls_tokens, x), dim=1) x = x + self.pos_embed return x ``` 3.3 الانتباه الذاتي متعدد الرؤوس (Multi-Head Self-Attention) يتم تنفيذ الانتباه الذاتي متعدد الرؤوس عن طريق إسقاط كل متجه في الإدخال إلى متجهات الاستفسار (Q)، والمفتاح (K)، والقيمة (V). يتم حساب الانتباه عبر هذه المتجهات بشكل متوازي، ثم يتم دمج النواتج وإسقاطها مرة أخرى إلى البُعد الأصلي. ```python class MyMultiheadAttention(nn.Module): def init(self, embed_dim, num_heads): super().init() assert embed_dim % num_heads == 0, "embed_dim must be divisible by num_heads" self.embed_dim = embed_dim self.num_heads = num_heads self.head_dim = embed_dim // num_heads self.q_proj = nn.Linear(embed_dim, embed_dim) self.k_proj = nn.Linear(embed_dim, embed_dim) self.v_proj = nn.Linear(embed_dim, embed_dim) self.out_proj = nn.Linear(embed_dim, embed_dim) def forward(self, x): B, T, C = x.shape Q = self.q_proj(x) K = self.k_proj(x) V = self.v_proj(x) def split_heads(tensor): return tensor.view(B, T, self.num_heads, self.head_dim).transpose(1, 2) Q = split_heads(Q) K = split_heads(K) V = split_heads(V) scores = torch.matmul(Q, K.transpose(-2, -1)) scores /= self.head_dim ** 0.5 attn = torch.softmax(scores, dim=-1) out = torch.matmul(attn, V) out = out.transpose(1, 2).contiguous().view(B, T, C) return self.out_proj(out) ``` 3.4 الكتلة المُحوَّلة (Transformer Block) تُجمع كل المكونات في كتلة مُحوَّلة تربط طبقات الانتباه الذاتي وMLP مع الاتصالات المتبقية، مما يسمح للنموذج بالاستدلال بشكل عام ثم تحويل تلك التمثيلات، مع الحفاظ على الاستقرار عبر الروابط المباشرة. ```python class TransformerBlock(nn.Module): def init(self, embed_dim, num_heads, mlp_ratio=4.0): super().init() self.norm1 = nn.LayerNorm(embed_dim) self.attn = nn.MultiheadAttention(embed_dim, num_heads, batch_first=True) self.norm2 = nn.LayerNorm(embed_dim) self.mlp = nn.Sequential( nn.Linear(embed_dim, int(embed_dim * mlp_ratio)), nn.GELU(), nn.Linear(int(embed_dim * mlp_ratio), embed_dim) ) def forward(self, x): x = x + self.attn(self.norm1(x), self.norm1(x), self.norm1(x))[0] x = x + self.mlp(self.norm2(x)) return x ``` 3.5 وضع كل شيء معًا بعد بناء جميع المكونات الرئيسية، يتم دمجها في نموذج كامل. ```python class SimpleViT(nn.Module): def init(self, img_size=224, patch_size=16, in_chans=3, embed_dim=768, depth=12, num_heads=12, num_classes=1000): super().init() self.patch_embed = PatchEmbed(img_size, patch_size, in_chans, embed_dim) num_patches = (img_size // patch_size) ** 2 self.vit_embed = ViTEmbed(num_patches, embed_dim) self.blocks = nn.Sequential(*[ TransformerBlock(embed_dim, num_heads) for _ in range(depth) ]) self.norm = nn.LayerNorm(embed_dim) self.head = nn.Linear(embed_dim, num_classes) def forward(self, x): x = self.patch_embed(x) x = self.vit_embed(x) x = self.blocks(x) x = self.norm(x) return self.head(x[:, 0]) ``` تدريب ViT 4.1 مجموعة البيانات: CIFAR-10 تم تدريب ViT على CIFAR-10، وهي مجموعة بيانات معروفة تحتوي على 60,000 صورة مقسمة إلى 10 فئات (مثل الطائرات والقطط والسفن). كل صورة في مجموعة البيانات هذه تكون بحجم 32×32 بكسل، مما يجعلها: مثالية لاختبار ViT. أقل تعقيدًا من مجموعات البيانات الكبيرة مثل ImageNet. 4.2 إعداد النموذج: تكييف ViT لـ CIFAR-10 في الأصل، تم تصميم ViTs لمجموعات بيانات كبيرة مثل ImageNet. لذلك، أجرينا بعض التعديلات لجعل التدريب ممكنًا على CIFAR-10 مع إمكانات الحوسبة المحدودة: python model = SimpleViT( img_size=32, # صور CIFAR-10 بحجم 32x32 patch_size=4, # مربعات 4x4 → 64 مربع in_chans=3, embed_dim=192, # حجم تضمين أصغر depth=6, # عدد أقل من الكتل المُحوَّلة num_heads=3, # يقسم بالتساوي على 192 num_classes=10 # لـ CIFAR-10 ).to(device) 4.3 إعداد التدريب تم تدريب النموذج باستخدام: تحسين AdamW. الدوال الخسارة CrossEntropyLoss. تعلم معدلات تعلمية ثابتة (0.001). تنظيم الأوزان (Weight decay) بقيمة 0.01. 4.4 النتائج تم تدريب ViT لمدة 30 عصرًا، بإجمالي حوالي 15 دقيقة على وحدة معالجة الرسومات (GPU). بحلول نهاية التدريب، حقق النموذج دقة تبلغ حوالي 60% على مجموعة بيانات الاختبار CIFAR-10، وهي نتيجة مقبولة بالنظر إلى بساطة النموذج وحجم مجموعة البيانات الصغير. التقدم في التعلم كما تظهر في رسومات التدريب أدناه: انخفضت الخسارة التدريبية بشكل مستمر. ارتفعت دقة الاختبار بشكل ملحوظ خلال العصور الأولى. التنبؤات المثالبة أظهر النموذج تنبؤات صحيحة للكثير من العينات (مثل القطط والضفادع)، ولكنه واجه صعوبة مع الفئات المشابهة بصريًا (مثل تسمية السفينة بالطائرة). الدقة حسب الفئة توضح الرسمة الشريطية أدناه أداء النموذج في كل فئة. لاحظ أن بعض الفئات مثل القطط والطير حققت دقة أعلى بكثير من غيرها. تقييم الحدث وفقًا للمتخصصين في مجال الرؤية الحاسوبية، يعتبر Vision Transformer خطوة هامة نحو بناء نماذج أكثر فعالية وقابلية للتوسع في مجال الرؤية الحاسوبية. بينما كانت CNNs مهيمنة لفترة طويلة، أثبت ViT قدرته على التقاط العلاقات البعيدة والمعقدة في الصور، مما يمكنه من تجاوز القيود المحلية لـ CNNs. من الجدير بالذكر أن Dosovitskiy وزملاؤه في غوغل كانوا الرواد في هذا المجال، وفتحوا الباب أمام العديد من التطورات الأخرى التي تستخدم الانتباه الذاتي في معالجة الصور.