# ML 5. Umělá inteligence ve zpracování obrazu
Formování obrazu (PSF, OTF, vzorkování). Klasifikace obrazu (VGGNet, GoogLeNet, ResNet, SENet). Detekce objektů (R-CNN, Fast R-CNN, Faster R-CNN, YOLO). Segmentace obrazu (FCN, UNet, Mask R-CNN). Podmíněné a nepodmíněné generativní modely (autoregresivní modely, VAEs, GANs). Modely založené na konvolučních sítích a transformerech (attention, CNN vs. ViT)
## Formování obrazu
Obraz = funkce ze souřadnic do intenzit
* spojitý obraz: $ℝ^m → ℝ^c$
* diskrétní obraz: $ℤ^𝑚 → ℝ^c$
* digitální obraz: $ℤ^𝑚 → ℤ^c$
m je počet dimenzí, například 1 pro audio signál, 2 pro obrázek
c je počet kanálů, např. 3 pro RGB, 1 pro grayscale
Zobrazovací systém (mikroskop, kamera, rentgen, …) zachycuje scénu do digitálního obrazu
Prvně se scéna zobrazí na “Image plane”, poté se vzorkováním a kvantizací převede na digitální obraz
### PSF - Point Spread Function (Rozptylová funkce)
Rozptylová funkce popisuje, jak zobrazovací systém (kamera, …) zobrazí bodový zdroj světla na vstupu do obrazu na výstup (**impulse response**). Samotnou scénou se už nebudeme zabývat a zobrazovací systém budeme brát že bere na vstup obraz (tzn. funkci) a jiný obraz dává na výstup (jinou funkci).
Rozptylová funkce může být obecně komplikovaná, ale velké množství zobrazovacích systémů lze dostatečně přesně modelovat jako **linear shift-invariant system (LSI)**, tzn:
* rozptylová funkce nezávisí na poloze (shift invariant), tzn:
$LSI(T(g)) = T(LSI(g))$
* rozptylová funkce je lineární, tzn:
* kombinace dvou obrazů na vstupu a poté zobrazit je jako zobrazit každý zvlášť a pak je zkombinovat: \
$LSI(g1 + g2) = LSI(g1) + LSI(g2)$
* zesílení signálu na vstupu zesílí signál na výstupu \
$LSI(n * g) = n * LSI(g)$
$g, g1, g2$ jsou obrazy, $n$ je skalár a $T$ je translation operátor (posunutí)
Pro LSI je rozptylová funkce jednodušší (protože odpadá hromada parametrů?) a celý zobrazovací systém lze vyjádřit jako **konvoluci**
Klasický příklad rozptylové funkce (PSF) je gaussovský noise (rozostření):
<table>
<tr>
<td>

kernel pro gaussovské rozostření
</td>
<td>

příklad
</td>
</tr>
</table>
Ukázka ze slidů, kde zobrazovací systém každý bod na vstupu zobrazí na “šmouhu” ve tvaru PSF

### Konvoluce, Vzájemná korelace (Cross-corelation)
není přímo v otázce, ale bez toho zbytek nepůjde :D

Spojitá varianta je prakticky stejná, ale má tam integrály místo sum
Konvoluce je operátor:
* lineární
* => zvýšení intenzity obrazu na vstupu přímo zvýší intenzitu na výstupu
* => 2 obrazy můžeme sečíst a pak konvolutit, nebo prvně konvolutit a pak sečíst a dostaneme stejný výsledek
* ekvivariantní k posunu (translation equivariance)
* => pokud posuneme něco na vstupu tak se posune zobrazení té věci na výstupu
Využití:
* hledání vzorů
* detekce hran, blobů, rohů, textur
* rozmázání, low-pass filter (denoising)
* CNN
### OTF - Optical Transfer Function
= Fourierova transformace rozptylové funkce (point spread funkce)
* charakterizuje frekvence, které se skrz zobrazovací systém přenesou
* ve skutečných zobrazovacích systémech je konečná
* nejvyšší přenesená frekvence se nazývá cut-off frekvence
Convolution theorem:

