PyTorch: problematika rozpoznávání a klasifikace obrázků (2. část)

15. 5. 2025
Doba čtení: 54 minut

Sdílet

Autor: Pixabay
Ukážeme si natrénování a validaci běžné neuronové sítě takovým způsobem, aby dokázala rozeznávat zašuměné a posunuté obrazy číslic. Bude se jednat o základ, který nám umožní přechod ke konvolučním neuronovým sítím.

Obsah

1. PyTorch: problematika rozpoznávání a klasifikace obrázků (2. část)

2. Klasifikace rastrových obrázků sítí běžnou neuronovou sítí s „vektorovým“ vstupem a výstupem

3. První skript: příprava datové sady pro trénink neuronové sítě s vektorovým vstupem a výstupem

4. Příprava vstupních dat, která mohou být zašuměna popř. posunuta

5. Druhý skript: příprava zašuměných a posunutých vstupních dat pro neuronovou síť

6. Náhodné rozdělení datové sady funkcí train_test_split

7. Realizace rozdělení datové sady na trénovací a testovací data

8. Třetí skript: rozdělení datové sady na trénovací a testovací data

9. Konstrukce neuronové sítě s vektorovým vstupem i výstupem

10. Trénink neuronové sítě

11. Čtvrtý skript: trénink neuronové sítě pro rozpoznávání rastrových obrázků

12. Zašuměné obrázky v trénovacích a testovacích datech

13. Pátý skript: trénink neuronové sítě pro rozpoznávání rastrových obrázků s využitím zašuměných obrázků

14. Posunuté obrázky v trénovacích a testovacích datech

15. Šestý skript: trénink neuronové sítě pro rozpoznání posunutých obrázků

16. Matice záměn pro síť, která odpovídá vždy korektně

17. Matice záměn pro zašuměné obrázky

18. Matice záměn pro posunuté obrázky

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

1. PyTorch: problematika rozpoznávání a klasifikace obrázků (2. část)

Na předchozí článek o neuronových sítích realizovaných s využitím knihovny PyTorch dnes navážeme. Ukážeme si trénink sítí, na jejichž vstupu budou rastrové obrázky s číslicemi a na výstupu konkrétní informace o číslici, kterou síť rozezná (nebo naopak nerozezná).

Připomeňme si, že vstupní data s číslicemi zakódovanými do bitmap s rozlišením 8×8 pixelů, vypadají takto:

# číslice reprezentované v masce 8x8 pixelů
digits = (
    (0x00, 0x3C, 0x66, 0x76, 0x6E, 0x66, 0x3C, 0x00),
    (0x00, 0x18, 0x1C, 0x18, 0x18, 0x18, 0x7E, 0x00),
    (0x00, 0x3C, 0x66, 0x30, 0x18, 0x0C, 0x7E, 0x00),
    (0x00, 0x7E, 0x30, 0x18, 0x30, 0x66, 0x3C, 0x00),
    (0x00, 0x30, 0x38, 0x3C, 0x36, 0x7E, 0x30, 0x00),
    (0x00, 0x7E, 0x06, 0x3E, 0x60, 0x66, 0x3C, 0x00),
    (0x00, 0x3C, 0x06, 0x3E, 0x66, 0x66, 0x3C, 0x00),
    (0x00, 0x7E, 0x60, 0x30, 0x18, 0x0C, 0x0C, 0x00),
    (0x00, 0x3C, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00),
    (0x00, 0x3C, 0x66, 0x7C, 0x60, 0x30, 0x1C, 0x00),
)

Dále máme k dispozici pomocnou funkci, která tato data převede do formy dvourozměrného pole (NumPy):

def digit_to_array(digits, n):
    digit = digits[n]
    rows = []
    # převod jednotlivých řádků na osmici bitů
    for scanline in digit:
        row = []
        # převod bitmapy představující řádek na osmici bitů
        for _ in range(8):
            bit = scanline & 0x01
            row.append(float(bit))
            scanline >>= 1
        rows.append(row)
    # transformace na n-dimenzionální pole
    return np.array(rows)

Výsledek lze vizualizovat s využitím knihovny Matplotlib:

PyTorch NN

Obrázek 1: Matice číslice 2 zobrazená v nepravých barvách.

Abychom zjistili, do jaké míry bude neuronová síť naučena na rozlišení reálných obrázků, budeme ji trénovat a validovat s využitím obrázků zašuměných, a to s mírou šumu od 0% do 100%:

PyTorch NN

Obrázek 2: Vizualizace matice se zašuměnou číslicí 2, úroveň šumu se postupně zvyšuje.

Navíc budeme síť učit a následně validovat i na posunutých obrázcích, což se ukazuje být pro klasické neuronové sítě větším problémem, než šum:

PyTorch NN

Obrázek 3: Vizualizovaná matice s posunutou číslicí 2.

2. Klasifikace rastrových obrázků sítí běžnou neuronovou sítí s „vektorovým“ vstupem a výstupem

Abychom zjistili limity klasických neuronových sítí pro klasifikaci rastrových obrázků, pokusíme se je skutečně pro klasifikaci použít. Zkonstruujeme tedy neuronovou síť, která bude jak na vstupu, tak i na výstupu obsahovat vektorová data. Konkrétně bude vstupem sítě 64prvkový vektor (s prvky typu float32), který obsahuje pixely původní bitmapy 8×8 pixelů, ovšem v „ploché“ podobě (z matice vytvoříme vektor). A na výstupu sítě bude taktéž vektor. Ten bude obsahovat deset prvků, přičemž pozice (index) největšího prvku určí číslici, kterou síť nalezla ve vstupním obrázku. Ideálně bude tento vektor vypadat například takto:

tensor([0., 0., 1., 0., 0., 0., 0., 0., 0., 0.]))

což odpovídá číslici 2.

V praxi však síť může odpovědět i takto:

tensor([0.1872, 0.1687, 0.9529, 0.0941, 0.0442, 0.1684, 0.0257, 0.0000, 0.0612, 0.2341])

což stále poměrně přesně určuje číslici 2 (prvek 0,9529).

Trénovací a testovací data budou reprezentována instancí třídy Data, která je odvozena od třídy torch.utils.data.Dataset. Tuto třídu jsme již několikrát použili, ovšem pro úplnost si ji ještě jednou ukažme:

# konverze původních dat z NumPy do tenzorů
class Data(Dataset):
    def __init__(self, X, y):
        self.X = torch.from_numpy(X.astype(np.float32))
        self.y = torch.from_numpy(y.astype(np.float32))
        self.len = self.X.shape[0]
 
    def __getitem__(self, index):
        return self.X[index], self.y[index]
 
    def __len__(self):
        return self.len

Podívejme se nyní na pomocnou funkci, která trénovací a/nebo testovací data připraví. Tato funkce využívá jak vstupní matici 8×8 pixelů digits, tak i funkci nazvanou digit_to_array a popsanou minule. Povšimněte si způsobu tvorby vektorů x_vector a y_vector, jejichž délka i hodnoty prvků odpovídají popisu uvedeném výše:

def prepare_data(digits, length):
    # příprava dat pro trénink a testování
    X = []
    y = []
 
    for i in range(length):
        # cislice
        digit = i % 10
        # vstupy
        array = digit_to_array(digits, digit)
        # prevod na vektor
        x_vector = array.flatten()
        X.append(x_vector)
        # očekávané výstupy
        y_vector = [0.0] * 10
        y_vector[digit] = 1.0
        y.append(y_vector)
 
    return Data(np.array(X), np.array(y))

3. První skript: příprava datové sady pro trénink neuronové sítě s vektorovým vstupem a výstupem

Postup popsaný v předchozí kapitole nyní použijeme ve skriptu, jehož úkolem je příprava datové sady (což je instance třídy Data odvozené od třídy torch.utils.data.Dataset), která bude z vnějšího pohledu obsahovat sekvenci dvojic tenzorů. První tenzor z těchto dvojic bude jednorozměrným vektorem se 64 prvky typu float32, jejichž hodnoty odpovídají intenzitě pixelů původní bitmapy. A druhý tenzor bude taktéž jednorozměrným vektorem, nyní ovšem s deseti prvky typu float32. Takový vektor bude mít devět prvků nulových a jeden z prvků nastavený na jedničku – pozice jedničky odpovídá číslici 0 až 9:

import torch
from torch.utils.data import Dataset
import numpy as np
 
 
# konverze původních dat z NumPy do tenzorů
class Data(Dataset):
    def __init__(self, X, y):
        self.X = torch.from_numpy(X.astype(np.float32))
        self.y = torch.from_numpy(y.astype(np.float32))
        self.len = self.X.shape[0]
 
    def __getitem__(self, index):
        return self.X[index], self.y[index]
 
    def __len__(self):
        return self.len
 
 
# číslice reprezentované v masce 8x8 pixelů
digits = (
    (0x00, 0x3C, 0x66, 0x76, 0x6E, 0x66, 0x3C, 0x00),
    (0x00, 0x18, 0x1C, 0x18, 0x18, 0x18, 0x7E, 0x00),
    (0x00, 0x3C, 0x66, 0x30, 0x18, 0x0C, 0x7E, 0x00),
    (0x00, 0x7E, 0x30, 0x18, 0x30, 0x66, 0x3C, 0x00),
    (0x00, 0x30, 0x38, 0x3C, 0x36, 0x7E, 0x30, 0x00),
    (0x00, 0x7E, 0x06, 0x3E, 0x60, 0x66, 0x3C, 0x00),
    (0x00, 0x3C, 0x06, 0x3E, 0x66, 0x66, 0x3C, 0x00),
    (0x00, 0x7E, 0x60, 0x30, 0x18, 0x0C, 0x0C, 0x00),
    (0x00, 0x3C, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00),
    (0x00, 0x3C, 0x66, 0x7C, 0x60, 0x30, 0x1C, 0x00),
)
 
 
def digit_to_array(digits, n):
    digit = digits[n]
    rows = []
    # převod jednotlivých řádků na osmici bitů
    for scanline in digit:
        row = []
        # převod bitmapy představující řádek na osmici bitů
        for _ in range(8):
            bit = scanline & 0x01
            row.append(float(bit))
            scanline >>= 1
        rows.append(row)
    # transformace na n-dimenzionální pole
    return np.array(rows)
 
 
def prepare_data(digits, length):
    # příprava dat pro trénink a testování
    X = []
    y = []
 
    for i in range(length):
        # cislice
        digit = i % 10
        # vstupy
        array = digit_to_array(digits, digit)
        # prevod na vektor
        x_vector = array.flatten()
        X.append(x_vector)
        # očekávané výstupy
        y_vector = [0.0] * 10
        y_vector[digit] = 1.0
        y.append(y_vector)
 
    return Data(np.array(X), np.array(y))
 
 
data = prepare_data(digits, 1000)
print(len(data))
 
for i in range(10):
    print(data[i])

Skript po svém spuštění připraví data pro neuronovou síť, poté vytiskne jejich počet a nakonec i obsah prvních deseti dvojic. Povšimněte si, jak se ve druhém tenzoru „posunuje“ jednička, která určuje cifru na obrázku:

1000
(tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 1., 0., 0., 0., 1.,
        1., 0., 0., 1., 1., 0., 0., 1., 1., 0., 1., 1., 1., 0., 0., 1., 1., 1.,
        0., 1., 1., 0., 0., 1., 1., 0., 0., 1., 1., 0., 0., 0., 1., 1., 1., 1.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]), tensor([1., 0., 0., 0., 0., 0., 0., 0., 0., 0.]))
(tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 0., 0., 0., 0., 0.,
        1., 1., 1., 0., 0., 0., 0., 0., 0., 1., 1., 0., 0., 0., 0., 0., 0., 1.,
        1., 0., 0., 0., 0., 0., 0., 1., 1., 0., 0., 0., 0., 1., 1., 1., 1., 1.,
        1., 0., 0., 0., 0., 0., 0., 0., 0., 0.]), tensor([0., 1., 0., 0., 0., 0., 0., 0., 0., 0.]))
(tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 1., 0., 0., 0., 1.,
        1., 0., 0., 1., 1., 0., 0., 0., 0., 0., 1., 1., 0., 0., 0., 0., 0., 1.,
        1., 0., 0., 0., 0., 0., 1., 1., 0., 0., 0., 0., 0., 1., 1., 1., 1., 1.,
        1., 0., 0., 0., 0., 0., 0., 0., 0., 0.]), tensor([0., 0., 1., 0., 0., 0., 0., 0., 0., 0.]))
        ...
        ...
        ...
(tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 1., 0., 0., 0., 1.,
        1., 0., 0., 1., 1., 0., 0., 0., 1., 1., 1., 1., 0., 0., 0., 1., 1., 0.,
        0., 1., 1., 0., 0., 1., 1., 0., 0., 1., 1., 0., 0., 0., 1., 1., 1., 1.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]), tensor([0., 0., 0., 0., 0., 0., 0., 0., 1., 0.]))
(tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 1., 0., 0., 0., 1.,
        1., 0., 0., 1., 1., 0., 0., 0., 1., 1., 1., 1., 1., 0., 0., 0., 0., 0.,
        0., 1., 1., 0., 0., 0., 0., 0., 1., 1., 0., 0., 0., 0., 1., 1., 1., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]), tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 1.]))

4. Příprava vstupních dat, která mohou být zašuměna popř. posunuta

V praxi však nebudou vstupní obrázky pro neuronovou síť ideální. Ostatně kdyby byly, stačilo by nám naprogramovat jednoduché mapování přesný_vstup→přesný_výstup. Na vstupní data tedy budeme aplikovat šum a popř. i operaci posunu obrázku ve směru horizontální a/nebo vertikální osy. Všechny potřebné pomocné funkce jsme si již vysvětlili v předchozím článku, takže jen ve stručnosti:

def add_noise(array, level):
    return (1.0 - level) * array + level * np.random.rand(8, 8)

a:

def shift(arr, x_shift, y_shift):
    # horizontální posun
    arr = np.roll(arr, x_shift, axis=1)
 
    # výplně těch částí, které byly orotovány na druhou stranu
    if x_shift < 0:
        arr[:, x_shift:] = 0.0
    elif x_shift > 0:
        arr[:, :x_shift] = 0.0
 
    # vertikální posun
    arr = np.roll(arr, y_shift, axis=0)
 
    # výplně těch částí, které byly orotovány na druhou stranu
    if y_shift < 0:
        arr[y_shift:] = 0.0
    elif y_shift > 0:
        arr[:y_shift] = 0.0
    return arr

Funkci pro přípravu dat upravíme takovým způsobem, že bude tyto dvě funkce volat a předávat jim vstupní parametry nastavené uživatelem – tedy úroveň šumu a maximální posun obrázků uvedený v pixelech:

def prepare_data(digits, length, noise_level=0.0, x_shift_amount=0, y_shift_amount=0):
    # příprava dat pro trénink a testování
    X = []
    y = []
 
    for i in range(length):
        # cislice
        digit = i % 10
        # vstupy
        array = digit_to_array(digits, digit)
        # zasumeni
        array = add_noise(array, noise_level)
        # posuny
        x_shift = random.randint(-x_shift_amount, x_shift_amount)
        y_shift = random.randint(-y_shift_amount, y_shift_amount)
        array = shift(array, x_shift, y_shift)
        # prevod na vektor
        x_vector = array.flatten()
        X.append(x_vector)
        # očekávané výstupy
        y_vector = [0.0] * 10
        y_vector[digit] = 1.0
        y.append(y_vector)
 
    return Data(np.array(X), np.array(y))

5. Druhý skript: příprava zašuměných a posunutých vstupních dat pro neuronovou síť

Opět se podívejme, jak by mohl vypadat skript, který připraví data pro trénink a testování klasické neuronové sítě s 64prvkovými vektory na vstupu a desetiprvkovými vektory na výstupu. Tentokrát však budou vstupní vektory obsahovat hodnoty, které jsou zašuměné, nebo mohou být obrázky posunuté v libovolném směru. Asi správně odhadnete, že právě posun bude kritický, protože jsme ztratili informaci o dvou rozměrech a vstupem je jen jednorozměrný vektor. Ukažme si zdrojový kód upraveného skriptu:

import random
import torch
from torch.utils.data import Dataset
import numpy as np
 
 
# konverze původních dat z NumPy do tenzorů
class Data(Dataset):
    def __init__(self, X, y):
        self.X = torch.from_numpy(X.astype(np.float32))
        self.y = torch.from_numpy(y.astype(np.float32))
        self.len = self.X.shape[0]
 
    def __getitem__(self, index):
        return self.X[index], self.y[index]
 
    def __len__(self):
        return self.len
 
 
# číslice reprezentované v masce 8x8 pixelů
digits = (
    (0x00, 0x3C, 0x66, 0x76, 0x6E, 0x66, 0x3C, 0x00),
    (0x00, 0x18, 0x1C, 0x18, 0x18, 0x18, 0x7E, 0x00),
    (0x00, 0x3C, 0x66, 0x30, 0x18, 0x0C, 0x7E, 0x00),
    (0x00, 0x7E, 0x30, 0x18, 0x30, 0x66, 0x3C, 0x00),
    (0x00, 0x30, 0x38, 0x3C, 0x36, 0x7E, 0x30, 0x00),
    (0x00, 0x7E, 0x06, 0x3E, 0x60, 0x66, 0x3C, 0x00),
    (0x00, 0x3C, 0x06, 0x3E, 0x66, 0x66, 0x3C, 0x00),
    (0x00, 0x7E, 0x60, 0x30, 0x18, 0x0C, 0x0C, 0x00),
    (0x00, 0x3C, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00),
    (0x00, 0x3C, 0x66, 0x7C, 0x60, 0x30, 0x1C, 0x00),
)
 
 
def digit_to_array(digits, n):
    digit = digits[n]
    rows = []
    # převod jednotlivých řádků na osmici bitů
    for scanline in digit:
        row = []
        # převod bitmapy představující řádek na osmici bitů
        for _ in range(8):
            bit = scanline & 0x01
            row.append(float(bit))
            scanline >>= 1
        rows.append(row)
    # transformace na n-dimenzionální pole
    return np.array(rows)
 
 
def add_noise(array, level):
    return (1.0 - level) * array + level * np.random.rand(8, 8)
 
 
def shift(arr, x_shift, y_shift):
    # horizontální posun
    arr = np.roll(arr, x_shift, axis=1)
 
    # výplně těch částí, které byly orotovány na druhou stranu
    if x_shift < 0:
        arr[:, x_shift:] = 0.0
    elif x_shift > 0:
        arr[:, :x_shift] = 0.0
 
    # vertikální posun
    arr = np.roll(arr, y_shift, axis=0)
 
    # výplně těch částí, které byly orotovány na druhou stranu
    if y_shift < 0:
        arr[y_shift:] = 0.0
    elif y_shift > 0:
        arr[:y_shift] = 0.0
    return arr
 
 
def prepare_data(digits, length, noise_level=0.0, x_shift_amount=0, y_shift_amount=0):
    # příprava dat pro trénink a testování
    X = []
    y = []
 
    for i in range(length):
        # cislice
        digit = i % 10
        # vstupy
        array = digit_to_array(digits, digit)
        # zasumeni
        array = add_noise(array, noise_level)
        # posuny
        x_shift = random.randint(-x_shift_amount, x_shift_amount)
        y_shift = random.randint(-y_shift_amount, y_shift_amount)
        array = shift(array, x_shift, y_shift)
        # prevod na vektor
        x_vector = array.flatten()
        X.append(x_vector)
        # očekávané výstupy
        y_vector = [0.0] * 10
        y_vector[digit] = 1.0
        y.append(y_vector)
 
    return Data(np.array(X), np.array(y))
 
 
data = prepare_data(digits, 1000, noise_level=0.0, x_shift_amount=2, y_shift_amount=2)
print(len(data))
 
for i in range(10):
    print(data[i])

Po spuštění skriptu se opět zobrazí počet prvků v trénovací/testovací sadě a posléze hodnoty prvních deseti prvků. Nyní je patrné, že první vektor už neobsahuje pouze hodnoty 0,0 a 1,0, ale více či méně odlišné hodnoty:

1000
(tensor([0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
        0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0991, 0.1470,
        0.0453, 0.1430, 0.0893, 0.0821, 0.0000, 0.0000, 0.9793, 0.8072, 0.9616,
        0.8820, 0.0053, 0.0436, 0.0000, 0.0000, 0.9762, 0.0246, 0.0278, 0.8587,
        0.9291, 0.0374, 0.0000, 0.0000, 0.9698, 0.0695, 0.8725, 0.8903, 0.9781,
        0.0564, 0.0000, 0.0000, 0.9405, 0.9431, 0.1012, 0.9844, 0.9339, 0.1723,
        0.0000, 0.0000, 0.8720, 0.1137, 0.1474, 0.8364, 0.8673, 0.0288, 0.0000,
        0.0000]), tensor([1., 0., 0., 0., 0., 0., 0., 0., 0., 0.]))
(tensor([0.1282, 0.1286, 0.9159, 0.9999, 0.0604, 0.0830, 0.1627, 0.0000, 0.0997,
        0.8669, 0.8799, 0.9667, 0.1710, 0.0166, 0.1434, 0.0000, 0.1858, 0.0872,
        0.9059, 0.8710, 0.1191, 0.1385, 0.0505, 0.0000, 0.0617, 0.0100, 0.9928,
        0.9849, 0.1692, 0.1496, 0.0553, 0.0000, 0.0863, 0.0691, 0.9267, 0.8241,
        0.1422, 0.1098, 0.0381, 0.0000, 0.8753, 0.8869, 0.9612, 0.8800, 0.9099,
        0.8483, 0.0810, 0.0000, 0.1768, 0.1332, 0.1881, 0.1756, 0.0358, 0.1965,
        0.1333, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
        0.0000]), tensor([0., 1., 0., 0., 0., 0., 0., 0., 0., 0.]))
        ...
        ...
        ...
(tensor([0.0000, 0.0000, 0.0733, 0.1337, 0.9931, 0.9026, 0.8485, 0.9472, 0.0000,
        0.0000, 0.1810, 0.8931, 0.8541, 0.0940, 0.1297, 0.1427, 0.0000, 0.0000,
        0.1165, 0.9050, 0.8881, 0.8289, 0.9612, 0.9271, 0.0000, 0.0000, 0.0582,
        0.8934, 0.9511, 0.1421, 0.0801, 0.8987, 0.0000, 0.0000, 0.1779, 0.8515,
        0.8020, 0.1619, 0.0649, 0.8573, 0.0000, 0.0000, 0.1445, 0.1342, 0.9168,
        0.8039, 0.9018, 0.9114, 0.0000, 0.0000, 0.1518, 0.0960, 0.0952, 0.0685,
        0.1568, 0.1162, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
        0.0000]), tensor([0., 0., 0., 0., 0., 0., 1., 0., 0., 0.]))
(tensor([0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0170,
        0.1181, 0.1175, 0.0079, 0.0321, 0.1954, 0.0000, 0.0000, 0.8095, 0.9161,
        0.9373, 0.9020, 0.1813, 0.1592, 0.0000, 0.0000, 0.9597, 0.1631, 0.1652,
        0.8400, 0.9894, 0.1514, 0.0000, 0.0000, 0.8719, 0.8249, 0.9083, 0.8364,
        0.1119, 0.0627, 0.0000, 0.0000, 0.8413, 0.0157, 0.0148, 0.8373, 0.9239,
        0.1173, 0.0000, 0.0000, 0.9013, 0.0727, 0.0251, 0.8195, 0.8519, 0.0888,
        0.0000, 0.0000, 0.8526, 0.8314, 0.8749, 0.9900, 0.1544, 0.1514, 0.0000,
        0.0000]), tensor([0., 0., 0., 0., 0., 0., 0., 0., 1., 0.]))
