PyTorch: problematika rozpoznávání a klasifikace obrázků

29. 4. 2025
Doba čtení: 45 minut

Sdílet

Autor: Redakce
Začneme se zabývat dalším problémem, který je poměrně dobře řešitelný s využitím knihovny PyTorch. Jedná se o rozpoznávání a klasifikaci rastrových obrázků. Řešení (většinou) spočívá ve využití konvolučních neuronových sítí.

Obsah

1. PyTorch: problematika rozpoznávání a klasifikace obrázků

2. První verze generátoru trénovacích obrázků číslic od 0 do 9

3. Konstrukce dvourozměrné matice 8×8 bodů pro vybranou číslici

4. Kooperace mezi knihovnami Matplotlib a NumPy: vizualizace obsahu 2D matice

5. Vizualizace matic s obrazy číslic v rastru 8×8

6. Vizualizace všech deseti matic s číslicemi

7. Problém rozpoznávání číslic na reálných obrázcích

8. Vliv šumu na kvalitu predikcí neuronové sítě

9. Vizualizace matice reprezentující zašuměnou číslici

10. Vizualizace vlivu parametru level na způsob zašumění matice s číslicí

11. Rozeznání číslic 5, 6 a 8

12. Posun číslic v matici pro přípravu trénovacích dat

13. Zobrazení všech variant posunů matice s číslicí 2 o &plusm; jeden nebo dva pixely

14. Numerické ohodnocení klasifikační neuronové sítě pro rozpoznávání obrázků

15. Matice záměn

16. Příklad klasifikační neuronové sítě se dvěma výstupy

17. Konvoluční neuronové sítě

18. Vrstvy v konvolučních neuronových sítích

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

20. Odkazy na Internetu

1. PyTorch: problematika rozpoznávání a klasifikace obrázků

Na předchozí tři části [1] [2] [3] seriálu o knihovně PyTorch, v nichž jsme se seznámili se základními postupy, které se používají při tvorbě umělých neuronových sítí s pravidelnou strukturou tvořenou jednotlivými vrstvami, u nichž učení probíhá s využitím takzvaného backpropagation algoritmu (algoritmu zpětného šíření), dnes navážeme. Začneme se totiž zabývat problematikou rozpoznávání a klasifikace rastrových obrázků. Ty sice budou zpočátku velmi malé a budou obsahovat poměrně dobře predikovatelná data, ovšem i takto malé obrázky nám umožní ukázat některé nevýhody klasických obecných neuronových sítí při jejich aplikaci na rastrová data.

Ve druhé části dnešního článku se ve stručnosti seznámíme s neuronovými sítěmi provádějícími klasifikaci (a nikoli regresi). Právě tento typ neuronových sítí nám umožní realizovat rozpoznávání obrázků stylem „zde je pes“ nebo „tady jsou tři stromy“. S tímto tématem souvisí i problematika vyjádření kvality sítě s využitím matice záměn (confusion matrix).

V závěru článku se navíc seznámíme s principy, na nichž jsou postaveny takzvané konvoluční neuronové sítě. Ty jsou v současnosti velmi populární, a to hned z několika důvodů – po jejich natrénování sítě (to je sice časově náročné, ovšem s moderními GPU již většinou uspokojivě řešitelné) jsou již konvoluční sítě poměrně rychlé a především se rozšiřují možnosti, kde je možné tyto sítě prakticky použít (doprava, tedy například automatické řízení vozidel, průmysl atd.).

Tím získáme všechny dílky potřebné k tomu, abychom příště vytvořili skutečnou konvoluční síť, natrénovali ji a nakonec ověřili její kvalitu s využitím matice záměn.

2. První verze generátoru trénovacích obrázků číslic od 0 do 9

Jak jsme si již řekli v úvodní kapitole, budeme se snažit s využitím jednoduchých neuronových sítí rozpoznávat objekty na velmi malých obrázcích. Konkrétně se bude zpočátku jednat o vstupní obrázky s pevným rozlišením pouhých 8×8 pixelů, což nám mj. umožní velmi rychlý tréning sítě a samozřejmě i její následnou validaci (a to bez nutnosti zdlouhavého tréninku s využitím GPU; prozatím si vystačíme s výpočty na CPU).

