مرا به خاطر بسپار

بررسی عمیق Pytorch: آموزش

بازدید: 285 آخرین به‌روزرسانی: 27 فروردين 1404

مقدمه

در بخش اول، با ویژگی‌های کلیدی PyTorch از جمله گراف‌های پویا، پشتیبانی از پردازنده گرافیکی و اکوسیستم غنی آن آشنا شدید.

پیشنهاد می‌شود ابتدا مقاله بخش اول را بخوانید.

حالا وقت آن رسیده که وارد عمل شویم و با مفاهیم پایه‌ای PyTorch آشنا شویم. در این بخش، نحوه نصب PyTorch، کار با تنسورها، ساخت یک شبکه عصبی ساده و آموزش آن روی یک مجموعه داده نمونه را قدم به قدم بررسی می‌کنیم. هدف این است که شما با اعتماد به نفس وارد دنیای یادگیری عمیق شوید.

نصب و راه‌اندازی PyTorch

برای شروع، باید PyTorch را نصب کنید. PyTorch با پایتون کار می‌کند و نصب آن بسیار ساده است. می‌توانید از pip یا conda استفاده کنید. در اینجا دستور نصب با pip برای نسخه سازگار با CUDA (برای استفاده از GPU) ارائه شده است. این دستور، نسخه PyTorch با پشتیبانی CUDA 12.1 را نصب می‌کند.
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121

برای اطمینان از نصب صحیح، کد زیر را اجرا کنید:

import torch
print("torch version:", torch.__version__)
print("cuda availability:", torch.cuda.is_available())
torch.cuda.device_count()
curdev = torch.cuda.current_device()
torch.cuda.device(curdev)
print("GPU type:", torch.cuda.get_device_name(curdev))
خروجی نمونه: نسخه PyTorch و وضعیت دسترسی به CUDA (برای GPU) را نمایش می‌دهد.
اگر False مشاهده کردید، یعنی GPU در دسترس نیست یا نیاز به تنظیمات اضافی دارید.

torch version: 2.4.1
cuda availability: True
GPU type:

اگر GPU ندارید، می‌توانید نسخه CPU را با حذف بخش CUDA نصب کنید:

pip install torch torchvision torchaudio

تسلط بر تنسورها: قلب PyTorch

تنسورها در پایتورچ بسیار مهم هستند چرا که ساختار اصلی داده در PyTorch هستند و درک آن‌ها برای پیاده‌سازی مدل‌ها ضروری است.
تنسورها در PyTorch ساختارهای داده‌ای چندبعدی هستند که مشابه آرایه‌های NumPy عمل می‌کنند، اما با قابلیت‌های پیشرفته‌تری مانند محاسبات خودکار گرادیان (AutoGrad) و اجرا روی GPU. در PyTorch از تنسورها برای نمایش تمام داده‌ها استفاده می‌شود - از مقادیر ورودی و وزن‌های مدل تا خروجی‌های محاسباتی. برای ایجاد یک تنسور ساده می‌توانید از تابع torch.tensor استفاده کنید. تنسورها از عملیات ریاضی پشتیبانی می‌کنند (مانند ضرب ماتریسی با torch.matmul یا جمع با +( و با تنظیم requires_grad=True می‌توانند گرادیان‌ها را برای آموزش مدل‌های عصبی محاسبه کنند. همچنین با متد toCuda می‌توان آن‌ها را به GPU منتقل کرد تا محاسبات با سرعت بسیار بالاتری انجام شوند. تنسورها قلب PyTorch هستند و تمام عملیات یادگیری عمیق از پیش‌پردازش داده تا آموزش مدل بر پایه آن‌ها ساخته شده‌اند.
انواع تنسورهای پرکاربرد:

FloatTensor      محاسبات گرادیان         torch.tensor([1.0, 2.0], requires_grad=True)
LongTensor      اندیس‌ها و برچسب‌ها     torch.tensor([1, 2], dtype=torch.long)
BoolTensor      ماسک‌های منطقی        torch.tensor([True, False])

نکته حیاتی: عملیات روی تنسورها باید هم‌شکل باشند، مگر از broadcasting استفاده کنید.
بیایید در مثال زیر یک محاسبه ساده یادگیری ماشین را شبیه‌سازی کنیم:
import torch

# ایجاد تنسور اولیه
weights = torch.tensor([[0.5, -0.2],
                        [1.3, 0.8]], requires_grad=True)

# داده ورودی
inputs = torch.tensor([[1.0, 2.0],
                      [3.0, 4.0]])

# محاسبه خروجی
outputs = torch.matmul(inputs, weights)  # ضرب ماتریسی
loss = outputs.sum()  # تابع خطا

# محاسبه گرادیان‌ها
loss.backward()

print("گرادیان‌های وزن‌ها:")
print(weights.grad)
weights یک تنسور x2 است که نشان‌دهنده وزن‌های یک لایه عصبی ساده است. پارامتر requires_grad=True به PyTorch می‌گوید که گرادیان‌ها را برای این تنسور محاسبه کند.
inputs داده‌های ورودی را نشان می‌دهد که در یک تنسور دیگر ذخیره شده‌اند.
torch.matmul عمل ضرب ماتریسی بین ورودی‌ها و وزن‌ها انجام می‌دهد که معادل محاسبه خروجی یک لایه عصبی ساده است.
loss یک مقدار اسکالر است که با جمع تمام عناصر خروجی به دست آمده (در مدل‌های واقعی معمولاً از توابع پیچیده‌تر مانند MSE استفاده می‌شود).
و در نهایت تابع loss.backward() به صورت خودکار گرادیان‌ها را محاسبه می‌کند و نتایج در weights.grad ذخیره می‌شود.
این مکانیزم پایه‌ای است که PyTorch از آن برای آموزش مدل‌های یادگیری عمیق استفاده می‌کند، جایی که گرادیان‌ها برای به‌روزرسانی وزن‌ها در جهت کاهش خطا استفاده می‌شوند.

طراحی شبکه‌های عصبی

در PyTorch، هر شبکه عصبی یک کلاس ارث‌برده از nn.Module است. این الگو به شما امکان می‌دهد که
  • لایه‌ها را به صورت ماژولار تعریف کنید،
  • جریان داده را در متد forward کنترل نمایید
  • و از مکانیزم‌های خودکار PyTorch استفاده کامل ببرید.
این کد یک شبکه عصبی کانولوشنی ساده با PyTorch پیاده‌سازی می‌کند که شامل یک لایه کانولوشن (با 6 فیلتر 5x5 برای پردازش تصاویر RGB)، یک لایه MaxPooling برای کاهش ابعاد، و یک لایه تمام‌متصل با 120 نورون است. پس از هر لایه، تابع فعال‌سازی ReLU اعمال می‌شود. شبکه ابتدا ویژگی‌های تصویر را استخراج کرده، سپس ابعاد آن را کاهش داده و در نهایت با تبدیل به بردار، خروجی 120 بعدی تولید می‌کند که برای کارهای طبقه‌بندی اولیه مناسب است
import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)  # 3 کانال ورودی (RGB)، 6 کانال خروجی، فیلتر 5x5
        self.pool = nn.MaxPool2d(2, 2)   # MaxPooling با پنجره 2x2 و گام (stride) 2
        self.fc1 = nn.Linear(6 * 30 * 30, 120)  # لایه خطی (تغییر ابعاد متناسب با خروجی conv1)
        
    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))  # کانولوشن → ReLU → MaxPooling
        x = x.view(-1, 6 * 30 * 30)  # تغییر شکل به بردار برای لایه خطی
        x = F.relu(self.fc1(x))       # لایه خطی → ReLU
        return x
اجزای کلیدی شبکه‌های عصبی:
  • لایه‌های کانولوشنی
این لایه‌ها با اعمال فیلترهای یادگرفتنی بر روی داده‌های ورودی (معمولاً تصاویر)، ویژگی‌های محلی مانند لبه‌ها، بافت‌ها و اشکال را استخراج می‌کنند. پارامترهای کلیدی شامل in_channels (تعداد کانال‌های ورودی)، out_channels (تعداد فیلترها)، kernel_size (اندازه فیلتر) و stride (گام حرکت فیلتر) می‌شوند. برای مثال، در پردازش تصاویر RGB، in_channels=3 تنظیم می‌شود. این لایه‌ها پایه اصلی شبکه‌های عصبی کانولوشنی (CNN) برای کاری مانند تشخیص تصویر هستند.