### Vzorkování
= diskretizace vstupního signálu (osa x pro 1D signál)
fundamentální teorie: **Nyquistův–Shannonův vzorkovací teorém**
* dává do spojitosti signály se spojitým časem a diskrétním časem
* v audiu mluvíme o diskretizaci na časové ose, v obrazu jde o 2 prostorové souřadnice x, y
* pro jednoduchost tu mluvíme o 1D signálu a časové ose
Pokud je spojitá funkce f(x) složená pouze z frekvencí nižších než B, potom je bezeztrátově a jednoznačně určena funkčními hodnotami vzorkovanými pravidelně v kratším intervalu než 1/(2B).
Pozorování:
* vysoké frekvence v signálu jsou obvykle šum a nemají význam
* senzory (příp. lidské ucho, atd.) mívají maximální frekvenci (cut-off frekvenci) a vyšší neumí rozlišit
Důsledky a význam **Nyquistova-Shannonova vzorkovacího teorému**:
* signál (audio, obraz,…) se spojitou časovou umíme “dokonale” zrekonstruovat pokud ho navzorkujeme dostatečně časo
* “dokonale” = do libovolné přesnosti, tzn. libovolně vysoké frekvence \
zejména do cut-off frekcence zobrazovacího systému (např. lidského ucha) \
nebo do frekvence, od které se dá všechno v signálu považovat za šum
Omezení:
* Nyquistův–Shannonův vzorkovací teorém mluví o diskretizaci časové osy, ne o amplitudě
* diskretizace amplitudy se nazývá kvantizace a podobný teorém nemá?
## Klasifikace obrazu
* dnes obvykle pomocí strojového učení
* hluboké neuronové sítě
### VGGNet
Hlavní nápad:
* několik po sobě jdoucích 3x3 konvolucí (a aktivačních fcí) má méně parametrů a více nelinearity než jedna velká konvoluce (např. 7x7) při stejném efektivním receptivním fieldu
* => nahradíme všechny konvoluce více 3x3 konvolucema

### GoogLeNet
Hlavní nápady:
* Zpochybnění toho, že 3x3 konvoluce jsou optimální, nebo že kterákoli jiná konkrétní velikost je optimální
* Můžeme nechat v neuronce víc velikostí naráz paralelně
* => inception blok - obrázek níže
* velké konvoluční filtry na hodně kanálech potřebují velké množství parametrů
* můžeme redukovat počet vstupních kanálu pomocí 1x1 konvoluce
* GoogLeNet 1x1 konvoluce zpopularizoval
* Odstraníme finální FC vrstvy na konci, protože mají velkou dimenzi:
* (WxHxC) x FC_dim, například 7x7x512 x 4096 = 100M parametrů
* většina parametrů je tam, což intuitivně nedává moc smysl
* nahradíme je za globální avg pooling který srazí (WxHxC) -> (1x1xC)
* snaha o vyřešení vanishing gradientu
* do třetiny a dvou třetin celé CNN přidáme pomocné vedlejší klasifikační hlavy
* při tréningu počítáme loss na všech 3 klasifikačních hlavách
* po tréningu pomocné hlavy odstřihneme

žluté bloky jsou bottlenecky, které snižují dimenzi a tím snižují počet parametrů které jsou potřeba ve vrstvě která na ně navazuje
Celková architektura:

### ResNet
Hlavní myšlenky:
* Hluboké sítě se nechtějí učit a jsou horší než mělké
* kvůli vanishing gradientu
* potřebujeme vyřešit
* řešení:
* Residuální bloky jsou all you need!

ResNet je potom jednoduchá architektura:
* za sebou naskládané residuální bloky s 3x3 konvolucí, ReLU
* buď se počet kanálů a velikost featur map nemění
* nebo se zdvojnásobí počet kanálů a redukuje výška a šířka na polovinu
* na konci global pooling a 1 FC vrstva pro klasifikaci
* basically gigachad v roce 2016
### SENet
* snaží se potlačit nepodstatné feature mapy a vyzdvihnout ty důležité
* kompatibilní s jinými architekturami
* stači do architektury vložit squeeze-and-excitation blok (SE blok):

