SAMSUNG_022024A Advertisement SAMSUNG_022024A Advertisement SAMSUNG_022024A Advertisement

ML v Pythone 8 – príklad rozpoznávanie obrazu

0

predchádzajúcej časti sme ukázali konfiguráciu NVIDIA CUDA pre výpočty pomocou grafickej karty. V ôsmej časti sa budeme venovať rozpoznávaniu obrazu. Potrebujete mať nainštalovanú platformu Anaconda a knižnicu PyTorch. Príklady sú koncipované tak, aby fungovali aj na PC, ktoré nemajú grafickú kartu NVIDIA.

Postup vytvorenia príkladov je v krátkom videu

Neurónová sieť ResNet

Pri rozpoznávaní obrazu využijeme už natrénovanú neurónovú sieť ResNet (Residual Neural Network) o ktorej sa viac dozviete napríklad na Wikipédii. Táto neurónová sieť bola vytrénovaná na obrovskej databáze obrazov Imagenet https://www.image-net.org/ ktorá obsahuje 150 GB farebných obrázkov Dokáže klasifikovať 1000 rôznych kategórií, ako sú  predmety, časti tela, zvieratá, vozidlá a podobne. Kategórie sú vymenované v textovom súbore https://github.com/pytorch/hub/blob/master/imagenet_classes.txt Tento súbor využijeme v našom kóde

Reziduálna konvolučná hlboká neurónová siet’ ResNet má okrem klasických prepojení medzi dvoma bezprostredne po sebe nasledujúcimi vrstvami aj takzvané reziduálne skokové spojenia, čiže prepojenia vrstiev, ktoré po sebe bezprostredne nenasledujú. Takáto architektúra urýchľuje trénovanie a eliminuje problém takzvaného preučenia. Klasické architektúry konvolučných  neurónových sietí neboli schopné škálovať na veľký počet vrstiev, čo malo za následok obmedzený výkon. Vrstvy, ktoré nie sú relevantné ResNet jednoducho preskakuje.

Príklad rozpoznávania obrazu

Budeme pracovať v pracovnom priestore, ktorý sme vytvorili v predchádzajúcom dieli. Máme tam nainštalovanú knižnicu PyTorch. Ak máte v PC alebo notebooku grafickú kartu NVIDIA určite využite vyšší výpočtový výkon prostredníctvom GPU s využitím platformy NVIDA CUDA. Postup inštalácie je tiež v predchádzajúcom dieli.

V konzolovej aplikácii Anaconda PowerShell Prompt sa prepneme sa do príslušného pracovného priestoru v našom prípade do ll_ML

conda activate ll_ML

 a nainštalujeme tam ešte aj knižnicu Pandas na prácu s údajmi ak ju ešte nemáte nainštalovanú

conda install pandas

Knižnica PIL (Python Imaging Library) https://pillow.readthedocs.io/en/stable/ je súčasťou platformy Anaconda.

Tento príklad môžete spustiť aj v prípade ak nemáte grafickú kartu NVIDIA. Vtedy použite riadky pre CPU, ktoré sú v komentári a do komentára dajte riadky kódu určené pre GPU. Do adresára, ktorý využíva PyTorch, v našom prípade je to adresár aktívneho používateľa skopírujte nejaký obrázok. Môže to byť obrázok vášho zvieratka, auta a podobne. Ja som použil obrázok nášho yorkšíra Tobbyho v súbore  tobby1.jpg. Ak máte všetko správne nainštalované a nakonfigurované, mal by sa vám v prostredí Jupyter Notebook váš obrázok zobraziť pod blokom kódu.

import pandas as pd
import torch
from torchvision import models, transforms
from PIL import Image
 
#pre GPU NVIDIA dajte riadok do komentára
model = models.resnet50(pretrained=True)
#pre GPU NVIDIA odkomentujte tento riadok
# model = models.resnet50(pretrained=True).to("cuda")
obrazok = Image.open("./tobby1.jpg")
obrazok

Na obrázok ktorý má v našom prípade veľkosť 754 x 567 pixelov a je v súbore, ktorý má 189 kB  je potrebné aplikovať niekoľko transformácií. Najskôr ho zmenšíme povedzme na 256 pixelov. Prvá transformácie teda bude zmena veľkosti, v našom prípade sa kratšia strana zmenší na 256 pixelov

transformacia = transforms.Compose([transforms.Resize(256)])
obrazok_t = transformacia(obrazok)
obrazok_t

Príkazom print(obrazok_t.size) zistíte rozmery obrázku po zmenšení, v našom prípade má obrázok teraz rozmery 340 x 256 pixelov. Samozrejme rozpoznávanie nebude pracovať s obrázkom ako takým, ale s údajmi.  Príkaz transforms.ToTensor() prekonvertuje obrázok do takzvaného tenzora, čiže viacrozmernej dátovej  štruktúry. Neurónová sieť očakáva takzvaný normalizovaný vstup. To znamená, že je potrebné zmenšiť rozsah hodnôt bez ovplyvnenia ich pomerov. Parametre pre normalizáciu príkazom transforms.Normalize() sme prevzali z príkladu z dokumentácie k platforme PyTorch https://pytorch.org/vision/stable/transforms.html  V dokumentácii na tejto stránke sa dozviete viac podrobností o transformáciách. V tomto príkaze urobíme dve transformácie súčasne. Mohli sme tu samozrejme urobiť naraz všetky transformácie, čiže aj transformáciu zmeny veľkosti, ale chceli sme pre názornosť zmenšený obrázok aj zobraziť, takže sme ju urobili samostatne

transformacia = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))
])
 
obrazok_t = transformacia(obrazok_t)
obrazok_t

Objekt obrazok_t sa po týchto transformáciách už nezobrazí ako obrázok, ale ako tenzor, čiže viacrozmerná štruktúra. Výpis na obrázku je samozrejme skrátený

Tenzor v tomto pre každý pixel z matice 256 x 340 obsahuje tri hodnoty pre farebné zložky RGB.  Necháme si zobraziť parametre tenzora

print(obrazok_t.shape)

torch.Size([3, 256, 340])

Ak každá hodnota typu float zaberá 8 bajtov, tak náš tenzor, v tomto prípade trojrozmerná matica s rozmermi 3 x 256 x 340 zaberá v pamäti 3 x 256 x 340 x 8 = 2 megabajty.  Údaje pre neurónovú sieť sa načítavajú v dávkach. Veľkosť dávky (batch size) určuje koľko dát sa použije v jednom cykle. Čim je hodnota väčšia, tým je zmena váh spôsobená väčšou vzorkou, takže odhad bude presnejší. Na druhej strane pri väčšej dávke je na výpočty potrebné viac pamäti. Väčšinou sa pracuje s veľkosťou dávky 32.

Na to treba pamätať pri príprave údajov. Prvý rozmer tenzorov udáva s veľkosť dávky. V našom prípade pre tenzor [3, 256, 340] by to znamenalo, že máme 3 obrázky na dávku. V tomto našom trojrozmernom tenzore však 3 znamená počet farebných kanálov, čiže hodnôt RGB udávajúcich počet červenej, zelenej a modrej pre každý farebný pixel. Preto musíme rozšíriť tenzor o ďalší rozmer, ktorý bude udávať veľkosť dávky. Použijeme príkaz torch.unsqueeze(). Parameter 0 znamená že tenzor rozširujeme o prvý rozmer Podrobnosti a príklad nájdete v dokumentácii 

#pre GPU NVIDIA dajte riadok do komentára
obr_upraveny = torch.unsqueeze(obrazok_t, 0)
#pre GPU NVIDIA odkomentujte tento riadok
# obr_upraveny = torch.unsqueeze(obrazok_t, 0).to("cuda")
obr_upraveny.shape
 
torch.Size([1, 3, 256, 340])

Príkazom eval() nastavíme model do hodnotiaceho režimu. Niektoré špecifické vrstvy modelu sa sa správajú odlišne počas trénovania a vyhodnocovania, takže príkaz eval() tieto vrstvy pri hodnotení vypne. Okrem toho je pri vyhodnocovaní dobrou praxou vypnúť gradienty príkazom torch.no_grad(). Gradienty, ktorým sa budeme venovať neskôr potrebujeme len pri trénovaní modelu, pri rozpoznávaní obrazu ich môžeme vypnúť, aby nespotrebovávali cennú pamäť.

# nastavíme model do hodnotiaceho režimu
model.eval()
# vypneme gradienty
with torch.no_grad():
    out_model = model(obr_upraveny)

V premennej pravdepodobnosti je zoznam pravdepodobností všetkých kategórií obrázkov, ktoré nami použitý model pozná. Funkcia softmax je aj so vzorcom vysvetlená v dokumentácii PyTorch.

# pravdepodobnosti kategórií obrázkov
pravdepodobnosti = torch.nn.functional.softmax(out_model[0], dim=0)
pravdepodobnosti

Nás zaujímajú triedy s najvyššou pravdepodobnosťou a výsledok potrebujeme v „ľudskej reči“ podľa už spomínaného zoznamu kategórií nášho modelu trénovaného podľa databázy obrazov Imagenet.   Zo stránky si skopírujeme odkaz na surové údaje https://github.com/pytorch/hub/blob/master/imagenet_classes.txt a načítame to ako CSV súbor. Hodnoty síce nie sú oddelené čiarkou, avšak každý riadok obsahuje jeden údaj, takže v podstate je to CSV. Knižnici Pandas na prácu s údajmi sme sa venovali v druhej a načítaniu údajov z CSV v tretej časti nášho seriálu.

kategorie = pd.read_csv('https://raw.githubusercontent.com/pytorch/hub/master/imagenet_classes.txt', header=None)
# na ilustráciu vypíšeme jeden záznam
kategorie[0][21]
 
'kite'

Chceme zobraziť päť najrelevantnejších odhadov.  Na našom obrázku je pes charakteristického plemena, takže relevantných bude len niekoľko málo prvých odhadov. Funkcia topk() vracia dve hodnoty, pravdepodobnosť spolu s indexom kategórie. Pravdepodobnosť  uložíme do pramennej percento. Je to desatinné číslo menšia ako 1, takže percentuálnu hodnotu dostaneme po vynásobení 100. Na základe indexu kategórie zistíme jej názov. 

pocet = 5
percenta, index_kategorie = torch.topk(pravdepodobnosti, pocet)
for i in range(pocet):
    percento = percenta[i].item()
    nazov_kategorie = kategorie[0][int(index_kategorie[i])]
    print("%{} {}".format(int(percento*100), nazov_kategorie))    
 
%44 Yorkshire terrier
%32 silky terrier
%15 Australian terrier
%0 Norfolk terrier
%0 toy terrier

Na záver kompletná zdrojovka. Vo videu sú výsledky pre iný obrázok psíka kríženca

import pandas as pd
import torch
from torchvision import models, transforms
from PIL import Image
 
#pre GPU NVIDIA dajte riadok do komentára
model = models.resnet50(pretrained=True)
#pre GPU NVIDIA odkomentujte tento riadok
# model = models.resnet50(pretrained=True).to("cuda")
obrazok = Image.open("./baska.jpg")
obrazok
 
transformacia = transforms.Compose([transforms.Resize(256)])
obrazok_t = transformacia(obrazok)
transformacia = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))
])
obrazok_t = transformacia(obrazok_t)
 
#pre GPU NVIDIA dajte riadok do komentára
obr_upraveny = torch.unsqueeze(obrazok_t, 0)
#pre GPU NVIDIA odkomentujte tento riadok
# obr_upraveny = torch.unsqueeze(obrazok_t, 0).to("cuda")
 
# nastavíme model do hodnotiaceho režimu
model.eval()
# vypneme gradienty
with torch.no_grad():
    out_model = model(obr_upraveny)
# pravdepodobnosti kategórií obrázkov
pravdepodobnosti = torch.nn.functional.softmax(out_model[0], dim=0)
kategorie = pd.read_csv('https://raw.githubusercontent.com/pytorch/hub/master/imagenet_classes.txt', header=None)
 
pocet = 5
percenta, index_kategorie = torch.topk(pravdepodobnosti, pocet)
for i in range(pocet):
    percento = percenta[i].item()
    nazov_kategorie = kategorie[0][int(index_kategorie[i])]
    print("%{} {}".format(int(percento*100), nazov_kategorie))

%26 cairn
%23 Australian terrier
%7 Norwich terrier
%4 Yorkshire terrier
%3 silky terrier   

 

Rekapitulácia doterajších dielov

Zobrazit Galériu

Luboslav Lacko

Všetky autorove články
strojove ucenie Python neuronova siet rozpoznávanie obrazu PyTorch Anaconda

Mohlo by vás zaujímať

Mohlo by vás zaujímať