Rastrové obrázky budou reprezentovány ve stupních šedi a úkolem postupně vytvářené neuronové sítě bude na těchto obrázcích rozpoznat číslice 0 až 9 zapsané pro jednoduchost předem známým fontem (příště už budeme mít horší úkol, protože číslice budou napsány rukou, navíc mnoha autory). Abychom získali představu, jak tyto číslice vypadají, necháme si vygenerovat testovací obrázky, a to z následujících vstupních dat:

# čí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),
)
 
# triviální výpis
print(digits)

Každá číslice, jejíž tvar je zakódován v n-tici digits, je reprezentována osmicí bajtů, protože každý bajt reprezentuje osm sousedních pixelů. Celkem tedy vstupní data obsahují osmdesát bajtů, protože máme deset číslic, každou uloženou v osmi bajtech:

((0, 60, 102, 118, 110, 102, 60, 0), (0, 24, 28, 24, 24, 24, 126, 0), (0, 60, 102, 48, 24, 12, 126, 0),
(0, 126, 48, 24, 48, 102, 60, 0), (0, 48, 56, 60, 54, 126, 48, 0), (0, 126, 6, 62, 96, 102, 60, 0),
(0, 60, 6, 62, 102, 102, 60, 0), (0, 126, 96, 48, 24, 12, 12, 0), (0, 60, 102, 60, 102, 102, 60, 0),
(0, 60, 102, 124, 96, 48, 28, 0))

3. Konstrukce dvourozměrné matice 8×8 bodů pro vybranou číslici

Výše uvedený způsob uložení bitových map s číslicemi je sice velmi úsporný, ovšem příliš se nehodí pro trénink neuronových sítí (minimálně ne v takové formě, jakou nám nabízí knihovna PyTorch). Proto musíme mít k dispozici pomocnou funkci, která pro zadanou číslici 0–9 vrátí matici o rozměrech 8×8 prvků obsahující hodnoty 0,0 nebo 1,0, v závislosti na tom, zda příslušný prvek odpovídá černému pixelu nebo naopak pixelu bílému. Povšimněte si, že i když by bylo možné vytvořit matici s pravdivostními hodnotami True/False nebo celočíselnými hodnotami 0/1, použijeme hodnoty s plovoucí řádovou čárkou. To nám později umožní nejenom simulovat šum, ale i přímo takové matice použít pro vstup do neuronových sítí.

Převodní funkce může vypadat následovně:

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)
Poznámka: tuto funkci lze zapsat i úspornějším a idiomatičtějším způsobem, který ovšem není příliš čitelný.

Ověřme si, jak například bude vypadat výsledná dvourozměrná matice s reprezentací číslice 2 (ta je nesymetrická, takže si ověříme korektnost převodu):

import numpy as np
 
# čí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)
 
 
# vytvoření matice, kterou budeme vizualizovat
array = digit_to_array(digits, 2)
 
# výpis pole s reprezentací číslice 2
print(array)

Výsledná matice by měla vypadat následovně:

[[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.]]

Pro zajímavost si můžeme doplnit krátký kód, který matici převede do tisknutelné podoby a navíc výslednou „bitmapu“ orámuje:

# převod na tisknutelnou podobu
print("+--------+")
for row in array:
    print("|", end="")
    for item in row:
        print(" "  if item==0.0 else "*", end="")
    print("|")
print("+--------+")

Nyní bude výsledek vypadat následovně:

+--------+
|        |
|  ****  |
| **  ** |
|    **  |
|   **   |
|  **    |
| ****** |
|        |
+--------+
Poznámka: většina fontů není určena pro tento typ výstupu, protože obdélníky okolo znaků nejsou čtvercové, ale mají typicky poměr 1:2. To znamená, že výsledná podoba číslice 2 je mnohem vyšší, než by tomu mělo být ve skutečném rastrovém obrázku se čtvercovými pixely.

4. Kooperace mezi knihovnami Matplotlib a NumPy: vizualizace obsahu 2D matice