(tensor([0.0936, 0.9051, 0.9126, 0.1106, 0.0446, 0.8483, 0.9484, 0.1938, 0.1982,
        0.0617, 0.9297, 0.8475, 0.8500, 0.9321, 0.9868, 0.1909, 0.1818, 0.0986,
        0.0904, 0.1327, 0.0302, 0.8697, 0.8073, 0.0833, 0.0850, 0.1306, 0.0666,
        0.1750, 0.8532, 0.8485, 0.0680, 0.0881, 0.0256, 0.0323, 0.9974, 0.9051,
        0.9478, 0.1259, 0.0662, 0.0194, 0.0463, 0.1651, 0.0389, 0.0865, 0.0547,
        0.1028, 0.0711, 0.1404, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
        0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
        0.0000]), tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 1.]))

6. Náhodné rozdělení datové sady funkcí train_test_split

Pro rozdělení dat, která byla získána postupem popsaným v předchozích kapitolách (dvojice 64prvkový vektor+desetiprvkový vektor) na data trénovací a validační (resp. testovací), použijeme funkci train_test_split. Tato funkce ve svém prvním parametru akceptuje přímo datovou sadu (nemusíme se tedy snažit o ruční získání naměřených dat a očekávaných výsledků), dále velikost testovacích a trénovacích dat (buď jako celé číslo, což je počet záznamů nebo hodnotu typu float, což bude zlomek od 0 do 1, nepovinnou hodnotu, která zamezí různým výsledkům pro několik volání této funkce a dále parametr povolující zamíchání dat (ve výchozím nastavení je povolen, což nám opět vyhovuje):

train_test_split(*arrays, test_size=None, train_size=None, random_state=None, shuffle=True, stratify=None)
    Split arrays or matrices into random train and test subsets.
 
    Quick utility that wraps input validation,
    ``next(ShuffleSplit().split(X, y))``, and application to input data
    into a single call for splitting (and optionally subsampling) data into a
    one-liner.
 
    Read more in the :ref:`User Guide <cross_validation>`.
 
    Parameters
    ----------
    *arrays : sequence of indexables with same length / shape[0]
        Allowed inputs are lists, numpy arrays, scipy-sparse
        matrices or pandas dataframes.
 
    test_size : float or int, default=None
        If float, should be between 0.0 and 1.0 and represent the proportion
        of the dataset to include in the test split. If int, represents the
        absolute number of test samples. If None, the value is set to the
        complement of the train size. If ``train_size`` is also None, it will
        be set to 0.25.
 
    train_size : float or int, default=None
        If float, should be between 0.0 and 1.0 and represent the
        proportion of the dataset to include in the train split. If
        int, represents the absolute number of train samples. If None,
        the value is automatically set to the complement of the test size.
 
    random_state : int, RandomState instance or None, default=None
        Controls the shuffling applied to the data before applying the split.
        Pass an int for reproducible output across multiple function calls.
        See :term:`Glossary <random_state>`.
 
    shuffle : bool, default=True
        Whether or not to shuffle the data before splitting. If shuffle=False
        then stratify must be None.
 
    stratify : array-like, default=None
        If not None, data is split in a stratified fashion, using this as
        the class labels.
        Read more in the :ref:`User Guide <stratification>`.

7. Realizace rozdělení datové sady na trénovací a testovací data

Nyní si funkci pro přípravu dat, s nimiž budeme při konstrukci, tréninku a validaci neuronové sítě pracovat, rozšíříme o volání funkce train_test_split. Vstupem této funkce jsou hodnoty X a y převedené ze seznamů na n-rozměrná pole typu np.array (ale vlastně už nyní by bylo možné použít tenzory). Výsledkem je čtveřice n-rozměrných polí X_train, X_test, y_train a y_test, která jsou použita pro konstrukci dvou objektů typu Data. Tím máme přípravné práce za sebou:

def prepare_data(digits, length, noise_level=0.0, x_shift_amount=0, y_shift_amount=0, test_size=1/2):
    # příprava dat pro trénink a testování
    X = []
    y = []
 
    for i in range(length):
        # cislice
        digit = i % 10
        # vstupy
        array = digit_to_array(digits, digit)
        # zasumeni
        array = add_noise(array, noise_level)
        # posuny
        x_shift = random.randint(-x_shift_amount, x_shift_amount)
        y_shift = random.randint(-y_shift_amount, y_shift_amount)
        array = shift(array, x_shift, y_shift)
        # prevod na vektor
        x_vector = array.flatten()
        X.append(x_vector)
        # očekávané výstupy
        y_vector = [0.0] * 10
        y_vector[digit] = 1.0
        y.append(y_vector)
 
    X_train, X_test, y_train, y_test = train_test_split(
        np.array(X), np.array(y),
        test_size=test_size, random_state=26
    )
 
    # trénovací sada
    train_data = Data(X_train, y_train)
 
    # testovací sada
    test_data = Data(X_test, y_test)
 
    return train_data, test_data

8. Třetí skript: rozdělení datové sady na trénovací a testovací data

Celý skript, který připraví data pro neuronovou síť na základě bitmap s číslicemi, se „natáhl“ na délku přibližně tří kilobajtů, ovšem stále by mělo být zřejmé, jaké operace se v něm provádí a proč:

import random
from sklearn.model_selection import train_test_split
import torch
from torch.utils.data import Dataset
import numpy as np
 
 
# konverze původních dat z NumPy do tenzorů
class Data(Dataset):
    def __init__(self, X, y):
        self.X = torch.from_numpy(X.astype(np.float32))
        self.y = torch.from_numpy(y.astype(np.float32))
        self.len = self.X.shape[0]
 
    def __getitem__(self, index):
        return self.X[index], self.y[index]
 
    def __len__(self):
        return self.len
 
 
# číslice reprezentované v masce 8x8 pixelů
digits = (
    (0x00, 0x3C, 0x66, 0x76, 0x6E, 0x66, 0x3C, 0x00),
    (0x00, 0x18, 0x1C, 0x18, 0x18, 0x18, 0x7E, 0x00),
    (0x00, 0x3C, 0x66, 0x30, 0x18, 0x0C, 0x7E, 0x00),
    (0x00, 0x7E, 0x30, 0x18, 0x30, 0x66, 0x3C, 0x00),
    (0x00, 0x30, 0x38, 0x3C, 0x36, 0x7E, 0x30, 0x00),
    (0x00, 0x7E, 0x06, 0x3E, 0x60, 0x66, 0x3C, 0x00),
    (0x00, 0x3C, 0x06, 0x3E, 0x66, 0x66, 0x3C, 0x00),
    (0x00, 0x7E, 0x60, 0x30, 0x18, 0x0C, 0x0C, 0x00),
    (0x00, 0x3C, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00),
    (0x00, 0x3C, 0x66, 0x7C, 0x60, 0x30, 0x1C, 0x00),
)
 
 
def digit_to_array(digits, n):
    digit = digits[n]
    rows = []
    # převod jednotlivých řádků na osmici bitů
    for scanline in digit:
        row = []
        # převod bitmapy představující řádek na osmici bitů
        for _ in range(8):
            bit = scanline & 0x01
            row.append(float(bit))
            scanline >>= 1
        rows.append(row)
    # transformace na n-dimenzionální pole
    return np.array(rows)
 
 
def add_noise(array, level):
    return (1.0 - level) * array + level * np.random.rand(8, 8)
 
 
def shift(arr, x_shift, y_shift):
    # horizontální posun
    arr = np.roll(arr, x_shift, axis=1)
 
    # výplně těch částí, které byly orotovány na druhou stranu
    if x_shift < 0:
        arr[:, x_shift:] = 0.0
    elif x_shift > 0:
        arr[:, :x_shift] = 0.0
 
    # vertikální posun
    arr = np.roll(arr, y_shift, axis=0)
 
    # výplně těch částí, které byly orotovány na druhou stranu
    if y_shift < 0:
        arr[y_shift:] = 0.0
    elif y_shift > 0:
        arr[:y_shift] = 0.0
    return arr
 
 
def prepare_data(digits, length, noise_level=0.0, x_shift_amount=0, y_shift_amount=0, test_size=1/2):
    # příprava dat pro trénink a testování
    X = []
    y = []
 
    for i in range(length):
        # cislice
        digit = i % 10
        # vstupy
        array = digit_to_array(digits, digit)
        # zasumeni
        array = add_noise(array, noise_level)
        # posuny
        x_shift = random.randint(-x_shift_amount, x_shift_amount)
        y_shift = random.randint(-y_shift_amount, y_shift_amount)
        array = shift(array, x_shift, y_shift)
        # prevod na vektor
        x_vector = array.flatten()
        X.append(x_vector)
        # očekávané výstupy
        y_vector = [0.0] * 10
        y_vector[digit] = 1.0
        y.append(y_vector)
 
    X_train, X_test, y_train, y_test = train_test_split(
        np.array(X), np.array(y),
        test_size=test_size, random_state=26
    )
 
    # trénovací sada
    train_data = Data(X_train, y_train)
 
    # testovací sada
    test_data = Data(X_test, y_test)
 
    return train_data, test_data
 
 
train, test = prepare_data(digits, 1000, noise_level=0.0, x_shift_amount=2, y_shift_amount=2)
print(len(train))
print(len(test))
 
for i in range(10):
    print(train[i])

Z prvních dvou vypsaných řádků je patrné, že jak trénovací data, tak i data pro otestování, mají shodně 500 prvků. Ovšem tyto prvky se pochopitelně od sebe odlišují:

500
500

Skript navíc vypíše prvních deset prvků trénovacích dat:

(tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        1., 1., 1., 1., 1., 1., 0., 0., 0., 0., 0., 1., 1., 0., 0., 0., 0., 0.,
        1., 1., 0., 0., 0., 0., 0., 0., 0., 1., 1., 0., 0., 0., 1., 1., 0., 0.,
        1., 1., 0., 0., 0., 1., 1., 1., 1., 0.]), tensor([0., 0., 0., 1., 0., 0., 0., 0., 0., 0.]))
(tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 1., 1., 1., 1., 0., 0., 0., 1., 1., 0., 0., 1., 1., 0., 0., 0., 0.,
        0., 1., 1., 0., 0., 0., 0., 0., 1., 1., 0., 0., 0., 0., 0., 1., 1., 0.,
        0., 0., 0., 0., 1., 1., 1., 1., 1., 1.]), tensor([0., 0., 1., 0., 0., 0., 0., 0., 0., 0.]))
(tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 1., 1., 1., 1., 0., 0., 0., 1., 1., 0., 0., 1., 1., 0., 0., 0., 1.,
        1., 1., 1., 1., 0., 0., 0., 0., 0., 0., 1., 1., 0., 0., 0., 0., 0., 1.,
        1., 0., 0., 0., 0., 1., 1., 1., 0., 0.]), tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 1.]))
...
...
...
(tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 1., 1., 1., 1., 0., 0., 0., 1., 1., 0., 0., 1., 1., 0., 0., 0., 0.,
        0., 1., 1., 0., 0., 0., 0., 0., 1., 1., 0., 0., 0., 0., 0., 1., 1., 0.,
        0., 0., 0., 0., 1., 1., 1., 1., 1., 1.]), tensor([0., 0., 1., 0., 0., 0., 0., 0., 0., 0.]))
(tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 1., 1., 1., 1., 0., 0., 0., 1., 1., 0., 0., 1., 1., 0., 0., 1., 1.,
        0., 1., 1., 1., 0., 0., 1., 1., 1., 0., 1., 1., 0., 0., 1., 1., 0., 0.,
        1., 1., 0., 0., 0., 1., 1., 1., 1., 0.]), tensor([1., 0., 0., 0., 0., 0., 0., 0., 0., 0.]))
(tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        1., 1., 1., 1., 1., 1., 0., 0., 0., 0., 0., 1., 1., 0., 0., 0., 0., 0.,
        1., 1., 0., 0., 0., 0., 0., 0., 0., 1., 1., 0., 0., 0., 1., 1., 0., 0.,
        1., 1., 0., 0., 0., 1., 1., 1., 1., 0.]), tensor([0., 0., 0., 1., 0., 0., 0., 0., 0., 0.]))

9. Konstrukce neuronové sítě s vektorovým vstupem i výstupem

V dalším kroku (konečně) zkonstruujeme neuronovou síť, na jejíž vstup budeme posílat 64prvkové vektory s hodnotami zašuměných a posunutých obrázků s číslicemi a na jejímž výstupu bude desetiprvkový vektor, jehož nejvyšší prvek určí index číslice rozeznané touto sítí. Pro jednoduchost a pro rychlé učení bude síť obsahovat pouze jednu skrytou vrstvu. Jako aktivační funkci mezi vstupy a skrytou vrstvou vybereme ReLU a pro dvojici vrstev naopak sigmoid (později tento výběr můžeme kdykoli modifikovat):

class NeuralNetwork(nn.Module):
    """Třída reprezentující neuronovou síť."""
 
    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)
 
    def forward(self, x):
        # propagace hodnot přes neuronovou síť
        x = torch.nn.functional.relu(self.layer_1(x))
        x = torch.nn.functional.sigmoid(self.layer_2(x))
        return x

Vstupem jsou 64prvkové vektory, výstupem desetiprvkový vektor. Tím jsou přímo určeny počty neuronů na vstupu i neuronů ve výstupní vrstvě. Skrytá vrstva může mít libovolný počet neuronů. Zvolme hodnotu 10. Ta není ani velká (problém nedoučení) ani malá (problém „hloupé“ sítě s malým množstvím stavů):

# konfigurace vrstev neuronové sítě
input_dim = 64
hidden_dim = 10
output_dim = 10
 
nn_64_10_10 = NeuralNetwork(input_dim, hidden_dim, output_dim)
 
# výpis základních informací o neuronové síti
print("Neural network:")
print(nn_64_10_10)

Skript v této zkrácené podobě by měl vypsat strukturu neuronové sítě:

Neural network:
NeuralNetwork(
  (layer_1): Linear(in_features=64, out_features=10, bias=True)
  (layer_2): Linear(in_features=10, out_features=10, bias=True)
)

Pokud vás mate, že skrytá vrstva má stejný počet neuronů, jako vrstva výstupní, můžeme skrytou vrstvu rozšířit na 99 neuronů:

Neural network:
NeuralNetwork(
  (layer_1): Linear(in_features=64, out_features=99, bias=True)
  (layer_2): Linear(in_features=99, out_features=10, bias=True)
)

10. Trénink neuronové sítě

Neuronovou síť máme nyní zkonstruovanou, ovšem váhy na vstupech neuronů jsou prozatím nastaveny na náhodnou hodnotu. Síť je tedy nutné natrénovat. Pro tento účel si necháme vygenerovat trénovací a testovací data, což není nic nového, protože jsme se s tímto konceptem seznámili v šesté a sedmé kapitole:

train_data, test_data = prepare_data(digits, 1000, noise_level=0.0, x_shift_amount=0, y_shift_amount=0)
print("Train data:")
print(len(train_data))
print("Test data:")
print(len(test_data))

Povšimněte si, že si sice necháme vygenerovat 1000 záznamů, ale vzhledem k tomu, že úroveň šumu je nulová a obrázky se neposunují, vlastně bude datová sada obsahovat každý záznam ve 100 identických kopiích:

Train data:
666
Test data:
334

Samotný trénink neuronové sítě taktéž není nová metoda. Na vstup sítě předáme jeden vektor vybraný z trénovacích dat, na výstup očekávaný výstup (taktéž vektor) a zpětným šířením chyby se poupraví váhy na vstupech jednotlivých neuronů:

# příprava na trénink neuronové sítě
learning_rate = 0.1
loss_fn = nn.BCELoss()
 
optimizer = optim.SGD(nn_64_10_10.parameters(), lr=learning_rate)
 
# zpracovat trénovací data
batch_size = 64
train_dataloader = DataLoader(dataset=train_data, batch_size=batch_size, shuffle=True)
print("Batches: ", len(train_dataloader))
 
# vlastní trénink
print("Training started")
num_epochs = 100
loss_values = []
for epoch in range(num_epochs):
    print(f"    Epoch {epoch}: ", end="")
    last_lost_value = None
    for X, y in train_dataloader:
        optimizer.zero_grad()
 
        # dopředný tok + zpětný tok + optimalizace
        pred = nn_64_10_10(X)
 
        # výpočet účelové funkce
        loss = loss_fn(pred, y)
 
        #loss = loss_fn(pred, y.unsqueeze(-1))
        loss_values.append(loss.item())
        loss.backward()
        optimizer.step()
        last_lost_value = loss.item()
        print(".", end="")
    print(last_lost_value)
 
print("Training completed")

V průběhu učení by se postupně měla snižovat hodnota vypočtená účelovou funkcí. V ideálním případě by měla klesnout k nule:

Batches:  11
Training started
    Epoch 0: ...........0.6447266936302185
    Epoch 1: ...........0.6193460822105408
    Epoch 2: ...........0.587082028388977
    Epoch 3: ...........0.5465092658996582
    Epoch 4: ...........0.4956221878528595
    Epoch 5: ...........0.44743767380714417
    Epoch 6: ...........0.38974180817604065
    Epoch 7: ...........0.3594760000705719
    Epoch 8: ...........0.3338333070278168
    Epoch 9: ...........0.3175426423549652
    Epoch 10: ...........0.32316410541534424
    Epoch 11: ...........0.32127857208251953
    Epoch 12: ...........0.3148137927055359
    Epoch 13: ...........0.3114672899246216
    Epoch 14: ...........0.316869854927063
    Epoch 15: ...........0.3137578070163727
    Epoch 16: ...........0.3157559633255005
    Epoch 17: ...........0.3102682828903198
    Epoch 18: ...........0.3075677454471588
    Epoch 19: ...........0.30908656120300293
    Epoch 20: ...........0.3039756715297699
    Epoch 21: ...........0.3026908338069916
    Epoch 22: ...........0.30455878376960754
    Epoch 23: ...........0.30317121744155884
    Epoch 24: ...........0.29968011379241943
    Epoch 25: ...........0.2984805703163147
    Epoch 26: ...........0.2998690903186798
    Epoch 27: ...........0.2988283932209015
    Epoch 28: ...........0.28817668557167053
    Epoch 29: ...........0.2917144298553467
    Epoch 30: ...........0.2874159812927246
    Epoch 31: ...........0.2909078598022461
    Epoch 32: ...........0.27771422266960144
    Epoch 33: ...........0.2821337580680847
    Epoch 34: ...........0.28642821311950684
    Epoch 35: ...........0.276723176240921
    Epoch 36: ...........0.2759454548358917
    Epoch 37: ...........0.27059292793273926
    Epoch 38: ...........0.2683849036693573
    Epoch 39: ...........0.268013060092926
    Epoch 40: ...........0.2737174928188324
    Epoch 41: ...........0.2644055187702179
    Epoch 42: ...........0.26093563437461853
    Epoch 43: ...........0.2565652132034302
    Epoch 44: ...........0.25182580947875977
    Epoch 45: ...........0.2551380693912506
    Epoch 46: ...........0.2450740933418274
    Epoch 47: ...........0.24896760284900665
    Epoch 48: ...........0.24437251687049866
    Epoch 49: ...........0.24140697717666626
    Epoch 50: ...........0.23914998769760132
    Epoch 51: ...........0.23454098403453827
    Epoch 52: ...........0.23361629247665405
    Epoch 53: ...........0.23802918195724487
    Epoch 54: ...........0.223032146692276
    Epoch 55: ...........0.2397080361843109
    Epoch 56: ...........0.21921715140342712
    Epoch 57: ...........0.21958164870738983
    Epoch 58: ...........0.22020205855369568
    Epoch 59: ...........0.2035556137561798
    Epoch 60: ...........0.21375465393066406
    Epoch 61: ...........0.1982254534959793
    Epoch 62: ...........0.19214391708374023
    Epoch 63: ...........0.2059931755065918
    Epoch 64: ...........0.1918933093547821
    Epoch 65: ...........0.20608077943325043
    Epoch 66: ...........0.19234557449817657
    Epoch 67: ...........0.1903819739818573
    Epoch 68: ...........0.1945323795080185
    Epoch 69: ...........0.1832999587059021
    Epoch 70: ...........0.18331988155841827
    Epoch 71: ...........0.1699090152978897
    Epoch 72: ...........0.1823926419019699
    Epoch 73: ...........0.17585024237632751
    Epoch 74: ...........0.16324113309383392
    Epoch 75: ...........0.17733824253082275
    Epoch 76: ...........0.17202644050121307
    Epoch 77: ...........0.16952469944953918
    Epoch 78: ...........0.15982866287231445
    Epoch 79: ...........0.16450724005699158
    Epoch 80: ...........0.15805110335350037
    Epoch 81: ...........0.16729946434497833
    Epoch 82: ...........0.14237038791179657
    Epoch 83: ...........0.16939890384674072
    Epoch 84: ...........0.15275999903678894
    Epoch 85: ...........0.14588870108127594
    Epoch 86: ...........0.15782424807548523
    Epoch 87: ...........0.15238074958324432
PyTorch: neuronové sítě

Obrázek 4: Postupně klesající hodnoty vypočítané účelovou funkcí naznačují korektní učení sítě – ovšem ne kvalitu jejich predikcí!

11. Čtvrtý skript: trénink neuronové sítě pro rozpoznávání rastrových obrázků

Postup popsaný v předchozích pěti kapitolách byl použit v dnešním čtvrtém skriptu, který vypadá takto:

import random
from sklearn.model_selection import train_test_split
import torch
from torch import nn
from torch import optim
from torch.utils.data import Dataset, DataLoader
import numpy as np
import matplotlib.pyplot as plt
 
 
class NeuralNetwork(nn.Module):
    """Třída reprezentující neuronovou síť."""
 
    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)
 
    def forward(self, x):
        # propagace hodnot přes neuronovou síť
        x = torch.nn.functional.relu(self.layer_1(x))
        x = torch.nn.functional.sigmoid(self.layer_2(x))
        return x
 
 
# konfigurace vrstev neuronové sítě
input_dim = 64
hidden_dim = 10
output_dim = 10
 
nn_64_10_10 = NeuralNetwork(input_dim, hidden_dim, output_dim)
 
# výpis základních informací o neuronové síti
print("Neural network:")
print(nn_64_10_10)
 
 
# konverze původních dat z NumPy do tenzorů
class Data(Dataset):
    def __init__(self, X, y):
        self.X = torch.from_numpy(X.astype(np.float32))
        self.y = torch.from_numpy(y.astype(np.float32))
        self.len = self.X.shape[0]
 
    def __getitem__(self, index):
        return self.X[index], self.y[index]
 
    def __len__(self):
        return self.len
 
 
# číslice reprezentované v masce 8x8 pixelů
digits = (
    (0x00, 0x3C, 0x66, 0x76, 0x6E, 0x66, 0x3C, 0x00),
    (0x00, 0x18, 0x1C, 0x18, 0x18, 0x18, 0x7E, 0x00),
    (0x00, 0x3C, 0x66, 0x30, 0x18, 0x0C, 0x7E, 0x00),
    (0x00, 0x7E, 0x30, 0x18, 0x30, 0x66, 0x3C, 0x00),
    (0x00, 0x30, 0x38, 0x3C, 0x36, 0x7E, 0x30, 0x00),
    (0x00, 0x7E, 0x06, 0x3E, 0x60, 0x66, 0x3C, 0x00),
    (0x00, 0x3C, 0x06, 0x3E, 0x66, 0x66, 0x3C, 0x00),
    (0x00, 0x7E, 0x60, 0x30, 0x18, 0x0C, 0x0C, 0x00),
    (0x00, 0x3C, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00),
    (0x00, 0x3C, 0x66, 0x7C, 0x60, 0x30, 0x1C, 0x00),
)
 
 
def digit_to_array(digits, n):
    digit = digits[n]
    rows = []
    # převod jednotlivých řádků na osmici bitů
    for scanline in digit:
        row = []
        # převod bitmapy představující řádek na osmici bitů
        for _ in range(8):
            bit = scanline & 0x01
            row.append(float(bit))
            scanline >>= 1
        rows.append(row)
    # transformace na n-dimenzionální pole
    return np.array(rows)
 
 
