PyTorبرنامج تعليمي حول نقل التعلم مع أمثلة

⚡ ملخص ذكي

يعيد التعلم بالنقل استخدام شبكة تم تدريبها بالفعل على مجموعة بيانات كبيرة بحيث يمكن حل مهمة جديدة ذات صلة بعدد أقل بكثير من الصور المصنفة وجزء من وقت التدريب الأصلي.

  • 🔘 الفكرة الأساسية: تقوم الأوزان التي تم تعلمها على ImageNet بالفعل بتشفير الحواف والقوام والأشكال التي تعيد معظم مهام الرؤية استخدامها.
  • ☑️ استراتيجيتان: مثال مميزtraction يثبط العمود الفقري، بينما يستمر الضبط الدقيق في تدريب بعض أو كل الطبقات الأصلية.
  • ✅ مثال عملي: يكفي ما يقرب من 700 صورة من فيلمي Alien و Predator لإعادة تدريب الطبقة الأخيرة من VGG19.
  • 🧪 PyTorقطع الفصل: تقوم ImageFolder و transforms.Compose و DataLoader بتجميع الدفعات التي تستهلكها الشبكة.
  • 🛠️ طبقات التجميد: يؤدي ضبط requires_grad على False إلى إيقاف التدرجات، وبالتالي فإن المصنف المستبدل فقط هو الذي يتعلم.
  • ⚠️ النتيجة المُبلغ عنها: تم الانتهاء من خمس وعشرين دورة تدريبية في أقل من ثلاث دقائق على مجموعة البيانات النموذجية.

PyTorبرنامج تعليمي حول نقل التعلم مع أمثلة محلولة

ما هو نقل التعلم؟

نقل التعلم هي تقنية تستخدم نموذجًا مُدرَّبًا لحل مهمة أخرى ذات صلة. تعلم آلة أسلوب بحثي يخزن المعرفة المكتسبة أثناء حل مشكلة معينة، ويستخدم هذه المعرفة نفسها لحل مشكلة أخرى مختلفة ولكنها ذات صلة. وهذا يحسن الكفاءة من خلال إعادة استخدام المعلومات التي تم جمعها من المهمة التي تم تعلمها سابقاً.

من الشائع إعادة استخدام أوزان نموذج شبكة آخر لأن تدريب شبكة من الصفر يتطلب كمية هائلة من البيانات. ولتقليل وقت التدريب، يتم استخدام شبكة موجودة وأوزانها، ثم تعديل الطبقة الأخيرة لحل المشكلة المطلوبة. وتكمن الميزة في إمكانية تدريب هذه الطبقة الأخيرة باستخدام مجموعة بيانات صغيرة.

قبل كتابة أي شيء بلغة بايثونTorفي الكود ch، من المفيد معرفة أي عائلة من التعلم النقل تنتمي إليها مشكلتك، لأن ذلك يحدد مقدار البيانات المصنفة التي تحتاجها.

أنواع نقل التعلم

تقسم الدراسات البحثية هذه التقنية إلى ثلاث مجموعات. ويعتمد تحديد المجموعة التي تنطبق عليك على أي جانب من جوانب المشكلة يحمل التصنيفات، وليس على الإطار الذي تستخدمه.

النوع مجال المصدر Target نطاق الاستخدام النموذجي
استقرائية المسمى مُصنّفة، لكنها مهمة مختلفة إعادة توجيه بنية ImageNet الأساسية نحو مشكلة تصنيف الكائنات الفضائية ضد المفترسات إلى فئتين
التحويلي المسمى بدون تصنيف، نفس المهمة، توزيع بيانات مختلف تكييف المجال، مثل نقل نموذج من صور الاستوديو إلى صور الهاتف
غير خاضع للرقابة غير مصنف غير مصنف Clusterأو تقليل الأبعاد حيث يكون تصنيف كل سجل غير عملي

