AI & GPU
Hogyan értsük meg könnyen a ResNet-et PyTorchban

Hogyan értsük meg könnyen a ResNet-et PyTorchban

Bevezetés a ResNet-be

Mi az a ResNet?

A ResNet, röviden a Residual Neural Network, egy mély tanulási architektúra, amelyet a Microsoft kutatói vezettek be 2015-ben. Célja a "halványuló/robbanó gradiens probléma" kezelése, egy gyakori probléma, amellyel szembesülünk, amikor nagyon mély neurális hálózatokat tanítunk.

  1. Residual Neural Network: A ResNet egy olyan típusú neurális hálózat, amely "ugró kapcsolatokat" vagy "maradék kapcsolatokat" használ a sokkal mélyebb modellek képzésének lehetővé tétele érdekében. Ezek az ugró kapcsolatok lehetővé teszik a hálózat számára, hogy kihagyjon bizonyos rétegeket, hatékonyan létrehozva egy "gyorsabb útvonalat", amely hozzájárul a halványuló gradiens probléma kezeléséhez.

  2. A halványuló/robbanó gradiens probléma kezelése: Nagyon mély neurális hálózatokban a visszaterjesztéshez használt gradiensek vagy eltűnhetnek (rendkívül kicsivé válnak) vagy robbanhatnak (rendkívül naggyá válnak), amint visszaterjesztésre kerülnek a hálózaton keresztül. Ez megnehezítheti a hatékony tanulást a hálózat számára, különösen a mélyebb rétegekben. A ResNet ugró kapcsolatai megkönnyítik ennek a problémának a kezelését, mert lehetővé teszik a gradiensek könnyebb áramlását a hálózaton keresztül.

A ResNet előnyei

  1. Jobb teljesítmény mély neurális hálózatokon: A ResNet ugró kapcsolatai lehetővé teszik a sokkal mélyebb neurális hálózatok képzését, amelyek jelentősen jobb teljesítményt eredményezhetnek különböző feladatokban, például kép osztályozásban, objektumfelismerésben és szemantikus szegmentálásban.

  2. Gyorsabb konvergencia a képzési folyamat során: A ResNet ugró kapcsolatai segíthetik a hálózatot gyorsabban konvergálni a képzési folyamat során, mivel lehetővé teszik a gradiensek hatékonyabb áramlását a hálózaton keresztül.

ResNet implementálása PyTorchban

Környezet beállítása

  1. PyTorch telepítése: A ResNet implementálásához először telepítenie kell a PyTorch könyvtárat. Letöltheti és telepítheti a PyTorch-ot a hivatalos weboldalról (https://pytorch.org/ (opens in a new tab)) az operációs rendszere és a Python verziója alapján.

  2. Szükséges könyvtárak importálása: Miután telepítette a PyTorch-ot, importálnia kell a projekthez szükséges könyvtárakat. Ez általában magában foglalja a PyTorch-ot, a NumPy-t és bármely más könyvtárat, amelyre szüksége lehet az adatok előfeldolgozásához, megjelenítéséhez vagy más feladatok végrehajtásához.

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

A ResNet architektúra definiálása

A hétköznapi építőelemek megértése

  1. Konvolúciós rétegek: A ResNet, mint sok más mély tanulási modell, a konvolúciós rétegeket használja az alapvető jellemzők kinyerésére a bemeneti adatokból.

  2. Batch Normalization: A ResNet továbbá Batch Normalization rétegeket használ a képzési folyamat stabilizálására és a modell teljesítményének javítására.

  3. Aktivációs függvények: A ResNet architektúra általában a ReLU (Rectified Linear Unit) aktivációs függvényt használja, amely a nemlinearitást bevezeti a modellbe.

  4. Pooling rétegek: A ResNet tartalmazhat továbbá pooling rétegeket, például max-poolingot vagy átlagoló-poolingot, a tulajdonság térképek területi dimenzióinak csökkentése és a transzlációs invariabilitás bevezetése érdekében.

A ResNet blokk implementálása

  1. Maradék kapcsolat: A ResNet kulcsinnovációja a maradék kapcsolat, amely lehetővé teszi a hálózat számára, hogy elkerülje bizonyos rétegeket a bemenet hozzáadásával a bemenetéhez. Ez segít a halványuló gradiens probléma kezelésében.
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
  1. Rövidített kapcsolat: A maradék kapcsolaton kívül a ResNet továbbá egy "rövidített kapcsolatot" is használ, hogy illeszkedjen a bemenet és a ResNet blokk kimeneti dimenzióihoz, ha szükséges.

Teljes ResNet modell létrehozása

  1. ResNet blokkok folyamatosan építése: A teljes ResNet modell létrehozásához több ResNet blokkot kell egymás után helyezni, a blokkokban található rétegek számának és a blokkokban lévő szűrők számának beállításával.

  2. Rétegek számának beállítása: A ResNet modellek különböző változatokban érhetők el, például a ResNet-18, ResNet-34, ResNet-50, ResNet-101 és ResNet-152, amelyek különböző rétegszámokkal rendelkeznek. A rétegek száma befolyásolja a modell összetettségét és a teljesítményét.

ResNet-18 implementálása PyTorchban

A ResNet-18 modell definíciója

  1. Bemeneti réteg: A ResNet-18 modell bemeneti rétege általában elfogad egy adott méretű képet, például 224x224 képpontot.

  2. Konvolúciós rétegek: A ResNet-18 modell kezdeti konvolúciós rétegei alapvető jellemzőket vonnak ki a bemeneti képből.

  3. ResNet blokkok: A ResNet-18 modell magját a több ResNet blokk egymás utáni elhelyezése képezi, amelyek kihasználják a maradék kapcsolatokat, hogy lehetővé tegyék egy mélyebb hálózat képzését.

  4. Teljesen összekapcsolt réteg: A konvolúciós és ResNet blokkok után a modellnek lesz egy teljesen összekapcsolt rétege a végső osztályozási vagy előrejelző feladat végrehajtásához.

  5. Output réteg: A ResNet-18 modell kimeneti rétegének számos egysége lesz, amelyek megfelelnek a megoldandó probléma osztályainak számának.

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

A modell inicializálása

Egy ResNet-18 modell példányt hozhat létre egyszerűen az ResNet18 osztály példányosításával:

model = ResNet18(num_classes=10)

A modell összefoglalásának kiírása

A ResNet-18 modell architektúrájának részletes összefoglalóját kiírhatja a torchsummary könyvtár summary() függvényével:

from torchsummary import summary
summary(model, input_size=(3, 224, 224))

Ez részletes áttekintést ad a modell rétegeiről, beleértve a paraméterek számát és az egyes rétegek kimeneti alakzatát.

A ResNet-18 modell képzése

Az adatkészlet előkészítése

Az adatkészlet letöltése és betöltése

Ebben az példában a CIFAR-10 adatkészletet fogjuk használni, amely egy széles körben használt benchmark a kép osztályozási feladatokhoz. A torchvision.datasets.CIFAR10 modult használhatja az adatkészlet letöltésére:

# CIFAR-10 adatkészlet letöltése és betöltése
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())

Az adatok előfeldolgozása

A modell tanítása előtt elő kell dolgoznia az adatokat, például normalizálni a képek pixelértékeit és alkalmazni az adatnövelési technikákat:

# Az adattranszformációk meghatározása
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))
])
 
# Adatbetöltők létrehozása
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)

A tanítási ciklus meghatározása

Eszköz beállítása (CPU vagy GPU)

GPU gyorsítás kihasználása érdekében áthelyezheti a modellt és az adatokat a GPU-ra:

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