def add_noise(array, level):
    return (1.0 - level) * array + level * np.random.rand(8, 8)
 
 
def shift(arr, x_shift, y_shift):
    # horizontální posun
    arr = np.roll(arr, x_shift, axis=1)
 
    # výplně těch částí, které byly orotovány na druhou stranu
    if x_shift < 0:
        arr[:, x_shift:] = 0.0
    elif x_shift > 0:
        arr[:, :x_shift] = 0.0
 
    # vertikální posun
    arr = np.roll(arr, y_shift, axis=0)
 
    # výplně těch částí, které byly orotovány na druhou stranu
    if y_shift < 0:
        arr[y_shift:] = 0.0
    elif y_shift > 0:
        arr[:y_shift] = 0.0
    return arr
 
 
def prepare_data(digits, length, noise_level=0.0, x_shift_amount=0, y_shift_amount=0, test_size=1/3):
    # příprava dat pro trénink a testování
    X = []
    y = []
 
    for i in range(length):
        # cislice
        digit = i % 10
        # vstupy
        array = digit_to_array(digits, digit)
        # zasumeni
        array = add_noise(array, noise_level)
        # posuny
        x_shift = random.randint(-x_shift_amount, x_shift_amount)
        y_shift = random.randint(-y_shift_amount, y_shift_amount)
        array = shift(array, x_shift, y_shift)
        # prevod na vektor
        x_vector = array.flatten()
        X.append(x_vector)
        # očekávané výstupy
        y_vector = [0.0] * 10
        y_vector[digit] = 1.0
        y.append(y_vector)
 
    X_train, X_test, y_train, y_test = train_test_split(
        np.array(X), np.array(y),
        test_size=test_size, random_state=26
    )
 
    # trénovací sada
    train_data = Data(X_train, y_train)
 
    # testovací sada
    test_data = Data(X_test, y_test)
 
    return train_data, test_data
 
 
train_data, test_data = prepare_data(digits, 1000, noise_level=0.0, x_shift_amount=0, y_shift_amount=0)
print("Train data:")
print(len(train_data))
print("Test data:")
print(len(test_data))
 
# příprava na trénink neuronové sítě
learning_rate = 0.1
loss_fn = nn.BCELoss()
 
optimizer = optim.SGD(nn_64_10_10.parameters(), lr=learning_rate)
 
# zpracovat trénovací data
batch_size = 64
train_dataloader = DataLoader(dataset=train_data, batch_size=batch_size, shuffle=True)
print("Batches: ", len(train_dataloader))
 
# vlastní trénink
print("Training started")
num_epochs = 100
loss_values = []
for epoch in range(num_epochs):
    print(f"    Epoch {epoch}: ", end="")
    last_lost_value = None
    for X, y in train_dataloader:
        optimizer.zero_grad()
 
        # dopředný tok + zpětný tok + optimalizace
        pred = nn_64_10_10(X)
 
        # výpočet účelové funkce
        loss = loss_fn(pred, y)
 
        #loss = loss_fn(pred, y.unsqueeze(-1))
        loss_values.append(loss.item())
        loss.backward()
        optimizer.step()
        last_lost_value = loss.item()
        print(".", end="")
    print(last_lost_value)
 
print("Training completed")
 
step = range(len(loss_values))
 
# příprava na vykreslení grafu
fig, ax = plt.subplots(figsize=(6.4, 4.8))
plt.plot(step, np.array(loss_values))
plt.title("Průběh tréninku neuronové sítě")
plt.xlabel("Epocha")
plt.ylabel("Chyba")
 
# uložení do souboru
plt.savefig("nn_16.png")
 
# vykreslení grafu
plt.show()

12. Zašuměné obrázky v trénovacích a testovacích datech

Naprosto stejnou neuronovou síť nyní natrénujeme s využitím zašuměných obrázků. A nutno říci, že úroveň šumu je nastavena poměrně vysoko – na 50%. To může v některých případech znamenat, že obrázky nebudou dobře rozeznatelné ani tou nejlepší neuronovou sítí pro rozpoznání obrazu – lidským mozkem:

train_data, test_data = prepare_data(digits, 1000, noise_level=0.5, x_shift_amount=0, y_shift_amount=0)

Průběh učení naznačuje, že hodnoty účelové funkce již neklesnou na hodnotu 0,15 tak, jako v příkladu předchozím:

Batches:  11
Training started
    Epoch 0: ...........0.6567670106887817
    Epoch 1: ...........0.6100022792816162
    Epoch 2: ...........0.5560295581817627
    Epoch 3: ...........0.491263747215271
    Epoch 4: ...........0.4276190996170044
    Epoch 5: ...........0.39090630412101746
    Epoch 6: ...........0.3492359519004822
    Epoch 7: ...........0.34494543075561523
    Epoch 8: ...........0.3371904492378235
    Epoch 9: ...........0.3227657973766327
    Epoch 10: ...........0.32668638229370117
    ...
    ...
    ...
    Epoch 90: ...........0.28734317421913147
    Epoch 91: ...........0.28829020261764526
    Epoch 92: ...........0.29428786039352417
    Epoch 93: ...........0.27999159693717957
    Epoch 94: ...........0.27175065875053406
    Epoch 95: ...........0.2788369059562683
    Epoch 96: ...........0.2692151367664337
    Epoch 97: ...........0.28449100255966187
    Epoch 98: ...........0.27109402418136597
    Epoch 99: ...........0.28655123710632324
Training completed

Totéž je ostatně velmi dobře patrné i ve vizualizovaném průběhu účelové funkce:

PyTorch: neuronové sítě

Obrázek 5: Postupně klesající hodnoty vypočítané účelovou funkcí naznačují korektní učení sítě. Nyní je ovšem učení pomalejší a model bude obsahovat chyby v predikci.

Vliv větší úrovně šumu (70%):

PyTorch: neuronové sítě

Obrázek 6: Postupně klesající hodnoty vypočítané účelovou funkcí naznačují korektní učení sítě.

13. Pátý skript: trénink neuronové sítě pro rozpoznávání rastrových obrázků s využitím zašuměných obrázků

Jen pro úplnost si ukažme celý skript, po jehož spuštění se neuronová síť natrénuje a následně i otestuje s využitím zašuměných obrázků:

import random
from sklearn.model_selection import train_test_split
import torch
from torch import nn
from torch import optim
from torch.utils.data import Dataset, DataLoader
import numpy as np
import matplotlib.pyplot as plt
 
 
class NeuralNetwork(nn.Module):
    """Třída reprezentující neuronovou síť."""
 
    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)
 
    def forward(self, x):
        # propagace hodnot přes neuronovou síť
        x = torch.nn.functional.relu(self.layer_1(x))
        x = torch.nn.functional.sigmoid(self.layer_2(x))
        return x
 
 
# konfigurace vrstev neuronové sítě
input_dim = 64
hidden_dim = 10
output_dim = 10
 
nn_64_10_10 = NeuralNetwork(input_dim, hidden_dim, output_dim)
 
# výpis základních informací o neuronové síti
print("Neural network:")
print(nn_64_10_10)
 
 
# konverze původních dat z NumPy do tenzorů
class Data(Dataset):
    def __init__(self, X, y):
        self.X = torch.from_numpy(X.astype(np.float32))
        self.y = torch.from_numpy(y.astype(np.float32))
        self.len = self.X.shape[0]
 
    def __getitem__(self, index):
        return self.X[index], self.y[index]
 
    def __len__(self):
        return self.len
 
 
# číslice reprezentované v masce 8x8 pixelů
digits = (
    (0x00, 0x3C, 0x66, 0x76, 0x6E, 0x66, 0x3C, 0x00),
    (0x00, 0x18, 0x1C, 0x18, 0x18, 0x18, 0x7E, 0x00),
    (0x00, 0x3C, 0x66, 0x30, 0x18, 0x0C, 0x7E, 0x00),
    (0x00, 0x7E, 0x30, 0x18, 0x30, 0x66, 0x3C, 0x00),
    (0x00, 0x30, 0x38, 0x3C, 0x36, 0x7E, 0x30, 0x00),
    (0x00, 0x7E, 0x06, 0x3E, 0x60, 0x66, 0x3C, 0x00),
    (0x00, 0x3C, 0x06, 0x3E, 0x66, 0x66, 0x3C, 0x00),
    (0x00, 0x7E, 0x60, 0x30, 0x18, 0x0C, 0x0C, 0x00),
    (0x00, 0x3C, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00),
    (0x00, 0x3C, 0x66, 0x7C, 0x60, 0x30, 0x1C, 0x00),
)
 
 
def digit_to_array(digits, n):
    digit = digits[n]
    rows = []
    # převod jednotlivých řádků na osmici bitů
    for scanline in digit:
        row = []
        # převod bitmapy představující řádek na osmici bitů
        for _ in range(8):
            bit = scanline & 0x01
            row.append(float(bit))
            scanline >>= 1
        rows.append(row)
    # transformace na n-dimenzionální pole
    return np.array(rows)
 
 
def add_noise(array, level):
    return (1.0 - level) * array + level * np.random.rand(8, 8)
 
 
def shift(arr, x_shift, y_shift):
    # horizontální posun
    arr = np.roll(arr, x_shift, axis=1)
 
    # výplně těch částí, které byly orotovány na druhou stranu
    if x_shift < 0:
        arr[:, x_shift:] = 0.0
    elif x_shift > 0:
        arr[:, :x_shift] = 0.0
 
    # vertikální posun
    arr = np.roll(arr, y_shift, axis=0)
 
    # výplně těch částí, které byly orotovány na druhou stranu
    if y_shift < 0:
        arr[y_shift:] = 0.0
    elif y_shift > 0:
        arr[:y_shift] = 0.0
    return arr
 
 
def prepare_data(digits, length, noise_level=0.0, x_shift_amount=0, y_shift_amount=0, test_size=1/3):
    # příprava dat pro trénink a testování
    X = []
    y = []
 
    for i in range(length):
        # cislice
        digit = i % 10
        # vstupy
        array = digit_to_array(digits, digit)
        # zasumeni
        array = add_noise(array, noise_level)
        # posuny
        x_shift = random.randint(-x_shift_amount, x_shift_amount)
        y_shift = random.randint(-y_shift_amount, y_shift_amount)
        array = shift(array, x_shift, y_shift)
        # prevod na vektor
        x_vector = array.flatten()
        X.append(x_vector)
        # očekávané výstupy
        y_vector = [0.0] * 10
        y_vector[digit] = 1.0
        y.append(y_vector)
 
    X_train, X_test, y_train, y_test = train_test_split(
        np.array(X), np.array(y),
        test_size=test_size, random_state=26
    )
 
    # trénovací sada
    train_data = Data(X_train, y_train)
 
    # testovací sada
    test_data = Data(X_test, y_test)
 
    return train_data, test_data
 
 
train_data, test_data = prepare_data(digits, 1000, noise_level=0.5, x_shift_amount=0, y_shift_amount=0)
print("Train data:")
print(len(train_data))
print("Test data:")
print(len(test_data))
 
# příprava na trénink neuronové sítě
learning_rate = 0.1
loss_fn = nn.BCELoss()
 
optimizer = optim.SGD(nn_64_10_10.parameters(), lr=learning_rate)
 