المثال الذي تم بناؤه في هذا البرنامج التعليمي هو التعلم الاستقرائي بالنقل. يصل VGG19 مزودًا بمعرفة ImageNet المصنفة، ثم يتم توجيهه إلى مشكلة تصنيف ثنائية مصنفة لم يسبق له رؤيتها من قبل.

ميزة إكسtracالضبط مقابل الضبط الدقيق

بعد اختيار شبكة مدربة مسبقًا، هناك طريقتان لتكييفها. الفرق ببساطة هو عدد الطبقات التي تسمح لها بمواصلة التعلم.

البعد مثال مميزtracالإنتاج الكون المثالى
طبقات التدريب المصنف المستبدل فقط المصنف بالإضافة إلى بعض أو كل الكتل الالتفافية
يتطلب_التخرج على العمود الفقري خطأ ينطبق هذا على الكتل التي يتم تحديثها
البيانات المطلوبة صور صغيرة، غالباً بضع مئات من الصور لكل فصل دراسي أكبر حجماً، عادةً ما يصل إلى الآلاف
تكلفة التدريب الأقل تكلفة، يعمل على وحدة المعالجة المركزية كلما ارتفع مستوى وحدة معالجة الرسومات، كلما أصبحت تستحق الاقتناء.
الدقة النموذجية يكون ذلك مفيدًا عندما تتشابه الصور المصدر والهدف. عادةً ما يكون الوضع أفضل عندما يختلف المجالان

تستخدم الخطوات التالية ميزة extracفي هذه الحالة، يتم تجميد جميع معلمات VGG19، وتتعلم الطبقة الخطية النهائية الجديدة فقط. أما الانتقال إلى الضبط الدقيق فهو تغيير بسيط، يتمثل في ترك خاصية requires_grad مضبوطة على True في الكتل التي ترغب بتحديثها، وخفض معدل التعلم حتى لا تُفقد الأوزان المُستعارة.

تحميل مجموعة البيانات

قبل البدء في استخدام التعلم بالنقل مع بايثونTorch، عليك فهم مجموعة البيانات التي ستستخدمها. في هذا التعلم النقل باستخدام بايثونTorفي المثال المذكور، ستصنف كائنًا فضائيًا ومفترسًا من بين ما يقارب 700 صورة. لا تتطلب هذه التقنية كمية كبيرة من البيانات للتدريب. يمكنك تنزيل مجموعة البيانات من كاغل: ألين ضد بريداتور.

المجموعة صغيرة عمداً، ويتم عرض عينة من الصور التي تحتويها أدناه.

مجموعة بيانات صور فيلم Alien vs Predator من Kaggle المستخدمة في هذا المشروع المكتوب بلغة بايثونTorأمثلة على التعلم الانتقالي

المصدر مقابل الغريبة المفترس Kaggle

التالي في هذا بايثونTorفي هذا الدرس التعليمي حول التعلم بالنقل، ستتعلم كيفية تطبيق التعلم بالنقل باستخدام لغة بايثون.Torخطوة بخطوة.

كيفية استخدام نقل التعلم؟

إليك شرحًا تفصيليًا لكيفية استخدام التعلم بالنقل في التعلم العميق باستخدام بايثونTorالفصل:

الخطوة 1) تحميل البيانات

تتمثل الخطوة الأولى في تحميل البيانات وتطبيق بعض التحويلات على الصور بحيث تتطابق مع متطلبات الشبكة.

ستقوم بتحميل البيانات من مجلد يحتوي على torchvision.datasets. يقوم البرنامج بتكرار العملية على المجلد لتقسيم البيانات إلى مجموعتي تدريب وتحقق. تقوم عملية التحويل المستخدمة هنا باقتصاص الصور من المركز، وتحويلها إلى موتر، ثم تطبيعها لـ تعلم عميق.

from __future__ import print_function, division
import os
import time
import torch
import torchvision
from torchvision import datasets, models, transforms
import torch.optim as optim
import numpy as np
import matplotlib.pyplot as plt