- SENet dynamicky zesiluje/zeslabuje feature mapy
## Detekce Objektů
### R-CNN
* historický a naivní přístup k detekci objektů
* algoritmicky generujeme kandidáty (region proposals) na vstupním obrázku
* CNN potom pro každý region proposal:
* klasifikuje třídu - jaká třída nebo NIC
* predikuje regresí t_x, t_y, t_h, t_w - korekce bounding boxu

Problém R-CNN je ve výpočetní náročnosti - pro 2k region proposalů potřebujeme 2k forward průchodů CNN
### Fast R-CNN
* zefektivnění R-CNN
* Používáme stejný region proposal algoritmus
* ale vyřezáváme regiony až na výstupu backbone CNN:

Drobný problém:
* feature mapy mají nižší rozlišení než originální obraz
* takže proposal region je potřeba rozumně namapovat na feature mapy
* řešení:
* ROI Pool - snap to closest pixels in feature maps
* ROI Align - bilineární interpolací zarovnat
* skrz obě operace se dá backpropagovat
<table>
<tr>
<td>

ROI Pool
</td>
<td>

ROI Align
</td>
</tr>
</table>
### Faster R-CNN
Motivace:
* hand-coded region proposal metody:
* nejsou kvalitní
* jsou pomalý (běží na CPU)
* chceme je naučit a mít součástí NN architektury
Architektura:
* na začátku je backbone steně jako Fast R-CNN
* potom následuje region proposal network (RPN)
* předdefinujeme si několik tvarů a velikostí (templaty) anchor boxů
* RPN predikuje pro každý pixel pro všech K templaty anchor boxů
* odpovídá tento pixel středu a tento anchor boxem ohraničení nějakého objektu?
= pro každá píxel K binárních klasifikací
* jak by se musel upravit anchor box template aby přesně ohraničil objekt? \
= pro každý pixel K regresí 4 floaty
* pro HxWxC feature mapu a K anchor box template má výstup shape \
HxWx(K+4K)
* top kandidáti se potom pošlou do per-region CNN která je stejná jako v Fast R-CNN

### YOLO (You Only Look Once)
pozorování:
* region propsal network a per-region network působí duplicitně (říká se tomu two-stage detector)
řešení:
* zjednoduššíme architekturu
* spojíme region proposal network a per-region network (single stage detector)
* proženeme vstup skrz backbone a pak
* 1 CNN která pro každý pixel a K anchor box šablon predikuje:
* odpovídá tento pixel středu a tento anchor boxem ohraničení nějakého objektu, a jakého?
* jak se tento anchor box template musí upravit?
* vstupní dimenze po backbone:
* HxWxC
* výstupní dimenze:
* klasifikační hlava: H x W x K x (N+1)
* regresní hlava: H x W x 4K x N
## Segmentace obrazu
* rozdělení obrazů do regionů odpovídající (sémantickým) třídám, jako obloha, pes, tráva
* lze přeformulovat jako klasifikace každého pixelu do N tříd
* přirozeně lze řešit konvolučníma sítěma
### FCN - Fully Convolutional Network
* naivní CNN architektura
* Konvoluční vrstvy které nemění spatial dimenze

Problém:
* výpočetně náročné pro jakékoli netriviální množství latentních feature map protože konvoluce na plném rozlišení jsou drahé
Zefektivnění:
* snižovat rozlišení se zvyšujícím počtem kanálů
* symetricky potom zrekonstruovat stejnou dimenzi jako originální input

Potřebujeme způsob jak zvýšit spatial dimenzi feature map
Příklady upsampling vrstev bez učených parametrů:
<table>
<tr>
<td>

</td>
<td>

</td>
</tr>
<tr>
<td colspan="2" >

</td>
</tr>
</table>
Učenou verze upsamplingu lze naimplementovat pomocí transposed konvoluce
Intuitivní představa je že Transposed konvoluce “rozkopírovává” input pronásobený kernelem.
Nejlépe je to vidět na variantě kde stride = velikost kernelu:

transposed konvoluce s 2x2 maticí plnou jedniček a stridem 2 odpovídá nearest-neighbor upsamplingu
Takhle vypadá varianta se stridem 1:

### UNet
Problémy, které chceme vyřešit:
* FCN která zachovává spacial dimenzi je neefektivní
* FCN která má uprostřed malou spacial dimenzi má “informační bottleneck”
* = hodně informace se může ztratit v prostřední reprezentaci
Řešení:
* přidáme skip connections mezi vrstvami se stejnou spatial dimenzí

### Mask R-CNN
= adaptace Faster R-CNN pro instance segmentaci
* kromě bounding boxu chceme masky pro každý objekt
* úprava achitektury je jednoduchá
* kromě klasifikace třídy a regrese pro korekci bounding boxu se na konci per-region CNN predikuje i binární maska

## Podmíněné a nepodmíněné generativní modely
Intuitivně:
* X jsou komplikovaná data
* Y jsou primitivní data (labely)
* P(Y) je prior na labelech (snadno aproximujeme z četností výskytu)
* P(Y | X) aproximují diskriminativní modely (klasifikátory)
* P(X) aproximují nepodmíněné generativní modely
* P(X | Y) aproximují podmíněné generativní model
Bayes rule je dává do souvislosti:

### Autoregresivní modely
* explicitně modelují hustotu p(x)
* tzn model nám na vstup x umí vrátit skalár který odhaduje jak je x pravděpodobný
* cíl je natrénovat model (odhadnout parametry w), který maximalizuje likelihood dat:

Autoregrese:
* předpokládáme že x se skládá z “částí” x_i
* pomocí chain rule vyjádříme p(X) = p(X_1) * p(X_2 | X_1) * p(X_3 | X_1, X_2) * …
* autoregresivní model potom modeluje p(X_i | X_0 … X_i-1)
* při generování x postupujeme od x_1 po x_n
* každý predikovaný prvek x_i strčíme zpět do modelu a predikujeme následující
Pixel RNN:
* u grayscale by části x_i byly jednotlivé pixely
* u RGB má máme tři části pro každý pixel
* generování každého pixelu je podmíněné všemi pixely nalevo a nahoře pomocí hidden statu RNN
* predikce pixelu je natřikrát, pro každý kanál softmax 0 - 256

Pixel CNN:
* podobné Pixel RNN, ale používá konvoluce na lokálním okolí místo RNN hidden stavu, který se musí počítat při tréningu úplně sekvenčně
* generování je pořád extrémně pomalé