# zpracovat trénovací data
batch_size = 64
train_dataloader = DataLoader(dataset=train_data, batch_size=batch_size, shuffle=True)
print("Batches: ", len(train_dataloader))
 
# vlastní trénink
print("Training started")
num_epochs = 100
loss_values = []
for epoch in range(num_epochs):
    print(f"    Epoch {epoch}: ", end="")
    last_lost_value = None
    for X, y in train_dataloader:
        optimizer.zero_grad()
 
        # dopředný tok + zpětný tok + optimalizace
        pred = nn_64_10_10(X)
 
        # výpočet účelové funkce
        loss = loss_fn(pred, y)
 
        #loss = loss_fn(pred, y.unsqueeze(-1))
        loss_values.append(loss.item())
        loss.backward()
        optimizer.step()
        last_lost_value = loss.item()
        print(".", end="")
    print(last_lost_value)
 
print("Training completed")
 
step = range(len(loss_values))
 
# příprava na vykreslení grafu
fig, ax = plt.subplots(figsize=(6.4, 4.8))
plt.plot(step, np.array(loss_values))
plt.title("Průběh tréninku neuronové sítě")
plt.xlabel("Epocha")
plt.ylabel("Chyba")
 
# uložení do souboru
plt.savefig("nn_17.png")
 
# vykreslení grafu
plt.show()

14. Posunuté obrázky v trénovacích a testovacích datech

Ověřme si, jak dobře či špatně se neuronová síť dokáže naučit a predikovat posunuté obrázky. Úroveň šumu vynulujeme a povolíme posun obrázků v obou směrech, ovšem maximálně o dva pixely:

train_data, test_data = prepare_data(digits, 1000, noise_level=0.0, x_shift_amount=2, y_shift_amount=2)

Průběh učení sítě:

Training started
    Epoch 0: ...........0.662143349647522
    Epoch 1: ...........0.6225242018699646
    Epoch 2: ...........0.5437997579574585
    Epoch 3: ...........0.47505685687065125
    Epoch 4: ...........0.4151655435562134
    Epoch 5: ...........0.3567219078540802
    Epoch 6: ...........0.35883376002311707
    Epoch 7: ...........0.3546479344367981
    Epoch 8: ...........0.33377087116241455
    Epoch 9: ...........0.33360761404037476
    Epoch 10: ...........0.32562145590782166
    ...
    ...
    ...
    Epoch 70: ...........0.31016451120376587
    Epoch 71: ...........0.32512548565864563
    Epoch 72: ...........0.3208373486995697
    Epoch 73: ...........0.320453405380249
    Epoch 74: ...........0.3224222660064697
    Epoch 75: ...........0.30925461649894714
    Epoch 76: ...........0.3184351325035095
    Epoch 77: ...........0.3149249255657196
    Epoch 78: ...........0.3213757276535034

Predikce sítě se pravděpodobně ještě více zhorší:

PyTorch: neuronové sítě

Obrázek 7: Postupně klesající hodnoty vypočítané účelovou funkcí naznačují korektní učení sítě. Nyní je učení ještě pomalejší a model bude obsahovat větší chyby v predikci.

Výsledky pro náhodný posun o ± tři pixely:

PyTorch: neuronové sítě

Obrázek 8: Postupně klesající hodnoty vypočítané účelovou funkcí naznačují korektní učení sítě.

15. Šestý skript: trénink neuronové sítě pro rozpoznání posunutých obrázků

Opět si, podobně jako ve třinácté kapitole, pro úplnost ukažme celý skript, po jehož spuštění se neuronová síť natrénuje a následně i otestuje s využitím zašuměných obrázků:

import random
from sklearn.model_selection import train_test_split
import torch
from torch import nn
from torch import optim
from torch.utils.data import Dataset, DataLoader
import numpy as np
import matplotlib.pyplot as plt
 
 
class NeuralNetwork(nn.Module):
    """Třída reprezentující neuronovou síť."""
 
    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)
 
    def forward(self, x):
        # propagace hodnot přes neuronovou síť
        x = torch.nn.functional.relu(self.layer_1(x))
        x = torch.nn.functional.sigmoid(self.layer_2(x))
        return x
 
 
# konfigurace vrstev neuronové sítě
input_dim = 64
hidden_dim = 10
output_dim = 10
 
nn_64_10_10 = NeuralNetwork(input_dim, hidden_dim, output_dim)
 
# výpis základních informací o neuronové síti
print("Neural network:")
print(nn_64_10_10)
 
 
# konverze původních dat z NumPy do tenzorů
class Data(Dataset):
    def __init__(self, X, y):
        self.X = torch.from_numpy(X.astype(np.float32))
        self.y = torch.from_numpy(y.astype(np.float32))
        self.len = self.X.shape[0]
 
    def __getitem__(self, index):
        return self.X[index], self.y[index]
 
    def __len__(self):
        return self.len
 
 
# číslice reprezentované v masce 8x8 pixelů
digits = (
    (0x00, 0x3C, 0x66, 0x76, 0x6E, 0x66, 0x3C, 0x00),
    (0x00, 0x18, 0x1C, 0x18, 0x18, 0x18, 0x7E, 0x00),
    (0x00, 0x3C, 0x66, 0x30, 0x18, 0x0C, 0x7E, 0x00),
    (0x00, 0x7E, 0x30, 0x18, 0x30, 0x66, 0x3C, 0x00),
    (0x00, 0x30, 0x38, 0x3C, 0x36, 0x7E, 0x30, 0x00),
    (0x00, 0x7E, 0x06, 0x3E, 0x60, 0x66, 0x3C, 0x00),
    (0x00, 0x3C, 0x06, 0x3E, 0x66, 0x66, 0x3C, 0x00),
    (0x00, 0x7E, 0x60, 0x30, 0x18, 0x0C, 0x0C, 0x00),
    (0x00, 0x3C, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00),
    (0x00, 0x3C, 0x66, 0x7C, 0x60, 0x30, 0x1C, 0x00),
)
 
 
def digit_to_array(digits, n):
    digit = digits[n]
    rows = []
    # převod jednotlivých řádků na osmici bitů
    for scanline in digit:
        row = []
        # převod bitmapy představující řádek na osmici bitů
        for _ in range(8):
            bit = scanline & 0x01
            row.append(float(bit))
            scanline >>= 1
        rows.append(row)
    # transformace na n-dimenzionální pole
    return np.array(rows)
 
 
def add_noise(array, level):
    return (1.0 - level) * array + level * np.random.rand(8, 8)
 
 
def shift(arr, x_shift, y_shift):
    # horizontální posun
    arr = np.roll(arr, x_shift, axis=1)
 
    # výplně těch částí, které byly orotovány na druhou stranu
    if x_shift < 0:
        arr[:, x_shift:] = 0.0
    elif x_shift > 0:
        arr[:, :x_shift] = 0.0
 
    # vertikální posun
    arr = np.roll(arr, y_shift, axis=0)
 
    # výplně těch částí, které byly orotovány na druhou stranu
    if y_shift < 0:
        arr[y_shift:] = 0.0
    elif y_shift > 0:
        arr[:y_shift] = 0.0
    return arr
 
 
def prepare_data(digits, length, noise_level=0.0, x_shift_amount=0, y_shift_amount=0, test_size=1/3):
    # příprava dat pro trénink a testování
    X = []
    y = []
 
    for i in range(length):
        # cislice
        digit = i % 10
        # vstupy
        array = digit_to_array(digits, digit)
        # zasumeni
        array = add_noise(array, noise_level)
        # posuny
        x_shift = random.randint(-x_shift_amount, x_shift_amount)
        y_shift = random.randint(-y_shift_amount, y_shift_amount)
        array = shift(array, x_shift, y_shift)
        # prevod na vektor
        x_vector = array.flatten()
        X.append(x_vector)
        # očekávané výstupy
        y_vector = [0.0] * 10
        y_vector[digit] = 1.0
        y.append(y_vector)
 
    X_train, X_test, y_train, y_test = train_test_split(
        np.array(X), np.array(y),
        test_size=test_size, random_state=26
    )
 
    # trénovací sada
    train_data = Data(X_train, y_train)
 
    # testovací sada
    test_data = Data(X_test, y_test)
 
    return train_data, test_data
 
 
train_data, test_data = prepare_data(digits, 1000, noise_level=0.0, x_shift_amount=2, y_shift_amount=2)
print("Train data:")
print(len(train_data))
print("Test data:")
print(len(test_data))
 
# příprava na trénink neuronové sítě
learning_rate = 0.1
loss_fn = nn.BCELoss()
 
optimizer = optim.SGD(nn_64_10_10.parameters(), lr=learning_rate)
 
# zpracovat trénovací data
batch_size = 64
train_dataloader = DataLoader(dataset=train_data, batch_size=batch_size, shuffle=True)
print("Batches: ", len(train_dataloader))
 
# vlastní trénink
print("Training started")
num_epochs = 100
loss_values = []
for epoch in range(num_epochs):
    print(f"    Epoch {epoch}: ", end="")
    last_lost_value = None
    for X, y in train_dataloader:
        optimizer.zero_grad()
 
        # dopředný tok + zpětný tok + optimalizace
        pred = nn_64_10_10(X)
 
        # výpočet účelové funkce
        loss = loss_fn(pred, y)
 
        #loss = loss_fn(pred, y.unsqueeze(-1))
        loss_values.append(loss.item())
        loss.backward()
        optimizer.step()
        last_lost_value = loss.item()
        print(".", end="")
    print(last_lost_value)
 
print("Training completed")
 
step = range(len(loss_values))
 
# příprava na vykreslení grafu
fig, ax = plt.subplots(figsize=(6.4, 4.8))
plt.plot(step, np.array(loss_values))
plt.title("Průběh tréninku neuronové sítě")
plt.xlabel("Epocha")
plt.ylabel("Chyba")
 
# uložení do souboru
plt.savefig("nn_18.png")
 
# vykreslení grafu
plt.show()

16. Matice záměn pro síť, která odpovídá vždy korektně

To, že účelová funkce klesá k nule, sice znamená, že se síť učí (a to bez velkých problémů), ovšem její skutečné vlastnosti otestujeme jedině tak, že síti předáme pro ni neznámá testovací data, tedy v našem případě takové obrázky, která síť předtím „neviděla“. Tato testovací data již máme připravena v proměnné test_data, takže test sítě můžeme velmi snadno provést následujícím kódem. Povšimněte si, že se zde volá funkce torch.topk, která vrátí indexy k největších prvků v tenzoru. V našem případě potřebujeme index největšího prvku, protože síť vrací desetiprvkový vektor, jehož hodnoty říkají, do jaké míry se obrázek na vstupu podobá jednotlivým číslicím. Například pokud tento vektor bude roven [0.0, 0.9, 0.4, 0.3, …], velmi pravděpodobně je na vstupu obrázek číslice 1. Do seznamu y_pred se ukládají číslice predikované sítí, do seznamu y_test pak správné hodnoty:

# otestování neuronové sítě
test_dataloader = DataLoader(dataset=test_data, batch_size=1, shuffle=True)
 
y_pred = []
y_test = []
 
with torch.no_grad():
    for X, y in test_dataloader:
        outputs = nn_64_10_10(X)
        correct_value = int(torch.topk(y, 1).indices[0][0])
        predicted_value = int(torch.topk(outputs, 1).indices[0][0])
        print(correct_value, predicted_value)
        y_test.append(correct_value)
        y_pred.append(predicted_value)
 
# výpočet matice záměn
disp = ConfusionMatrixDisplay.from_predictions(
    y_test, y_pred,
    cmap=plt.cm.Blues,
    normalize=None,
)
 
# zobrazení matice záměn
print(disp.confusion_matrix)
 
# uložení výsledků
plt.savefig("confusion_matrix.png")
 
# vykreslení matice záměn
plt.show()

Pro síť, která je naučena na klasifikaci nezašuměných obrázků (a je na těchto obrázcích i otestována) bude matice záměn obsahovat nenulové prvky jen na hlavní diagonále. To značí, že je síť na 100% úspěšná:

PyTorch: neuronové sítě

Obrázek 9: Matice záměn pro síť, která odpovídá vždy korektně.

Ovšem pozor – v případě, že budou trénovací data obsahovat malé množství obrázků (například jen 100 a nikoli 600), hrozí problém nedoučení sítě. Potom výsledky vypadají takto:

PyTorch: neuronové sítě

Obrázek 10: Matice záměn pro nedoučenou síť.

17. Matice záměn pro zašuměné obrázky

Nyní se podívejme, jak bude vypadat kvalita odpovědí sítě v případě, že obrázky v trénovací i testovací sadě budou zašuměny. Úroveň šumu se postupně zvyšuje od 25% přes 50% až do 75% (což je hodně – takové obrázky nerozpozná ani člověk – viz též obrázek z úvodní kapitoly).

PyTorch: neuronové sítě

Obrázek 11: Matice záměn pro obrázky zašuměné z 25%. Síť odpovídá korektně.

PyTorch: neuronové sítě

Obrázek 12: Matice záměn pro obrázky zašuměné z 50%. Vysoké hodnoty na hlavní diagonále značí korektní odpovědi, ovšem je patrné, že například číslice 6 není rozeznána nikdy. Nejlépe síť rozeznává číslice 1, 4 a 7.

PyTorch: neuronové sítě

Obrázek 13: Matice záměn pro obrázky zašuměné ze 75%. Nyní jsou odpovědi zcela náhodné a tudíž nepoužitelné.

18. Matice záměn pro posunuté obrázky

Na závěr si ukažme matice záměn pro síť, která by měla umět predikovat obrázky posunuté o jeden či o dva pixely:

PyTorch: neuronové sítě

Obrázek 14: Matice záměn pro číslice posunuté maximálně o jeden pixel.

PyTorch: neuronové sítě

Obrázek 15: Matice záměn pro číslice posunuté maximálně o dva pixely.

hacking_tip

Výsledky jsou zajímavé. Při pohledu na klesající účelovou funkci by se mohlo zdát, že je síť naučena relativně kvalitně, ovšem její odpovědi jsou opět prakticky zcela nepoužitelné. A právě zde se ukazuje teoretický i praktický limit běžných neuronových sítí při práci s jednorozměrnými nebo dvourozměrnými daty. Pro předpovědi na základě těchto dat potřebujeme lepší mechanismus – a tím jsou konvoluční neuronové sítě, které dokážou vliv posunu (a dalších transformací) do značné míry eliminovat.

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:

# Příklad Stručný popis Adresa příkladu
1 tensor_constructor_scalar1.py konstrukce tenzoru nultého a prvního řádu https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_constructor_scalar1.py
2 tensor_constructor_scalar2.py inicializace tenzoru prvního řádu s jedním prvkem https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_constructor_scalar2.py
3 tensor_constructor_vector1.py konstrukce tenzoru prvního řádu (tříprvkový vektor) https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_constructor_vector1.py
4 tensor_constructor_vector2.py konstrukce tenzoru prvního řádu s inicializací prvků https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_constructor_vector2.py
5 tensor_constructor_vector3.py konstrukce tenzoru prvního řádu s využitím generátoru range https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_constructor_vector3.py
6 tensor_constructor_matrix1.py vytvoření a inicializace tenzoru druhého řádu, který může být reprezentován maticí https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_constructor_matrix1.py
7 tensor_constructor_matrix2.py inicializace prvků matice https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_constructor_matrix2.py
8 tensor_constructor_3D1.py tenzor třetího řádu reprezentovaný „3D maticí“ https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_constructor_3D1.py
9 tensor_constructor_3D2.py tenzor třetího řádu reprezentovaný „3D maticí“ (jiná forma inicializace) https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_constructor_3D2.py
       
10 tensor_constructor_scalar_zero.py vynulování prvků tenzoru nultého řádu https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_constructor_scalar_ze­ro.py
11 tensor_constructor_vector_zero.py vynulování prvků tenzoru prvního řádu https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_constructor_vector_ze­ro.py
12 tensor_constructor_matrix_zero.py vynulování prvků tenzoru druhého řádu https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_constructor_matrix_ze­ro.py
13 tensor_constructor_3D_zero.py vynulování prvků tenzoru třetího řádu https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_constructor_3D_zero.py
       
14 tensor_zeros_shape.py použití konstruktoru zeros pro tenzory různých řádů a tvarů https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_zeros_shape.py
15 tensor_ones_shape.py použití konstruktoru ones pro tenzory různých řádů a tvarů https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_ones_shape.py
16 tensor_eye.py konstrukce jednotkové matice https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/tensor_eye.py
       
17 tensor_range.py využití konstruktoru range https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_range.py
18 tensor_arange.py využití konstruktoru arange https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_arange.py
       
19 tensor_shape.py zjištění tvaru tenzoru https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_shape.py
20 tensor_zeros_shape.py zjištění tvaru tenzoru vytvořeného konstruktorem zeros https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_zeros_shape.py
21 tensor_ones_shape.py zjištění tvaru tenzoru vytvořeného konstruktorem ones https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_ones_shape.py
22 tensor_read_dtype.py zjištění, jakého typu jsou prvky tenzoru https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_read_dtype.py
23 tensor_set_dtype.py nastavení či změna typu prvků tenzoru https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_set_dtype.py
       
24 tensor_storage1.py získání datové struktury se zdrojem dat pro tenzor https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_storage1.py
25 tensor_storage2.py získání datové struktury se zdrojem dat pro tenzor https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_storage2.py
26 tensor_storage3.py získání datové struktury se zdrojem dat pro tenzor https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_storage3.py
27 tensor_storage_casts.py přetypování datové struktury se zdrojem dat pro tenzor https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_storage_casts.py
       
28 tensor_slice_operation1.py konstrukce řezu z tenzoru prvního řádu https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_slice_operation1.py
29 tensor_slice_operation2.py konstrukce řezu z tenzoru druhého řádu (přes řádky a sloupce) https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_slice_operation2.py
30 tensor_slice_operation3.py konstrukce řezu s jeho následnou modifikací https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_slice_operation3.py
31 tensor_slice_operation4.py konstrukce řezu s jeho následnou modifikací (odlišné operace od předchozího příkladu) https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_slice_operation4.py
32 tensor_is_slice.py test základních vlastností řezů https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_is_slice.py
       
33 tensor_stride1.py význam atributů stride a storage_offset https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_stride1.py
34 tensor_stride2.py význam atributů stride a storage_offset https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_stride2.py
35 tensor_stride3.py význam atributů stride a storage_offset https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_stride3.py
       
36 tensor_reshape.py změna tvaru tenzoru operací reshape https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_reshape.py
37 tensor_reshape2.py změna tvaru tenzoru operací reshape https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_reshape2.py
       
38 tensor_narrow_operation1.py operace nad celým tenzorem typu narrow, první ukázka https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_narrow_operation1.py
39 tensor_narrow_operation1_B.py operace nad celým tenzorem typu narrow, první ukázka přepsaná do volání metody https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_narrow_operation1_B.py
40 tensor_narrow_operation2.py operace nad celým tenzorem typu narrow, druhá ukázka https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_narrow_operation2.py
41 tensor_narrow_operation2_B.py operace nad celým tenzorem typu narrow, druhá ukázka přepsaná do volání metody https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_narrow_operation2_B.py
42 tensor_narrow_operation3.py operace nad celým tenzorem typu narrow, třetí ukázka https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_narrow_operation3.py
43 tensor_narrow_operation3_B.py operace nad celým tenzorem typu narrow, třetí ukázka přepsaná do volání metody https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_narrow_operation3_B.py
44 tensor_narrow_operation4.py přepis původní matice přes pohled na ni (view) https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_narrow_operation4.py
45 tensor_narrow_operation5.py přepis původní matice přes pohled na ni (view)narrow, třetí ukázka https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_narrow_operation5.py
       
46 tensor_operator_add.py součet dvou tenzorů prvek po prvku https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_operator_add.py
47 tensor_operator_sub.py rozdíl dvou tenzorů prvek po prvku https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_operator_sub.py
48 tensor_operator_mul.py součin dvou tenzorů prvek po prvku https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_operator_mul.py
49 tensor_operator_div.py podíl dvou tenzorů prvek po prvku https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_operator_div.py
50 tensor_dot_product.py skalární součin dvou tenzorů prvního řádu https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_dot_product.py
50 tensor_operator_matmul.py maticové násobení (dvou tenzorů druhého řádu) https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_operator_matmul.py
51 tensor_operator_matmul2.py maticové násobení (dvou tenzorů druhého řádu) https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_operator_matmul2.py
52 tensor_operator_matmul3.py maticové násobení v případě nekompatibilních tvarů matic https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_operator_matmul3.py
53 tensor_operator_matmul4.py maticové násobení s broadcastingem https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_operator_matmul4.py
54 tensor_operator_matmul5.py násobení vektoru a matice https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_operator_matmul5.py
       
55 tensor_broadcast1.py operace broadcast (součin každého prvku tenzoru se skalárem) https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_broadcast1.py
56 tensor_broadcast2.py operace broadcast (součin tenzoru druhého řádu s vektorem) https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_broadcast2.py
57 tensor_broadcast3.py operace broadcast (součin vektoru s tenzorem druhého řádu) https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_broadcast3.py
58 tensor_broadcast4.py operace broadcast (součet tenzorů druhého a třetího řádu) https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_broadcast4.py
       
59 tensor_sparse.py konstrukce řídkého tenzoru https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ten­sor_sparse.py
       
60 activation_function_relu_.py aktivační funkce ReLU vypočtená knihovnou NumPy https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ac­tivation_function_relu_num­py.py
61 activation_function_relu_pytorch.py aktivační funkce ReLU vypočtená knihovnou PyTorch https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ac­tivation_function_relu_py­torch.py
62 activation_function_sigmoid_.py aktivační funkce sigmoid vypočtená knihovnou NumPy https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ac­tivation_function_sigmoid_num­py.py
63 activation_function_sigmoid_pytorch.py aktivační funkce sigmoid vypočtená knihovnou PyTorch https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ac­tivation_function_sigmoid_py­torch.py
64 activation_function_tanh_.py aktivační funkce tanh vypočtená knihovnou NumPy https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ac­tivation_function_tanh_num­py.py
65 activation_function_tanh_pytorch.py aktivační funkce tanh vypočtená knihovnou PyTorch https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ac­tivation_function_tanh_py­torch.py
       
66 make_circles.py vygenerování dat pro neuronovou síť funkcí make_circles https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ma­ke_circles.py
67 make_circles_labels.py vizualizace dat společně s jejich skupinou (ohodnocením) https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ma­ke_circles_labels.py
68 make_more_noise_circles.py získání náhodnějších dat funkcí make_circles https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ma­ke_more_noise_circles.py
69 make_data_set.py náhodné rozdělení datové sady funkcí train_test_split https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/ma­ke_data_set.py
70 prepare_for_training.py konverze původních dat z n-dimenzionálních polí do tenzorů https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/pre­pare_for_training.py
       
71 compute_train_and_test_data.py výpočet trénovacích a testovacích dat pro neuronové sítě https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/com­pute_train_and_test_data.py
72 print_train_and_test_data.py tisk dat získaných předchozím skriptem https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/prin­t_train_and_test_data.py
73 nn01.py deklarace třídy představující neuronovou síť https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/nn01.py
74 nn02.py definice vrstev neuronové sítě https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/nn02.py
75 nn03.py trénink neuronové sítě https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/nn03.py
76 nn04.py trénink neuronové sítě se zobrazením kvality tréninku https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/nn04.py
77 nn05.py neuronová síť s jednou skrytou vrstvou https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/nn05.py
78 nn06.py neuronová síť s více skrytými vrstvami, která nebude dotrénována https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/nn06.py
79 nn07.py vliv parametru learning_rate na rychlosti naučení sítě https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/nn07.py
80 nn08.py výpočet kvality neuronové sítě https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/nn08.py
81 nn09.py vizualizace predikce neuronové sítě https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/nn09.py
       