data_dir = "alien_pred"
input_shape = 224
mean = [0.5, 0.5, 0.5]
std = [0.5, 0.5, 0.5]

#data transformation
data_transforms = {
   'train': transforms.Compose([
       transforms.CenterCrop(input_shape),
       transforms.ToTensor(),
       transforms.Normalize(mean, std)
   ]),
   'validation': transforms.Compose([
       transforms.CenterCrop(input_shape),
       transforms.ToTensor(),
       transforms.Normalize(mean, std)
   ]),
}

image_datasets = {
   x: datasets.ImageFolder(
       os.path.join(data_dir, x),
       transform=data_transforms[x]
   )
   for x in ['train', 'validation']
}

dataloaders = {
   x: torch.utils.data.DataLoader(
       image_datasets[x], batch_size=32,
       shuffle=True, num_workers=4
   )
   for x in ['train', 'validation']
}

dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'validation']}

print(dataset_sizes)
class_names = image_datasets['train'].classes

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

والآن، قم بتصور مجموعة البيانات. تأخذ خطوة التصور الدفعة التالية من الصور والتصنيفات من مُحمِّل بيانات التدريب وتعرضها باستخدام مكتبة Matplotlib.

images, labels = next(iter(dataloaders['train']))

rows = 4
columns = 4
fig=plt.figure()
for i in range(16):
   fig.add_subplot(rows, columns, i+1)
   plt.title(class_names[labels[i]])
   img = images[i].numpy().transpose((1, 2, 0))
   img = std * img + mean
   plt.imshow(img)
plt.show()

تشغيل هذا المقتطف يرسم شبكة من أربعة في أربعة من صور التدريب، كل منها يحمل عنوان اسم الفئة التي أعادها المحمل.

مجموعة من ست عشرة صورة تدريبية مرسومة في شبكة Matplotlib مكونة من أربعة في أربعة مع عناوين الفئات

الخطوة 2) تحديد النموذج

في عملية التعلم العميق هذه، ستستخدم VGG19 من وحدة torchvision.

ستستخدم مكتبة torchvision.models لتحميل مجموعة بيانات vgg19 مع تفعيل الأوزان المدربة مسبقًا. بعد ذلك، ستقوم بتجميد الطبقات بحيث لا يمكن تدريبها. ثم ستُعدّل الطبقة الأخيرة بإضافة طبقة خطية مناسبة للمشكلة، والتي تعني هنا فئتين. سيتم استخدام دالة خسارة الإنتروبيا المتقاطعة (CrossEntropyLoss)، والمُحسِّن هو SGD بمعدل تعلّم 0.001 ومعامل زخم 0.9، كما هو موضح في كود بايثون أدناه.Torمثال على التعلم الانتقالي.

## Load the model based on VGG19
vgg_based = torchvision.models.vgg19(pretrained=True)

## freeze the layers
for param in vgg_based.parameters():
   param.requires_grad = False

# Modify the last layer
number_features = vgg_based.classifier[6].in_features
features = list(vgg_based.classifier.children())[:-1] # Remove last layer
features.extend([torch.nn.Linear(number_features, len(class_names))])
vgg_based.classifier = torch.nn.Sequential(*features)

vgg_based = vgg_based.to(device)

print(vgg_based)

criterion = torch.nn.CrossEntropyLoss()
optimizer_ft = optim.SGD(vgg_based.parameters(), lr=0.001, momentum=0.9)

ملاحظة حول الإصدار: القادم pretrained=True لا تزال الحجة صالحة، ولكن تم استبدالها منذ إصدار torchvision 0.13 بواسطة الأوزان لذا تتوقع عمليات التثبيت الأحدث وجود هذا الوسيط. torchvision.models.vgg19(weights=VGG19_Weights.DEFAULT) وإلا، فسيتم طباعة تحذير بشأن الإهمال. كلا النموذجين يحملان نفس أوزان ImageNet.