nn.Conv2d(
    in_channels=3,   # مثلاً 3 کانال برای تصاویر RGB
    out_channels=16, # 16 فیلتر برای استخراج ۱۶ ویژگی مختلف
    kernel_size=3,   # فیلتر 3x3
    stride=1,        # گام 1 پیکسل
    padding=1        # حفظ ابعاد فضایی
)
  • لایه‌های بازگشتی (RNN/LSTM):
این لایه‌ها برای پردازش داده‌های متوالی (مانند متن، سری‌های زمانی) طراحی شده‌اند. LSTM با مکانیزم "حافظه بلندمدت"، مشکل فراموشی در RNNهای معمولی را حل می‌کند. پارامترهای کلیدی شامل input_size (بعد هر گام زمانی)، hidden_size (بعد حالت پنهان) و num_layers (تعداد لایه‌های stacked) می‌شوند. برای مثال در پردازش زبان طبیعی، input_size معمولاً بعد embedding کلمات است.

  • اتصال‌های باقیمانده (Residual Connections):
این روش با ایجاد مسیرهای میانبر، مشکل vanishing gradient در شبکه‌های عمیق را حل می‌کند. در بلوک‌های باقیمانده، خروجی هر لایه به ورودی لایه بعدی اضافه می‌شود (x + residual). این روش پایه معماری‌های پیشرفته مانند ResNet است و به شبکه اجازه می‌دهد تا عمیق‌تر شود بدون اینکه دچار مشکل آموزش گردد.

  • مکانیزم توجه (Attention):
این مکانیزم به مدل یاد می‌دهد به بخش‌های مهم ورودی "توجه" کند. در پردازش زبان طبیعی، به مدل کمک می‌کند هنگام ترجمه هر کلمه، روی کلمات مرتبط متن مبدأ تمرکز کند. سه جزء اصلی دارد: query (آنچه جستجو می‌شود)، key (ویژگی‌های ورودی) و value (مقادیر مرتبط).

  • لایه‌های نرمال‌سازی (BatchNorm/LayerNorm):
BatchNorm با نرمال‌سازی فعال‌سازی‌ها در هر batch، آموزش شبکه‌های عمیق را پایدارتر می‌کند. LayerNorm نسخه‌ای مستقل از batch است که برای RNNها و ترنسفورمرها مناسب‌تر است. هر دو به کاهش مشکل covariance shift کمک می‌کنند.

  • Droput
این لایه با تصادفی حذف کردن نورون‌ها در طول آموزش (معمولاً با احتمال 0.2-0.5)، از overfitting جلوگیری می‌کند. در تست، تمام نورون‌ها فعال می‌شوند اما خروجی به نسبت احتمال dropout مقیاس می‌شود.

  • لایه‌های تمام‌متصل (Linear):
این لایه‌ها که به عنوان Dense نیز شناخته می‌شوند، هر نورون ورودی را به تمام نورون‌های خروجی متصل می‌کنند. معمولاً در انتهای شبکه برای طبقه‌بندی استفاده می‌شوند. پارامتر in_features باید برابر بعد ورودی باشد.

  • لایه‌های Pooling:
MaxPooling و AvgPooling برای کاهش ابعاد فضایی و حفظ مهم‌ترین ویژگی‌ها استفاده می‌شوند. MaxPooling مقادیر ماکزیمم در هر پنجره را انتخاب می‌کند، در حالی که AvgPooling میانگین می‌گیرد.

در کد زیر تمام کدهای بخش‌های توضیح داده شده را مشاهده می‌کنید.
 
nn.LSTM(
    input_size=100,   # بعد کلمات
    hidden_size=50,   # بعد فضای پنهان
    num_layers=2,     # 2 لایه LSTM روی هم
    batch_first=True  # فرمت (batch, sequence, features)
)

class ResidualBlock(nn.Module):
    def forward(self, x):
        residual = x
        x = F.relu(self.conv1(x))
        x = self.conv2(x)
        x += residual  # اتصال باقیمانده
        return F.relu(x)

# مکانیزم توجه
scores = torch.matmul(Q, K.transpose(1, 2))  # محاسبه اهمیت
attention = F.softmax(scores, dim=-1)  # نرمال‌سازی به احتمال