### VAE - Variational Autoencoder
klasické autoencoder:
* 2 části, encoder a decoder
* trénujeme:
* z = enc(x)
* x’ = dec(z)
* minimize loss(x, x’)
* **z** se nazývá latentní vektor
* dim z < dim x, jinak by se encoder mohl naučit prostě kopírovat **x** do **x’**
* jak generovat?
* a pak vyrobit nová data jako: x’ = dec(random z)
* ale my nevíme nic o tom jakou distribuci má **z**
* takže jsme namydlení
Variační autoencoder:
* snaží se vynutit konkrétní distribuci na **z**
* potom z ní můžeme samplovat a generovat nová data pomocí decoderu
* enc(x) nově predikuje distribuci p(z | x)
* rodinu distribucí si vybereme předem
* a encoder predikuje pouze parametry
* obvyklá volba je gausián s diagonální kovarianční maticí
* pak encoder predikuje pouze n průměrů a n rozptylů (nebo std)
* z se potom nasampluje z téhle distribuce z ~ enc(x)
* analogicky, decoder predikuje distribuci p(x’ | z)
* ale někde se používá i deterministický encoder, který přímo predikuje jedno konkrétní x’
* obvyklá volba je opět gausián s diagonální kovarianční maticí
Loss funkce pro VAE:
* skládá se ze 2 částí: reconstruction loss - regularizace
* reconstruction loss:
* snaží se o aby VAE přiřazoval velkou pravděpodobnost examplum z datasetu
* regularizace:
* snaží se vynutit, aby encoder predikoval distribuce pro z blízké **prior distribuci**
* pro gaussiány s kovarianční maticí bude prior, že průměry = 1 a rozptyly = 1
* díky tomu můžeme pak generovat nová data:
* samplovat z ~ prior
* nová data x’ ~ dec(z)
Formálně:
* enc(x) … predikovaná distribuce přes **z**
* dec(z) … predikovaná distribuce přes **x’**
* dec(x | z) … predikovaná pravděpodobnost x za podmínky **z**
* Celková loss:
$$ \mathbb{E}_{x \sim D, z \sim \text{enc}(x)} \log \text{dec}(x \mid z) - \mathbb{E}_{x \sim D} \text{KL} \left( \text{enc}(x) \parallel Z_{\text{prior}} \right) $$
* první části taky říkáme Variational Lower Bound (ELBO)
* není to přímo pravděpodobnost kterou autoencoder přiřazuje x protože místo toho abychom integrovali (sumovali) přes všechny možný z, prostě náhodně samplujeme
* jenže integrovat přes všechny **z** je drahé, samplovat ze **z** je levné
* probabilita **x** given jedno **z** je je nižší než suma probabilit pro **x** given z přes všechny **z**
* => to je ta “lower bound”
* předpoklad je, že když maximalizujeme lower bound funkce F, tak tím nakonec maximalizujeme i funkci F, protože jí “zespodu vytláčíme” :D
V případě, že omezujeme Z~enc(x) a X’~dec(x) na gausiány s kovarianční maticí, tak:
* KL(z || Z_prior) má closed-form řešení (vzoreček)
* dec(x | z) je dosazení **x** do hustoty gausiánu, kterou decoder predikuje: dec(z)
Repametrizační trik
* my samplujeme latentní vektory **z** uprostřed forward průchodu VAE
* potřebujeme zajistit aby skrz samplování šlo backpropagovat
* → reperametrizační trik:
* přeformulujeme jak vzniká latentní vektor z
* místo originálního:
* z ~ enc(x)
* použijeme:
* means, variances := enc(x)
* eps ~ N(0, 1)
* z = means + variances * eps
* díky tomu je **z** diferencovatelné vzhledem k **means** a **variances**, a tím k params encoderu

### GAN - Generativní Adversariální Síť

* GAN modeluje p(x) implicitně
* můžeme samplovat z aproximace datové distribuce
* ale neodhadujeme p(x)
* D(x) není aproximace p(x) !!!
* D(x) nemodeluje hustotu (nebo pravděpodobnost) x
* a rozhodně se neitegruje (nesčítá) do 1 přes všechny možné vstupy
* G a D hrají adversariální hru
* D se snaží rozpoznat fake data
* G se snaží oblafnout D
* Účelová funkce je založená na binární cross-entropii
* vysvětlení z formely: [Binary Cross-entropy](https://www.notion.so/GAN-formela-7bbfd09528a343dcb429a8f150dce7e0?pvs=4#10c2504b25fe46e899b5014d064ef83a)
GAN účelová funkce:

Řešení hry:
* (předpokládáme bezmeznou expresivní sílu D i G)
* pro fixní G, existuje unikátní globálně optimální D
* za předpokladu, že D bude optimální protihráč, existuje globálně optimální G
* → tzn, existuje globální a unikátní Nashovo ekvilibrium
* v ekvilibriu:
* G dokonale modeluje datovou distribuci
* D neumí rozpoznat fakes a reaálné data
* ani jeden si sám nemůže přilepšit (z definice ekvilibria)
tréning konverguje do ekvilibria, za divokých teorietických předpokladů
* NNs mají nekonečnou expresivní sílu
* máme nekonečně dat
* po každým updatu G, děláme nekonečně mnoho updatů pro D
* …
Praktické problémy:
* nedostatečné gradienty
* pro D je snadné poznat fakes, takže posílá do G malý grady
* (= G neumí žádnou malou změnou rozumě ovlivnit výstup D)
* Mode collapse:
* G najde slabinu v D and exploituje ho
* ⇒ G produkuje nediverzní popřípadě degenerovaný samply
* oscilace:
* tréning mode-collapsne
* D se naučí rozpoznávat fakes z toho kolapsu, ale zapomene něco z dřívějšího tréningu
* G najde novou slabinu → nový mode collapse → opakuje se dokola
## Transformer
### Self-Attention
parametry:
* query matice W_Q: D x D_q
* key matice W_K: D x D_q
* value matice W_V: D x D_v
vstup = posloupnost vektorů
* coby matice X: N x D
* může být třeba:
* posloupnost slov
* patche obrázku
výstup = posloupnost vektorů:
* coby matice Y: N x D_v
Výpočet:
* Q = X @ W_Q
shape N x D_q
* K = X @ W_K
shape N x D_q
* V = X @ W_V
shape N x D_v
* E = Q @ K.T / sqrt(D_q)
shape N x N
* A = softmax(E, row-wise)
shape N x N, každý řádek se sčítá do jedné
* Y = A @ V
shape N x D_v
Y jsou nové reprezentace vstupu X, ve kterém se jednotlivé vektory “vzájemně obohatili” o kontext
Vizualizace výpočtu (ale nesedí tu pořadí řádky/sloupce s formální notací)

### Cross-Attention
= rozšíření self-attention
Nově máme 2 vstupy:
* jeden, který obohacujeme - Src
* druhý, pomocí kterého obohacujeme - Tgt
* Src a Tgt mohou mít různé dimenze, matice W_* nám to “zarovnají”
Změny:
* queries se počítají ze Src
* keys a values se počítají z Tgt
* Y jsou na konci výpočtu nové reprezentace pro Tgt
Využití:
* flexibilní varianta attention
* umožňuje kombinovat více vstupů nebo dokonce modalit
* příklady:
* similarita textu a obrázku
* popisování obrázku
* audio transkript
* strojový překlad
* …
### Multi-Head Attention
= zapojení několika paralelních attention vrstev vedle sebe
Výstupy se concatenují a pak pomocí jedné učené lineární vrstvy zobrazí na požadovanou dimenzi
### Poziční kódování
Není součástí otázky explicitně, ale byla by asi chyba o něm nic nevědet, když je v otázce transformer
Pozorování:
* attention vrstva je “permutation equivariant”
* tzn, pokud přemícháme pořadí vektorů na vstupu:
* přemíchá se pořadí vektorů na výstupu
* ale jejich obsah bude stejný
* → attention vrstvy nemají (a teedy ani nepoužívají) informaci o tom:
* kde ve vstupu se vektor nachází
* jak jsou od sebe ve vstupu daleko
* to je důležité protože na pořadí a relativních vzdálenostech ve vstupu záleží
* například pořadí slov ve větě mění význam
* pokud máme patch v obrázku obsahující červené kalhoty, patch nad ním má modré tričko a patch 10 bloků daleko obsahuje zelené triko, lze usoudit že osoba s červenými kalhotami má modré tričko a patch se zeleným je irelevantní
Poziční kódování přidává do vstupu informaci o tom, kde se jednotlivé části vstupu nachází
Populární typy:
* naučené
* máme učitelnou embedding matici E dimenze (N x D_model)
* k i-té části vstupu se přičte (popř. concatenuje) poziční kódování E[i]
* parametry E se trénují souběžně s ostatními parametry modelu
* relativní
* nemění reprezentace vektorů
* ovlivňuje pouze výpočet attention
* předpokládá že je důležitá relativní vzdálenost částí a ne jejich absolutní pozice ve vstupu

* sinusoidní
* používá stejnou matici E jako naučené, ale nemá trénovatelné parametry
* má dobré vlastnosti, například:
* attention hlava se může naučit attendovat o n pozic dopředu a dozadu
* matice E vypadá asi takhle:

### Transformer Encoder
= model, který využívá:
* poziční kódování
* Bloky složené z:
* attention
* MLP
* residuálních spojení
* normalizace

### Vision Transformer

Lineární vrstvy v transformer MLP části lze chápat jako konvoluce se kernelem velikost 1:
* protože jde o lineární vrstvu, která se aplikuje na každou pozici samostatně