نحوهی درك آسان ResNet در PyTorch
مقدمهای بر ResNet
ResNet چیست؟
ResNet که مخفف شبکهی عصبی برادریان است، یک معماری یادگیری عمیق است که در سال ۲۰۱۵ توسط محققان مایکروسافت معرفی شد. آن به منظور حل مشکل ناپدید شدن/انفجار شدن گرادیان، یک روش راه حل است.
- شبکهی عصبی برادریان: ResNet یک نوع شبکهی عصبی است که از اتصالهای ردگیری یا اتصالات برادری برای فعال کردن آموزش مدلهای عمیقتر استفاده میکند. این اتصالات برادری اجازه میدهند که شبکه اجزای خاصی را رد کند و به طور مؤثری یک «مسیر میانبر» ایجاد کند که
gradient
ناپدید شدنی را بهبود خواهد بخشید.
- حل مشکل کاهش/افزایش گرادیان: در شبکههای عصبی عمیق، گرادیانهای استفاده شده برای عقبانتشار میتوانند با عبور از شبکه همزمان ناپدید شوند(به شدت کوچک شوند) یا انفجار کنند (به شدت بزرگ شوند). این مشکل میتواند باعث سخت شدن یادگیری شبکه شود، به ویژه در لایههای عمیقتر. اتصالات برادری ResNet کمک میکند که این مشکل را حل کند؛ زیرا این اتصالات کمک میکنند که گرادیانها به راحتیتر از طریق شبکه جریان یابند.
مزیتهای ResNet
-
بهبود عملکرد در شبکههای عصبی عمیق: اتصالات برادری ResNet، آموزش شبکههای عصبی عمیقتر را ممکن میکند و میتواند منجر به بهبود قابل توجهی در عملکردهای مختلفی مانند طبقهبندی تصویر، آشکارسازی اشیا و تقسیمبندی معنایی شود.
-
سرعت بیشتر در هنگام همگرایی در آموزش: اتصالات برادری ResNet همچنین میتواند در فرآیند آموزش شبکه باعث همگرایی سریعتر شود؛ زیرا این اتصالات گرادیانها را از طریق شبکه به طور کارآمدتر جریان میدهند.
پیادهسازی ResNet در PyTorch
راهاندازی محیط
-
نصب PyTorch: برای شروع پیادهسازی ResNet در PyTorch، ابتدا باید کتابخانه PyTorch را نصب کنید. شما میتوانید PyTorch را براساس سیستمعاملتان و نسخه Python خود از وبسایت رسمی (https://pytorch.org/ (opens in a new tab)) دانلود و نصب کنید.
-
وارد کردن کتابخانههای لازم: بعد از نصب PyTorch، باید کتابخانههای مورد نیاز برای پروژه خود را وارد کنید. به طور معمول این کتابخانهها شامل PyTorch، NumPy و هر کتابخانهی دیگری است که برای پیشپردازش داده، تصویرسازی و یا وظایف دیگر ممکن است نیاز داشته باشید.
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
import numpy as np
import matplotlib.pyplot as plt
تعریف معماری ResNet
درک بلوکهای ساختاری اصلی
-
لایههای کانولوشنی: ResNet، مانند بسیاری از مدلهای یادگیری عمیق دیگر، لایههای کانولوشنی را به عنوان اصلیترین سازنده برای استخراج ویژگیهای اولیه از تصویر استفاده میکند.
-
نرمالسازی دسته جمعی: ResNet همچنین از لایههای نرمالسازی دسته جمعی برای کمک به استوحاکسازی فرآیند آموزش و بهبود عملکرد مدل استفاده میکند.
-
توابع فعالسازی: معماری ResNet معمولاً از تابع فعالساز ReLU (واحد خطی تعدیل شده) به عنوان تابع فعالسازی استفاده میکند، که به کمک معدالت غیرخطی به مدل کمک میکند.
-
لایههای پولینگ: ResNet ممکن است شامل لایههای پولینگ مانند پولینگ بیشتر یا پولینگ میانگین باشد تا ابعاد مکانی نقشه ویژگی را کاهش داده و ناخواسته تحمل انتقال را وارد کند.
پیادهسازی بلوک ResNet
- اتصال برادری: نوآوری کلیدی در ResNet اتصال برادری است که به شبکه اجازه میدهد تا با اضافه کردن ورودی یک لایه به خروجی آن لایه، از طریق لایههای خاصی رد گردد. این کار به بهبود مشکل ناپدید شدن گرادیان کمک میکند.
class ResNetBlock(nn.Module):
def __init__(self, in_channels, out_channels, stride=1):
super(ResNetBlock, self).__init__()
self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(out_channels)
self.relu = nn.ReLU(inplace=True)
self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(out_channels)
self.shortcut = nn.Sequential()
if stride != 1 or in_channels != out_channels:
self.shortcut = nn.Sequential(
nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride, bias=False),
nn.BatchNorm2d(out_channels)
)
def forward(self, x):
residual = self.shortcut(x)
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out += residual
out = self.relu(out)
return out
- اتصال میانبر: علاوه بر اتصال برادری، ResNet همچنین از «اتصال میانبر» برای تطبیق ابعاد ورودی و خروجی بلوک ResNet استفاده میکند.
ساختن مدل کامل ResNet
-
ترکیب بلوکهای ResNet: برای ایجاد مدل کامل ResNet، باید چند بلوک ResNet را به هم رکابی کنید و تعداد لایهها و تعداد فیلترها را در هر بلوک تنظیم کنید.
-
تنظیم تعداد لایهها: مدلهای ResNet با نسخههای مختلفی، مانند ResNet-18، ResNet-34، ResNet-50، ResNet-101 و ResNet-152 آمده است که اعداد مختلفی از لایهها دارند. تعداد لایهها تأثیری بر پیچیدگی و عملکرد مدل دارد.
پیادهسازی ResNet-18 در PyTorch
تعریف مدل ResNet-18
-
لایهی ورودی: لایهی ورودی مدل ResNet-18 به طور معمول تصویری از اندازهای خاص مانند ۲۲۴×۲۲۴ پیکسل را دریافت میکند.
-
لایههای کانولوشنی: لایههای کانولوشنی اولیه مدل ResNet-18 ویژگیهای پایه را از تصویر ورودی استخراج میکنند.
-
بلوکهای ResNet: مغزهی مدل ResNet-18 ترکیب چند بلوک ResNet میباشد، که از اتصالات برادری برای اجازه آموزش شبکهای عمیقتر استفاده میکند.
-
لایه کاملاً متصل: پس از لایههای کانولوشنی و بلوکهای ResNet، مدل لایه کاملاً متصلی برای انجام وظیفه طبقهبندی یا پیشبینی نهایی خواهد داشت.
-
لایه خروجی: لایه خروجی مدل ResNet-18 دارای تعدادی واحد متناسب با تعداد کلاسها در مسئلهای است که در حال حل است.
class ResNet18(nn.Module):
def __init__(self, num_classes=10):
super(ResNet18, self).__init__()
self.in_channels = 64
self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False)
self.bn1 = nn.BatchNorm2d(64)
self.relu = nn.ReLU(inplace=True)
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
self.layer1 = self._make_layer(64, 64, 2, stride=1)
self.layer2 = self._make_layer(64, 128, 2, stride=2)
self.layer3 = self._make_layer(128, 256, 2, stride=2)
self.layer4 = self._make_layer(256, 512, 2, stride=2)
self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
self.fc = nn.Linear(512, num_classes)
def _make_layer(self, in_channels, out_channels, num_blocks, stride):
layers = []
layers.append(ResNetBlock(in_channels, out_channels, stride))
self.in_channels = out_channels
for i in range(1, num_blocks):
layers.append(ResNetBlock(out_channels, out_channels))
return nn.Sequential(*layers)
def forward(self, x):
x = self.conv1(x)
x = self.bn1(x)
x = self.relu(x)
x = self.maxpool(x)
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
x = self.avgpool(x)
x = x.view(x.size(0), -1)
x = self.fc(x)
return x
مقداردهی اولیه به مدل
برای ایجاد یک نمونه از مدل ResNet-18، میتوانید به سادگی کلاسResNet18
را نمونهبرداری کنید:
model = ResNet18(num_classes=10)
چاپ خلاصه مدل
میتوانید خلاصهای از معماری مدل ResNet-18 را با استفاده از تابع summary()
از کتابخانه torchsummary
چاپ کنید:
from torchsummary import summary
summary(model, input_size=(3, 224, 224))
این کار یک مرور جامع از لایههای مدل را به همراه تعداد پارامترها و اشکال خروجی هر لایه ارائه میدهد.
آموزش مدل ResNet-18
آمادهسازی مجموعه داده
دانلود و بارگیری مجموعه داده
برای این نمونه، از مجموعهداده CIFAR-10 استفاده خواهیم کرد که یکی از بنچمارکهای رایج برای وظایف طبقهبندی تصویر است. در محیطه، میتوانید مجموعه داده را با استفاده از ماژول CIFAR10 از torchvision.datasets
دانلود کنید:
# دانلود و بارگیری مجموعهداده CIFAR-10
train_dataset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transforms.ToTensor())
test_dataset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transforms.ToTensor())
پیشپردازش داده
پیش از آموزش مدل، باید دادهها را پیشپردازش کنید، مثل نرمال سازی مقادیر پیکسل و اعمال تکنیکهای افزایش داده:
# تعریف تبدیل داده
transform_train = transforms.Compose([
transforms.RandomCrop(32, padding=4),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))
])
transform_test = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))
])
# ایجاد بارگیریکننده داده
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=128, shuffle=True, num_workers=2)
test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=100, shuffle=False, num_workers=2)
تعریف حلقه آموزش
تنظیم دستگاه (CPU و GPU)
برای استفاده از شتابدهنده GPU، میتوانید مدل و داده را به GPU منتقل کنید:
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model = model.to(device)
تعریف تابع خطا و بهینهساز
با توجه به وظیفه، یک تابع خطا و یک بهینهساز میتواند انتخاب شود.بعداز آن، شما باید تابع از دست دادن و بهینه ساز را که در هنگام فرآیند آموزش استفاده خواهد شد تعریف کنید:
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.1, momentum=0.9, weight_decay=5e-4)
پیادهسازی حلقه آموزش
حلقه آموزش شامل مراحل زیر خواهد بود:
۱. عبور پیش به رو از مدل ۲. محاسبه از دست دادن ۳. پخش گرفتن گرادیانها به پشتگاه ۴. بهروزرسانی پارامترهای مدل ۵. پیگیری از دست دادن و دقت آموزش
num_epochs = 100
train_losses = []
train_accuracies = []
val_losses = []
val_accuracies = []
for epoch in range(num_epochs):
# فاز آموزش
model.train()
running_loss = 0.0
correct = 0
total = 0
for i, (inputs, labels) in enumerate(train_loader):
inputs, labels = inputs.to(device), labels.to(device)
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
## بهینهسازی مدل
### تنظیم
تنظیم یک تکنیک استفاده شده برای جلوگیری از بیشبرازش در مدلهای یادگیری عمیق است. بیشبرازش وقوع میکند وقتی که یک مدل عملکرد خوبی روی دادههای آموزشی ارائه میدهد، اما در تعمیم به دادههای تازه و دیده نشده شکست میخورد. تکنیکهای تنظیم به مدل کمک میکنند تا بهتر تعمیم دهد با تعریف یک جریمه برای پیچیدگی یا اضافه کردن نویز به فرآیند آموزش.
یکی از تکنیکهای محبوب تنظیم، تنظیم L2 است که همچنین به عنوان کاهش وزن شناخته میشود. این روش یک عبارت جریمه را به تابع از دست دادن اضافه میکند که به قدرت مربع وزنهای مدل متناسب است. تابع از دست دادن با تنظیم L2 میتواند به صورت زیر نوشته شود:
از دست دادن = از دست دادن اصلی + لمبدا * جمع (w ^ 2)
که `لمبدا` قوت تنظیم و `w` وزنهای مدل هستند.
یکی دیگر از تکنیکهای تنظیم محبوب، Dropout است. Dropout در طول آموزش یک بخش از فعالیتها در یک لایه را به صفر میرساند و به این ترتیب ظرفیت مدل را کاهش داده و مجبور میکند به ویژگیهای محکمتری یاد بگیرد. این باعث پیشگیری از بیشبرازش و بهبود عملکرد تعمیم مدل میشود.
در اینجا مثالی از نحوه پیادهسازی Dropout در مدل PyTorch وجود دارد:
```python
import torch.nn as nn
class MyModel(nn.Module):
def __init__(self):
super(MyModel, self).__init__()
self.fc1 = nn.Linear(64, 128)
self.dropout = nn.Dropout(p=0.5)
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
x = self.fc1(x)
x = self.dropout(x)
x = self.fc2(x)
return x
در این مثال، لایه Dropout پس از لایه کاملا متصل اول با نرخ Dropout 0.5 باقی میماند، به این معنی که 50% از فعالیتها در طول آموزش به صفر تنظیم میشوند.
الگوریتم بهینهسازی
انتخاب الگوریتم بهینهسازی میتواند تأثیر قابل توجهی در عملکرد و همگرایی یک مدل یادگیری عمیق داشته باشد. در زیر چندین الگوریتم محبوب بهینهسازی مورد استفاده در یادگیری عمیق آورده شده است:
کاهش گرادیان تصادفی (SGD)
SGD سادهترین الگوریتم بهینهسازی است که در آن گرادیانها روی یک نمونه یا یک دسته کوچک از نمونهها محاسبه میشود و وزنها به پاسخ میشوند. SGD میتواند آهسته به همگرایی برسد اما ساده و موثر است.
import torch.optim as optim
model = MyModel()
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
آدام
آدام (Adam) الگوریتم بهینهسازی پیشرفتهتری است که نرخ یادگیری تطبیقی را برای هر پارامتر محاسبه میکند. این الگوریتم مزایای مومنتوم و RMSProp را ترکیب میکند و برای بسیاری از وظایف یادگیری عمیق گزینه محبوبی است.
optimizer = optim.Adam(model.parameters(), lr=0.001)
آداگراد
آداگراد (Adagrad) الگوریتم بهینهسازی است که نرخ یادگیری را برای هر پارامتر براساس گرادیانهای تاریخی تطبیق میدهد. برای دادههای پراکنده موثر است اما ممکن است نرخ یادگیری خود را در طول زمان کاهش دهد.
optimizer = optim.Adagrad(model.parameters(), lr=0.01)
RMSProp
RMSProp (Root Mean Square Propagation) یک الگوریتم بهینهسازی نرخ یادگیری تطبیقی دیگر است که میانگین متحرکی از گرادیانهای مربع شده را حفظ میکند. برای اهداف غیرثابت، مانند هدفهای موجود در شبکههای عصبی تکراری، کاربرد بسیاری دارد.
optimizer = optim.RMSprop(model.parameters(), lr=0.001, alpha=0.99)
انتخاب الگوریتم بهینهسازی بستگی به مسئله خاص، ساختار مدل و خصوصیات دادهها دارد. معمولاً ایده خوبی است که با الگوریتمهای مختلف آزمایش کنید و کارایی آنها را بر روی وظیفهی خود مقایسه کنید.
یادگیری انتقالی
یادگیری انتقالی تکنیکی است که در آن یک مدل آموزش دیده بر روی یک مجموعه داده بزرگ به عنوان نقطه شروع برای یک مدل بر روی یک وظیفه مختلف ولی مرتبط استفاده میشود. این میتواند به خصوص در صورتی مفید باشد که مجموعه داده هدف کوچک باشد، زیرا این امکان را فراهم میکند تا مدل از ویژگیهایی که روی مجموعه داده بزرگ یاد گرفته شده است استفاده کند.
یک رویکرد معمول یادگیری انتقالی در یادگیری عمیق استفاده از یک مدل از پیش آموزش دیده، مانند مدلهای موجود برای وظیفههای پردازش تصویری یا پردازش زبان طبیعی، و تنظیم مجدد مدل بر روی مجموعه داده هدف است. این شامل یخزدن لایههای پایینتر مدل پیش آموزش دیده و فقط آموزش دادن لایههای بالاتر روی داده جدید است.
در زیر نمونهای از تنظیم مجدد یک مدل پیشآموزشدیده ResNet برای وظیفه طبقهبندی تصویر در PyTorch آورده شده است:
import torchvision.models as models
import torch.nn as nn
# بارگیری مدل پیشآموزشدیده ResNet
resnet = models.resnet18(pretrained=True)
# یخزدایی پارامترهای مدل پیشآموزشدیده
for param in resnet.parameters():
param.requires_grad = False
# جایگزینی آخرین لایه با لایه کاملا متصل جدید
num_features = resnet.fc.in_features
resnet.fc = nn.Linear(num_features, 10) # فرض میکنیم 10 کلاس وجود دارد
# آموزش مدل روی مجموعه داده جدید
optimizer = optim.Adam(resnet.fc.parameters(), lr=0.001)
در این مثال، ابتدا مدل پیشآموزشدیده ResNet18 را بارگیری و پارامترهای لایههای پایینتر را یخبسته میکنیم. سپس لایه کاملاً متصل آخر را با لایه جدیدی که برای وظیفه هدف ما تعداد خروجی مناسب را دارد (در این حالت ۱۰ کلاس) جایگزین میکنیم. در نهایت، مدل را با استفاده از بهینهساز Adam آموزش میدهیم با روشی که تنها پارامترهای لایه کاملاً متصل جدید را بهروز کند.
یادگیری انتقالی میتواند به طور قابلتوجهی عملکرد مدلهای یادگیری عمیق را بهبود بخشد، به ویژه در صورتی که مجموعه داده هدف کوچک باشد. این تکنیک توانایی کمک به زمان و منابع در طول توسعه مدل را دارد.
تفسیرپذیری مدل
همچنین حائز اهمیت شده است که هنگامی که مدلهای یادگیری عمیق پیچیده و فراگیر میشوند، نیاز به مدلهای با قابلیت تفسیری بیشتر داریم. تفسیرپذیری به امکان تفهیم و توضیح فرآیند تصمیمگیری داخلی مدل اشاره دارد.
یکی از تکنیکهای محبوب برای بهبود تفسیرپذیری مدل استفاده از مکانیزمهای توجه است. توجه به مدل امکان میدهد تا در هنگام پیشبینی به بخشهای مهمتر ورودی تمرکز کند و میتوان آن را به تصویری برای درک اینکه مدل از چه ویژگیهایی استفاده میکند، تبدیل کرد.
در زیر مثالی از نحوه پیادهسازی یک مکانیزم توجه در یک مدل PyTorch برای وظیفه پردازش زبان طبیعی آمده است:
import torch.nn as nn
import torch.nn.functional as F
class AttentionModel(nn.Module):
def __init__(self, vocab_size, embedding_dim, hidden_dim):
super(AttentionModel, self).__init__()
self.embedding = nn.Embedding(vocab_size, embedding_dim)
self.lstm = nn.LSTM(embedding_dim, hidden_dim, bidirectional=True, batch_first=True)
self.attention = nn.Linear(hidden_dim * 2, 1)
def forward(self, input_ids):
# ورودی را تعبیه کنید
embedded = self.embedding(input_ids)
# ورودی تعبیه شده را از طریق LSTM منتقل کنید
lstm_output, _ = self.lstm(embedded)
# وزنهای توجه را محاسبه کنید
attention_weights = F.softmax(self.attention(lstm_output), dim=1)
# جمعبندی وزندار خروجیهای LSTM
context = torch.sum(attention_weights * lstm_output, dim=1)
return context
در این مثال، مکانیزم توجه به عنوان یک لایه خطی پیادهسازی شده است که ورودیهای LSTM را به عنوان ورودی میگیرد و مجموعهای از وزنهای توجه را تولید میکند. این وزنها سپس برای محاسبه مجموع وزندار خروجیهای LSTM استفاده میشود که خروجی نهایی مدل است.
با تصویرسازی وزنهای توجه، میتوانید برداشتهایی از اینکه مدل در زمان پیشبینی به کدام بخشهای ورودی تمرکز دارد، کسب کنید. اینکه کدام بخشهای ورودی مدل در هنگام تصمیمگیری استفاده میکند، به شما کمک میکند از فرآیند تصمیمگیری مدل درک بهتری پیدا کنید و نقاط ضعف یا نقاط قابل بهبود را شناسایی کنید.
تکنیک دیگری برای بهبود تفسیرپذیری مدل استفاده از تجزیه شدگی ویژگیها است. این شامل شناسایی مهمترین ویژگیهایی است که مدل برای پیشبینی استفاده میکند. یکی از روشهای محبوب برای این کار، ارزشهای Shapley است که راهی برای اندازهگیری میزان مشارکت هر ویژگی در خروجی مدل ارائه میدهد.
بهبود تفسیرپذیری مدل یک حوزه مهم در تحقیقات یادگیری عمیق است، زیرا میتواند به ساخت اعتماد به مدلهای قدرتمند کمک کند و مطمئن شویم که از این مدلها بهطور مسئولانه استفاده میشود.
نتیجهگیری
در این آموزش، ما بر روی مباحث مرتبط با یادگیری عمیق، از جمله بهینهسازی مدل، یادگیری انتقالی و تفسیرپذیری مدل پرداختیم. ما درباره تکنیکهایی مانند تنظیم، الگوریتمهای بهینهسازی و مکانیزمهای توجه صحبت کردیم و مثالهایی از نحوه پیادهسازی این مفاهیم در PyTorch ارائه دادیم.
با پیشرفت یادگیری عمیق و گسترش آن، مهم است که این مباحث پیشرفته را درک کنیم و بتوانیم آنها را در پروژههای خود به کار ببندیم. با آشنایی با این تکنیکها، شما بهتر خواهید توانست مدلهای یادگیری عمیق قوی، قابل اطمینان و تفسیرپذیری ایجاد کنید که میتواند در حل مسائل گوناگون موثر باشد.
به یاد داشته باشید که یادگیری عمیق یک زمینهای در حال تغییر سریع است و مهم است که با تحقیقات و شیوههای بهتر جدید روز باقی بمانید. ادامه جستجو، تجربهکردن و یادگیری مواجهه و به سمت تبدیل شدن به یک متخصص یادگیری عمیق پیش خواهید رفت.