V praxi, když začneme pracovat s obrázky ve stupních šedi, se zašuměnými obrázky atd., již není zobrazení obrázku na konzoli ideální. Pokusme se tedy využitím kooperace mezi knihovnami Matplotlib a NumPy. Konkrétně si ukážeme vizualizaci obsahu dvourozměrné matice (NumPy podporuje obecná n-rozměrná pole a matice jsou tedy pouze podtypem). Vytvoříme si matici o rozměrech 10×10 prvků a naplníme ji náhodnými hodnotami. Výsledek si necháme zobrazit na grafu a tento graf taktéž uložíme do souboru (formát PNG):

#!/usr/bin/env python
 
# budeme provádět vykreslování de facto standardní knihovnou Matplotlib
import matplotlib.pyplot as plt
 
import numpy as np
 
# vytvoření matice, kterou budeme vizualizovat
array = np.random.rand(10, 10)
 
# vykreslení
plt.matshow(array)
 
# uložení vizualizované matice
plt.savefig("random.png")
 
# vizualizace na obrazovku
plt.show()
 
# finito

Výsledek bude vypadat následovně:

Obrázek 1: Vizualizace pole s náhodným obsahem.

Povšimněte si, že se při vizualizaci použily nepravé barvy. Těch se později zbavíme, protože nás budou spíše mást. Prozatím jsou však užitečné.

5. Vizualizace matic s obrazy číslic v rastru 8×8

Podívejme se nyní na způsob vizualizace matic s rozměry 8×8, jejichž prvky mají hodnoty 0,0 nebo 1,0. Tyto matice mohou obsahovat bitmapy číslic v rastru 8×8 pixelů. Nejdříve při vizualizaci použijeme nepravé barvy, přesněji řečeno barvovou paletu, která provádí mapování mezi hodnotami uloženými v matici na barvovou škálu. Algoritmus nejdříve zjistí minimální a maximální hodnoty prvků (což jsou v našem případě hodnoty 0,0 a 1,0) a následně určí měřítko použité při hledání barev:

import matplotlib.pyplot as plt
import numpy as np
 
# čí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)
 
 
# vytvoření matice, kterou budeme vizualizovat
array = digit_to_array(digits, 2)
 
# vykreslení matice
plt.matshow(array)
 
# uložení vizualizované matice
plt.savefig("conv_nn_03.png")
 
# vizualizace matice na obrazovce
plt.show()

Opět jsme si nechali zobrazit matici odpovídající číslici 2:

PyTorch NN

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

Alternativně je možné zavoláním plt.gray() dosáhnout převodu na stupně šedi, což může být v tomto případě výhodnější způsob vizualizace matice s de facto černobílým obrázkem. Opět si to ukažme:

import matplotlib.pyplot as plt
import numpy as np
 
# čí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)
 
 
# vytvoření matice, kterou budeme vizualizovat
array = digit_to_array(digits, 2)
 
# vykreslení matice
plt.matshow(array)
 
# převod na stupně šedi
plt.gray()
 
# uložení vizualizované matice
plt.savefig("conv_nn_04.png")
 
# vizualizace matice na obrazovce
plt.show()

Výsledek:

PyTorch NN

Obrázek 3: Matice číslice 2 zobrazená ve stupních šedi.

6. Vizualizace všech deseti matic s číslicemi

V navazujícím článku budeme zobrazovat velké množství číslic, resp. přesněji řečeno vizualizaci jejich matic. Pravděpodobně by nebylo praktické každou číslici vykreslit do samostatného grafu. Můžeme však využít další vlastností Matplotlibu – jeho schopnosti do jediné plochy grafu přidat více menších grafů. Plocha je rozdělena na pomyslnou mřížku, jejíž rozměry zadáváme při přidávání menšího grafu do jednotlivých políček:

ax = plt.subplot(počet řádků, počet sloupců, pozice/index v rámci mřížky)

Snadno tak můžeme zobrazit všech deset číslic do mřížky se třemi sloupci a čtyřmi řádky. Dvě políčka nebudou obsazena, což nám umožňuje simulovat rozložení numerických kláves na číselníku telefonu:

import matplotlib.pyplot as plt
import numpy as np
 
# čí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)
 
 
# velikost obrázku s grafem
plt.subplots(figsize=(6.4, 6.4))
plt.axis("off")
 
# vykreslení číslic 1-9
for digit in range(1, 10):
    array = digit_to_array(digits, digit)
    ax = plt.subplot(4, 3, digit)
    plt.gray()
    ax.matshow(array)
 
# dokreslení číslice 0
array = digit_to_array(digits, 0)
ax = plt.subplot(4, 3, 11)
plt.gray()
ax.matshow(array)
 
# uložení vizualizované matice
plt.savefig("conv_nn_05.png")
 
# vizualizace matice na obrazovce
plt.show()

Výsledky:

PyTorch NN

Obrázek 4: Vizualizace matic všech deseti číslic.

7. Problém rozpoznávání číslic na reálných obrázcích

Mohlo by se zdát, že pro rozpoznání obrázků nám stačí si natrénovat běžnou neuronovou síť se 64 vstupy a deseti výstupy. Jednalo by se o klasifikační síť, na jejíž vstup by se přivedly hodnoty všech 64 pixelů a na výstupu by se (ideálně) měla objevit hodnota 1,0 na jednom z výstupů a naopak hodnoty 0,0 na ostatních devíti výstupech. Ve skutečnosti je však nutné přiznat, že je to v praxi mnohem složitější, a to minimálně ze tří důvodů:

  1. Takto natrénovaná neuronová síť dokáže rozpoznat pouze jeden font, což obecně bude vadit, například ve chvíli, kdy namísto námi připravených trénovacích dat použijeme například ručně psané číslice z již zmíněné databáze MNIST. A raději ji vůbec nepouštějte na obrázky získané ze systémů CAPTCHA :-)
  2. Klasickou neuronovou síť je možné velmi snadno zmást i při použití stále stejného fontu. Postačuje pouze obraz číslice posunout o jeden jediný pixel (jakýmkoli směrem)!
  3. Síť nemusí být dobře připravena na klasifikaci zašuměných obrázků, tedy například obrázků číslic získaných z fotografií, po naskenování číslic z papíru, ale například číslic uložených do formátů se ztrátovou komprimací (JPEG) atd.

8. Vliv šumu na kvalitu predikcí neuronové sítě

Po natrénování neuronové sítě s využitím pouhých deseti vstupních obrázků by se mohlo stát, že by síť prakticky vůbec nebyla schopna rozeznat i nepatrně změněná vstupní data. Proto funkci pro vytvoření trénovacích obrázků vhodně pozměníme takovým způsobem, že se do obrázků zanese šum. Pro vytvoření šumu používám pro jednoduchost funkci np.random.rand, ovšem v případě potřeby samozřejmě můžete využít i funkci pro generování náhodných hodnot s normálním rozložením atd.:

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

Povšimněte si dále, že i zašuměné obrázky mají přesně stanovenou hranici mezi pixely, které tvoří číslici a pixely tvořícími pozadí (záleží na hodnotě level v rozsahu od 0,0 do 1,0). Tuto část si samozřejmě můžete upravit, a to i takovým způsobem, aby tato hranice byla z obou stran překračována. Ovšem takto obecně naučená síť nebude dávat jednoznačné výsledky – ostatně si to vyzkoušíme příště.

9. Vizualizace matice reprezentující zašuměnou číslici

Skript, po jehož spuštění se provede „zašumění“ matice s reprezentací číslice 2 a následné vykreslení takto upravené matice, vypadá takto:

import matplotlib.pyplot as plt
import numpy as np
 
# čí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)
 
 
# vytvoření matice, kterou budeme vizualizovat
array = digit_to_array(digits, 2)
array = add_noise(array, 0.2)
 
# vykreslení matice
plt.matshow(array)
 
# převod na stupně šedi
plt.gray()
 
# uložení vizualizované matice
plt.savefig("conv_nn_06.png")
 
# vizualizace matice na obrazovce
plt.show()

Zašuměný obraz číslice 2 nyní vypadá následovně:

PyTorch NN

Obrázek 5: Vizualizace matice se zašuměnou číslicí 2.

10. Vizualizace vlivu parametru level na způsob zašumění matice s číslicí

Jak jsme si již řekli v předchozím textu, může parametr level, který ovlivňuje výpočet šumu, nabývat hodnot od 0,0 do 1,0. Vyzkoušejme si tedy, jak budou vypadat matice číslic v případě, kdy postupně úroveň šumu zvyšujeme v tomto rozsahu. Opět využijeme možnost vykreslení více grafů do jedné plochy:

import matplotlib.pyplot as plt
import numpy as np
 
# čí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)
 
 
# velikost obrázku s grafem
plt.subplots(figsize=(6.4, 6.4))
plt.axis("off")
 
for i in range(1, 17):
    array = digit_to_array(digits, 2)
    level = (i - 1.0) / 16.0
    array = add_noise(array, level)
    ax = plt.subplot(4, 4, i)
    # plt.gray()
    ax.matshow(array)
 
 
# uložení vizualizované matice
plt.savefig("conv_nn_07.png")
 
# vizualizace matice na obrazovce
plt.show()

Z vizualizace je patrné, že pro úrovně vyšší než 0,5 je výsledná číslice prakticky nerozeznatelná (ovšem to je logický důsledek):

PyTorch NN

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

11. Rozeznání číslic 5, 6 a 8

Font s číslicemi, který používáme, má jednu zajímavou vlastnost – rozdíly mezi číslicemi 5, 6 a 8 jsou pouze několika pixelové (a to existují i fonty, ve kterých je rozdíl jen jednopixelový). Bude tedy zajímavé zjistit, jak dobře budeme moci rozlišit tyto tři číslice v případě, že se postupně bude zvyšovat úroveň zašumění. Další demonstrační příklad zobrazí tři sloupce s vizualizovanými maticemi s číslicemi 5, 6 a 8. Úroveň šumu postupně roste od 0,0 do 1,0:

import matplotlib.pyplot as plt
import numpy as np
 
# čí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)
 
 
# velikost obrázku s grafem
plt.subplots(figsize=(3.2, 9.8))
plt.axis("off")
 
for j, digit in enumerate([5, 6, 8]):
    for i in range(1, 12):
        level = (i - 1.0) / 12.0
        array = digit_to_array(digits, digit)
        array = add_noise(array, level)
        ax = plt.subplot(12, 3, j + 1 + i * 3)
        ax.matshow(array)
        ax.axis("off")
 
 
# uložení vizualizované matice
plt.savefig("conv_nn_08.png")
 
# vizualizace matice na obrazovce
plt.show()

Schválně si sami vyzkoušejte, jak dobře dokážete číslice rozeznat. Vaše vlastní neuronová síť je přitom mnohem výkonnější, než budou konvoluční umělé sítě, které si otestujeme příště:

PyTorch NN

Obrázek 7: Matice číslic 5, 6 a 8 s postupně rostoucí úrovní šumu.

12. Posun číslic v matici pro přípravu trénovacích dat

Neuronovou síť je nutné natrénovat i na takových číslicích, které jsou v libovolném směru posunuty. Vzhledem k tomu, že možnosti posunu číslic v rastru 8×8 pixelů jsou velmi malé, bude mít v našem případě význam pouze posun o ±2 pixely v libovolném směru: horizontálním, vertikálním a/nebo šikmém. Pro posun v rámci matice kupodivu v knihovně NumPy neexistuje specializovaná funkce, ale to nevadí, protože posun lze nasimulovat rotací realizovanou funkcí numpy.roll zkombinovanou s výplní těch částí matice, které byly orotovány na její druhou stranu. Implementace funkce pro posun může vypadat takto (posun může být jak kladný, tak i záporný):

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

Začlenění této funkce do skriptu, který vykreslí matici posunuté číslice 2, vypadá takto:

import matplotlib.pyplot as plt
import numpy as np
 
# čí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 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
 
 
# vytvoření matice, kterou budeme vizualizovat
array = digit_to_array(digits, 2)
array = shift(array, 1, 1)
 
# vykreslení matice
plt.matshow(array)
 
# převod na stupně šedi
# plt.gray()
 
# uložení vizualizované matice
plt.savefig("conv_nn_09.png")
 
# vizualizace matice na obrazovce
plt.show()
PyTorch NN

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

13. Zobrazení všech variant posunů matice s číslicí 2 o &plusm; jeden nebo dva pixely

Nyní si vizuálně ověříme, jak vypadá matice s číslicí 2, která je posunutá o jeden či dva pixely v libovolném směru. Spuštěním skriptu, jehož zdrojový kód je zobrazen pod tímto odstavcem, získáme celkem 24 obrázků (dvacátý pátý je obrázek původní číslice), které jsou vloženy do jediného grafu:

import matplotlib.pyplot as plt
import numpy as np
 
# čí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 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
 
 
# velikost obrázku s grafem
plt.subplots(figsize=(6.4, 6.4))
plt.axis("off")
 
i = 1
for y_shift in range(-2, 3):
    for x_shift in range(-2, 3):
        array = digit_to_array(digits, 2)
        array = shift(array, x_shift, y_shift)
        ax = plt.subplot(5, 5, i)
        i += 1
        ax.matshow(array)
 
# převod na stupně šedi
plt.gray()
 
# uložení vizualizované matice
plt.savefig("conv_nn_10.png")
 
# vizualizace matice na obrazovce
plt.show()

Takto by měly vypadat výsledky:

PyTorch NN

Obrázek 9: Číslice 2, která je posunutá o jeden až dva pixely v libovolném směru.

14. Numerické ohodnocení klasifikační neuronové sítě pro rozpoznávání obrázků

Pro klasifikační neuronové sítě si nevystačíme s výpočtem absolutní či relativní odchylky výsledku od korektní hodnoty. Typicky se pro tyto účely používá matice záměn zmíněná v následující kapitole. Ovšem pomoci nám může i funkce classification_report z knihovny Scikit-learn (již jsme se s ní setkali). Vyzkoušejme tedy, jaké výsledky získáme pro simulované odpovědi (prozatím neexistující) neuronové sítě pro rozpoznávání číslic s odpověďmi očekávanými. Tyto hodnoty jsou uloženy v seznamech y_pred a y_test:

from sklearn.metrics import classification_report
 
 
y_pred = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
y_test = [0, 1, 2, 8, 4, 5, 6, 7, 6, 9, 0, 1, 2, 2, 4, 5, 8, 7, 8, 9]
 
# tisk zprávy o výsledcích klasifikace
print(classification_report(y_test, y_pred))

Z výsledků získáme informace o tom, které číslice jsou z pohledu predikce sítě nejvíce problematické. Jedná se o číslici 3 a dále o dvojici 6 a 8, které se zaměňují:

              precision    recall  f1-score   support

           0       1.00      1.00      1.00         2
           1       1.00      1.00      1.00         2
           2       1.00      0.67      0.80         3
           3       0.00      0.00      0.00         0
           4       1.00      1.00      1.00         2
           5       1.00      1.00      1.00         2
           6       0.50      0.50      0.50         2
           7       1.00      1.00      1.00         2
           8       0.50      0.33      0.40         3
           9       1.00      1.00      1.00         2
 
    accuracy                           0.80        20
   macro avg       0.80      0.75      0.77        20
weighted avg       0.88      0.80      0.83        20

15. Matice záměn

Při zjišťování kvality modelů, které provádí klasifikaci, se s úspěchem používá takzvaná matice záměn neboli confusion matrix. Jedná se o matici, která ve sloupcích obsahuje očekávané hodnoty a v řádcích pak předpovědi/odpovědi modelu (popř. je matice transponovaná, to však nevadí). Pokud model odpoví ve všech případech správně, bude matice obsahovat nenulové hodnoty pouze na hlavní diagonále a tyto hodnoty budou znamenat „očekávalo se X odpovědí A a model takto odpověděl skutečně X-krát“. Ovšem ve chvíli, kdy se model splete, vypíše se tato hodnota mimo hlavní diagonálu; tj. hodnoty mimo hlavní diagonálu znamenají chyby a navíc můžeme zjistit, které odpovědi způsobují modelu největší problémy (tj. například které číslice se nejčastěji zaměňují).

Podívejme se na příklad modelu, který vždy odpoví korektně:

  | A    B   C
--+------------
A | 10   0   0
B |  0  20   0
C |  0   0  30

Model pro 10 očekávaných odpovědí A skutečně desetkrát odpověděl „A“ atd. Celkem se provedlo 10+20+30 testů.

Naopak může model nesprávně rozlišovat mezi odpověďmi A a B. Potom může matice vypadat například takto:

  | A    B   C
--+------------
A |  7   3   0
B |  0  20   0
C |  0   0  30

Nebo takto:

  | A    B   C
--+------------
A | 10   0   0
B | 10  10   0
C |  0   0  30

Zkoumáním obsahu matice záměn můžeme zjistit nejenom citlivost modelu, ale i specificitu modelu (což je mnohdy důležitější atribut – ještě se k němu vrátíme).

Matice záměn může obsahovat i relativní hodnoty, které jsou nezávislé na počtu měření. Maximální hodnota prvku v takové matici je rovna 1.0 a minimální pochopitelně 0.0.

Ukažme si nyní využití matice záměn pro vizualizaci kvality (prozatím neexistující) neuronové sítě rozpoznávající číslice. Správné odpovědi jsou uloženy v seznamu y_test, odpovědi sítě pak v seznamu y_pred. Skript po svém spuštění vypočítá a zobrazí matici záměn:

import matplotlib.pyplot as plt
from sklearn.metrics import ConfusionMatrixDisplay
 
 
y_pred = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
y_test = [0, 1, 2, 8, 4, 5, 6, 7, 6, 9, 0, 1, 2, 2, 4, 5, 8, 7, 8, 9]
 
# 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()

Výsledky, které získáme po spuštění tohoto skriptu:

[[2 0 0 0 0 0 0 0 0 0]
 [0 2 0 0 0 0 0 0 0 0]
 [0 0 2 1 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 2 0 0 0 0 0]
 [0 0 0 0 0 2 0 0 0 0]
 [0 0 0 0 0 0 1 0 1 0]
 [0 0 0 0 0 0 0 2 0 0]
 [0 0 0 1 0 0 1 0 1 0]
 [0 0 0 0 0 0 0 0 0 2]]
confusion_matrix

Obrázek 10: Vizualizovaná matice záměn.

Nejvíce problémů tedy způsobuje záměna číslic 6 a 8.

16. Příklad klasifikační neuronové sítě se dvěma výstupy

V dnešním posledním demonstračním příkladu je ukázána velmi jednoduchá klasifikační neuronová síť, která pro dvě vstupní hodnoty v rozsahu 0,0 až 1,0 vrátí informaci o tom, jaký je vztah mezi těmito hodnotami. Síť se naučí do jisté míry rozpoznávat relaci „menší než“ a „rovnost“ (rozšíření na číslice bude snadné):

import torch
from torch import nn
from torch import optim
from torch.utils.data import Dataset, DataLoader
import numpy as np
 
 
 
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, hidden_dim)
        self.layer_3 = nn.Linear(hidden_dim, output_dim)
 
    def forward(self, x):
        # propagace hodnot přes neuronovou síť
        x = torch.nn.functional.sigmoid(self.layer_1(x))
        x = torch.nn.functional.sigmoid(self.layer_2(x))
        x = torch.nn.functional.sigmoid(self.layer_3(x))
        return x
 
 
# konfigurace vrstev neuronové sítě
input_dim = 2
hidden_dim = 4
output_dim = 2
 
# konstrukce neuronové sítě
nn1 = NeuralNetwork(input_dim, hidden_dim, output_dim)
 
# výpis základních informací o neuronové síti
print(nn1)
 
 
# příprava na trénink neuronové sítě
learning_rate = 0.1
loss_fn = nn.BCELoss()
 
optimizer = optim.SGD(nn1.parameters(), lr=learning_rate)
 
 
# 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
 
 
# příprava trénovacích dat
X_train = []
y_train = []
 