هيكل نموذج الإخراج

طباعة النموذج تُعيد رسم VGG19 البياني الكامل. اقرأ السطر الأخير من كتلة المصنف للتأكد من نجاح عملية التبديل: فهو يُنتج الآن مُخرجين بدلاً من 1,000 فئة من فئات ImageNet.

VGG(
  (features): Sequential(
	(0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
	(1): ReLU(inplace)
	(2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
	(3): ReLU(inplace)
	(4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
	(5): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
	(6): ReLU(inplace)
	(7): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
	(8): ReLU(inplace)
	(9): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
	(10): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
	(11): ReLU(inplace)
	(12): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
	(13): ReLU(inplace)
	(14): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
	(15): ReLU(inplace)
	(16): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
	(17): ReLU(inplace)
	(18): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
	(19): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
	(20): ReLU(inplace)
	(21): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
	(22): ReLU(inplace)
	(23): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
	(24): ReLU(inplace)
	(25): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
	(26): ReLU(inplace)
	(27): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
	(28): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
	(29): ReLU(inplace)
	(30): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
	(31): ReLU(inplace)
	(32): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
	(33): ReLU(inplace)
	(34): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
	(35): ReLU(inplace)
	(36): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (classifier): Sequential(
	(0): Linear(in_features=25088, out_features=4096, bias=True)
	(1): ReLU(inplace)
	(2): Dropout(p=0.5)
	(3): Linear(in_features=4096, out_features=4096, bias=True)
	(4): ReLU(inplace)
	(5): Dropout(p=0.5)
	(6): Linear(in_features=4096, out_features=2, bias=True)
  )
)

الخطوة 3) تدريب واختبار النموذج

سنستخدم بعض الوظائف من هذا PyTorالفصل التعليمي لمساعدتنا في تدريب وتقييم نموذجنا.

def train_model(model, criterion, optimizer, num_epochs=25):
   since = time.time()

   for epoch in range(num_epochs):
       print('Epoch {}/{}'.format(epoch, num_epochs - 1))
       print('-' * 10)

       #set model to trainable
       # model.train()

       train_loss = 0

       # Iterate over data.
       for i, data in enumerate(dataloaders['train']):
           inputs , labels = data
           inputs = inputs.to(device)
           labels = labels.to(device)

           optimizer.zero_grad()
          
           with torch.set_grad_enabled(True):
               outputs  = model(inputs)
               loss = criterion(outputs, labels)

           loss.backward()
           optimizer.step()

           train_loss += loss.item() * inputs.size(0)

           print('{} Loss: {:.4f}'.format(
               'train', train_loss / dataset_sizes['train']))
          
   time_elapsed = time.time() - since
   print('Training complete in {:.0f}m {:.0f}s'.format(
       time_elapsed // 60, time_elapsed % 60))

   return model

def visualize_model(model, num_images=6):
   was_training = model.training
   model.eval()
   images_so_far = 0
   fig = plt.figure()

   with torch.no_grad():
       for i, (inputs, labels) in enumerate(dataloaders['validation']):
           inputs = inputs.to(device)
           labels = labels.to(device)

           outputs = model(inputs)
           _, preds = torch.max(outputs, 1)

           for j in range(inputs.size()[0]):
               images_so_far += 1
               ax = plt.subplot(num_images//2, 2, images_so_far)
               ax.axis('off')
               ax.set_title('predicted: {} truth: {}'.format(class_names[preds[j]], class_names[labels[j]]))
               img = inputs.cpu().data[j].numpy().transpose((1, 2, 0))
               img = std * img + mean
               ax.imshow(img)

               if images_so_far == num_images:
                   model.train(mode=was_training)
                   return
       model.train(mode=was_training)

وأخيرًا في هذا التعلم النقل في بايثونTorفي المثال المذكور، ابدأ عملية التدريب بضبط عدد الدورات التدريبية على 25، ثم قيّم الشبكة. في كل خطوة تدريب، يأخذ النموذج المدخلات ويتنبأ بالمخرجات. تُمرر التنبؤات إلى المعيار لحساب الخسارة، ثم تحسب خوارزمية الانتشار العكسي التدرجات، ويُحدّث المُحسِّن الأوزان باستخدام التدرج التلقائي.

في وظيفة التصور، يتم اختبار الشبكة المدربة بمجموعة من الصور للتنبؤ بالتصنيفات، ويتم رسم النتيجة باستخدام Matplotlib.

vgg_based = train_model(vgg_based, criterion, optimizer_ft, num_epochs=25)

visualize_model(vgg_based)

plt.show()

الخطوة 4) النتائج

بلغت دقة هذه العملية 92%. يُظهر السجل المطبوع في نهاية التدريب نسبة الخطأ في آخر دورتين تدريبيتين بالإضافة إلى إجمالي وقت التدريب.

Epoch 23/24
----------
train Loss: 0.0044
train Loss: 0.0078
train Loss: 0.0141
train Loss: 0.0221
train Loss: 0.0306
train Loss: 0.0336
train Loss: 0.0442
train Loss: 0.0482
train Loss: 0.0557
train Loss: 0.0643
train Loss: 0.0763
train Loss: 0.0779
train Loss: 0.0843
train Loss: 0.0910
train Loss: 0.0990
train Loss: 0.1063
train Loss: 0.1133
train Loss: 0.1220
train Loss: 0.1344
train Loss: 0.1382
train Loss: 0.1429
train Loss: 0.1500
Epoch 24/24
----------
train Loss: 0.0076
train Loss: 0.0115
train Loss: 0.0185
train Loss: 0.0277
train Loss: 0.0345
train Loss: 0.0420
train Loss: 0.0450
train Loss: 0.0490
train Loss: 0.0644
train Loss: 0.0755
train Loss: 0.0813
train Loss: 0.0868
train Loss: 0.0916
train Loss: 0.0980
train Loss: 0.1008
train Loss: 0.1101
train Loss: 0.1176
train Loss: 0.1282
train Loss: 0.1323
train Loss: 0.1397
train Loss: 0.1436
train Loss: 0.1467
Training complete in 2m 47s

ثم يتم عرض تنبؤات النموذج باستخدام Matplotlib، كما هو موضح أدناه.

صور التحقق مصنفة حسب الفئة المتوقعة والفئة الحقيقية بعد التدريب

أخطاء شائعة في التعلم الانتقالي وكيفية إصلاحها

معظم حالات الفشل في برنامج التعلم بالنقل هي حالات ميكانيكية وليست رياضية. هذه هي الحالات التي تمنع تشغيل الكود المذكور أعلاه، وهذا شرح لمعنى كل حالة منها.

  • عدم تطابق الحجم في الطبقة الأخيرة: يجب أن تقبل الطبقة الخطية المُستبدلة الميزات المُدخلة التي أبلغ عنها المصنف الأصلي، وأن تُخرج عددًا من المخرجات يساوي طول (أسماء الفئات). تُعد طباعة النموذج، كما في الخطوة 2، أسرع طريقة للتأكد من كلا الرقمين.
  • لم يتجمد العمود الفقري أبداً: إذا تم ترك قيمة requires_grad على True، فسيتم تحديث جميع معلمات VGG19، مما يؤدي إلى تباطؤ كبير في عملية التشغيل على وحدة المعالجة المركزية. لذا، يُنصح بتغييرها إلى False قبل استبدال المصنف.
  • عدم تطابق التطبيع: يجب أن تكون قيم المتوسط ​​والانحراف المعياري المستخدمة في التحويلات.Normalize هي نفسها في وقت التدريب والاستدلال، وإلا فإن التنبؤات ستنحرف دون سبب واضح.
  • وسيط الأوزان المهمل: في الإصدار 0.13 من torchvision والإصدارات الأحدث، يؤدي pretrained=True إلى ظهور تحذير بشأن الإهمال ويتم تفضيل تعداد الأوزان.
  • عدد العمال قيد التشغيل Windows والدفاتر: يحتاج مُحمِّل البيانات الذي يحتوي على num_workers=4 إلى حماية نقطة الدخول، لذا اضبط num_workers=0 إذا Python يُثير خطأً في عملية التفريخ أو التخليل.
  • التقييم في وضع التدريب: قم باستدعاء model.eval() قبل التسجيل حتى يتصرف Dropout و BatchNorm بشكل حتمي، ثم قم بالتبديل مرة أخرى باستخدام model.train() بعد ذلك.

الأسئلة الشائعة

عادةً ما تكفي بضع مئات من الصور لكل فئة عند تدريب الطبقة الأخيرة فقط؛ يستخدم هذا المثال حوالي 700 صورة إجمالاً. يتطلب ضبط الطبقات الأعمق عددًا أكبر من الصور - غالبًا عدة آلاف - نظرًا لتحديث عدد أكبر بكثير من المعلمات.

تأتي جميع هياكل ResNet وVGG وEfficientNet وVision Transformer مزودة بأوزان torchvision. يُعد ResNet50 هو الإعداد الافتراضي الشائع لأنه يوازن بين الدقة والحجم. أما VGG19، المستخدم هنا، فهو أثقل. الشبكة الالتفافية لكن من السهل تحليلها طبقة تلو الأخرى.

لا. تجميد البنية الأساسية وتدريب طبقة واحدة يعمل بشكل مقبول على وحدة المعالجة المركزية، ولهذا السبب تنتهي العملية المذكورة أعلاه في أقل من ثلاث دقائق. أما ضبط الشبكة بأكملها بدقة، أو التدريب على آلاف الصور، فهو ما يجعل وحدة معالجة الرسومات ضرورية.

يقوم البحث الآلي عن النماذج بتقييم أداء العديد من الشبكات المدربة مسبقًا على عينة من بياناتك، ويصنفها حسب الدقة وزمن الاستجابة والحجم. هذا يزيل التخمين من عملية اختيار البنية الأساسية ويتكامل بشكل جيد مع عمليات البحث عن المعلمات الفائقة في تعلم الحروف.

يُسهّل البرنامج عملية كتابة التعليمات البرمجية الأساسية، لأن عمليات التحويل وإعداد مُحمّل البيانات وحلقات التدريب تتبع أنماطًا مألوفة. تحقق من الأجزاء التي لا يستطيع البرنامج استنتاجها: عدد فئات الإخراج، وإحصائيات التطبيع، وما إذا كان خيار requires_grad مُعطّلاً بالفعل.

يحدث النقل السلبي عندما يكون مجال المصدر مختلفًا جدًا عن مجال الهدف، لذا فإن الأوزان المستعارة تضر بدلًا من أن تنفع. انتبه إلى خسارة التحقق التي تستقر مبكرًا، أو التي تتجاوز قيمة معينة في شبكة صغيرة تم تدريبها من الصفر.

نعم. يتم تدريب نماذج اللغة مسبقًا على مجموعات نصوص كبيرة ثم يتم تكييفها للتصنيف أو الإجابة على الأسئلة، وينطبق نفس المبدأ على نماذج التسلسل، تضمينات صوتية وجدولية. التغييرات تقتصر على البنية الأساسية فقط.

عدد الدورات التدريبية أقل بكثير من الشبكة المدربة من الصفر. تم استخدام 25 دورة تدريبية هنا، ولكن عادةً ما يكفي من 10 إلى 20 دورة لاستخلاص الميزات.traction. توقف عندما يتوقف تحسن خسارة التحقق بدلاً من تشغيل عدد ثابت.

تلخيص هذه التدوينة بـ: