Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds

PyTorch - dwa filary deep learningu

Klasyczne modele ze scikit-learn miały granicę. Gdy dane są ogromne i skomplikowane - miliony zdjęć zwierząt - potrzebujesz sieci neuronowych, a do nich narzędzia cięższego kalibru: PyTorch. Zanim zaczniesz, poznaj dwa filary, na których stoi cała biblioteka - tensor i autograd. Reszta to szczegóły.

1pip install torch torchvision

Filar pierwszy: tensor

Tensor to podstawowe pudełko na dane w PyTorch - jak tablica NumPy, ale z dwiema supermocami, które poznasz za chwilę: umie liczyć na karcie graficznej i umie zapamiętać, jak powstał. Na razie potraktuj go jak znajomą tablicę liczb.

1import torch
2
3tensor = torch.tensor([1, 2, 3, 4, 5])
4matrix = torch.tensor([[1, 2], [3, 4], [5, 6]])
5
6zeros = torch.zeros(3, 4)   # macierz 3x4 zer
7random = torch.rand(3, 3)   # losowe wartości 0-1
8
9print(tensor.shape)    # rozmiar
10print(tensor.device)   # cpu czy cuda (GPU)

Tworzysz tensory prawie tak jak w NumPy, ale zwróć uwagę na ostatni atrybut -

device
. To on zdradza pierwszą supermoc: tensor wie, czy leży w pamięci procesora (
cpu
), czy karty graficznej (
cuda
). Do tego zaraz wrócimy.

Operacje na tensorach też przypominają NumPy - dodajesz, mnożysz, liczysz statystyki:

1a = torch.tensor([[1, 2], [3, 4]], dtype=torch.float32)
2b = torch.tensor([[5, 6], [7, 8]], dtype=torch.float32)
3
4c = a + b        # dodawanie element po elemencie
5c = a @ b        # mnożenie macierzowe (operator @)
6
7print(a.mean())  # średnia
8print(a.argmax())  # indeks największej wartości
9
10reshaped = torch.arange(12).reshape(3, 4)  # zmiana kształtu

Ważne rozróżnienie:

+
i
*
działają element po elemencie, a
@
to prawdziwe mnożenie macierzy - w sieciach neuronowych używasz głównie tego drugiego.
reshape
zmienia kształt bez ruszania danych, co przyda się przy przepychaniu obrazów przez sieć.

Supermoc tensora: obliczenia na GPU

Oto dlaczego deep learning w ogóle jest możliwy w rozsądnym czasie. Sieci wykonują miliardy mnożeń macierzy, a karta graficzna (GPU) robi je setki razy szybciej niż procesor. Tensor przenosisz na GPU jedną metodą.

1device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
2
3tensor = torch.tensor([1, 2, 3]).to(device)   # przenieś na GPU (jeśli jest)
4tensor_gpu = torch.rand(3, 3, device='cuda')  # albo od razu tam stwórz

Wzorzec

device = 'cuda' if ... else 'cpu'
to standard: kod działa na GPU, gdy jest dostępne, a na zwykłym procesorze, gdy go nie ma - bez przepisywania. Zapamiętaj jedną zasadę: żeby tensory mogły się dodać czy pomnożyć, muszą leżeć na tym samym urządzeniu. Mieszanie CPU i GPU to najczęstszy błąd początkującego.

Filar drugi: autograd

Teraz druga supermoc - ta, dzięki której model w ogóle potrafi się uczyć. Uczenie to poprawianie parametrów w stronę mniejszego błędu, a do tego trzeba pochodnych (gradientów). Autograd liczy je za Ciebie: gdy oznaczysz tensor

requires_grad=True
, PyTorch zapamiętuje każdą operację i potrafi cofnąć rachunek.

1x = torch.tensor([2.0], requires_grad=True)
2y = x ** 2 + 3 * x + 1
3
4y.backward()      # policz pochodną wstecz
5print(x.grad)     # dy/dx = 2x + 3 = 7

To jest sedno PyTorcha. Ty zapisałeś tylko wzór "w przód" (

y = x**2 + 3x + 1
), a
backward()
sam wyliczył pochodną - 7, dokładnie jak z kartki. Nigdzie nie różniczkowałeś ręcznie. W prawdziwym modelu ten sam mechanizm liczy, jak zmienić każdą wagę, by błąd zmalał:

1w = torch.tensor([1.0], requires_grad=True)
2b = torch.tensor([0.0], requires_grad=True)
3
4y_pred = w * torch.tensor([2.0]) + b   # przewidywanie modelu
5loss = (y_pred - torch.tensor([5.0])) ** 2  # jak bardzo się mylimy
6
7loss.backward()
8print(w.grad, b.grad)   # w którą stronę poprawić w i b

Prześledź logikę: liczysz przewidywanie, liczysz błąd (

loss
), wołasz
backward()
- a
w.grad
i
b.grad
mówią, w którą stronę pchnąć każdy parametr, żeby błąd zmalał. Cała nauka sieci to powtarzanie tego kroku tysiące razy. To dlatego autograd jest filarem, nie ciekawostką.

Podawanie danych: Dataset i DataLoader

Zostało praktyczne pytanie: jak karmić model danymi? Sieci uczą się na małych porcjach (batystach), więc PyTorch daje dwa klocki -

Dataset
opakowuje Twoje dane, a
DataLoader
porcjuje je i miesza.

1from torch.utils.data import Dataset, DataLoader
2
3class SafariDataset(Dataset):
4    def __init__(self, X, y):
5        self.X = torch.tensor(X, dtype=torch.float32)
6        self.y = torch.tensor(y, dtype=torch.long)
7
8    def __len__(self):
9        return len(self.X)
10
11    def __getitem__(self, idx):
12        return self.X[idx], self.y[idx]
13
14dataset = SafariDataset(X_train, y_train)
15dataloader = DataLoader(dataset, batch_size=32, shuffle=True)
16
17for batch_X, batch_y in dataloader:
18    print(batch_X.shape, batch_y.shape)
19    break

Twój

Dataset
musi umieć dwie rzeczy: powiedzieć, ile ma przykładów (
__len__
) i zwrócić jeden po numerze (
__getitem__
).
DataLoader
bierze to i podaje w paczkach po 32 (
batch_size
), za każdą epoką tasując kolejność (
shuffle=True
) - dzięki czemu model nie uczy się przypadkowej kolejności danych.

Pamiętaj z tej lekcji dwa filary: tensor to dane, które liczą na GPU, a autograd sam wylicza pochodne, dzięki którym model się uczy. Gdy to rozumiesz, budowa samej sieci jest już tylko układaniem warstw.

Przejdź do CodeWorlds