for a in np.linspace(0, 1.0, 101):
    for b in np.linspace(0, 1.0, 101):
        # vstupy
        X_train.append([a, b])
        # očekávané výstupy
        y_train.append([1.0 if a>b else 0.0, 1.0 if abs(a-b)<0.2 else 0.0])
 
train_data = Data(np.array(X_train), np.array(y_train))
 
# 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 = nn1(X)
 
        # výpočet účelové funkce
        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")
 
# naivní otestování neuronové sítě
for x in np.linspace(0, 1, 11):
    X = torch.tensor([float(x), 1.0-float(x)])
    y = nn1(X)
    gt = y[0] >= 0.5
    eq = y[1] >= 0.5
    relation = "?"
    if eq:
        relation = "=="
    elif gt:
        relation = "> "
    else:
        relation = "< "
    print(f"{x:4.3} {relation} {1.0-x:4.3}")

Výsledky po natrénování této sítě:

 0.0 <   1.0
 0.1 <   0.9
 0.2 <   0.8
 0.3 <   0.7
 0.4 <   0.6
 0.5 ==  0.5
 0.6 >   0.4
 0.7 >   0.3
 0.8 >   0.2
 0.9 >   0.1
 1.0 >   0.0

17. Konvoluční neuronové sítě

Jak je tedy možné zlepšit odhad sítě i v případě, že očekáváme, že obrázky posílané na její vstup budou posunuty, nepatrně otočeny, zkoseny atd.? Máme k dispozici více řešení. Buď udělat síť mnohem víc robustní, což znamená výrazně zvětšit počet skrytých vrstev, zvětšit počet neuronů v těchto vrstvách a o několik řádů zvětšit i množství trénovacích dat (různé formy offsetu, posun jen některých pixelů atd.). To je sice skutečně možné zařídit (ostatně zaplatíme za to „jen“ strojovým časem), ovšem stále zde narážíme na principiální omezení klasických vrstvených neuronových sítí – jednotlivé neurony se učí izolovaně od ostatních neuronů, zatímco na vstupu máme „plovoucí“ obrázek. Bylo by tedy výhodnější se zaměřit na vylepšení samotné architektury neuronové sítě specializované právě na to, že na vstupu bude mít bitmapy a tudíž by sousední neurony měly nějakým způsobem sdílet své váhy na vstupech. Taková architektura již ve skutečnosti byla dávno vymyšlena a jmenuje se konvoluční neuronová sít.

Poznámka: stále musíme mít na paměti, že i konvoluční neuronové sítě jsou založené na klasických dopředných sítích, které navíc bývají tzv. hluboké.

18. Vrstvy v konvolučních neuronových sítích

V konvolučních neuronových sítích se používají vrstvy se speciálním významem i chováním. Jedná se především o takzvané konvoluční vrstvy, které jsou napojeny přímo na vstupní vrstvu popř. na subsamplingové vrstvy. Konvoluční vrstvy se skládají z obecně libovolného množství příznakových map, podle toho, jaké objekty nebo vlastnosti vlastně v obrázku rozpoznáváme. Zpracovávaná bitmapa se zde rozděluje na podoblasti, které se vzájemně překrývají. Neurony přitom mohou sdílet své váhy přiřazené vstupům. Jak přesně to funguje si řekneme příště. Mezi jednotlivé konvoluční vrstvy se vkládají subsamplingové vrstvy, které jsou z výpočetního hlediska jednodušší, protože neurony zde obsahují jen dvě váhy (součet vstupů+práh). Tyto vrstvy získaly svoje jméno podle toho, že umožňují provádět podvzorkování založené většinou na velmi jednoduchých funkcích aplikovaných na okolí každého pixelu (maximální hodnota, střední hodnota…).

Typicky se vrstvy střídají takto:

hacking_tip

  1. Vstupní vrstva
  2. Konvoluční vrstva #1
  3. Subsamplingová vrstva #1
  4. Konvoluční vrstva #2
  5. Subsamplingová vrstva #2
  6. Klasická skrytá vrstva
  7. Výstupní vrstva

Existují ovšem i další možnosti, opět se o nich zmíníme příště.

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

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.