82 nn_linear_help.py zobrazení nápovědy ke třídě torch.nn.Linear i k parametrům konstruktorů této třídy https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/nn_li­near_help.py
83 nn_linear1.py konstrukce objektu typu torch.nn.Linear s biasem (transformace s jedním vstupem a jedním výstupem) https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/nn_li­near1.py
84 nn_linear2.py konstrukce objektu typu torch.nn.Linear bez biasu (transformace s jedním vstupem a jedním výstupem) https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/nn_li­near2.py
85 nn_linear3.py specifikace vah a biasu po konstrukci objektu typu torch.nn.Linear https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/nn_li­near3.py
86 nn_linear4.py aplikace lineární transformace reprezentované objektem typu torch.nn.Linear https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/nn_li­near4.py
87 nn_linear5.py aplikace lineární transformace na větší soubor vstupních tenzorů https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/nn_li­near5.py
88 nn_linear6.py 2D transformace prováděná objektem typu torch.nn.Linear https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/nn_li­near6.py
89 nn_linear7.py 2D transformace – otočení bodů v rovině https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/nn_li­near7.py
90 nn_linear_zeros.py konstrukce objektu typu torch.nn.Linear pro nulový počet vstupů a/nebo výstupů https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/nn_li­near_zeros.py
91 nn_forward1.py neuronová síť s jedním vstupem a jedním výstupem, bez aktivační funkce https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/nn_for­ward1.py
92 nn_forward2.py neuronová síť s jedním vstupem a dvěma výstupy, bez aktivační funkce https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/nn_for­ward2.py
93 nn_forward3.py neuronová síť se dvěma vstupy a jedním výstupem, bez aktivační funkce https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/nn_for­ward3.py
94 nn_forward4.py přidání aktivační funkce do neuronové sítě https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/nn_for­ward4.py
       
95 convnn_01_digits.py první verze generátoru trénovacích obrázků číslic od 0 do 9 https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/con­vnn_01_digits.py
96 convnn_02_digits_as_bitmaps.py konstrukce dvourozměrné matice 8×8 bodů pro vybranou číslici https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/con­vnn_02_digits_as_bitmaps.py
97 convnn_03_show_digits.py vizualizace matic s obrazy číslic v rastru 8×8 v nepravých barvách https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/con­vnn_03_show_digits.py
98 convnn_04_show_digits.py vizualizace matic s obrazy číslic v rastru 8×8 ve stupních šedi https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/con­vnn_04_show_digits.py
99 convnn_05_show_all_digits.py vizualizace všech deseti matic s číslicemi https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/con­vnn_05_show_all_digits.py
100 convnn_06_noise.py přidání šumu do obrázků s číslicemi https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/con­vnn_06_noise.py
101 convnn_07_noise_levels.py šum se sílou od 0% do 100% https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/con­vnn_07_noise_levels.py
102 convnn_08_5_6_or8.py rozdíl mezi číslicemi 5, 6 a 8 https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/con­vnn_08_5_6_or8.py
103 convnn_09_shift.py posun obrazu číslice v rámci matice https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/con­vnn_09_shift.py
104 convnn_10_shifts.py různé posuny obrazu číslice v rámci matice https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/con­vnn_10_shifts.py
105 convnn_11_classification_report.py numerická podoba ověření sítě provádějící klasifikaci https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/con­vnn_11_classification_repor­t.py
106 convnn_12_confusion_matrix.py výpočet a vizualizace matice záměn (confusion matrix) https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/con­vnn_12_confusion_matrix.py
107 nn10.py jednoduchá neuronová síť provádějící klasifikaci (nikoli regresi) https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/nn10.py
       
108 convnn_13_input_data.py příprava datové sady pro trénink neuronové sítě s vektorovým vstupem a výstupem https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/con­vnn_13_input_data.py
109 convnn_14_real_input_data.py příprava zašuměných a posunutých vstupních dat pro neuronovou síť https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/con­vnn_14_real_input_data.py
110 convnn_15_train_test_data.py rozdělení datové sady na trénovací a testovací data https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/con­vnn_15_train_test_data.py
111 convnn_16_simplenn.py trénink neuronové sítě pro rozpoznávání rastrových obrázků https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/con­vnn_16_simplenn.py
112 convnn_17_noisenn.py trénink neuronové sítě pro rozpoznávání rastrových obrázků s využitím zašuměných obrázků https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/con­vnn_17_noisenn.py
113 convnn_18_shiftnn.py trénink neuronové sítě pro rozpoznání posunutých obrázků https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/con­vnn_18_shiftnn.py
114 convnn_19_noise_and_shiftnn.py trénink neuronové sítě pro rozpoznání posunutých obrázků a současně i zašuměných obrázků https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/con­vnn_19_noise_and_shiftnn.py
115 convnn_20_confusion_matrix.py matice záměn pro naučenou neuronovou síť https://github.com/tisnik/most-popular-python-libs/blob/master/PyTorch/con­vnn_20_confusion_matrix.py

20. Odkazy na Internetu

  1. Seriál Programovací jazyk Lua na Rootu:
    https://www.root.cz/seria­ly/programovaci-jazyk-lua/
  2. 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/
  3. PyTorch Tutorial: Building a Simple Neural Network From Scratch
    https://www.datacamp.com/tu­torial/pytorch-tutorial-building-a-simple-neural-network-from-scratch
  4. 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/
  5. 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/
  6. 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/
  7. Stránky projektu PyTorch:
    https://pytorch.org/
  8. Informace o instalaci PyTorche:
    https://pytorch.org/get-started/locally/
  9. Tenzor (Wikipedia):
    https://cs.wikipedia.org/wiki/Tenzor
  10. Introduction to Tensors:
    https://www.youtube.com/wat­ch?v=uaQeXi4E7gA
  11. Introduction to Tensors: Transformation Rules:
    https://www.youtube.com/wat­ch?v=j6DazQDbEhQ
  12. Tensor Attributes:
    https://pytorch.org/docs/sta­ble/tensor_attributes.html
  13. Tensors Explained Intuitively: Covariant, Contravariant, Rank :
    https://www.youtube.com/wat­ch?v=CliW7kSxxWU
  14. What is the relationship between PyTorch and Torch?:
    https://stackoverflow.com/qu­estions/44371560/what-is-the-relationship-between-pytorch-and-torch
  15. What is a tensor anyway?? (from a mathematician):
    https://www.youtube.com/wat­ch?v=K7f2pCQ3p3U
  16. Visualization of tensors – part 1 :
    https://www.youtube.com/wat­ch?v=YxXyN2ifK8A
  17. Visualization of tensors – part 2A:
    https://www.youtube.com/wat­ch?v=A95jdIuUUW0
  18. Visualization of tensors – part 2B:
    https://www.youtube.com/wat­ch?v=A95jdIuUUW0
  19. What the HECK is a Tensor?!?:
    https://www.youtube.com/wat­ch?v=bpG3gqDM80w
  20. Stránka projektu Torch
    http://torch.ch/
  21. Torch na GitHubu (několik repositářů)
    https://github.com/torch
  22. Torch (machine learning), Wikipedia
    https://en.wikipedia.org/wi­ki/Torch_%28machine_learnin­g%29
  23. Torch Package Reference Manual
    https://github.com/torch/tor­ch7/blob/master/README.md
  24. Torch Cheatsheet
    https://github.com/torch/tor­ch7/wiki/Cheatsheet
  25. An Introduction to Tensors
    https://math.stackexchange­.com/questions/10282/an-introduction-to-tensors
  26. Differences between a matrix and a tensor
    https://math.stackexchange­.com/questions/412423/dif­ferences-between-a-matrix-and-a-tensor
  27. Qualitatively, what is the difference between a matrix and a tensor?
    https://math.stackexchange­.com/questions/1444412/qu­alitatively-what-is-the-difference-between-a-matrix-and-a-tensor?
  28. Tensors for Neural Networks, Clearly Explained!!!:
    https://www.youtube.com/wat­ch?v=L35fFDpwIM4
  29. Tensor Processing Unit:
    https://en.wikipedia.org/wi­ki/Tensor_Processing_Unit
  30. Třída Storage:
    http://docs.pytorch.wiki/en/sto­rage.html
  31. Funkce torch.dot
    https://pytorch.org/docs/sta­ble/generated/torch.dot.html#tor­ch.dot
  32. Funkce torch.narrow
    https://pytorch.org/docs/sta­ble/generated/torch.narrow­.html
  33. Funkce torch.matmul
    https://pytorch.org/docs/sta­ble/generated/torch.matmul­.html
  34. Funkce torch.reshape
    https://pytorch.org/docs/sta­ble/generated/torch.resha­pe.html
  35. Funkce torch.arange
    https://pytorch.org/docs/sta­ble/generated/torch.arange­.html
  36. Funkce torch.range
    https://pytorch.org/docs/sta­ble/generated/torch.range­.html
  37. Třída torch.Tensor
    https://pytorch.org/docs/sta­ble/tensors.html
  38. Atributy tenzorů
    https://pytorch.org/docs/sta­ble/tensor_attributes.html
  39. Pohledy vytvořené nad tenzory
    https://pytorch.org/docs/sta­ble/tensor_view.html
  40. Broadcasting v knihovně
    https://numpy.org/doc/sta­ble/user/basics.broadcastin­g.html
  41. Broadcasting semantics (v knihovně PyTorch)
    https://pytorch.org/docs/sta­ble/notes/broadcasting.html
  42. 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/
  43. scikit-learn: Getting Started
    https://scikit-learn.org/stable/getting_started.html
  44. Support Vector Machines
    https://scikit-learn.org/stable/modules/svm.html
  45. Use Deep Learning to Detect Programming Languages
    http://searene.me/2017/11/26/use-neural-networks-to-detect-programming-languages/
  46. Data pro neuronové sítě
    http://archive.ics.uci.edu/ml/in­dex.php
  47. Feedforward neural network
    https://en.wikipedia.org/wi­ki/Feedforward_neural_net­work
  48. Biologické algoritmy (4) – Neuronové sítě
    https://www.root.cz/clanky/biologicke-algoritmy-4-neuronove-site/
  49. Biologické algoritmy (5) – Neuronové sítě
    https://www.root.cz/clanky/biologicke-algoritmy-5-neuronove-site/
  50. Umělá neuronová síť (Wikipedia)
    https://cs.wikipedia.org/wi­ki/Um%C4%9Bl%C3%A1_neuronov%C3%A1_s%C3%AD%C5%A5
  51. AI vs Machine Learning (Youtube)
    https://www.youtube.com/wat­ch?v=4RixMPF4×is
  52. Machine Learning | What Is Machine Learning? | Introduction To Machine Learning | 2024 | Simplilearn (Youtube)
    https://www.youtube.com/wat­ch?v=ukzFI9rgwfU
  53. A Gentle Introduction to Machine Learning (Youtube)
    https://www.youtube.com/wat­ch?v=Gv9_4yMHFhI
  54. Machine Learning vs Deep Learning
    https://www.youtube.com/wat­ch?v=q6kJ71tEYqM
  55. Umělá inteligence (slajdy)
    https://slideplayer.cz/sli­de/12119218/
  56. Úvod do umělé inteligence
    https://slideplayer.cz/slide/2505525/
  57. Umělá inteligence I / Artificial Intelligence I
    https://ktiml.mff.cuni.cz/~bartak/ui/
  58. Třída torch.nn.Linear
    https://pytorch.org/docs/sta­ble/generated/torch.nn.Li­near.html
  59. Třída torch.nn.Parameter
    https://pytorch.org/docs/sta­ble/generated/torch.nn.pa­rameter.Parameter.html
  60. Třída torch.nn.Sigmoid
    https://pytorch.org/docs/sta­ble/generated/torch.nn.Sig­moid.html

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.