{"id":243,"date":"2021-10-11T12:59:10","date_gmt":"2021-10-11T12:59:10","guid":{"rendered":"http:\/\/liipetti.net\/retkia\/?p=243"},"modified":"2021-10-11T19:57:04","modified_gmt":"2021-10-11T19:57:04","slug":"neurokuvatreenit-harj-2","status":"publish","type":"post","link":"https:\/\/liipetti.net\/retkia\/neurokuvatreenit-harj-2\/","title":{"rendered":"Neurokuvatreenit, harj 2"},"content":{"rendered":"\n<p>Ensimm\u00e4isess\u00e4 osassa tutustuimme kuvan luomiseen tavoitefunktion avulla, siit\u00e4 syntyi vihre\u00e4 neli\u00f6. Alustimme uuden kuvan satunnaisesti ja tavoitekuvan taas alustimme pelk\u00e4st\u00e4\u00e4n vihreill\u00e4 pikseleill\u00e4. Annoimme sitten koulutusprosessin mitata eroa tavoitteeseen n\u00e4hden ja sen avulla ohjasimme pikseleiden arvot kohti tavoitetta. T\u00e4ss\u00e4 ei toki tapahtunut viel\u00e4 mit\u00e4\u00e4n uutta tai hy\u00f6dyllist\u00e4, saimme kopion vihre\u00e4st\u00e4 neli\u00f6st\u00e4. Tutustuimme kuitenkin toimintamalliin, jossa kuvan synty\u00e4 ohjaa tavoitefunktio. T\u00e4m\u00e4 malli toistuu kaikissa my\u00f6hemmiss\u00e4kin harjoituksissa ja teemme sen avulla paljon mutkikkaampia asioita.<br><br>Seuraavaksi katsotaan kuinka tavoitteeksi voi ottaa jonkin tietyn kuvan. Kokeillaan samalla kuinka k\u00e4y jos meill\u00e4 onkin kaksi eri tavoitetta.<br><\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/liipetti.net\/retkia\/wp-content\/uploads\/2021\/10\/harj2.png\" alt=\"\" class=\"wp-image-244\" width=\"650\" height=\"406\" srcset=\"https:\/\/liipetti.net\/retkia\/wp-content\/uploads\/2021\/10\/harj2.png 852w, https:\/\/liipetti.net\/retkia\/wp-content\/uploads\/2021\/10\/harj2-300x188.png 300w, https:\/\/liipetti.net\/retkia\/wp-content\/uploads\/2021\/10\/harj2-768x480.png 768w, https:\/\/liipetti.net\/retkia\/wp-content\/uploads\/2021\/10\/harj2-240x150.png 240w, https:\/\/liipetti.net\/retkia\/wp-content\/uploads\/2021\/10\/harj2-150x94.png 150w\" sizes=\"auto, (max-width: 650px) 100vw, 650px\" \/><\/figure>\n\n\n\n<p>Kuvan lukemiseen k\u00e4yt\u00e4mme PIL-kirjastoa, ja m\u00e4\u00e4rittelemme kuvan esik\u00e4sittelyketjun, jossa kuva skaalataan haluttuun kokoon, muunnetaan tensoriksi ja normalisoidaan v\u00e4lille -1..1. K\u00e4ytimme n\u00e4ist\u00e4 normalisointia jo ensimm\u00e4isess\u00e4 osassa, mutta silloin loimme kuvan valmiiksi oikeankokoiseksi tensoriksi pytorchia varten. Nyt, kun kuva luetaan tiedostosta, se pit\u00e4\u00e4 skaalata tarpeen mukaan ja muuttaa tensoriksi ennen normalisointia, ja sit\u00e4 varten m\u00e4\u00e4rittelemme esik\u00e4sittelyketjun. <\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">import torch\nfrom torchvision.utils import save_image\nfrom torchvision.transforms import Compose, Resize, ToTensor, Normalize\nfrom PIL import Image\n\n# define preprocessing pipeline\n\npreprocess = Compose([\n    Resize((256, 256)),    # resize image\n    ToTensor(),            # convert to pytorch tensor\n    Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))            # normalize to range -1...1\n])<\/pre>\n\n\n\n<p>Nyt voimme sitten lukea kuvan tiedostosta ja esik\u00e4sitell\u00e4 sen tensoriksi. Sen j\u00e4lkeen meill\u00e4 on valmis imgT, joka vastaa ykk\u00f6sharjoituksessa vihre\u00e4ksi alustamaamme tensoria.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">filename = \"test.png\"\nimgT = preprocess(Image.open(filename).convert(\"RGB\"))<\/pre>\n\n\n\n<p>M\u00e4\u00e4rittelemme viel\u00e4 kokeeksi toisen tavoitekuvan, samanlaisen vihre\u00e4n neli\u00f6n kuin ykk\u00f6sharjoituksessa. Luomme my\u00f6s satunnaisesti alustetun tensorin josta sitten syntyy se tekeill\u00e4 oleva kuva. T\u00e4ss\u00e4 ei k\u00e4ytet\u00e4 esik\u00e4sittelyketjua, koska imgT2 ja imgG ovat valmiiksi tensoreita, mutta imgG:lle tehd\u00e4\u00e4n normalisointi koska pikselit ovat alustuksen j\u00e4lkeen v\u00e4lill\u00e4 0..1. Huomaa, ett\u00e4 voisimme kyll\u00e4 alustaa imgG:n valmiiksi v\u00e4lille -1..1, mutta koska jatkossa joudutaan joka tapauksessa muuntamaan eri esitystapojen v\u00e4lill\u00e4, se kannattaa oppia jo nyt, ennenkuin k\u00e4yd\u00e4\u00e4n muuten vaativampien sovellusten kimppuun.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"># just for fun, lets define an alternative target too\n\nimgT2 =  torch.zeros(3,256,256)\nimgT2[1,:,:] = 1\nimgT2 = norm(imgT2)\n\n# make a 256x256 rgb image with random pixels\n# pixel values here float in range is 0..1 \n\nimgG = torch.Tensor(3,256,256).normal_(mean=0.5, std=0.5).clamp_(0,1) \nnorm = Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))\nimgG = norm(imgG)<\/pre>\n\n\n\n<p>Nyt meill\u00e4 on tyhj\u00e4, alustettu tensori josta on tarkoitus kasvattaa kuva. M\u00e4\u00e4ritell\u00e4\u00e4n siis se optimoinnin kohteeksi.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"># We are going to optimize imgG, so we need gradients for it\n\nlr = 0.05 # you might want to change this and see what happens\n\nimgG.requires_grad = True\n\noptimizer = torch.optim.Adam([imgG], lr)\n<\/pre>\n\n\n\n<p>Nyt kaikki on valmista itse optimoinnille. Luodaan silmukka, jossa mitataan tavoitefunktiolla kuinka kaukana ollaan tavoitteesta, ja tehd\u00e4\u00e4n vastavirtavalutus, jonka pohjalta optimoija s\u00e4\u00e4t\u00e4\u00e4 imgG:n pikseleit\u00e4. Talletamme kuvaa levylle jotta voimme seurata sen kehittymist\u00e4. <\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">niter = 150\n\nfor i in range(0, niter):\n    optimizer.zero_grad()\n\n    # calculate loss\n    loss1 = torch.abs(imgT - imgG).mean()\n\n    # an alternative loss: green target\n    loss2 = torch.abs(imgT2 - imgG).mean()\n    \n    b = 0  # by default, do not use green target, but you can test varying it later\n    \n    loss = loss1 + b * loss2\n\n    # run backwards to find gradient (how to change imgG to make loss smaller) \n    loss.backward()\n\n    # run optimizer to change imgT\n    optimizer.step()\n\n    # print loss to show how we are doing\n    print(i, loss.item(), loss1.item(), loss2.item())\n\n    # save image\n    if (i &lt; 10) or (i % 10 == 0):\n        save_image(imgG, \"harj2-\"+str(i)+\".jpg\")<\/pre>\n\n\n\n<p>Katsotaan tuosta tarkemmin paria yksityiskohtaa. Kuvien v\u00e4list\u00e4 eroa laskemme vertaamalla pikseleitt\u00e4in lasketun eron keskiarvoa. Virhe lasketaan erikseen kumpaankin tavoitekuvaan verrattuna. Muuttamalla muuttujan b arvoa voimme s\u00e4\u00e4t\u00e4\u00e4, kuinka paljon kakkostavoite vaikuttaa.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">   # calculate loss\n    loss1 = torch.abs(imgT - imgG).mean()\n\n    # an alternative loss: green target\n    loss2 = torch.abs(imgT2 - imgG).mean()\n    \n    b = 0  # by default, do not use green target, but you can test varying it later\n    \n    loss = loss1 + b * loss2<\/pre>\n\n\n\n<p>Huomaa, ett\u00e4 imgG ja imgT ovat kokonaisia rgb-kuvia, siis 3 x H x W -tensoreita. Kun laskemme niiden erotuksen, tuloksena on my\u00f6s 3 x H x W -tensori, josta l\u00f6ytyy erotus jokaisen pikselin kohdalla. Otamme viel\u00e4 itseisarvon, koska on sama onko erotus plus- vai miinusmerkkinen, ja sitten laskemme kaikkien pikselien virheen keskiarvon. Selv\u00e4stikin, jos saamme muuttumaan sen kohti nollaa, kuva l\u00e4hestyy haluttua.<br><\/p>\n\n\n\n<figure class=\"wp-block-gallery columns-3 is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex\"><ul class=\"blocks-gallery-grid\"><li class=\"blocks-gallery-item\"><figure><img loading=\"lazy\" decoding=\"async\" width=\"256\" height=\"256\" src=\"https:\/\/liipetti.net\/retkia\/wp-content\/uploads\/2021\/10\/harj2-0.jpg\" alt=\"\" data-id=\"247\" data-link=\"https:\/\/liipetti.net\/retkia\/?attachment_id=247\" class=\"wp-image-247\" srcset=\"https:\/\/liipetti.net\/retkia\/wp-content\/uploads\/2021\/10\/harj2-0.jpg 256w, https:\/\/liipetti.net\/retkia\/wp-content\/uploads\/2021\/10\/harj2-0-150x150.jpg 150w\" sizes=\"auto, (max-width: 256px) 100vw, 256px\" \/><\/figure><\/li><li class=\"blocks-gallery-item\"><figure><img loading=\"lazy\" decoding=\"async\" width=\"256\" height=\"256\" src=\"https:\/\/liipetti.net\/retkia\/wp-content\/uploads\/2021\/10\/harj2-10.jpg\" alt=\"\" data-id=\"248\" data-link=\"https:\/\/liipetti.net\/retkia\/?attachment_id=248\" class=\"wp-image-248\" srcset=\"https:\/\/liipetti.net\/retkia\/wp-content\/uploads\/2021\/10\/harj2-10.jpg 256w, https:\/\/liipetti.net\/retkia\/wp-content\/uploads\/2021\/10\/harj2-10-150x150.jpg 150w\" sizes=\"auto, (max-width: 256px) 100vw, 256px\" \/><\/figure><\/li><li class=\"blocks-gallery-item\"><figure><img loading=\"lazy\" decoding=\"async\" width=\"256\" height=\"256\" src=\"https:\/\/liipetti.net\/retkia\/wp-content\/uploads\/2021\/10\/harj2-30.jpg\" alt=\"\" data-id=\"249\" data-link=\"https:\/\/liipetti.net\/retkia\/?attachment_id=249\" class=\"wp-image-249\" srcset=\"https:\/\/liipetti.net\/retkia\/wp-content\/uploads\/2021\/10\/harj2-30.jpg 256w, https:\/\/liipetti.net\/retkia\/wp-content\/uploads\/2021\/10\/harj2-30-150x150.jpg 150w\" sizes=\"auto, (max-width: 256px) 100vw, 256px\" \/><\/figure><\/li><li class=\"blocks-gallery-item\"><figure><img loading=\"lazy\" decoding=\"async\" width=\"256\" height=\"256\" src=\"https:\/\/liipetti.net\/retkia\/wp-content\/uploads\/2021\/10\/harj2-80.jpg\" alt=\"\" data-id=\"250\" data-link=\"https:\/\/liipetti.net\/retkia\/?attachment_id=250\" class=\"wp-image-250\" srcset=\"https:\/\/liipetti.net\/retkia\/wp-content\/uploads\/2021\/10\/harj2-80.jpg 256w, https:\/\/liipetti.net\/retkia\/wp-content\/uploads\/2021\/10\/harj2-80-150x150.jpg 150w\" sizes=\"auto, (max-width: 256px) 100vw, 256px\" \/><\/figure><\/li><li class=\"blocks-gallery-item\"><figure><img loading=\"lazy\" decoding=\"async\" width=\"256\" height=\"256\" src=\"https:\/\/liipetti.net\/retkia\/wp-content\/uploads\/2021\/10\/harj2-140.jpg\" alt=\"\" data-id=\"251\" data-full-url=\"https:\/\/liipetti.net\/retkia\/wp-content\/uploads\/2021\/10\/harj2-140.jpg\" data-link=\"https:\/\/liipetti.net\/retkia\/?attachment_id=251\" class=\"wp-image-251\" srcset=\"https:\/\/liipetti.net\/retkia\/wp-content\/uploads\/2021\/10\/harj2-140.jpg 256w, https:\/\/liipetti.net\/retkia\/wp-content\/uploads\/2021\/10\/harj2-140-150x150.jpg 150w\" sizes=\"auto, (max-width: 256px) 100vw, 256px\" \/><\/figure><\/li><\/ul><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>Ensimm\u00e4isess\u00e4 osassa tutustuimme kuvan luomiseen tavoitefunktion avulla, siit\u00e4 syntyi vihre\u00e4 neli\u00f6. Alustimme uuden kuvan satunnaisesti ja tavoitekuvan taas alustimme pelk\u00e4st\u00e4\u00e4n vihreill\u00e4 pikseleill\u00e4. Annoimme sitten koulutusprosessin mitata eroa tavoitteeseen n\u00e4hden ja sen avulla ohjasimme pikseleiden arvot kohti tavoitetta. T\u00e4ss\u00e4 ei toki tapahtunut viel\u00e4 mit\u00e4\u00e4n uutta tai hy\u00f6dyllist\u00e4, saimme kopion vihre\u00e4st\u00e4 neli\u00f6st\u00e4.\u2026<\/p>\n<p class=\"continue-reading-button\"> <a class=\"continue-reading-link\" href=\"https:\/\/liipetti.net\/retkia\/neurokuvatreenit-harj-2\/\">Continue reading<i class=\"crycon-right-dir\"><\/i><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8],"tags":[],"class_list":["post-243","post","type-post","status-publish","format-standard","hentry","category-neurokuvatreenit"],"_links":{"self":[{"href":"https:\/\/liipetti.net\/retkia\/wp-json\/wp\/v2\/posts\/243","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/liipetti.net\/retkia\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/liipetti.net\/retkia\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/liipetti.net\/retkia\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/liipetti.net\/retkia\/wp-json\/wp\/v2\/comments?post=243"}],"version-history":[{"count":5,"href":"https:\/\/liipetti.net\/retkia\/wp-json\/wp\/v2\/posts\/243\/revisions"}],"predecessor-version":[{"id":259,"href":"https:\/\/liipetti.net\/retkia\/wp-json\/wp\/v2\/posts\/243\/revisions\/259"}],"wp:attachment":[{"href":"https:\/\/liipetti.net\/retkia\/wp-json\/wp\/v2\/media?parent=243"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/liipetti.net\/retkia\/wp-json\/wp\/v2\/categories?post=243"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/liipetti.net\/retkia\/wp-json\/wp\/v2\/tags?post=243"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}