Neurokuvatreenit, harj 3

Tähänastisissa harjoituksissa olemme antaneet kuvan kehittyä vertaamalla sitä pikseli pikseliltä tavoitteeseen. Malli näyttää toimivan, mutta yleisemmin siinä on se ongelma, että tämä malli ei oikeasti mittaa samankaltaisuutta. Ei tarvitse kuin siirtää pikseleitä hieman sivuun, ja malli arvioi kuvan ihan erilaiseksi, vaikka se todellisuudessa näyttää ihan samalta.

Pikselikohtainen virhe on silti hyödyllinen ja paljon käytössä, mutta sen rinnalla käytetään myös muita tapoja. Tässä harjoituksessa otamme käyttöön kaksi. SSIM (structural similarity) jossa lasketaan matemaattinen arvio vertailemalla kuvien rakennetta, ja ns. perceptual loss, jossa kuvaa tulkitsevan neuroverkon avulla mitataan, ei pikseleitä vaan eri tasoisia piirteitä. Palaamme myöhemmissä harjoituksissa tarkemmin siihen miten neuroverkkoa voi käyttää tunnistamaan ja mittamaan kuvassa olevia piirteitä.

Saamme nuo molemmat vertailukeinot valmiina kirjasto-ohjelmina. Ne pitää ensin asentaa, se onnistuu parhaiten jollakin pakettihallintaohjelmalla, joista lisää asennusta käsittelevässä artikkelissa. Esim. komentorivillä

pip install pytorch_msssim
pip install lpips
pip install scipy
pip install tqdm

Sinänsä ohjelmamme toimii edelleen saman kaavan mukaan kuin harjoituksessa 2. Luemme kaksi kuvaa sisään ja luomme tyhjäksi alustetun tensorin ikäänkuin kuvan kasvualustaksi. Käymme mittaamaan samankaltaisuutta ja säädämme kuvaa sen mukaan. Oleellisin ero on tavoitefunktiossa, jossa nyt käytämme edellä mainittuja funktioita. Painotamme myös kumpaakin niistä erikseen, jotta on helppo kokeilla mikä vaikutus on rakenteellisella ja perseptuaalisella mittauksella.

Mikä koodissa sitten muuttuu? Tarvitsemme ainakin kaksi uutta parametria, jotta voimme painottaa mittaustapoja erikseen.

parser.add_argument('--ssim', type=float, default=1, help='blend factor, 0 for none')
parser.add_argument('--lpips', type=float, default=0, help='blend factor, 0 for none')

Meidän myös pitää ottaa kirjastot käyttöön ohjelmassamme, ei riitä että ne on koneelle asennettu. Näistä ssim on suoraan valmis funktio käyttöön tavoitefunktiossa, lpips:iltä taas ikäänkuin haemme halutunlaisen funktion ja nimeämme sen ploss.

from pytorch_msssim import ssim
import lpips

ploss = lpips.LPIPS(net="alex", spatial=True)

Tavoitefunktiota, siis virheen laskentaa, pitää myös muuttaa. Laskemme virheen kumpaankin tavoitteeseen nähden, painottaen kahta laskutapaa halutulla tavalla. Sitten laskemme virheet yhteen, painottaen jälkimmäistä b-parametrin mukaan, kuten teimme kakkosharjoituksessakin.

    # calculate loss
    loss1 = opt.ssim * (1 - ssim(imgT.unsqueeze(0), imgG.unsqueeze(0))) + opt.lpips * ploss(imgT.unsqueeze(0), imgG.unsqueeze(0)).mean()

    loss2 = opt.ssim * (1 - ssim(imgT2.unsqueeze(0), imgG.unsqueeze(0))) + opt.lpips * ploss(imgT2.unsqueeze(0), imgG.unsqueeze(0)).mean()

    loss2 = opt.b*loss2
    
    loss = loss1 + loss2

    # run backwards to find gradient (how to change imgG to make loss smaller) 
    loss.backward()

    # run optimizer to change imgT
    optimizer.step()

    # print loss to show how we are doing
    print(i, loss.item(), loss1.item(), loss2.item())

Tarkasti katsoen huomaamme eron siinä miten ssim ja lpips/ploss käsitellään. Joudumme laskemaan rakenteellisen virheen

1 - ssim(kuva1, kuva2)

koska haluamme mitata eroa, mutta SSIM mittaakin samankaltaisuutta. Tiedämme myös, että SSIM on 1 silloin kun kuvat ovat samanlaiset, eli näin laskien meillä on virhefunktio joka lähestyy nollaa mitä paremmin kuvat muistuttavat toisiaan.

Voi tuntua hankalalta, että eri kirjastot ja niiden tarjoamat funktiot käyttäytyvät eri tavoin. Tässä funktiot mittaavat eri suuntaan ja niitä käytetään muutenkin eri lailla. Olemme myös huomanneet että kuvien käsittelyssä joudumme normalisoimaan pikselien arvot eri tavoin ja välillä muuttamaan niitä. Kannattaa suhtautua niin että näin vain on. Kuhunkin kirjastoon kannattaa perehtyä ja niiden käyttöä voi itse kokeilla helpoilla esimerkeillä. Me kun kuitenkin tarvitsemme niitä kirjastoja, juuri ne tekevät tämän homman mahdolliseksi. Yleensä noille valinnoille on hyvät syyt.

Katsotaanpa sitten millaista kuvaa nämä tekevät. Käytetään testikuvina näitä kahta. Kokeillaan sitten SSIMiä kumpaankin erikseen ja sitten molempiin yhdessä.

Ja sitten LPIPS vastaavasti. Se tuntuu ainakin vaativan huomattavasti enemmän iteraatioita ja mahdollisesti tarkemmin säädetyn koulutustahdin päästäkseen lähellekään valmista kuvaa. Vähän vaikuttaa siltäkin että LPIPS tunnistaa hyvin kuvissa olevia pieniä piirteitä mutta ei oikein hahmota isoa kokonaisuutta. Voi siis hyvin olla, että se sopii paremmin käytettäväksi yhdessä jonkin muun tavan kanssa, vaikkapa pikselikohtaisen eron tai SSIM:in.

Ja nyt sama vielä niin että molemmat menetelmät ovat käytössä. Ja samantien on huomautettava että eri asetuksilla (painotukset ja koulutustahti) tulokset muuttuvat ja vain kokeilemalla lopulta tietää kuinka paljon. Tässä nyt ssim 300 ja lpips 1, jolloin ssim aluksi säätää kuvan alustavsti kohdalleen ja sitten kun ssim:in virhe on tarpeeksi pieni, lpips pääsee vaikuttamaan hienosäätöön.

Kaikki lpips:iä käyttäen syntyvät kuvat jäävät varsin keskisävyisiksi, harmaiksi. Tätä ei nyt kannata pysähtyä ihmettelemään. On mahdollista ja aika todennäköistäkin, että LPIPS neuroverkkopohjaisena on herkempi kuvan arvoalueen suhteen kuin SSIM, ja ehkä vaatisi sille syötetyn kuvan pikselinarvojen säätöä.

Tarvittaessa myös valmiin kuvan kirkkautta/kontrastia tai histogrammiakin voi säätää tarpeen mukaan.

Vastaa

Sähköpostiosoitettasi ei julkaista. Pakolliset kentät on merkitty *