#لایه‌های نرمال‌سازی
nn.BatchNorm2d(64)  # برای لایه‌های کانولوشنی
nn.LayerNorm(128)   # برای ترنسفورمرها

#Dropout
nn.Dropout(0.5)  # 50% نورون‌ها در آموزش غیرفعال می‌شوند

#linear
nn.Linear(
    in_features=256,  # بعد ورودی
    out_features=10   # تعداد کلاس‌ها
)

#pooling
nn.MaxPool2d(2, 2)  # پنجره 2x2 با گام 2
nn.AvgPool2d(2)     # میانگین‌گیری روی 2x2

آموزش شبکه‌های عصبی در PyTorch

حالا که با طراحی یک شبکه عصبی آشنا شدید، زمان آن رسیده که مدل خود را آموزش دهید. آموزش شبکه‌های عصبی در PyTorch شامل چند مرحله کلیدی است: آماده‌سازی داده‌ها، تعریف مدل، انتخاب تابع خطا، بهینه‌ساز، و حلقه آموزش. در این بخش، این مراحل را با یک مثال عملی بررسی می‌کنیم.
  1. آماده‌سازی داده‌ها
PyTorch ابزارهای قدرتمندی برای مدیریت داده‌ها ارائه می‌دهد، از جمله ماژول torch.utils.data. برای کار با مجموعه داده‌ها، معمولاً از دو کلاس اصلی استفاده می‌شود:
Dataset: برای تعریف مجموعه داده و دسترسی به نمونه‌ها.
DataLoader: برای بارگذاری داده‌ها به‌صورت دسته‌ای (batch) و به‌صورت بهینه.
فرض کنید می‌خواهیم از مجموعه داده معروف MNIST (تصاویر دست‌نوشته اعداد) استفاده کنیم. PyTorch این مجموعه داده را در torchvision.datasets ارائه می‌دهد. کد زیر نحوه بارگذاری و آماده‌سازی داده‌ها را نشان می‌دهد:
import torch
from torch.utils.data import DataLoader
from torchvision import datasets, transforms

# تعریف پیش‌پردازش‌ها
transform = transforms.Compose([
    transforms.ToTensor(),  # تبدیل تصویر به تنسور
    # نرمال‌سازی با میانگین و انحراف معیار MNIST
    transforms.Normalize((0.1307,), (0.3081,))
])

# بارگذاری داده‌های آموزشی و آزمایشی
train_dataset = datasets.MNIST(
    root='./data', train=True, download=True, transform=transform)
test_dataset = datasets.MNIST(
    root='./data', train=False, download=True, transform=transform)

# تعریف DataLoader
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False)
: تبدیل‌هایی مثل نرمال‌سازی یا تبدیل به تنسور را اعمال می‌کند.

DataLoader: داده‌ها را به‌صورت دسته‌ای بارگذاری می‌کند و قابلیت‌هایی مثل هم‌زدن داده‌ها (shuffle) را فراهم می‌کند.

  1. تعریف مدل
بیایید یک شبکه عصبی ساده برای طبقه‌بندی تصاویر MNIST طراحی کنیم. این شبکه شامل چند لایه کانولوشنی و تمام‌متصل خواهد بود:
import torch.nn as nn
import torch.nn.functional as F

class MNISTNet(nn.Module):
    def __init__(self):
        super(MNISTNet, self).__init__()
        self.conv1 = nn.Conv2d(1, 16, 3)  # 1 کانال ورودی (تصاویر خاکستری)
        self.conv2 = nn.Conv2d(16, 32, 3)
        self.pool = nn.MaxPool2d(2, 2)
        self.fc1 = nn.Linear(32 * 5 * 5, 128)
        self.fc2 = nn.Linear(128, 10)  # 10 کلاس برای اعداد 0 تا 9

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 32 * 5 * 5)  # تغییر شکل برای لایه خطی
        x = F.relu(self.fc1(x))
        x = self.fc2(x)  # خروجی خام (logits)
        return x
3. تعریف تابع خطا و بهینه‌ساز
برای طبقه‌بندی چندکلاسی (مانند MNIST)، تابع خطای رایج CrossEntropyLoss است که ترکیب LogSoftmax و Negative Log-Likelihood را انجام می‌دهد. برای بهینه‌سازی، از SGD (گرادیان نزولی تصادفی) یا Adam استفاده می‌کنیم:

