آموزش شبکه عصبی کانولوشن چند لایه در پایتورچ برای طبقه بندی تصویر

استفاده از شبکه عصبی کانولوشنال برای طبقه بندی ارقام دست نویس در پایتورچ
آگوست 25, 2023
آموزش و یادگیری زبان انگلیسی از صفر- جلسه اول
آگوست 27, 2023

آموزش شبکه عصبی کانولوشن چند لایه در پایتورچ برای طبقه بندی تصویر

در بخش قبلی با فیلترهای کانولوشنال آشنا شدیم که می توانند الگوها را از تصاویر استخراج کنند. برای طبقه‌بندی کننده MNIST ما از 9 فیلتر 5×5 استفاده کردیم که منجر به ایجاد تانسور 9x24x24 شد.

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

!wget https://raw.githubusercontent.com/MicrosoftDocs/pytorchfundamentals/main/computer-vision-pytorch/pytorchcv.py
import torch
import torch.nn as nn
import torchvision
import matplotlib.pyplot as plt
from torchinfo import summary
import numpy as np

from pytorchcv import load_mnist, train, plot_results, plot_convolution, display_dataset
load_mnist(batch_size=128)

لایه های ادغام

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

هنگام انجام این کار، ما همچنین باید یک ترفند را اعمال کنیم: کاهش اندازه مکانی تصویر. هنگامی که ما تشخیص دادیم که یک لبه افقی در یک پنجره کشویی وجود دارد، مهم نیست که دقیقاً در کدام پیکسل رخ داده است. بنابراین ما می توانیم اندازه تصویر را که با استفاده از یکی از لایه های ادغام انجام می شود، “کاهش” دهیم:

Average Pooling یک پنجره کشویی می گیرد (مثلاً 2×2 پیکسل) و میانگین مقادیر درون پنجره را محاسبه می کند.
Max Pooling پنجره را با حداکثر مقدار جایگزین می کند. ایده پشت حداکثر ادغام تشخیص وجود یک الگوی خاص در پنجره کشویی است.


بنابراین، یک CNN معمولی، از چندین لایه کانولوشن تشکیل شده است که لایه‌هایی ادغام در بین انها وجود دارد تا ابعاد تصویر کاهش یابد. ما همچنین تعداد فیلترها را افزایش خواهیم داد، زیرا با پیشرفته‌تر شدن الگوها – ترکیب‌های جالب‌تری وجود دارد که باید به دنبال آنها باشیم.

به دلیل کاهش ابعاد مکانی و افزایش ابعاد ویژگی/فیلتر، این معماری را معماری هرمی نیز می نامند.

در مثال بعدی، از CNN دو لایه استفاده خواهیم کرد:

class MultiLayerCNN(nn.Module):
    def __init__(self):
        super(MultiLayerCNN, self).__init__()
        self.conv1 = nn.Conv2d(1, 10, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(10, 20, 5)
        self.fc = nn.Linear(320,10)

    def forward(self, x):
        x = self.pool(nn.functional.relu(self.conv1(x)))
        x = self.pool(nn.functional.relu(self.conv2(x)))
        x = x.view(-1, 320)
        x = nn.functional.log_softmax(self.fc(x),dim=1)
        return x

net = MultiLayerCNN()
summary(net,input_size=(1,1,28,28))
Layer (type:depth-idx)                   Output Shape              Param #
==========================================================================================
├─Conv2d: 1-1                            [1, 10, 24, 24]           260
├─MaxPool2d: 1-2                         [1, 10, 12, 12]           --
├─Conv2d: 1-3                            [1, 20, 8, 8]             5,020
├─MaxPool2d: 1-4                         [1, 20, 4, 4]             --
├─Linear: 1-5                            [1, 10]                   3,210
==========================================================================================
Total params: 8,490
Trainable params: 8,490
Non-trainable params: 0
Total mult-adds (M): 0.47
==========================================================================================
Input size (MB): 0.00
Forward/backward pass size (MB): 0.06
Params size (MB): 0.03
Estimated Total Size (MB): 0.09
==========================================================================================

در مورد تعریف به چند نکته توجه کنید:

به جای استفاده از لایه Flatten، تانسور را در داخل تابع فوروارد با استفاده از تابع view مسطح می کنیم، که مشابه تابع تغییر شکل در numpy است. از آنجایی که لایه مسطح وزن قابل آموزش ندارد، نیازی نیست که یک نمونه لایه جداگانه در کلاس خود ایجاد کنیم – فقط می توانیم از یک تابع از فضای نام torch.nn.functional استفاده کنیم.
ما فقط از یک نمونه از لایه ادغام در مدل خود استفاده می کنیم، همچنین به این دلیل که حاوی هیچ پارامتر قابل آموزش نیست، و بنابراین یک نمونه می تواند به طور موثر مورد استفاده مجدد قرار گیرد.
تعداد پارامترهای قابل آموزش (~8.5K) به طور چشمگیری کمتر از موارد قبلی است (80K در Perceptron، 50Kدر CNN یک لایه). این به این دلیل اتفاق می افتد که لایه های کانولوشن به طور کلی پارامترهای کمی دارند، مستقل از اندازه تصویر ورودی. همچنین به دلیل ادغام، ابعاد تصویر قبل از اعمال لایه متراکم نهایی به میزان قابل توجهی کاهش می یابد. تعداد کمی از پارامترها تأثیر مثبتی بر مدل‌های ما دارد، زیرا به جلوگیری از برازش بیش از حد حتی در اندازه‌های داده کوچکتر کمک می‌کند.

hist = train(net,train_loader,test_loader,epochs=5)
Epoch  0, Train acc=0.949, Val acc=0.978, Train loss=0.001, Val loss=0.001

آنچه احتمالاً باید مشاهده کنید این است که ما قادریم به دقت بالاتر و بسیار سریعتر دست یابیم – فقط با 1 یا 2 دوره. این بدان معناست که معماری شبکه پیچیده به داده های بسیار کمتری نیاز دارد تا بفهمد چه اتفاقی دارد می افتد و الگوهای عمومی را از تصاویر ما استخراج کند.

بازی با تصاویر واقعی از مجموعه داده CIFAR-10 در حالی که مشکل تشخیص رقم دست‌نویس ما ممکن است یک مسئله خیلی راحت به نظر برسد، اکنون آماده انجام کاری جدی‌تر هستیم. بیایید مجموعه داده های پیشرفته تری از تصاویر اشیاء مختلف به نام CIFAR-10 را بررسی کنیم. این شامل 60 هزار تصویر رنگی 32×32 است که به 10 کلاس تقسیم شده است.

transform = torchvision.transforms.Compose(
    [torchvision.transforms.ToTensor(),
     torchvision.transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=14, shuffle=True)
testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=14, shuffle=False)
classes = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to ./data/cifar-10-python.tar.gz
Extracting ./data/cifar-10-python.tar.gz to ./data
Files already downloaded and verified
display_dataset(trainset,classes=classes)

یک معماری شناخته شده برای CIFAR-10 LeNet نام دارد و توسط Yann LeCun پیشنهاد شده است. از همان اصولی پیروی می کند که در بالا ذکر کردیم. با این حال، از آنجایی که همه تصاویر رنگی هستند، اندازه تانسور ورودی 3×32×32 است، و فیلتر کانولوشنال 5×5 نیز در ابعاد رنگ اعمال می شود – به این معنی که اندازه ماتریس هسته کانولوشن 3×5×5 است.

ما همچنین یک ساده‌سازی دیگر برای این مدل انجام می‌دهیم – از log_softmax به عنوان تابع فعال‌سازی خروجی استفاده نمی‌کنیم و فقط خروجی آخرین لایه کاملاً متصل را برمی‌گردانیم. در این مورد ما فقط می توانیم از تابع ضرر CrossEntropyLoss برای بهینه سازی مدل استفاده کنیم.

class LeNet(nn.Module):
    def __init__(self):
        super(LeNet, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.pool = nn.MaxPool2d(2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.conv3 = nn.Conv2d(16,120,5)
        self.flat = nn.Flatten()
        self.fc1 = nn.Linear(120,64)
        self.fc2 = nn.Linear(64,10)

    def forward(self, x):
        x = self.pool(nn.functional.relu(self.conv1(x)))
        x = self.pool(nn.functional.relu(self.conv2(x)))
        x = nn.functional.relu(self.conv3(x))
        x = self.flat(x)
        x = nn.functional.relu(self.fc1(x))
        x = self.fc2(x)
        return x

net = LeNet()

summary(net,input_size=(1,3,32,32))
==========================================================================================
Layer (type:depth-idx)                   Output Shape              Param #
==========================================================================================
├─Conv2d: 1-1                            [1, 6, 28, 28]            456
├─MaxPool2d: 1-2                         [1, 6, 14, 14]            --
├─Conv2d: 1-3                            [1, 16, 10, 10]           2,416
├─MaxPool2d: 1-4                         [1, 16, 5, 5]             --
├─Conv2d: 1-5                            [1, 120, 1, 1]            48,120
├─Flatten: 1-6                           [1, 120]                  --
├─Linear: 1-7                            [1, 64]                   7,744
├─Linear: 1-8                            [1, 10]                   650
==========================================================================================
Total params: 59,386
Trainable params: 59,386
Non-trainable params: 0
Total mult-adds (M): 0.65
==========================================================================================
Input size (MB): 0.01
Forward/backward pass size (MB): 0.05
Params size (MB): 0.24
Estimated Total Size (MB): 0.30
==========================================================================================

آموزش صحیح این شبکه به زمان قابل توجهی نیاز دارد و ترجیحاً باید روی سیستم های دارای GPU انجام شود.

برای دستیابی به نتایج آموزشی بهتر، ممکن است لازم باشد برخی از پارامترهای آموزشی مانند نرخ یادگیری را آزمایش کنیم. بنابراین، ما به صراحت یک بهینه ساز گرادیان کاهشی تصادفی (SGD) را در اینجا تعریف می کنیم و پارامترهای آموزشی را پاس می کنیم. شما می توانید آن پارامترها را تنظیم کنید و مشاهده کنید که چگونه بر آموزش تاثیر می گذارند.

opt = torch.optim.SGD(net.parameters(),lr=0.001,momentum=0.9)
hist = train(net, trainloader, testloader, epochs=3, optimizer=opt, loss_fn=nn.CrossEntropyLoss())
Epoch  0, Train acc=0.261, Val acc=0.388, Train loss=0.143, Val loss=0.121
Epoch  1, Train acc=0.437, Val acc=0.491, Train loss=0.110, Val loss=0.101
Epoch  2, Train acc=0.508, Val acc=0.522, Train loss=0.097, Val loss=0.094

دقتی که با 3 دوره آموزش توانسته ایم به آن برسیم زیاد به نظر نمی رسد. با این حال، به یاد داشته باشید که حدس زدن کورکورانه فقط 10٪ دقت را به ما می دهد و مشکل ما در واقع به طور قابل توجهی دشوارتر از طبقه بندی رقم MNIST است. دستیابی به دقت بالای 50 درصد در چنین زمان آموزشی کوتاه، موفقیت خوبی به نظر می رسد.

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

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *