Obsah
1. Realizace neuronových sítí s využitím knihovny PyTorch (3. část)
2. Třída torch.nn.Linear: základ pro tvorbu neuronových sítí
3. Přečtení vah i biasu z objektu typu torch.nn.Linear
4. Afinní transformace bez vstupů a výstupů?
5. Specifikace vah a biasu po konstrukci objektu typu torch.nn.Linear
6. Aplikace lineární transformace reprezentované objektem typu torch.nn.Linear
7. Aplikace lineární transformace na tenzor vyššího řádu
8. Transformace v rovině prováděné instancemi třídy Linear
9. Realizace transformace otáčející body v rovině o 90°
10. Neuronová síť s jedním vstupem a jedním výstupem, bez aktivační funkce
11. Realizace neuronové sítě, výpis výsledků
12. Neuronová síť s jedním vstupem a dvěma výstupy, bez aktivační funkce
13. Realizace neuronové sítě, výpis výsledků
14. Neuronová síť se dvěma vstupy a jedním výstupem, bez aktivační funkce
15. Realizace neuronové sítě, výpis výsledků
17. Realizace neuronové sítě, výpis výsledků
19. Repositář s demonstračními příklady
1. Realizace neuronových sítí s využitím knihovny PyTorch (3. část)torch.nn.Linear: základ pro tvorbu neuronových sítí
V předchozím článku o tvorbě, tréninku a validaci neuronových sítí s využitím knihovny PyTorch jsme při konstrukci neuronových sítí použili třídu nazvanou torch.nn.Linear. Připomeňme si, že tato třída byla použita při konfiguraci jednotlivých vrstev neuronové sítě. Například konstruktor neuronové sítě s jednou vstupní vrstvou, jednou vrstvou skrytou a jednou výstupní vrstvou vypadal následovně:
def __init__(self, input_dim, hidden_dim, output_dim): super().__init__() # vrstvy neuronové sítě self.layer_1 = nn.Linear(input_dim, hidden_dim) self.layer_2 = nn.Linear(hidden_dim, output_dim)
V první části dnešního článku si popíšeme základní vlastnosti této třídy, která sice provádí v podstatě triviální matematické operace, ovšem současně je nezbytným prvkem většiny klasických neuronových sítí.
2. Třída torch.nn.Linear: základ pro tvorbu neuronových sítí
Třída torch.nn.Linear slouží pro konstrukci objektu, který dokáže provádět afinní (a popř. současně i lineární) transformace. A vzhledem k tomu, že knihovna PyTorch nepracuje s běžnými vektory a maticemi, ale s tenzory, jsou tyto transformace prováděny právě s tenzory (které mohou být různého řádu):
y = xAT + b
Interně si tedy instance třídy torch.nn.Linear musí pamatovat tenzory A a b, ovšem vzhledem k tomu, že primárním účelem PyTorche je práce s neuronovými sítěmi, jsou tyto tenzory uloženy do atributů nazvaných weight a bias, protože právě tyto termíny se v oblasti neuronových sítí používají.
Při konstrukci objektů tohoto typu se volí počet vstupů a výstupů, z čehož se odvodí rozměry tenzorů. A taktéž je možné zvolit, zda se bude při výpočtech používat i posun (bias) či nikoli:
Help on class Linear in module torch.nn.modules.linear: class Linear(torch.nn.modules.module.Module) | Linear(in_features: int, out_features: int, bias: bool = True, device=None, dtype=None) -> None | | Applies an affine linear transformation to the incoming data: :math:`y = xA^T + b`. | | This module supports :ref:`TensorFloat32<tf32_on_ampere>`. | | On certain ROCm devices, when using float16 inputs this module will use :ref:`different precision<fp16_on_mi200>` for backward. | | Args: | in_features: size of each input sample | out_features: size of each output sample | bias: If set to ``False``, the layer will not learn an additive bias. | Default: ``True`` | | Shape: | - Input: :math:`(*, H_{in})` where :math:`*` means any number of | dimensions including none and :math:`H_{in} = \text{in\_features}`. | - Output: :math:`(*, H_{out})` where all but the last dimension | are the same shape as the input and :math:`H_{out} = \text{out\_features}`. | | Attributes: | weight: the learnable weights of the module of shape | :math:`(\text{out\_features}, \text{in\_features})`. The values are | initialized from :math:`\mathcal{U}(-\sqrt{k}, \sqrt{k})`, where | :math:`k = \frac{1}{\text{in\_features}}` | bias: the learnable bias of the module of shape :math:`(\text{out\_features})`. | If :attr:`bias` is ``True``, the values are initialized from | :math:`\mathcal{U}(-\sqrt{k}, \sqrt{k})` where | :math:`k = \frac{1}{\text{in\_features}}` ... ... ...
3. Přečtení vah i biasu z objektu typu torch.nn.Linear
Při konstrukci objektu typu torch.nn.Linear se v tom nejjednodušším případě specifikuje pouze počet vstupů a výstupů (afinní transformace). V tom nejjednodušším (praktickém) případě budeme mít pouze jeden vstup a jeden výstup, tj. transformace provede mapování jedné skalární hodnoty na jinou skalární hodnotu (interně se však stále pracuje s tenzory!). Tenzor s váhami i tenzor s biasem budou mít každý jen jeden prvek naplněný náhodnou hodnotou v rozmezí 0..1.
Ověřme si tato tvrzení spuštěním následujícího skriptu:
from torch import nn l = nn.Linear(1, 1) print("Weights:", l.weight) print() print("Bias:", l.bias)
Výsledkem bude tenzor vah s jediným prvkem, ovšem bude se stále jednat o tenzor druhého řádu (matici). Naproti tomu biasy jsou reprezentovány vektorem – tenzorem prvního řádu:
Weights: Parameter containing: tensor([[0.0015]], requires_grad=True) Bias: Parameter containing: tensor([-0.4622], requires_grad=True)
V případě, že při konstrukci objektu typu torch.nn.Linear zakážeme použití biasů, neměl by být příslušný tenzor vůbec inicializován. Opět si to ověříme:
from torch import nn l = nn.Linear(1, 1, bias=False) print("Weights:", l.weight) print() print("Bias:", l.bias)
Výsledky ukazují, že se v tomto případě skutečně inicializuje pouze tenzor druhého řádu (matice) s vahami:
Weights: Parameter containing: tensor([[-0.7296]], requires_grad=True) Bias: None
4. Afinní transformace bez vstupů a výstupů?
Teoreticky je možné zkonstruovat třídu torch.nn.Linear, která bude reprezentovat afinní transformaci bez vstupů popř. bez výstupů. V praxi to nedává smysl, ale knihovna PyTorch nám tuto konstrukci umožňuje, což si opět ověříme:
from torch import nn l = nn.Linear(0, 0) print("Weights:", l.weight) print() print("Bias:", l.bias) print() l = nn.Linear(1, 0) print("Weights:", l.weight) print() print("Bias:", l.bias) print() l = nn.Linear(0, 1) print("Weights:", l.weight) print() print("Bias:", l.bias)
Knihovna PyTorch vypíše varování, že výsledek nemusí být užitečný:
torch/nn/init.py:511: UserWarning: Initializing zero-element tensors is a no-op warnings.warn("Initializing zero-element tensors is a no-op")
Ovšem všechny tři objekty budou zkonstruovány. Povšimněte si, jak jsou nastaveny jejich atributy v těchto mezních případech:
Weights: Parameter containing: tensor([], size=(0, 0), requires_grad=True) Bias: Parameter containing: tensor([], requires_grad=True) Weights: Parameter containing: tensor([], size=(0, 1), requires_grad=True) Bias: Parameter containing: tensor([], requires_grad=True) Weights: Parameter containing: tensor([], size=(1, 0), requires_grad=True) Bias: Parameter containing: tensor([0.], requires_grad=True)
5. Specifikace vah a biasu po konstrukci objektu typu torch.nn.Linear
Objekt představující afinní transformaci s náhodnými hodnotami uloženými v matici pro lineární transformaci i vektoru pro posun je sice užitečný při tréninku neuronové sítě, ovšem v některých případech potřebujeme prvky obou tenzorů (matice i vektoru) naplnit explicitně zadanými daty. To je možné provést přímým zápisem do atributů weight a bias. Ovšem je nutné vědět, že se nezapisují přímo tenzory, ale hodnoty typu torch.nn.Parameter, které si prozatím můžeme představit jako tenzory doplněné a metadata.
Pokusme se tedy zkonstruovat objekt, který bude provádět tuto jednoduchou transformaci:
y = 2x + 10
Provedeme to následujícím způsobem:
from torch import tensor, nn l = nn.Linear(1, 1) l.weight = nn.Parameter(data=tensor([[2.0]])) l.bias = nn.Parameter(data=tensor([10.0])) print("Weights:", l.weight) print() print("Bias:", l.bias)
Po spuštění tohoto skriptu si lze snadno ověřit, že parametry transformace jsou nastaveny korektně:
Weights: Parameter containing: tensor([[2.]], requires_grad=True) Bias: Parameter containing: tensor([10.], requires_grad=True)
6. Aplikace lineární transformace reprezentované objektem typu torch.nn.Linear
Nyní se konečně dostáváme k praktickému využití objektů typu torch.nn.Linear. Provedeme lineární transformaci, kterou jsme zmínili v předchozí kapitole:
y = 2x + 10
Vstupem bude tenzor s jediným prvkem [1.0] (jedná se o jednorozměrný vektor). Po provedení afinní transformace bychom měli dostat hodnotu 2×1.0+10=12 (resp. tenzor s touto hodnotou), což si ověříme:
from torch import tensor, nn l = nn.Linear(1, 1) l.weight = nn.Parameter(data=tensor([[2.0]])) l.bias = nn.Parameter(data=tensor([10.0])) x = tensor([1.0]) y = l(x) print(y)
Výsledkem je skutečně tenzor s jediným prvkem majícím hodnotu 12:
tensor([12.], grad_fn=<ViewBackward0>)
7. Aplikace lineární transformace na tenzor vyššího řádu
Lineární transformaci je možné aplikovat i na větší množství tenzorů. V praxi postačuje, aby tenzor měl více dimenzí (větší řád), než odpovídá nakonfigurované transformaci. V tomto případě je transformace vypočtena pro celý vstupní tenzor. Asi nejlépe bude celý postup patrný z dalšího demonstračního příkladu, ve kterém je (opět) nakonfigurována transformace:
y = 2x + 10
Teoreticky by tedy měl být na vstupu tenzor s jediným prvkem. Pokud ovšem předáme (de facto) vektor, bude transformace aplikována na všechny prvky tohoto vektoru. To ovšem není nic překvapivého a odpovídá to popisu uvedeného v úvodní kapitole. V dalším příkladu budeme transformovat vektor s deseti prvky. Povšimněte si, jaký je tvar tohoto tenzoru:
import torch from torch import nn l = nn.Linear(1, 1) l.weight = nn.Parameter(data=torch.tensor([[2.0]])) l.bias = nn.Parameter(data=torch.tensor([10.0])) x = torch.reshape(torch.arange(1.0, 11), (10, 1)) y = l(x) print(y)
Výsledek by měl vypadat následovně:
tensor([[12.], [14.], [16.], [18.], [20.], [22.], [24.], [26.], [28.], [30.]], grad_fn=<.AddmmBackward0>)
Jedná se tedy postupně o prvky s hodnotou 1×2+10=12, 2×2+10=14 atd.
8. Transformace v rovině prováděné instancemi třídy Linear
V případě, že bude tenzor s váhami tvořit matici 2×2 prvky a vektor s biasy bude dvouprvkovým vektorem, bude možné provádět afinní transformaci bodů v rovině. Tato transformace se obecně skládá z rotace, změny měřítka (nezávisle v obou osách) a zkosení zkombinovaných s posunem. Ukažme si nejprve, jak vypadá reprezentace takové transformace ihned po konstrukci objektu typu torch.nn.Linear:
from torch import tensor, nn l = nn.Linear(2, 2) print("Weights:", l.weight) print() print("Bias:", l.bias)
Z vypsaných hodnot je patrné, že transformace je skutečně popsána maticí 2×2 prvky a vektorem se dvěma prvky. Hodnoty jsou náhodné:
Weights: Parameter containing: tensor([[ 0.2398, 0.0439], [-0.2865, 0.5355]], requires_grad=True) Bias: Parameter containing: tensor([ 0.3383, -0.6409], requires_grad=True)
9. Realizace transformace otáčející body v rovině o 90°
V dalším kroku si vyzkoušíme nastavit objekt typu torch.nn.Linear takovým způsobem, aby se skutečně prováděla základní lineární transformace v rovině. Konkrétně se bude jednat o otáčení bodů o 90°. Takovou transformaci lze reprezentovat maticí 2×2 prvky, přičemž hodnoty prvků budou rovny hodnotám cos φ a sin φ (v jednom místě se změnou znaménka):
| | | cos φ -sin φ | | | | sin φ cos φ | | |
Nejprve si necháme vypočítat hodnoty sinů a kosinů pro zadaný úhel:
phi = 90 cos_phi = math.cos(math.radians(phi)) sin_phi = math.sin(math.radians(phi))
Dále matici zkonstruujeme, a to prozatím ve formě seznamu seznamů:
rotation_matrix = [ [cos_phi, -sin_phi], [sin_phi, cos_phi], ]
Provádět budeme jen rotaci, nikoli posun, takže vektor (biasů) bude obsahovat nulové prvky:
translation_vector = [0.0, 0.0]
Následně jen musíme zkonstruovat objekt představující lineární transformaci a nastavit příslušné tenzory vah a biasů:
l = nn.Linear(2, 2) l.weight = nn.Parameter(data=torch.tensor(rotation_matrix)) l.bias = nn.Parameter(data=torch.tensor(translation_vector))
V ukázkovém skriptu se pokusíme o rotaci bodu [1, 2] v rovině:
import math import torch from torch import nn l = nn.Linear(2, 2) phi = 90 cos_phi = math.cos(math.radians(phi)) sin_phi = math.sin(math.radians(phi)) rotation_matrix = [ [cos_phi, -sin_phi], [sin_phi, cos_phi], ] translation_vector = [0.0, 0.0] l.weight = nn.Parameter(data=torch.tensor(rotation_matrix)) l.bias = nn.Parameter(data=torch.tensor(translation_vector)) x = torch.tensor([1.0, 2.0]) y = l(x) print(y)
Z výsledků je patrné, že rotace skutečně proběhla:
tensor([-2., 1.], grad_fn=<ViewBackward0>)
Což si můžeme i vizualizovat:
| |......+ [1,2] [-2,1] | : +.........| : : | : : | : -------------+-------------- | [0,0] | | | | |
10. Neuronová síť s jedním vstupem a jedním výstupem, bez aktivační funkce
Nyní již známe všechny důležité vlastnosti třídy torch.nn.Linear. Můžeme jít dále – vytvoříme si velmi jednoduchou neuronovou síť, která bude mít jediný vstup, jediný výstup a nebude obsahovat aktivační funkci (namísto ní si představte identitu). Tato síť tedy bude provádět nám již známou transformaci jednoho skaláru na jiný skalár:
y = xAT + b
přičemž A je matice 1×1 a b je jednoprvkový vektor.
Konstrukce neuronové sítě s nastavením matice A i vektoru b na nějaké „rozumné“ hodnoty může vypadat takto:
def __init__(self, input_dim, output_dim): super().__init__() # vrstvy neuronové sítě self.layer_1 = nn.Linear(input_dim, output_dim) self.layer_1.weight = nn.Parameter(data=torch.tensor([[2.0]])) self.layer_1.bias = nn.Parameter(data=torch.tensor([10.0]))
Důležitá je metoda forward, která využívá lineární transformaci uloženou do atributu layer1:
def forward(self, x): # propagace hodnot přes neuronovou síť return self.layer_1(x)
Povšimněte si, že jsme jednoduše pro vstup x použili přímo lineární transformaci pro výpočet výstupní hodnoty neuronové sítě.
11. Realizace neuronové sítě, výpis výsledků
Skript s realizací neuronové sítě popsané v desáté kapitole ověří, zda síť pracuje přesně tak, jak je očekáváno, tj. zda se provádí korektní transformace:
import torch from torch import nn class NeuralNetwork(nn.Module): """Třída reprezentující neuronovou síť.""" def __init__(self, input_dim, output_dim): super().__init__() # vrstvy neuronové sítě self.layer_1 = nn.Linear(input_dim, output_dim) self.layer_1.weight = nn.Parameter(data=torch.tensor([[2.0]])) self.layer_1.bias = nn.Parameter(data=torch.tensor([10.0])) def forward(self, x): # propagace hodnot přes neuronovou síť return self.layer_1(x) # konfigurace vrstev neuronové sítě input_dim = 1 output_dim = 1 # konstrukce neuronové sítě nn1 = NeuralNetwork(input_dim, output_dim) # výpis základních informací o neuronové síti print("Neural network:", nn1) print() # výpis informace o vrstvě neuronové sítě print("Layer 1:", nn1.layer_1) print() # výpis informace o transformační matici a biasech print("Weights:", nn1.layer_1.weight) print("Bias:", nn1.layer_1.bias) print() with torch.no_grad(): X = torch.reshape(torch.arange(1.0, 11), (10, 1)) output = nn1(X) print(output)
Po spuštění skriptu se nejdříve zobrazí základní informace o neuronové síti:
Neural network: NeuralNetwork( (layer_1): Linear(in_features=1, out_features=1, bias=True) ) Layer 1: Linear(in_features=1, out_features=1, bias=True)
Dále se zobrazí matice A a vektor b jediné vrstvy sítě:
Weights: Parameter containing: tensor([[2.]], requires_grad=True) Bias: Parameter containing: tensor([10.], requires_grad=True)
Nejdůležitější jsou však „predikce“ této sítě pro hodnoty 1–10 uložené v tenzoru. Výsledky by měly vypadat následovně:
tensor([[12.], [14.], [16.], [18.], [20.], [22.], [24.], [26.], [28.], [30.]])
Síť tedy odpovídá přesně na základě dat, kterými jsme ji naučili – a to přímo (bez zpětné propagace).
12. Neuronová síť s jedním vstupem a dvěma výstupy, bez aktivační funkce
Samozřejmě nám nic nebrání v úpravě konfigurace neuronové sítě takovým způsobem, že namísto jedné výstupní hodnoty (skaláru) získáme dvouprvkový vektor. Transformace, která se bude provádět, bude stále stejná:
y = xAT + b
ovšem lišit se bude velikost matice A (nikoli jeden prvek, ale sloupec se dvěma řádky) i délka vektoru b (namísto jednoho prvku bude mít vektor prvky dva).
Samotný konstruktor neuronové sítě se změní následovně:
def __init__(self, input_dim, output_dim): super().__init__() # vrstvy neuronové sítě self.layer_1 = nn.Linear(input_dim, output_dim) self.layer_1.weight = nn.Parameter(data=torch.tensor([[1.0], [2.0]])) self.layer_1.bias = nn.Parameter(data=torch.tensor([0.0, 0.0]))
13. Realizace neuronové sítě, výpis výsledků
Opět si ukažme úplný skript, který po svém spuštění vytvoří neuronovou síť s jediným vstupem a s dvojicí výstupů, dále naplní hodnoty matice A a vektoru b a následně ověří, zda síť provádí korektní transformace:
import torch from torch import nn class NeuralNetwork(nn.Module): """Třída reprezentující neuronovou síť.""" def __init__(self, input_dim, output_dim): super().__init__() # vrstvy neuronové sítě self.layer_1 = nn.Linear(input_dim, output_dim) self.layer_1.weight = nn.Parameter(data=torch.tensor([[1.0], [2.0]])) self.layer_1.bias = nn.Parameter(data=torch.tensor([0.0, 0.0])) def forward(self, x): # propagace hodnot přes neuronovou síť return self.layer_1(x) # konfigurace vrstev neuronové sítě input_dim = 1 output_dim = 2 # konstrukce neuronové sítě nn1 = NeuralNetwork(input_dim, output_dim) # výpis základních informací o neuronové síti print("Neural network:", nn1) print() # výpis informace o vrstvě neuronové sítě print("Layer 1:", nn1.layer_1) print() # výpis informace o transformační matici a biasech print("Weights:", nn1.layer_1.weight) print("Bias:", nn1.layer_1.bias) print() with torch.no_grad(): X = torch.reshape(torch.arange(1.0, 11), (10, 1)) output = nn1(X) print(output)
Po spuštění skriptu se opět nejdříve zobrazí základní parametry sítě:
Neural network: NeuralNetwork( (layer_1): Linear(in_features=1, out_features=2, bias=True) ) Layer 1: Linear(in_features=1, out_features=2, bias=True)
Dále se zobrazí matice A a vektor b, tedy váhy a biasy jediné vrstvy:
Weights: Parameter containing: tensor([[1.], [2.]], requires_grad=True) Bias: Parameter containing: tensor([0., 0.], requires_grad=True)
A konečně si necháme pro vstupní vektor s prvky s hodnotami 1..10 vypočítat výslednou matici – každý prvek (skalár) je totiž transformován na dvojici hodnot. Například prvek 1 je transformován na vektor [1, 2] atd.:
tensor([[ 1., 2.], [ 2., 4.], [ 3., 6.], [ 4., 8.], [ 5., 10.], [ 6., 12.], [ 7., 14.], [ 8., 16.], [ 9., 18.], [10., 20.]])
Neuronová síť tedy opět provádí přesně tu transformaci, na kterou jsme ji natrénovali.
14. Neuronová síť se dvěma vstupy a jedním výstupem, bez aktivační funkce
Naposledy se podívejme na konstrukci neuronové sítě, ve které se bude (stále) používat stejná afinní transformace:
y = xAT + b
Nyní bude ovšem síť akceptovat dva vstupy (tedy vektor) a bude mít jen jeden výstup. To znamená, že nyní je x dvouprvkovým vektorem a b i y jsou naopak skaláry (resp. přesněji řečeno skaláry reprezentované formou tenzorů).
Pro jednoduchost bude síť provádět výpočet průměru prvků vstupního dvouprvkového vektoru x=[x1, x2]:
y = (x1+x2)/2
To lze zapsat:
y = x1*0,5 + x2*0,5 = x*[0,5, 0,5]T + [0]
To znamená, že v matici A budou uloženy hodnoty [0,5 0,5] a ve vektoru b bude jediný nulový prvek:
def __init__(self, input_dim, output_dim): super().__init__() # vrstvy neuronové sítě self.layer_1 = nn.Linear(input_dim, output_dim) self.layer_1.weight = nn.Parameter(data=torch.tensor([[0.5, 0.5]])) self.layer_1.bias = nn.Parameter(data=torch.tensor([0.0]))
15. Realizace neuronové sítě, výpis výsledků
V dnešním předposledním skriptu je zkonstruována síť popsaná v předchozí kapitole a následně je provedena transformace tří vstupních vektorů na trojici skalárních hodnot. Vstupy jsou zvoleny takovým způsobem, aby se dalo jednoduše ověřit, zda síť skutečně provádí očekávané výpočty:
import torch from torch import nn class NeuralNetwork(nn.Module): """Třída reprezentující neuronovou síť.""" def __init__(self, input_dim, output_dim): super().__init__() # vrstvy neuronové sítě self.layer_1 = nn.Linear(input_dim, output_dim) self.layer_1.weight = nn.Parameter(data=torch.tensor([[0.5, 0.5]])) self.layer_1.bias = nn.Parameter(data=torch.tensor([0.0])) def forward(self, x): # propagace hodnot přes neuronovou síť return self.layer_1(x) # konfigurace vrstev neuronové sítě input_dim = 2 output_dim = 1 # konstrukce neuronové sítě nn1 = NeuralNetwork(input_dim, output_dim) # výpis základních informací o neuronové síti print("Neural network:", nn1) print() # výpis informace o vrstvě neuronové sítě print("Layer 1:", nn1.layer_1) print() # výpis informace o transformační matici a biasech print("Weights:", nn1.layer_1.weight) print("Bias:", nn1.layer_1.bias) print() with torch.no_grad(): print(nn1(torch.tensor([1.0, 1.0]))) print(nn1(torch.tensor([1.0, 2.0]))) print(nn1(torch.tensor([2.0, 2.0])))
Struktura sítě zobrazená po spuštění skriptu:
Neural network: NeuralNetwork( (layer_1): Linear(in_features=2, out_features=1, bias=True) ) Layer 1: Linear(in_features=2, out_features=1, bias=True)
Parametry jediné vrstvy naší neuronové sítě:
Weights: Parameter containing: tensor([[0.5000, 0.5000]], requires_grad=True) Bias: Parameter containing: tensor([0.], requires_grad=True)
Na závěr skript zobrazí výsledky tří transformací v této podobě:
tensor([1.]) tensor([1.5000]) tensor([2.])
Realizované výpočty si můžeme zobrazit následujícím způsobem:
[1.0, 1.0] → [1.]) [1.0, 2.0] → [1.5000]) [2.0, 2.0] → [2.])
Na levé straně jsou vektory přivedené na vstup neuronové sítě, na straně pravé pak výsledné skaláry, ovšem stále reprezentované formou tenzoru.
16. Přidání aktivační funkce
Prozatím všechny tři neuronové sítě, které jsme si ukázali, nepoužívaly aktivační funkci (resp. namísto ní byla použita identita). Samotná transformace prováděná neuronovou sítí je deklarována v metodě forward. Pro síť s jedinou vrstvou, navíc bez aktivační funkce, tato metoda vypadá následovně:
def forward(self, x): # propagace hodnot přes neuronovou síť return self.layer_1(x)
V posledním kroku, který si dnes ukážeme, do neuronové sítě přidáme aktivační funkci. Konkrétně se bude jednat o ReLU (rectified linear unit), jejíž průběh dobře známe:

Obrázek 1: Aktivační funkce ReLU.
Metodu forward je tedy nutné upravit do této podoby:
def forward(self, x): # propagace hodnot přes neuronovou síť x = nn.functional.relu(self.layer_1(x)) return x
17. Realizace neuronové sítě, výpis výsledků
Pro otestování neuronové sítě s přidanou aktivační funkcí ReLU si nepatrně upravíme příklad z desáté kapitoly. Konkrétně síť nakonfigurujeme tak, že bude provádět afinní transformaci:
y = x - 5
To znamená, že matice A bude obsahovat jediný prvek 1,0 a vektor b bude obsahovat jediný prvek s hodnotou –5:
import torch from torch import nn class NeuralNetwork(nn.Module): """Třída reprezentující neuronovou síť.""" def __init__(self, input_dim, output_dim): super().__init__() # vrstvy neuronové sítě self.layer_1 = nn.Linear(input_dim, output_dim) self.layer_1.weight = nn.Parameter(data=torch.tensor([[1.0]])) self.layer_1.bias = nn.Parameter(data=torch.tensor([-5.0])) def forward(self, x): # propagace hodnot přes neuronovou síť x = nn.functional.relu(self.layer_1(x)) return x # konfigurace vrstev neuronové sítě input_dim = 1 output_dim = 1 # konstrukce neuronové sítě nn1 = NeuralNetwork(input_dim, output_dim) # výpis základních informací o neuronové síti print("Neural network:", nn1) print() # výpis informace o vrstvě neuronové sítě print("Layer 1:", nn1.layer_1) print() # výpis informace o transformační matici a biasech print("Weights:", nn1.layer_1.weight) print("Bias:", nn1.layer_1.bias) print() with torch.no_grad(): X = torch.reshape(torch.arange(1.0, 11), (10, 1)) output = nn1(X) print(X) print(output)
Skript po svém spuštění opět nejdříve zobrazí konfiguraci neuronové sítě:
Neural network: NeuralNetwork( (layer_1): Linear(in_features=1, out_features=1, bias=True) ) Layer 1: Linear(in_features=1, out_features=1, bias=True)
Dále se zobrazí vlastnosti její jediné vrstvy:
Weights: Parameter containing: tensor([[1.]], requires_grad=True) Bias: Parameter containing: tensor([-5.], requires_grad=True)
A nakonec se zobrazí dvojice tenzorů. První tenzor obsahuje vstupní prvky 1, 2 až 10, druhý tenzor pak výsledky vyprodukované neuronovou sítí, což jsou konkrétně hodnoty 0, 0, …0, 1, 2, 3, 4 a 5:
tensor([[ 1.], [ 2.], [ 3.], [ 4.], [ 5.], [ 6.], [ 7.], [ 8.], [ 9.], [10.]]) tensor([[0.], [0.], [0.], [0.], [0.], [1.], [2.], [3.], [4.], [5.]])
Výsledky si opět můžeme zobrazit formou jednoduché tabulky:
x x-5.0 ReLU(x-5.0) ------------------------- 1 -4 0 2 -3 0 3 -2 0 4 -1 0 5 0 0 6 1 1 7 2 2 8 3 3 9 4 4 10 5 5
18. Závěr
V tento okamžik již velmi dobře víme, jakým způsobem neuronová síť provádí dopředné (forward) výpočty, tedy transformace a aplikace aktivačních funkcí. Máme taktéž odzkoušenu práci s tenzory. Zbývá nám jediné – zjistit způsob učení sítě formou zpětné propagace očekávaných výsledků pro zadané vstupy. To je, jak jsme již ostatně viděli minule, velmi důležité téma, protože při špatném nastavení sítě může docházet k jejímu nedoučení, přeučení atd. Příště tedy toto téma dokončíme a ukážeme si další variantu neuronových sítí. Bude se jednat o konvoluční neuronové sítě, které jsou v současnosti používány velmi často.
19. Repositář s demonstračními příklady
Všechny demonstrační příklady využívající knihovnu PyTorch lze nalézt v repositáři https://github.com/tisnik/most-popular-python-libs. Následují odkazy na jednotlivé příklady:
20. Odkazy na Internetu
- Seriál Programovací jazyk Lua na Rootu:
https://www.root.cz/serialy/programovaci-jazyk-lua/ - PDM: moderní správce balíčků a virtuálních prostředí Pythonu:
https://www.root.cz/clanky/pdm-moderni-spravce-balicku-a-virtualnich-prostredi-pythonu/ - PyTorch Tutorial: Building a Simple Neural Network From Scratch
https://www.datacamp.com/tutorial/pytorch-tutorial-building-a-simple-neural-network-from-scratch - Interní reprezentace numerických hodnot: od skutečného počítačového pravěku po IEEE 754–2008:
https://www.root.cz/clanky/interni-reprezentace-numerickych-hodnot-od-skutecneho-pocitacoveho-praveku-po-ieee-754–2008/ - Interní reprezentace numerických hodnot: od skutečného počítačového pravěku po IEEE 754–2008 (dokončení):
https://www.root.cz/clanky/interni-reprezentace-numerickych-hodnot-od-skutecneho-pocitacoveho-praveku-po-ieee-754–2008-dokonceni/ - Brain Floating Point – nový formát uložení čísel pro strojové učení a chytrá čidla:
https://www.root.cz/clanky/brain-floating-point-ndash-novy-format-ulozeni-cisel-pro-strojove-uceni-a-chytra-cidla/ - Stránky projektu PyTorch:
https://pytorch.org/ - Informace o instalaci PyTorche:
https://pytorch.org/get-started/locally/ - Tenzor (Wikipedia):
https://cs.wikipedia.org/wiki/Tenzor - Introduction to Tensors:
https://www.youtube.com/watch?v=uaQeXi4E7gA - Introduction to Tensors: Transformation Rules:
https://www.youtube.com/watch?v=j6DazQDbEhQ - Tensor Attributes:
https://pytorch.org/docs/stable/tensor_attributes.html - Tensors Explained Intuitively: Covariant, Contravariant, Rank :
https://www.youtube.com/watch?v=CliW7kSxxWU - What is the relationship between PyTorch and Torch?:
https://stackoverflow.com/questions/44371560/what-is-the-relationship-between-pytorch-and-torch - What is a tensor anyway?? (from a mathematician):
https://www.youtube.com/watch?v=K7f2pCQ3p3U - Visualization of tensors – part 1 :
https://www.youtube.com/watch?v=YxXyN2ifK8A - Visualization of tensors – part 2A:
https://www.youtube.com/watch?v=A95jdIuUUW0 - Visualization of tensors – part 2B:
https://www.youtube.com/watch?v=A95jdIuUUW0 - What the HECK is a Tensor?!?:
https://www.youtube.com/watch?v=bpG3gqDM80w - Stránka projektu Torch
http://torch.ch/ - Torch na GitHubu (několik repositářů)
https://github.com/torch - Torch (machine learning), Wikipedia
https://en.wikipedia.org/wiki/Torch_%28machine_learning%29 - Torch Package Reference Manual
https://github.com/torch/torch7/blob/master/README.md - Torch Cheatsheet
https://github.com/torch/torch7/wiki/Cheatsheet - An Introduction to Tensors
https://math.stackexchange.com/questions/10282/an-introduction-to-tensors - Differences between a matrix and a tensor
https://math.stackexchange.com/questions/412423/differences-between-a-matrix-and-a-tensor - Qualitatively, what is the difference between a matrix and a tensor?
https://math.stackexchange.com/questions/1444412/qualitatively-what-is-the-difference-between-a-matrix-and-a-tensor? - Tensors for Neural Networks, Clearly Explained!!!:
https://www.youtube.com/watch?v=L35fFDpwIM4 - Tensor Processing Unit:
https://en.wikipedia.org/wiki/Tensor_Processing_Unit - Třída Storage:
http://docs.pytorch.wiki/en/storage.html - Funkce torch.dot
https://pytorch.org/docs/stable/generated/torch.dot.html#torch.dot - Funkce torch.narrow
https://pytorch.org/docs/stable/generated/torch.narrow.html - Funkce torch.matmul
https://pytorch.org/docs/stable/generated/torch.matmul.html - Funkce torch.reshape
https://pytorch.org/docs/stable/generated/torch.reshape.html - Funkce torch.arange
https://pytorch.org/docs/stable/generated/torch.arange.html - Funkce torch.range
https://pytorch.org/docs/stable/generated/torch.range.html - Třída torch.Tensor
https://pytorch.org/docs/stable/tensors.html - Atributy tenzorů
https://pytorch.org/docs/stable/tensor_attributes.html - Pohledy vytvořené nad tenzory
https://pytorch.org/docs/stable/tensor_view.html - Broadcasting v knihovně
https://numpy.org/doc/stable/user/basics.broadcasting.html - Broadcasting semantics (v knihovně PyTorch)
https://pytorch.org/docs/stable/notes/broadcasting.html - Dot Product In Physics: What Is The Physical Meaning of It?
https://profoundphysics.com/dot-product-in-physics-what-is-the-physical-meaning-of-it/ - scikit-learn: Getting Started
https://scikit-learn.org/stable/getting_started.html - Support Vector Machines
https://scikit-learn.org/stable/modules/svm.html - Use Deep Learning to Detect Programming Languages
http://searene.me/2017/11/26/use-neural-networks-to-detect-programming-languages/ - Data pro neuronové sítě
http://archive.ics.uci.edu/ml/index.php - Feedforward neural network
https://en.wikipedia.org/wiki/Feedforward_neural_network - Biologické algoritmy (4) – Neuronové sítě
https://www.root.cz/clanky/biologicke-algoritmy-4-neuronove-site/ - Biologické algoritmy (5) – Neuronové sítě
https://www.root.cz/clanky/biologicke-algoritmy-5-neuronove-site/ - Umělá neuronová síť (Wikipedia)
https://cs.wikipedia.org/wiki/Um%C4%9Bl%C3%A1_neuronov%C3%A1_s%C3%AD%C5%A5 - AI vs Machine Learning (Youtube)
https://www.youtube.com/watch?v=4RixMPF4×is - Machine Learning | What Is Machine Learning? | Introduction To Machine Learning | 2024 | Simplilearn (Youtube)
https://www.youtube.com/watch?v=ukzFI9rgwfU - A Gentle Introduction to Machine Learning (Youtube)
https://www.youtube.com/watch?v=Gv9_4yMHFhI - Machine Learning vs Deep Learning
https://www.youtube.com/watch?v=q6kJ71tEYqM - Umělá inteligence (slajdy)
https://slideplayer.cz/slide/12119218/ - Úvod do umělé inteligence
https://slideplayer.cz/slide/2505525/ - Umělá inteligence I / Artificial Intelligence I
https://ktiml.mff.cuni.cz/~bartak/ui/ - Třída torch.nn.Linear
https://pytorch.org/docs/stable/generated/torch.nn.Linear.html - Třída torch.nn.Parameter
https://pytorch.org/docs/stable/generated/torch.nn.parameter.Parameter.html - Třída torch.nn.Sigmoid
https://pytorch.org/docs/stable/generated/torch.nn.Sigmoid.html