سپس مرحله بعدی 
4. حلقه آموزش
حلقه آموزش شامل ارسال داده‌ها به مدل، محاسبه خطا، به‌روزرسانی وزن‌ها و ارزیابی مدل است. کد زیر یک حلقه آموزشی ساده را نشان می‌دهد:

مراحل 3 و 4 در کد زیر نشان داده شده است:
#3
import torch.optim as optim

model = MNISTNet()
criterion = nn.CrossEntropyLoss()  # تابع خطا
optimizer = optim.Adam(model.parameters(), lr=0.001)  # بهینه‌ساز

#4
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)  # انتقال مدل به GPU (در صورت موجود بودن)

def train(model, train_loader, criterion, optimizer, epoch):
    model.train()  # حالت آموزشی
    running_loss = 0.0
    for i, (images, labels) in enumerate(train_loader):
        images, labels = images.to(device), labels.to(device)

        # صفر کردن گرادیان‌ها
        optimizer.zero_grad()

        # پیش‌رو (Forward)
        outputs = model(images)
        loss = criterion(outputs, labels)

        # پس‌رو (Backward) و به‌روزرسانی
        loss.backward()
        optimizer.step()

        running_loss += loss.item()
        if i % 200 == 199:  # چاپ هر 200 دسته
            print(
                f'[Epoch {epoch}, Batch {i+1}] Loss: {running_loss / 200:.3f}')
            running_loss = 0.0

# اجرای آموزش برای 5 دوره
for epoch in range(1, 6):
    train(model, train_loader, criterion, optimizer, epoch)

  1. ارزیابی مدل
پس از آموزش، باید عملکرد مدل را روی داده‌های آزمایشی بررسی کنیم:
def test(model, test_loader, criterion):
    model.eval()  # حالت ارزیابی
    test_loss = 0
    correct = 0
    with torch.no_grad():  # غیرفعال کردن محاسبه گرادیان
        for images, labels in test_loader:
            images, labels = images.to(device), labels.to(device)
            outputs = model(images)
            test_loss += criterion(outputs, labels).item()
            # کلاس با بالاترین احتمال
            pred = outputs.argmax(dim=1, keepdim=True)
            correct += pred.eq(labels.view_as(pred)).sum().item()

    test_loss /= len(test_loader.dataset)
    accuracy = 100. * correct / len(test_loader.dataset)
    print(
        f'Test set: Average loss: {test_loss:.4f}, Accuracy: {correct}/{len(test_loader.dataset)} ({accuracy:.2f}%)')

# اجرای ارزیابی
test(model, test_loader, criterion)

خروجی نمونه ممکن است چیزی شبیه این باشد که نشان‌دهنده دقت بالای مدل روی مجموعه داده MNIST است:

Test set: Average loss: 0.0009, Accuracy: 9876/10000 (98.76%)

جمع‌بندی

در این مقاله، شما را قدم به قدم با دنیای PyTorch آشنا کردیم. از نصب و کار با تنسورها شروع کردیم، سپس به طراحی و آموزش شبکه‌های عصبی پرداختیم. با ما همراه باشید تا در بخش سوم به مفاهیم پیشرفته‌تری مانند انتقال یادگیری و مدیریت حافظه خواهیم پرداخت.

سوالات متداول

  1. تنسور در پایتورچ چیست؟
تنسور ساختار داده اصلی پایتورچ (مشابه آرایه در NumPy) است که میتواند روی CPU/GPU اجرا شود.
  1. Autograd در PyTorch چیست؟
ماژول autograd مکانیسم تمایز خودکار (Auto Differentiation) را فراهم میکند. با ثبت عملیات روی تنسورها، گرادیان‌ها را به طور خودکار محاسبه میکند. این ویژگی برای آموزش مدل‌های عصبی با Backpropagation ضروری است.
  1. علت استفاده از Module در پایتورچ چیست؟
تمام لایه‌ها و پارامترها را مدیریت میکند. به علاوه، سهولت در انتقال مدل را ممکن می‌سازد.
تا چه حد این مطلب برای شما مفید بود؟
بر اساس رای 0 نفر

اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.

ثبت نظر

نظر دادن