A veszteségfüggvény és az optimalizátor meghatározásaEz következőben, definiálnod kell a veszteségfüggvényt és az optimalizálót, amelyeket a képzési folyamat során fogsz használni:

criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.1, momentum=0.9, weight_decay=5e-4)

A képzési ciklus implementálása

A képzési ciklus a következő lépésekből fog állni:

  1. Előremenet a modellen
  2. A veszteség kiszámítása
  3. A gradiensek visszaterjesztése
  4. A modell paramétereinek frissítése
  5. A képzési veszteség és pontosság követése
num_epochs = 100
train_losses = []
train_accuracies = []
val_losses = []
val_accuracies = []
 
for epoch in range(num_epochs):
    # Képzési fázis
    model.train()
    running_loss = 0.0
    helyesek = 0
    összesen = 0
    for i, (bemenetek, címkék) in enumerate(train_loader):
        bemenetek, címkék = bemenetek.to(device), címkék.to(device)
        
        optimizer.zero_grad()
        kimenetek = model(bemenetek)
        veszteség = criterion(kimenetek, címkék)
 
## Modell optimalizációja
 
### Regularizáció
 
A regularizáció egy olyan technika, amely segít megakadályozni az elrugaszkodást a mélytanuló modelleken. Az elrugaszkodás akkor fordul elő, amikor egy modell jól teljesít a képzési adatokon, de nem tud általánosítani az új, láthatatlan adatokra. A regularizációs technikák segítenek a modellnek jobban általánosítani azáltal, hogy büntetést vezetnek be a komplexitásért vagy zajt adnak a képzési folyamathoz.
 
Egy népszerű regularizációs technika az L2 regularizáció, amit súlycsökkentésnek is nevezünk. Ez a módszer büntető tagot ad a veszteségfüggvényhez, ami arányos a modell súlyainak négyzetes nagyságával. A veszteségfüggvény L2 regularizációval a következőképpen írható fel:
 

veszteség = eredeti_veszteség + lambda * összeg(w^2)


ahol `lambda` a regularizációs erősség, `w` pedig a modell súlyai.

Egy másik népszerű regularizációs technika a Dropout. A Dropout véletlenszerűen beállít egy részét a rétegek aktivációinak zérusra a képzés során, ezáltal csökkentve a modell kapacitását és kényszerítve azt, hogy robosztusabb jellemzőket tanuljon meg. Ez segít az elrugaszkodás megelőzésében és javíthatja a modell általánosítási képességét.

Itt van egy példa arra, hogyan lehet implementálni a Dropoutot egy PyTorch modellben:

```python
import torch.nn as nn

class SajatModell(nn.Module):
    def __init__(self):
        super(SajatModell, 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

Ebben a példában a Dropout réteget az első teljesen kapcsolt réteg után alkalmazzuk, 0,5-es eldobási aránnyal, ami azt jelenti, hogy az aktivációk 50%-át véletlenszerűen 0-ra állítjuk be a képzés során.

Optimalizációs algoritmusok

Az optimalizációs algoritmus választása jelentősen befolyásolhatja a mélytanuló modell teljesítményét és konvergenciáját. Itt vannak népszerű mélytanulóban használt optimalizációs algoritmusok:

Sztochasztikus gradiens módszer (Stochastic Gradient Descent, SGD)

Az SGD a legáltalánosabb optimalizációs algoritmus, ahol a gradiensek egyetlen képzési példa vagy egy kis adatbatch alapján kerülnek kiszámításra, és a súlyok ennek megfelelően frissülnek. Az SGD lassan konvergálhat, de egyszerű és hatékony.

import torch.optim as optim
 
model = SajatModell()
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)

Adam

Az Adam (Adaptive Moment Estimation) egy fejlettebb optimalizációs algoritmus, amely minden paraméterhez adaptív tanulási rátákat számít ki. Összekapcsolja az impulzus előnyeit és az RMSProp előnyeit, így népszerű választás sok mélytanulós feladathoz.

optimizer = optim.Adam(model.parameters(), lr=0.001)

AdaGrad

Az AdaGrad (Adaptive Gradient) egy olyan optimalizációs algoritmus, amely a történeti gradiensek alapján alkalmazkodik a tanulási ráta minden paraméteréhez. Hatékony a ritka adatok esetén, de előfordulhat, hogy idővel agresszíven csökken a tanulási ráta.

optimizer = optim.Adagrad(model.parameters(), lr=0.01)

RMSProp

Az RMSProp (Root Mean Square Propagation) egy másik adaptív tanulási ráta optimalizációs algoritmus, amely karban tart egy mozgó átlagot a gradiensek négyzetekének. Különösen hasznos nem-stacionárius célfüggvények esetén, például az ismétlődő neurális hálózatokban találhatók esetén.

optimizer = optim.RMSprop(model.parameters(), lr=0.001, alpha=0.99)

Az optimalizációs algoritmus választása a konkrét problémától, a modell struktúrájától és az adatok jellemzőitől függ. Általában jó ötlet különböző algoritmusokkal kísérletezni és összehasonlítani a teljesítményüket adott feladaton.

Tartás tanulás

A tartás tanulás egy technika, ahol egy nagy adathalmazon képzett modellt használunk kiindulópontként egy más, de kapcsolódó feladathoz. Ez különösen hasznos, ha a céladathalmaz kicsi, mivel lehetővé teszi a modell számára, hogy a nagyobb adathalmazon megtanult jellemzőket felhasználja.

A mély tanulásban az egyik gyakori tartás tanulási megközelítés az, hogy előképzett modelleket használunk, például azokat, amelyek népszerű látási vagy természetes nyelvfeldolgozási feladatokra érhetők el, és a modellt finomhangoljuk a céladathalmazon. Ez magában foglalja a gazdálkodás rétegeinek befagyasztását az előképzett modellben, és csak a magasabb rétegeket képezzük a következő adatokon.

Íme egy példa, hogyan finomítsunk egy előképzett ResNet modellt egy kép osztályozási feladathoz PyTorchban:

import torchvision.models as models
import torch.nn as nn
 
# Betöltjük az előképzett ResNet modellt
resnet = models.resnet18(pretrained=True)
 
# Befagyasztjuk az előképzett modell paramétereit
for param in resnet.parameters():
    param.requires_grad = False
 
# Lecseréljük az utolsó réteget egy új teljesen kapcsolt rétegre
num_features = resnet.fc.in_features
resnet.fc = nn.Linear(num_features, 10)  # 10 osztályban gondolkodunk
 
# Képezzük a modellt az új adatokon
optimizer = optim.Adam(resnet.fc.parameters(), lr=0.001)

Ebben a példában először betöltjük az előképzett ResNet18 modellt, majd befagyasztjuk az alsó rétegei paramétereit. Azután lecseréljük az utolsó teljesen kapcsolt réteget egy új rétegre, amelynek megfelelő számú kimenete van a céladathoz (ezen esetben 10 osztály). Végül a modellt képezzük az új adatokon az Adam optimalizálóval, csak az új teljesen kapcsolt réteg paramétereit frissítve.

A tartás tanulás jelentősen javíthatja a mély tanuló modellek teljesítményét, különösen, ha a céladathalmaz kicsi. Ez egy erőteljes technika, amely időt és erőforrásokat takaríthat meg a modellek fejlesztése során.

Modell értelmezése

Ahogy a mély tanuló modellek egyre összetettebbek és elterjedtebbek lesznek, egyre fontosabbá válik az értelmezhető modellek létrehozása. Az értelmezhetőség azt jelenti, hogy képesek vagyunk megérteni és magyarázni a modell belső döntéshozatali folyamatát.

Az értelmezhetőség növelésének egyik népszerű módszere az figyelem mechanizmus használata. Az figyelem lehetővé teszi a modell számára, hogy a legrelevánsabb részre fókuszáljon a bemeneten való előrejelzéskor, és könnyen vizualizálható, hogy a modell mely jellemzőket használja.

Itt van egy példa arra, hogyan implementáljunk egy figyelem mechanizmust egy PyTorch modellben a természetes nyelvfeldolgozás feladathoz:

import torch.nn as nn
import torch.nn.functional as F
 
class FigyelemModell(nn.Module):
    def __init__(self, vocab_méret, beágyazás_dim, rejtett_dim):
        super(FigyelemModell, self).__init__()
        self.beágyazás = nn.Embedding(vocab_méret, beágyazás_dim)
        self.lstm = nn.LSTM(beágyazás_dim, rejtett_dim, bidirectional=True, batch_first=True)
        self.figyelem = nn.Linear(rejtett_dim * 2, 1)
 
    def forward(self, input_ids):
        # Beágyazás
        beágyazott = self.beágyazás(input_ids)
 
        # Átadás az LSTMon
        lstm_kimenet, _ = self.lstm(beágyazott)
 
        # Figyelem súlyok kiszámítása
        figyelem_súlyok = F.softmax(self.figyelem(lstm_kimenet), dim=1)
 
        # Az LSTMon kimeneteinek súlyozott összegének kiszámítása
        kontextus = torch.sum(figyelem_súlyok * lstm_kimenet, dim=1)
 
        vissza kontextus

Ebben a példában a figyelem mechanizmust egy lineáris rétegként implementáltuk, amely az LSTM kimeneteket veszi bemenetként, és létrehoz egy figyelem súlyokat tartalmazó halmazt. Ezután ezeket a súlyokat használjuk az LSTM kimeneteinek súlyozott összegének kiszámítására, amely a modell végső kimenete.

A figyelem súlyok vizualizálásával betekintést nyerhetünk abba, hogy a modell mely bemeneti részekre összpontosít a becslésekor. Ez segíthet az modell döntéshozatali folyamatának megértésében és potenciális elfogultságok vagy fejlesztési lehetőségek azonosításában.

Egy másik módszer a modell értelmezhetőségének növelésére a jellemzőfontosság elemzés használata. Ez azt jelenti, hogy azonosítjuk a legfontosabb jellemzőket, amelyeket a modell a becslésekhez használ. Ehhez gyakran használják a Shapley-értékeket, amelyek lehetővé teszik hogy az egyes jellemzők hozzájárulásának mennyiségét kvantifikálják a modell kimenetéhez.

A modell értelmezhetőségének javítása fontos kutatási terület a mély tanulásban, mivel segíthet a felhasználók bizalomépítésében ezekben a hatékony modellekben, és biztosítja, hogy felelősségteljesen használják őket.

Következtetés

Ebben a bemutatóban számos mély tanuláshoz kapcsolódó témát tárgyaltunk, beleértve a modell optimalizálását, a tartás tanulást és a modell értelmezhetőségét is. Megvitattuk a regularizáció, az optimalizációs algoritmusok és a figyelem mechanizmusok olyan technikáit, és bemutattunk példákat arra, hogyan lehet ezeket a koncepciókat PyTorchban implementálni.

Ahogy a mély tanulás folyamatosan fejlődik és egyre szélesebb körben elterjed, fontos megérteni ezeket a haladó témákat és hogyan lehet őket alkalmazni saját projektekben. Ezeket a technikákat elsajátítva jobban fel leszel szerelve arra, hogy magas teljesítményű, robosztus és értelmezhető mély tanuló modelleket hozz létre, amelyek sokféle problémát tudnak megoldani.

Ne feledkezz meg arról, hogy a mély tanulás gyorsan változó terület, és fontos képben lenned a legújabb kutatási eredményekkel és legjobb gyakorlatokkal. Folytasd az felfedezést, az kísérletezést és az tanulást, és hamarosan te is mély tanulás szakértővé válsz.