{"id":976,"date":"2023-02-01T09:24:15","date_gmt":"2023-02-01T09:24:15","guid":{"rendered":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/?post_type=chapter&#038;p=976"},"modified":"2023-02-02T10:08:54","modified_gmt":"2023-02-02T10:08:54","slug":"piltide-klassifitseerimine","status":"publish","type":"chapter","link":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/chapter\/piltide-klassifitseerimine\/","title":{"raw":"Piltide klassifitseerimine","rendered":"Piltide klassifitseerimine"},"content":{"raw":"<h2 id=\"treeningandmete-piltide-leidmine\">Treeningandmete (piltide) leidmine<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"https:\/\/pydoc.pages.taltech.ee\/image_recognition\/v2\/image_classification.html#treeningandmete-piltide-leidmine\">\uf0c1<\/a><\/h2>\r\nSelleks, et masin saaks \u00f5ppida, kuidas \u00fcks v\u00f5i teine objekt v\u00e4lja n\u00e4eb, on tal vaja n\u00e4idisandmeid. Meie puhul see t\u00e4hendab, et treenimiseks on vaja koguda pilte. Mida rohkem n\u00e4idisandmeid on, seda paremini saab masin \u00f5ppida. Ei ole \u00fchest vastust, kui palju on piisav kogus mingi \u00fclesande jaoks. Me v\u00f5ime siin n\u00e4ite jaoks \u00f6elda, et 100 pilti on piisav, et mingisuguseid tulemusi juba saavutada. Aga vahepeal l\u00e4hevad need kogused miljonitesse. Teisest k\u00fcljest: mida rohkem on treeningandmeid (pilte), seda rohkem v\u00f5tab treenimine aega.\r\n\r\nLisaks kogusele on t\u00e4htis ka piltide sobivus. Pildid peaks olema \u00fcksteisest v\u00f5imalikult erinevad, et n\u00e4rviv\u00f5rk saaks n\u00e4ha erinevaid kujusid ja v\u00e4rve. Soovitatav on v\u00f5tta pildid, kus on v\u00f5imalikult v\u00e4he h\u00e4irivaid faktoreid. See aitab v\u00e4ltida olukorda, kus n\u00e4rviv\u00f5rk \u00f5pib selgeks vale omaduse, mida esineb sageli, kuid ei ole defineeriv pildi juures. Kui n\u00e4iteks treenida j\u00e4neseid tuvastama piltidelt, kus on alati porgand, siis v\u00f5ib n\u00e4rviv\u00f5rk \u00f5ppida tuvastama j\u00e4nese asemel hoopis porgandit.\r\n<div class=\"textbox textbox--examples\"><header class=\"textbox__header\">\r\n<p class=\"textbox__title\">Lisainfo: Treeningandmete moonutamine<\/p>\r\n\r\n<\/header>\r\n<div class=\"textbox__content\">\r\n\r\nSamuti tasub proovida enne treenimist\u00a0<a class=\"reference external\" href=\"https:\/\/blog.keras.io\/building-powerful-image-classification-models-using-very-little-data.html\">treeningandmete moonutamist<\/a>\u00a0(venitamine, p\u00f6\u00f6ramine, m\u00fcra lisamine), et tulemuseks oleks t\u00f6\u00f6kindlam n\u00e4rviv\u00f5rk. See v\u00f5imaldab saada rohkem kasu samast pildist, sest igat pilti saab mitut eri moodi moonutada.\r\n\r\n<\/div>\r\n<\/div>\r\n<h2 id=\"andmete-jagamine-osadeks\">Andmete jagamine osadeks<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"https:\/\/pydoc.pages.taltech.ee\/image_recognition\/v2\/image_classification.html#andmete-jagamine-osadeks\">\uf0c1<\/a><\/h2>\r\nSelleks, et n\u00e4rviv\u00f5rku treenida ja tulemust kontrollida, on vaja andmed jagada osadeks ja m\u00e4\u00e4rata, millistel osadel treenitakse n\u00e4rviv\u00f5rku ja millistel osadel kontrollitakse tulemust. Kui seda mitte teha, v\u00f5ib n\u00e4rviv\u00f5rk lihtsalt k\u00f5ik andmed \"p\u00e4he tuupida\". Kui see juhtub, siis n\u00e4rviv\u00f5rk ei omanda \u00fcldistavat oskust ja ei \u00f5pi tundma andmetes olevaid mustreid. Selline n\u00e4rviv\u00f5rk on kasutu kui ta kohtab andmeid, mida ta pole varem p\u00e4he tuupinud. Selle \"p\u00e4he tuupimise\" n\u00e4htuse nimi on\u00a0<strong>\u00fclesobitamine<\/strong>\u00a0(<em>overfitting<\/em>).\r\n\r\nAndmete osadeks jagamiseks on mitmeid erinevaid v\u00f5imalikke lahendusi. Siin tutvustame \u00fchte lihtsaimat, mis eeldab, et andmed jagatakse kaheks osaks: treeningandmestik (<em>training set<\/em>) ja testandmestik (<em>test set<\/em>).\r\n\r\n<strong>Treeningandmestik<\/strong>\u00a0(<em>training set<\/em>) koosneb piltidest, mille peal n\u00e4rviv\u00f5rk \u00f5pib. Treeningandmed moodustavad enamuse kogu n\u00e4idisandmete hulgast. Neid pilte kasutab n\u00e4rviv\u00f5rk selleks, et leida optimaalsed neuronite kaalud.\r\n\r\n<strong>Testandmestik<\/strong>\u00a0(<em>test set<\/em>) koosneb piltidest, mida kasutatakse l\u00f5pliku hinnangu andmiseks. Tegu on piltidega, mida n\u00e4rviv\u00f5rk pole siiani n\u00e4inud. Kuna need andmed pole treenimist m\u00f5jutanud, siis see aitab simuleerida reaalset olukorda, kus n\u00e4rviv\u00f5rk kohtab uusi andmeid. Nende andmete peal arvutatakse ka n\u00e4rviv\u00f5rgu l\u00f5plik t\u00e4psus.\r\n\r\nOsade suuruste jaoks puudub alati toimiv lahendus, kuid tihti kasutatakse treening- ja testiandmestiku suhteid nagu 90:10, 80:20 v\u00f5i 70:30. Mida v\u00e4hem on kogutud andmeid, seda rohkem s\u00f5ltub tulemus osade suurusest. Lisaks suurustele on t\u00e4htis ka sisu. Kui testandmed on liiga erinevad treeningandmetest, siis ei vasta testimistulemused treenimisele.\r\n<div class=\"textbox textbox--examples\"><header class=\"textbox__header\">\r\n<p class=\"textbox__title\">Lisainfo: Andmegrupid<\/p>\r\n\r\n<\/header>\r\n<div class=\"textbox__content\">\r\n\r\nP\u00f5hjalikuma \u00fclevaate andmete gruppidest, kaasa arvatud metoodikatest, kus jagatakse andmeid kolmeks osaks, leiab <a class=\"reference external\" href=\"https:\/\/en.wikipedia.org\/wiki\/Training,_validation,_and_test_sets\">siit<\/a>.\r\n\r\n<\/div>\r\n<\/div>\r\n<h2>M\u00f5tle ja nuputa!<\/h2>\r\n[h5p id=\"47\"]\r\n<h2 id=\"andmete-laadimine-pytorchi\">Andmete laadimine PyTorchi<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"https:\/\/pydoc.pages.taltech.ee\/image_recognition\/v2\/image_classification.html#andmete-laadimine-pytorchi\">\uf0c1<\/a><\/h2>\r\nSelles \u00fclesandes kasutame me masin\u00f5ppekogukonnas populaarset andmekogumit CIFAR-10, mis on mugavalt otse l\u00e4bi PyTorchi saadav ning ette jagatud treenimis- ja testimisosadeks (l\u00e4bi\u00a0<cite>train<\/cite>\u00a0argumendi). Seal olevad pildid on piisavalt kvaliteetsed otseseks kasutamiseks. CIFAR-10 nimi tuleneb sellest, et ta pildid jagunevad k\u00fcmnesse erinevasse klassi.\r\n\r\n[caption id=\"attachment_984\" align=\"alignnone\" width=\"595\"]<img class=\"wp-image-984 size-full\" src=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/cifar10-classes.png\" alt=\"CIFAR-10 klassid ja n\u00e4idispildid. \" width=\"595\" height=\"458\" \/> Pildi allikas: https:\/\/www.cs.toronto.edu\/~kriz\/cifar.html[\/caption]\r\n\r\nT\u00e4ielikku nimekirja PyTorchi poolt (p\u00f5hiliselt \u00f5ppe- ja uuringeesm\u00e4rkidel) pakutavatest andmekogumitest saab vaadata <a class=\"reference external\" href=\"https:\/\/pytorch.org\/vision\/stable\/datasets.html\">siit<\/a>. CIFAR-10 pildid saab k\u00e4tte (automaatse internetist allalaadimisega) kasutades\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">DataSet<\/span><\/code>-i niiviisi:\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">from torchvision import datasets, transforms\r\n\r\ntrain_data = datasets.CIFAR10(\r\n    root='data',  # kaust, kuhu andmed laetakse alla\r\n    train=True,   # kas soovime treeningandmeid (True) v\u00f5i testimisandmeid (False)\r\n    download=True,   # kas laeme vajadusel andmed automaatselt alla internetist\r\n    transform=transforms.ToTensor()  # teisendame pildid tensoriteks\r\n)\r\ntest_data = datasets.CIFAR10(\r\n    root='data',\r\n    train=False,\r\n    download=True,\r\n    transform=transforms.ToTensor()\r\n)\r\n\r\nx, y = next(iter(train_data))  # v\u00f5tame treenimisandmetest esimese pildi\r\nprint(x.shape)  # torch.Size([3, 32, 32]) - 3 v\u00e4rvikanalit (RGB), 32x32 pikslit\r\nprint(y)  # 6 - pildi klass<\/pre>\r\n<div class=\"textbox textbox--examples\"><header class=\"textbox__header\">\r\n<p class=\"textbox__title\">Lisainfo: Enda piltide laadimine<\/p>\r\n\r\n<\/header>\r\n<div class=\"textbox__content\">\r\n\r\nJuhul kui meil on olemas oma enda pildid, saame kasutada klassi\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">ImageFolder<\/span><\/code>\u00a0(dokumentatsiooni saab lugeda\u00a0<a class=\"reference external\" href=\"https:\/\/pytorch.org\/vision\/stable\/generated\/torchvision.datasets.ImageFolder.html\">siit<\/a>). Sellele klassile tuleb anda kaust, kus on eraldi kaustad iga klassi jaoks, ning igas klassikaustas on antud klassi (<code>y)<\/code> kuuluvad pildid (<code>x<\/code>). Selles \u00fclesandes piirdume ainult CIFAR-10-ga.\r\n\r\n<\/div>\r\n<\/div>\r\nKuigi \u00fcleval on toodud v\u00e4lja n\u00e4ide, kuidas pilte\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">DataSet<\/span><\/code>-ist \u00fche kaupa k\u00e4tte saada, on soovituslik lisaks sellele kasutada PyTorchi poolt pakutavat\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">DataLoader<\/span><\/code>-it, mis v\u00f5imaldab andmeid laadida suvalises j\u00e4rjekorras ja mitme kaupa (miniplokkides, ingl.k. <em>minibatches<\/em>), t\u00e4nu millele muutub treenimisprotsess efektiivsemaks.\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">DataLoader<\/span><\/code>-i kasulikest omadustest saab rohkem lugeda\u00a0<a class=\"reference external\" href=\"https:\/\/pytorch.org\/docs\/stable\/data.html#torch.utils.data.DataLoader\">siit<\/a>.\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">from torch.utils.data import DataLoader\r\n\r\ntrain_loader = DataLoader(train_data, batch_size=16, shuffle=True)\r\ntest_loader = DataLoader(test_data, batch_size=16, shuffle=True)\r\n\r\nx, y = next(iter(train_loader))  # v\u00f5tame miniploki\r\nprint(x.shape)  # torch.Size([16, 3, 32, 32]) - 16 pilti, 3 v\u00e4rvikanalit (RGB), 32x32 pikslit\r\nprint(y.shape)  # torch.Size([16]) - 16 m\u00e4rgendit (klassi, kuhu miniploki pildid kuuluvad)<\/pre>\r\nMitu pilti korraga \u00fchte miniplokki laetakse on m\u00e4\u00e4ratud l\u00e4bi\u00a0<cite>batch_size<\/cite>\u00a0argumendi; t\u00fc\u00fcpiliselt on see 32 v\u00f5i 64, siin piirdume 16-ga. N\u00e4rviv\u00f5rgu parameetreid uuendatakse vaid \u00fche korra iga miniploki l\u00f5pus l\u00e4bi iga pildi ennustuse summeeritud kahju. Argument\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">shuffle=True<\/span><\/code>\u00a0t\u00e4hendab seda, et andmeid antakse suvalises j\u00e4rjekorras.\r\n\r\n&nbsp;\r\n\r\n[caption id=\"attachment_985\" align=\"alignnone\" width=\"674\"]<img class=\"wp-image-985 \" src=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/cifar10-loss-landscape-1024x791.png\" alt=\"\" width=\"674\" height=\"521\" \/> \u00dcks v\u00f5imalik CIFAR-10 kahjufunktsioonimaastiku visualisatsioon (interaktiivne versioon on saadaval http:\/\/www.telesens.co\/loss-landscape-viz\/viewer.html). Pildi allikas: https:\/\/www.cs.umd.edu\/~tomg\/projects\/landscapes\/[\/caption]\r\n<h2 id=\"pytorchi-narvivorgumoodul\">PyTorchi n\u00e4rviv\u00f5rgumoodul<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"https:\/\/pydoc.pages.taltech.ee\/image_recognition\/v2\/image_classification.html#pytorchi-narvivorgumoodul\">\uf0c1<\/a><\/h2>\r\nSelles peat\u00fckis v\u00f5tame kokku k\u00f5ik olemasolevad detailid ja hakkame ehitama lihtsat n\u00e4rviv\u00f5rgu, mis oskab klassifitseerida CIFAR-10 pilte. Treenimiseks kasutame PyTorchi poolt pakutavat\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">nn.Module<\/span><\/code>\u00a0klassi, mis lihtsustab n\u00e4rviv\u00f5rkude koostamist. Suurem osa koodist j\u00e4\u00e4b samaks, nagu varasemates n\u00e4idetes.\r\n<div class=\"highlight-python notranslate\">\r\n<div class=\"highlight\">\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">from torch import nn\r\n\r\nclass NeuralNetwork(nn.Module):  # anname nn.Module-ile nime \"NeuralNetwork\"\r\n    def __init__(self):\r\n        super().__init__()  # vajalik rida nn.Module-i kasutamiseks!\r\n\r\n    def forward(self, x):\r\n        print(x.shape)  # torch.Size([16, 3, 32, 32]) - 16 pilti, 3 v\u00e4rvikanalit (RGB), 32x32 pikslit<\/pre>\r\nEt\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">nn.Module<\/span><\/code>-it kasutada, pole vaja teada klassidest palju. Peamine detail on see, et\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">__init__<\/span><\/code>\u00a0meetodis tuleb luua k\u00f5ik kihid ning\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">forward<\/span><\/code>\u00a0meetodis tuleb kirjeldada, kuidas andmed neid kihte l\u00e4bivad.\r\n\r\nDeklareerime mooduli kihid pannes kihimuutujate nimede ette\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">self.<\/span><\/code>\u00a0(t\u00e4nu sellele saame neid kasutada\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">forward<\/span><\/code>\u00a0meetodis j\u00e4lle l\u00e4bi\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">self<\/span><\/code>\u00a0parameetri):\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">def __init__(self):\r\n    super().__init__()  # vajalik rida nn.Module-i kasutamiseks!\r\n\r\n    self.hidden_layer = nn.Linear(3 * 32 * 32, 30)  # 32x32x3 pikslit, 30 v\u00e4ljundit\r\n    self.sigmoid = nn.Sigmoid()  # aktivatsioonifunktsioon peidetud kihile\r\n    self.output_layer = nn.Linear(30, 10)  # 30 sisendit, 10 v\u00e4ljundit (\u00fcks iga klassi kohta)\r\n    self.softmax = nn.Softmax(dim=1)  # aktivatsioonifunktsioon v\u00e4ljundkihile<\/pre>\r\nViimasel real\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">self.softmax<\/span>\u00a0<span class=\"pre\">=<\/span>\u00a0<span class=\"pre\">nn.Softmax(dim=1)<\/span><\/code>\u00a0m\u00e4\u00e4rab argument\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">dim=1<\/span><\/code>\u00a0\u00e4ra, et\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">softmax<\/span><\/code>\u00a0rakendataks iga pildi kohta eraldi. See on vajalik, kuna me kasutame miniplokke, mille puhul antakse meile\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">x<\/span><\/code>-i 16 pilti korraga. Aktivatsioonifunktsioon\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">nn.Softmax<\/span><\/code>\u00a0viib v\u00e4ljundkihi neuronid kujule kus nende kogusumma on 1, mis v\u00f5imaldab neid v\u00e4\u00e4rtusi interpreteerida piltide klassi kuuluvuse t\u00f5en\u00e4osustena (n\u00e4rviv\u00f5rgu \"ennustuseks\" loeme suurima t\u00f5en\u00e4osusega klassi). N\u00e4iteks:\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">data = torch.tensor([5.5, 3.5, 4.2])\r\nactivation_fn = nn.Softmax()\r\nprint(activation_fn(data))  # tensor([0.7103, 0.0961, 0.1936]) - 71%, 10%, 19%<\/pre>\r\nN\u00fc\u00fcd kui kihimuutujad on deklareeritud, kirjeldame, kuidas andmed neid kihte l\u00e4bivad iga\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">x<\/span><\/code>-i korral:\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">def forward(self, x):\r\n    x = x.flatten(start_dim=1)  # 16x3x32x32 -&gt; 16x3072\r\n    z_1 = self.hidden_layer(x)  # 16x3072 -&gt; 16x30\r\n    a_1 = self.sigmoid(z_1)  # 16x30 -&gt; 16x30\r\n    z_2 = self.output_layer(a_1)  # 16x30 -&gt; 16x10\r\n    a_2 = self.softmax(z_2)  # 16x10 -&gt; 16x10\r\n    return a_2<\/pre>\r\nEsimene\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">x<\/span><\/code>-i transformeeriv rida\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">x<\/span>\u00a0<span class=\"pre\">=<\/span>\u00a0<span class=\"pre\">x.flatten(start_dim=1)<\/span><\/code>\u00a0on vajalik, sest\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">nn.Linear<\/span><\/code>\u00a0ootab sisendandmeid 1D-kujul, v\u00e4lja arvatud esimene miniplokkide dimensioon (indeks 0), millega tegeleb\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">nn.Linear<\/span><\/code>\u00a0automaatselt. Argument\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">start_dim=1<\/span><\/code>\u00a0t\u00e4hendab seda, et tensori lamestamisel esimest dimensiooni ignoreeritakse (lamestamist alustatakse indeks 1 dimensiooniga). N\u00fc\u00fcd kui\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">x<\/span><\/code>\u00a0on \u00f5igel kujul, l\u00e4bivad andmed peidetud kihist v\u00e4ljundkihini, kus l\u00f5plik tulemus tagastatakse.\r\n\r\n[caption id=\"attachment_986\" align=\"alignnone\" width=\"719\"]<img class=\"wp-image-986 \" src=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-graph-890x1024.png\" alt=\"N\u00e4rviv\u00f5rgu visualiseeritud struktuur\" width=\"719\" height=\"827\" \/> Meie n\u00e4rviv\u00f5rgu visualiseeritud struktuur. Pilt autori koostatud.[\/caption]\r\n<h2 id=\"epohhid-ja-narvivorgu-treenimine\">Epohhid ja n\u00e4rviv\u00f5rgu treenimine<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"https:\/\/pydoc.pages.taltech.ee\/image_recognition\/v2\/image_classification.html#epohhid-ja-narvivorgu-treenimine\">\uf0c1<\/a><\/h2>\r\nN\u00e4rviv\u00f5rgu treenimisprotsess koosneb epohhitest (<em>epoch<\/em>). Epohhi jooksul k\u00e4iakse treeningandmed \u00fche korra l\u00e4bi. Treenimine koosneb tavaliselt rohkem kui \u00fches epohhist. Kui teha liiga palju epohhe, siis tekib \u00fcletreenimise oht, sest n\u00e4rviv\u00f5rgule antakse piisavalt aega andmetele \u00fclesobituda.\r\n\r\nLoome n\u00e4rviv\u00f5rgu instantsi, koostame MSE kahjufunktsiooni ja m\u00e4\u00e4rame optimeerija \u00f5pisammuga 0.1:\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">net = NeuralNetwork()\r\nloss_fn = nn.MSELoss()\r\noptimizer = torch.optim.SGD(net.parameters(), lr=0.1)<\/pre>\r\n<div class=\"textbox textbox--examples\"><header class=\"textbox__header\">\r\n<p class=\"textbox__title\">Lisainfo: CUDA kasutajatele<\/p>\r\n\r\n<\/header>\r\n<div class=\"textbox__content\">\r\n\r\nJuhul kui oled siiamaani kasutanud CUDA-t, tuleb m\u00e4\u00e4rata ka\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">nn.Module<\/span><\/code>-i seadme:\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">net<\/span>\u00a0<span class=\"pre\">=<\/span>\u00a0<span class=\"pre\">NeuralNetwork().to(device)<\/span><\/code>. Seade rakendub k\u00f5ikidele kihtidele, mis on deklareeritud antud <code class=\"docutils literal notranslate\"><span class=\"pre\">class<\/span>\u00a0<span class=\"pre\">NeuralNetwork(nn.Module)<\/span><\/code>-i sees.\r\n\r\n<\/div>\r\n<\/div>\r\nJ\u00e4rgmisena hakkame kirjutama ts\u00fcklit, mis treenib n\u00e4rviv\u00f5rku 10 epohhit:\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">for epoch in range(10):\r\n    for x, y in train_loader:  # k\u00e4ime l\u00e4bi k\u00f5ik miniplokid\r\n        y_hat = net(x)  # teeme ennustuse\r\n        print(y.shape, y_hat.shape)  # torch.Size([16]) torch.Size([16, 10])\r\n\r\n        optimizer.zero_grad()  # nullime eelnevad gradiendid\r\n        loss = loss_fn(y_hat, y)  # arvutame kahju\r\n        # error! y ja y_hat on erineva kujuga, seega ei saa kahju arvutada<\/pre>\r\nSiin tekib probleem! Muutuja\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">y<\/span><\/code>\u00a0on kujul\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">[2,<\/span>\u00a0<span class=\"pre\">5,<\/span>\u00a0<span class=\"pre\">...,<\/span>\u00a0<span class=\"pre\">1,<\/span>\u00a0<span class=\"pre\">5]<\/span><\/code>, muutuja\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">y_hat<\/span><\/code>\u00a0aga kujul\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">[[0.1,<\/span>\u00a0<span class=\"pre\">0.2,<\/span>\u00a0<span class=\"pre\">...,<\/span>\u00a0<span class=\"pre\">0.5],<\/span>\u00a0<span class=\"pre\">[0.4,<\/span>\u00a0<span class=\"pre\">0.1,<\/span>\u00a0<span class=\"pre\">...,<\/span>\u00a0<span class=\"pre\">0.1],<\/span>\u00a0<span class=\"pre\">...,<\/span>\u00a0<span class=\"pre\">[0.0,<\/span>\u00a0<span class=\"pre\">0.7,<\/span>\u00a0<span class=\"pre\">...,<\/span>\u00a0<span class=\"pre\">0.2]]<\/span><\/code>. Esimeses on tegemist \u00f5igete klasside indeksitega, teises aga ennustatud klasside t\u00f5en\u00e4osustega. Et kahjufunktsioon t\u00f6\u00f6taks, peame muutma\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">y<\/span><\/code>\u00a0samale kujule nagu\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">y_hat<\/span><\/code>. N\u00e4iteks kui\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">y[0]<\/span><\/code>\u00a0on\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">2<\/span><\/code>, peaks temast saama vektor\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">[0,<\/span>\u00a0<span class=\"pre\">0,<\/span>\u00a0<span class=\"pre\">1,<\/span>\u00a0<span class=\"pre\">0,<\/span>\u00a0<span class=\"pre\">0,<\/span>\u00a0<span class=\"pre\">0,<\/span>\u00a0<span class=\"pre\">0,<\/span>\u00a0<span class=\"pre\">0,<\/span>\u00a0<span class=\"pre\">0,<\/span>\u00a0<span class=\"pre\">0]<\/span><\/code>. Kuigi saaksime teoorias kirjutada selle probleemi lahendamiseks enda koodi, pakub PyTorch mugavat funktsiooni\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">nn.functional.one_hot<\/span><\/code>, mis teeb terve t\u00f6\u00f6 \u00e4ra meie eest:\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">for x, y in train_loader:  # k\u00e4ime l\u00e4bi k\u00f5ik miniplokid\r\n    y_hat = net(x)  # teeme ennustuse\r\n\r\n    y = nn.functional.one_hot(y, 10).to(torch.float)  # muudame y-i \u00f5igele kujule (10 klassi)\r\n    optimizer.zero_grad()  # nullime k\u00f5ik varasemad gradiendid\r\n    loss = loss_fn(y_hat, y)  # arvutame kahju<\/pre>\r\nN\u00fc\u00fcd kui kahju on arvutatud, peame me ka arvutama, kuidas see kahju m\u00f5jutab iga parameetri (<code>w<\/code>) v\u00e4\u00e4rtust l\u00e4bi funktsiooni\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">loss.backward()<\/span><\/code>. See funktsioon arvutab gradiendid ja salvestab nad kihtide sisse. Ja l\u00f5puks kasutame optimeerijat, et muuta kaalufaktoreid vastavalt gradiendile.\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">loss.backward()  # leiame, kui palju peame muutma iga kihi parameetrit, et kahju oleks v\u00e4iksem\r\noptimizer.step()  # teeme sammu optimeerija abil (muudame kaale vastavalt gradiendile)<\/pre>\r\nNing sellega on meie treeningprotsess valmis.\r\n\r\n[caption id=\"attachment_987\" align=\"alignnone\" width=\"371\"]<img class=\"wp-image-987 size-full\" src=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/xkcd.png\" alt=\"Machine lerning system\" width=\"371\" height=\"439\" \/> Pildi allikas: https:\/\/xkcd.com\/1838\/[\/caption]\r\n\r\n<\/div>\r\n<h2 id=\"narvivorgu-testimine\">N\u00e4rviv\u00f5rgu testimine<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"https:\/\/pydoc.pages.taltech.ee\/image_recognition\/v2\/image_classification.html#narvivorgu-testimine\">\uf0c1<\/a><\/h2>\r\nKui tahame n\u00e4ha, kui h\u00e4sti meie n\u00e4rviv\u00f5rk t\u00f6\u00f6tab, siis saame selleks kasutada testimisandmeid. Igas epohhis k\u00e4ime algselt l\u00e4bi k\u00f5ik treeningandmed (<code class=\"docutils literal notranslate\"><span class=\"pre\">for<\/span>\u00a0<span class=\"pre\">x,<\/span>\u00a0<span class=\"pre\">y<\/span>\u00a0<span class=\"pre\">in<\/span>\u00a0<span class=\"pre\">train_loader<\/span><\/code>) ja siis k\u00f5ik testimisandmed (<code class=\"docutils literal notranslate\"><span class=\"pre\">for<\/span>\u00a0<span class=\"pre\">x,<\/span>\u00a0<span class=\"pre\">y<\/span>\u00a0<span class=\"pre\">in<\/span>\u00a0<span class=\"pre\">test_loader<\/span><\/code>).\r\n\r\nTestimisandmete ts\u00fckkel erineb selle poolest, et me ei tee optimeerimist (ei muuda kaale \/ treeni n\u00e4rviv\u00f5rku). T\u00e4nu sellele ei saa n\u00e4rviv\u00f5rk testiandmed \"meelde j\u00e4tta\": simuleerime reaalse elus olukorda, kus me ei tea, millised andmed meile tulevad. Kui me seda ei teeks, oleks meil v\u00f5imatu teada kui h\u00e4sti n\u00e4rviv\u00f5rk reaalselt oskab meie probleemi mustreid generaliseerida.\r\n\r\nTreenimise v\u00e4ltimiseks j\u00e4tame \u00e4ra read\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">loss.backward()<\/span><\/code>,\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">optimizer.zero_grad()<\/span><\/code>\u00a0ja\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">optimizer.step()<\/span><\/code>. Olenedes statistikatest mis meid huvitavad, v\u00f5ime \u00e4ra j\u00e4tta ka l\u00f5pliku kahju arvutamise (<code class=\"docutils literal notranslate\"><span class=\"pre\">loss_fn(y_hat,<\/span>\u00a0<span class=\"pre\">y)<\/span><\/code>).\r\n\r\nLisaks sellele ei pea me ka taustas arvutama gradiente, mida muidu\u00a0<a class=\"reference external\" href=\"https:\/\/pytorch.org\/tutorials\/beginner\/blitz\/autograd_tutorial.html\">tehakse PyTorchis vaikimisi<\/a>. Et gradientide arvutamist v\u00e4ltida, peame panema oma koodi\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">with<\/span>\u00a0<span class=\"pre\">torch.no_grad()<\/span><\/code>\u00a0konteksti:\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">for epoch in range(10):\r\n    for x, y in train_loader:\r\n        ...  # sama kood, mis varem\r\n\r\n    with torch.no_grad():  # me ei soovi arvutada gradiente\r\n        for x, y in test_loader:  # k\u00e4ime l\u00e4bi k\u00f5ik miniplokid\r\n            y_hat = net(x)  # teeme ennustuse\r\n\r\n            y = nn.functional.one_hot(y, 10).to(torch.float)  # muudame y-i \u00f5igele kujule (10 klassi)\r\n            loss = loss_fn(y_hat, y)  # arvutame kahju statistika jaoks<\/pre>\r\nN\u00fc\u00fcd on meie treenimiskood palju kiirem, sest me ei arvuta enam m\u00f5ttetult gradiente.\r\n\r\nJ\u00e4rgmine samm on hakata arvutama statistikaid nii treenimis- kui ka testimisandmete peal. M\u00f5lema m\u00f5\u00f5tmine on t\u00e4htis selle jaoks, et m\u00e4rgata \u00fclesobitamist. \u00dclesobitamise selgeim s\u00fcmptom on k\u00f5rge \u00f5igsus (\u00f5igete ennustuste protsent; ingl.k.\u00a0<em>accuracy<\/em>) treeningandmete peal, kuid tunduvalt madalamatesse \u00f5igsus testimisandmete peal. Kui n\u00e4rviv\u00f5rk t\u00f6\u00f6tab h\u00e4sti, siis on \u00f5igsus k\u00f5rge nii treening- kui ka testimisandmetel.\r\n\r\nKuna meie koodis tulevad igas epohhis testimisandmed p\u00e4rast treeningandmeid, siis epohhi-keskse n\u00e4rviv\u00f5rgu \u00f5ppimise t\u00f5ttu v\u00f5ib testimisandmete \u00f5igsus tulla k\u00f5rgem kui treeningandmete \u00f5igsus.\r\n<div class=\"textbox textbox--examples\"><header class=\"textbox__header\">\r\n<p class=\"textbox__title\">Lisainfo: \u00dclesobitamise p\u00f5hjused<\/p>\r\n\r\n<\/header>\r\n<div class=\"textbox__content\">\r\n\r\n\u00dclesobitamist saab tekitada n\u00e4iteks:\r\n<ul class=\"simple\">\r\n \t<li>Liiga suur n\u00e4rviv\u00f5rk, mille suur parameetrite arv v\u00f5imaldab tal treeningandmed meelde j\u00e4tta; n\u00e4rviv\u00f5rgul pole m\u00f5tet olla \"kokkuhoidlik\" ja otsida mustreid.<\/li>\r\n \t<li>Liiga v\u00e4he treeningandmeid, mis j\u00e4llegi teeb p\u00e4he tuupimise liiga kergeks. Tegu on sisuliselt sama probleemiga nagu eelmises punktis: n\u00e4rviv\u00f5rk on asjatult keeruline treeningandmete koguse suhtes.<\/li>\r\n \t<li>Liiga palju epohhe. Tihtipeale \u00f5pib n\u00e4rviv\u00f5rk mingiks epohhiks mustrid selgeks, ning edaspidi hakkab \u00fclesobituma. Seet\u00f5ttu on oluline valida \u00f5ige epohhide arv.<\/li>\r\n<\/ul>\r\n<\/div>\r\n<\/div>\r\n<h2 id=\"narvivorgu-meetrikad\">N\u00e4rviv\u00f5rgu meetrikad<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"https:\/\/pydoc.pages.taltech.ee\/image_recognition\/v2\/image_classification.html#narvivorgu-meetrikad\">\uf0c1<\/a><\/h2>\r\nJ\u00e4rgmine samm on hakata arvutama n\u00e4rviv\u00f5rgu ennustuste \u00f5igsust (\u00f5igete ennustuste protsenti). Koostame igas epohhis muutujad\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">train_correct<\/span><\/code>\u00a0ja\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">train_total<\/span><\/code>, ning\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">test_correct<\/span><\/code>\u00a0ja\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">test_total<\/span><\/code>. Need muutujad hakkavad hoidma meie \u00f5igete ennustuste ja koguennustuste arvu. Koostame ka\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">train_loss_sum<\/span><\/code>\u00a0ja\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">test_loss_sum<\/span><\/code>\u00a0muutujad, et arvutada keskmist kahju. S\u00e4time k\u00f5igi nelja muutuja algseteks v\u00e4\u00e4rtuseks null. J\u00e4rgmisena k\u00e4ime l\u00e4bi m\u00f5lemad ts\u00fcklid ja liidame\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">train_total<\/span><\/code>\u00a0\/\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">test_total<\/span><\/code>\u00a0muutujatele 1 igas iteratsioonis.\r\n\r\nN\u00fc\u00fcd kui see on tehtud, peame me leidma, kas mingi ennustus oli \u00f5ige. Kasutame selleks funktsiooni\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">torch.argmax<\/span><\/code>, mis leiab k\u00f5ige suurema v\u00e4\u00e4rtusega elemendi ja tagastab ta indeksi (ehk klassinumbri). N\u00e4iteks\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">torch.tensor([0.1,<\/span>\u00a0<span class=\"pre\">0.2,<\/span>\u00a0<span class=\"pre\">0.5,<\/span>\u00a0<span class=\"pre\">0.2]).argmax()<\/span><\/code>\u00a0tagastab\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">torch.tensor(2)<\/span><\/code>. Kui ennustatud klass on sama, mis tegelik klass, siis suurendame\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">train_correct<\/span><\/code>\u00a0\/\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">test_correct<\/span><\/code>\u00a0muutujat \u00fche v\u00f5rra. L\u00f5puks arvutame \u00f5igsuse ja kahju keskmiseid.\r\n\r\nSiin on n\u00e4idiskood\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">test_loader<\/span><\/code>-i jaoks:\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">for x, y in test_loader:\r\n    y_hat = net(x)  # teeme ennustuse\r\n\r\n    for prediction, correct_class in zip(y_hat, y):  # l\u00e4bime paarikaupa k\u00f5ik y\/y_hat v\u00e4\u00e4rtused miniplokis\r\n        prediction_class = torch.argmax(prediction)  # leiame suurima v\u00e4\u00e4rtusega indeksi\r\n        if prediction_class == correct_class:  # kas ennustus oli \u00f5ige?\r\n            test_correct += 1\r\n        test_total += 1\r\n\r\n    y = nn.functional.one_hot(y, 10).to(torch.float)  # muudame y-i \u00f5igele kujule kahju arvutamiseks\r\n    loss = loss_fn(y_hat, y)  # arvutame l\u00f5pliku kahju statistika jaoks\r\n    test_loss_sum += loss.item()  # lisame l\u00f5pliku kahju summale<\/pre>\r\n<div class=\"textbox textbox--examples\"><header class=\"textbox__header\">\r\n<p class=\"textbox__title\">Lisainfo: Rohkem meetrikaid<\/p>\r\n\r\n<\/header>\r\n<div class=\"textbox__content\">\r\n<div class=\"admonition-lisainfo-rohkem-meetrikaid admonition\">\r\n\r\nOn olemas\u00a0<a class=\"reference external\" href=\"https:\/\/towardsdatascience.com\/the-5-classification-evaluation-metrics-you-must-know-aa97784ff226\">v\u00e4ga palju teisi meetrikaid<\/a>, mida saab kasutada n\u00e4rviv\u00f5rgu t\u00f6\u00f6 hindamiseks:\r\n<ul class=\"simple\">\r\n \t<li><strong>\u00d5igsus<\/strong>\u00a0(<em>accuracy<\/em>) kirjeldab kui suur osa andmetest klassifitseeriti \u00f5igesti. Olukorras, kus \u00fchte klassi on rohkem kui teisi on tulemus kallutatud selle \u00fche klassi poole. V\u00f5tame n\u00e4iteks andmestiku kahe klassiga: 99 koera ja 1 kass. Kui n\u00e4rviv\u00f5rk alati ennustab, et tegu on koeraga, siis \u00f5igsus on 99%, aga ta ei oska tegelikult midagi teha. Kasuta kui klasside osakaal andmetes on v\u00f5rdne (nii see on nt CIFAR-10 puhul).<\/li>\r\n \t<li><strong>T\u00e4psus<\/strong>\u00a0(<em>precision<\/em>) kirjeldab kui suurt osa moodustavad \u00f5igesti ennustatud positiivseid (<em>true positive<\/em>) tulemused k\u00f5igist positiivselt ennustatud tulemustest. Kasuta kui tulemuses peab olema v\u00e4ga kindel ja pead v\u00e4ltima valesid positiivseid (<em>false positive<\/em>) tulemusi.<\/li>\r\n \t<li><strong>Saagis<\/strong>\u00a0(<em>recall<\/em>) kirjeldab \u00f5igesti ennustatud positiivsete tulemuste osakaalu k\u00f5igist tegelikult t\u00f5estest tulemustest. Kasuta kui eesm\u00e4rgiks on klassifitseerida positiivselt v\u00f5imalikult palju positiivseid tulemusi.<\/li>\r\n \t<li><strong>F1 skoor<\/strong>\u00a0kombineerib t\u00e4psuse (<em>precision<\/em>) ja saagise (<em>recall<\/em>), et anda tulemus, mis arvestab m\u00f5lemat.<\/li>\r\n<\/ul>\r\n<\/div>\r\n<\/div>\r\n<\/div>\r\nPeale samasuguse koodi kirjutamist ka\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">train_loader<\/span><\/code>-i ts\u00fcklisse, saame printida epohhi l\u00f5pus statistikat. Selle osa v\u00f5ib vabalt kirjutada nii, kuidas soov on. Siin on n\u00e4itena toodud f-stringidega formeeritud versioon:\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">print(f'Epoch {epoch + 1:03} train | avg loss: {train_loss_sum \/ train_total:.6f}, '\r\n      f'accuracy: {train_correct \/ train_total:.2%}')\r\nprint(f'           test | avg loss: {test_loss_sum \/ test_total:.6f}, '\r\n      f'accuracy: {test_correct \/ test_total:.2%}')<\/pre>\r\nEelneva koodi puhul tuleb v\u00e4ljund selline:\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">Epoch 001 train | avg loss: 0.005552, accuracy: 20.98%\r\n           test | avg loss: 0.005468, accuracy: 25.72%\r\nEpoch 002 train | avg loss: 0.005386, accuracy: 26.99%\r\n           test | avg loss: 0.005310, accuracy: 27.91%\r\n...\r\nEpoch 014 train | avg loss: 0.004688, accuracy: 39.11%\r\n           test | avg loss: 0.004678, accuracy: 39.73%\r\nEpoch 015 train | avg loss: 0.004663, accuracy: 39.60%\r\n           test | avg loss: 0.004659, accuracy: 39.53%\r\n...\r\nEpoch 114 train | avg loss: 0.003915, accuracy: 51.45%\r\n           test | avg loss: 0.004193, accuracy: 47.24%\r\n...\r\nEpoch 200 train | avg loss: 0.003642, accuracy: 55.95%\r\n           test | avg loss: 0.004221, accuracy: 47.05%<\/pre>\r\nNing ka esimese 250 epohhi graaf:\r\n\r\n[caption id=\"attachment_988\" align=\"alignnone\" width=\"1024\"]<img class=\"wp-image-988 size-large\" src=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-metrics-1024x224.png\" alt=\"N\u00e4rviv\u00f5rgu meetrikute graafid\" width=\"1024\" height=\"224\" \/> N\u00e4rviv\u00f5rgu meetrikute graaf, kus x-telg t\u00e4histab m\u00f6\u00f6dunud epohhite arvu ning y-teljed \u00f5igsust ja keskmist kadu. Pilt autori koostatud.[\/caption]\r\n\r\nSiin n\u00e4eme, et mingil hetkel ei suuda meie n\u00e4rviv\u00f5rk enam \u00f5ppida \u00f5igesti ennustama rohkem kui 47% piltidest. Treenimist\u00e4psus on aga muutumas aina k\u00f5rgemaks, mis v\u00f5ib m\u00f5nel hetkel p\u00f5hjustada isegi m\u00e4rgatavat testimist\u00e4psuste langust.\r\n<div class=\"textbox textbox--examples\"><header class=\"textbox__header\">\r\n<p class=\"textbox__title\">Lisainfo: N\u00e4rviv\u00f5rgu t\u00e4iustamine<\/p>\r\n\r\n<\/header>\r\n<div class=\"textbox__content\">\r\n\r\nInimesed suudavad ennustada \u00f5igesti 94% CIFAR-10 klassidest. Parimad n\u00e4rviv\u00f5rgud, mis kasutavad palju suuremaid andmekogumeid (<em>transfer learning<\/em>, loe rohkem\u00a0<a class=\"reference external\" href=\"https:\/\/d2l.ai\/chapter_computer-vision\/fine-tuning.html\">siit<\/a>) ja sadu miljoneid parameetreid, suudavad \u00f5igesti ennustada \u00fcle 99% CIFAR-10 klassidest. \u00dche miljoni parameetriga n\u00e4rviv\u00f5rgud suudavad ennustada \u00fcle 96%. Isegi koduarvutis treenitavatel n\u00e4rviv\u00f5rkudel, millele antakse vaid CIFAR-10 treeningandmeid, on v\u00f5imalik saavutada \u00fcle 80% \u00f5igsuse.\r\n\r\nSoovi korral v\u00f5ib \u00fcritada teha paremaks meie olemasolevat n\u00e4rviv\u00f5rku l\u00e4bi meetodite, mida siin peat\u00fckis ei k\u00e4sitleta. Selleks v\u00f5ib proovida:\r\n<ul class=\"simple\">\r\n \t<li>Tuunida h\u00fcperparameetreid: mis juhtub, kui valime suurema v\u00f5i v\u00e4iksema \u00f5pisammu\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">lr<\/span><\/code>, epohhite arvu v\u00f5i miniplokkide suuruse\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">batch_size<\/span><\/code>?<\/li>\r\n \t<li>Kasutada keerulisemaid kahjufunktsioone: mis juhtub, kui asendada MSE\u00a0<em>cross-entropy<\/em>-ga (loe rohkem\u00a0<a class=\"reference external\" href=\"https:\/\/pytorch.org\/docs\/stable\/generated\/torch.nn.CrossEntropyLoss.html\">siit<\/a>)?<\/li>\r\n \t<li>Kasutada keerulisemaid optimisatsioonialgoritme: mis juhtub, kui v\u00f5tta kasutusse <a class=\"reference external\" href=\"https:\/\/pytorch.org\/docs\/stable\/generated\/torch.optim.SGD.html\">SGD-d momentumi ja L2 regularisatsiooniga<\/a>\u00a0v\u00f5i\u00a0<a class=\"reference external\" href=\"https:\/\/pytorch.org\/docs\/stable\/generated\/torch.optim.Adam.html\">Adam<\/a>?<\/li>\r\n \t<li>Muuta n\u00e4rviv\u00f5rgu kihte: mis juhtub, kui lisame teise peidetud\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">nn.Linear<\/span><\/code>\u00a0kihi, koostame\u00a0<a class=\"reference external\" href=\"https:\/\/www.youtube.com\/watch?v=bNb2fEVKeEo\">konvolutsioonilised kihid<\/a>, muudame aktivatsioonifunktsioone v\u00f5i lisame olemasolevale rohkem neuroneid?<\/li>\r\n \t<li>Moonutada pilte: mis juhtub, kui muudame juhuslikult kontrasti, suurust v\u00f5i v\u00e4rve?<\/li>\r\n<\/ul>\r\nTihtipeale kasutatakse optimaalsete h\u00fcperparameetrite leidmiseks algoritme (nt\u00a0<em>grid search<\/em>), mis proovivad ise l\u00e4bi erinevaid v\u00e4\u00e4rtuste kombinatsioone, et leida parim. Nende kasutamine on aga keerukas ja aegan\u00f5udev. Rohkem saab lugeda\u00a0<a class=\"reference external\" href=\"https:\/\/pytorch.org\/tutorials\/beginner\/hyperparameter_tuning_tutorial.html\">siit<\/a>.\r\n\r\n<\/div>\r\n<\/div>\r\n<\/div>\r\n<h2>M\u00f5tle ja nuputa!<\/h2>\r\n[h5p id=\"48\"]\r\n<h1 id=\"lisalugemist\">Lisalugemist<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"https:\/\/pydoc.pages.taltech.ee\/image_recognition\/v2\/image_classification.html#lisalugemist\">\uf0c1<\/a><\/h1>\r\n<ul class=\"simple\">\r\n \t<li>\"Neural networks\", 3Blues1Brown \u2014\u00a0<a class=\"reference external\" href=\"https:\/\/www.youtube.com\/playlist?list=PLZHQObOWTQDNU6R1_67000Dx_ZCJB-3pi\">https:\/\/www.youtube.com\/playlist?list=PLZHQObOWTQDNU6R1_67000Dx_ZCJB-3pi<\/a><\/li>\r\n \t<li>\"Tehisintellekti algkursus\", Tartu \u00dclikool \u2014\u00a0<a class=\"reference external\" href=\"https:\/\/courses.cs.ut.ee\/2020\/Tehisintellekti_algkursus\">https:\/\/courses.cs.ut.ee\/2020\/Tehisintellekti_algkursus<\/a><\/li>\r\n \t<li>\"Neural Networks and Deep Learning\", Michael Nielsen \u2014\u00a0<a class=\"reference external\" href=\"http:\/\/neuralnetworksanddeeplearning.com\/\">http:\/\/neuralnetworksanddeeplearning.com<\/a><\/li>\r\n \t<li>\"Learn the Basics\", PyTorch \u2014\u00a0<a class=\"reference external\" href=\"https:\/\/pytorch.org\/tutorials\/beginner\/basics\/intro.html\">https:\/\/pytorch.org\/tutorials\/beginner\/basics\/intro.html<\/a>\u00a0ja\u00a0<a class=\"reference external\" href=\"https:\/\/www.youtube.com\/playlist?list=PL_lsbAsL_o2CTlGHgMxNrKhzP97BaG9ZN\">https:\/\/www.youtube.com\/playlist?list=PL_lsbAsL_o2CTlGHgMxNrKhzP97BaG9ZN<\/a><\/li>\r\n \t<li>\"CS231n: Convolutional Neural Networks for Visual Recognition\", Stanford University \u2014\u00a0<a class=\"reference external\" href=\"https:\/\/www.youtube.com\/playlist?list=PLC1qU-LWwrF64f4QKQT-Vg5Wr4qEE1Zxk\">https:\/\/www.youtube.com\/playlist?list=PLC1qU-LWwrF64f4QKQT-Vg5Wr4qEE1Zxk<\/a><\/li>\r\n \t<li>\"Dive into Deep Learning\" \u2014\u00a0<a class=\"reference external\" href=\"https:\/\/d2l.ai\/\">https:\/\/d2l.ai<\/a>\u00a0ja\u00a0<a class=\"reference external\" href=\"https:\/\/d2l.ai\/d2l-en.pdf\">https:\/\/d2l.ai\/d2l-en.pdf<\/a><\/li>\r\n<\/ul>","rendered":"<h2 id=\"treeningandmete-piltide-leidmine\">Treeningandmete (piltide) leidmine<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"https:\/\/pydoc.pages.taltech.ee\/image_recognition\/v2\/image_classification.html#treeningandmete-piltide-leidmine\">\uf0c1<\/a><\/h2>\n<p>Selleks, et masin saaks \u00f5ppida, kuidas \u00fcks v\u00f5i teine objekt v\u00e4lja n\u00e4eb, on tal vaja n\u00e4idisandmeid. Meie puhul see t\u00e4hendab, et treenimiseks on vaja koguda pilte. Mida rohkem n\u00e4idisandmeid on, seda paremini saab masin \u00f5ppida. Ei ole \u00fchest vastust, kui palju on piisav kogus mingi \u00fclesande jaoks. Me v\u00f5ime siin n\u00e4ite jaoks \u00f6elda, et 100 pilti on piisav, et mingisuguseid tulemusi juba saavutada. Aga vahepeal l\u00e4hevad need kogused miljonitesse. Teisest k\u00fcljest: mida rohkem on treeningandmeid (pilte), seda rohkem v\u00f5tab treenimine aega.<\/p>\n<p>Lisaks kogusele on t\u00e4htis ka piltide sobivus. Pildid peaks olema \u00fcksteisest v\u00f5imalikult erinevad, et n\u00e4rviv\u00f5rk saaks n\u00e4ha erinevaid kujusid ja v\u00e4rve. Soovitatav on v\u00f5tta pildid, kus on v\u00f5imalikult v\u00e4he h\u00e4irivaid faktoreid. See aitab v\u00e4ltida olukorda, kus n\u00e4rviv\u00f5rk \u00f5pib selgeks vale omaduse, mida esineb sageli, kuid ei ole defineeriv pildi juures. Kui n\u00e4iteks treenida j\u00e4neseid tuvastama piltidelt, kus on alati porgand, siis v\u00f5ib n\u00e4rviv\u00f5rk \u00f5ppida tuvastama j\u00e4nese asemel hoopis porgandit.<\/p>\n<div class=\"textbox textbox--examples\">\n<header class=\"textbox__header\">\n<p class=\"textbox__title\">Lisainfo: Treeningandmete moonutamine<\/p>\n<\/header>\n<div class=\"textbox__content\">\n<p>Samuti tasub proovida enne treenimist\u00a0<a class=\"reference external\" href=\"https:\/\/blog.keras.io\/building-powerful-image-classification-models-using-very-little-data.html\">treeningandmete moonutamist<\/a>\u00a0(venitamine, p\u00f6\u00f6ramine, m\u00fcra lisamine), et tulemuseks oleks t\u00f6\u00f6kindlam n\u00e4rviv\u00f5rk. See v\u00f5imaldab saada rohkem kasu samast pildist, sest igat pilti saab mitut eri moodi moonutada.<\/p>\n<\/div>\n<\/div>\n<h2 id=\"andmete-jagamine-osadeks\">Andmete jagamine osadeks<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"https:\/\/pydoc.pages.taltech.ee\/image_recognition\/v2\/image_classification.html#andmete-jagamine-osadeks\">\uf0c1<\/a><\/h2>\n<p>Selleks, et n\u00e4rviv\u00f5rku treenida ja tulemust kontrollida, on vaja andmed jagada osadeks ja m\u00e4\u00e4rata, millistel osadel treenitakse n\u00e4rviv\u00f5rku ja millistel osadel kontrollitakse tulemust. Kui seda mitte teha, v\u00f5ib n\u00e4rviv\u00f5rk lihtsalt k\u00f5ik andmed &#8220;p\u00e4he tuupida&#8221;. Kui see juhtub, siis n\u00e4rviv\u00f5rk ei omanda \u00fcldistavat oskust ja ei \u00f5pi tundma andmetes olevaid mustreid. Selline n\u00e4rviv\u00f5rk on kasutu kui ta kohtab andmeid, mida ta pole varem p\u00e4he tuupinud. Selle &#8220;p\u00e4he tuupimise&#8221; n\u00e4htuse nimi on\u00a0<strong>\u00fclesobitamine<\/strong>\u00a0(<em>overfitting<\/em>).<\/p>\n<p>Andmete osadeks jagamiseks on mitmeid erinevaid v\u00f5imalikke lahendusi. Siin tutvustame \u00fchte lihtsaimat, mis eeldab, et andmed jagatakse kaheks osaks: treeningandmestik (<em>training set<\/em>) ja testandmestik (<em>test set<\/em>).<\/p>\n<p><strong>Treeningandmestik<\/strong>\u00a0(<em>training set<\/em>) koosneb piltidest, mille peal n\u00e4rviv\u00f5rk \u00f5pib. Treeningandmed moodustavad enamuse kogu n\u00e4idisandmete hulgast. Neid pilte kasutab n\u00e4rviv\u00f5rk selleks, et leida optimaalsed neuronite kaalud.<\/p>\n<p><strong>Testandmestik<\/strong>\u00a0(<em>test set<\/em>) koosneb piltidest, mida kasutatakse l\u00f5pliku hinnangu andmiseks. Tegu on piltidega, mida n\u00e4rviv\u00f5rk pole siiani n\u00e4inud. Kuna need andmed pole treenimist m\u00f5jutanud, siis see aitab simuleerida reaalset olukorda, kus n\u00e4rviv\u00f5rk kohtab uusi andmeid. Nende andmete peal arvutatakse ka n\u00e4rviv\u00f5rgu l\u00f5plik t\u00e4psus.<\/p>\n<p>Osade suuruste jaoks puudub alati toimiv lahendus, kuid tihti kasutatakse treening- ja testiandmestiku suhteid nagu 90:10, 80:20 v\u00f5i 70:30. Mida v\u00e4hem on kogutud andmeid, seda rohkem s\u00f5ltub tulemus osade suurusest. Lisaks suurustele on t\u00e4htis ka sisu. Kui testandmed on liiga erinevad treeningandmetest, siis ei vasta testimistulemused treenimisele.<\/p>\n<div class=\"textbox textbox--examples\">\n<header class=\"textbox__header\">\n<p class=\"textbox__title\">Lisainfo: Andmegrupid<\/p>\n<\/header>\n<div class=\"textbox__content\">\n<p>P\u00f5hjalikuma \u00fclevaate andmete gruppidest, kaasa arvatud metoodikatest, kus jagatakse andmeid kolmeks osaks, leiab <a class=\"reference external\" href=\"https:\/\/en.wikipedia.org\/wiki\/Training,_validation,_and_test_sets\">siit<\/a>.<\/p>\n<\/div>\n<\/div>\n<h2>M\u00f5tle ja nuputa!<\/h2>\n<div id=\"h5p-47\">\n<div class=\"h5p-iframe-wrapper\"><iframe id=\"h5p-iframe-47\" class=\"h5p-iframe\" data-content-id=\"47\" style=\"height:1px\" src=\"about:blank\" frameBorder=\"0\" scrolling=\"no\" title=\"Pildituvastus (m\u00f5isted treeningandmestik ja testandmestik)\"><\/iframe><\/div>\n<\/div>\n<h2 id=\"andmete-laadimine-pytorchi\">Andmete laadimine PyTorchi<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"https:\/\/pydoc.pages.taltech.ee\/image_recognition\/v2\/image_classification.html#andmete-laadimine-pytorchi\">\uf0c1<\/a><\/h2>\n<p>Selles \u00fclesandes kasutame me masin\u00f5ppekogukonnas populaarset andmekogumit CIFAR-10, mis on mugavalt otse l\u00e4bi PyTorchi saadav ning ette jagatud treenimis- ja testimisosadeks (l\u00e4bi\u00a0<cite>train<\/cite>\u00a0argumendi). Seal olevad pildid on piisavalt kvaliteetsed otseseks kasutamiseks. CIFAR-10 nimi tuleneb sellest, et ta pildid jagunevad k\u00fcmnesse erinevasse klassi.<\/p>\n<figure id=\"attachment_984\" aria-describedby=\"caption-attachment-984\" style=\"width: 595px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-984 size-full\" src=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/cifar10-classes.png\" alt=\"CIFAR-10 klassid ja n\u00e4idispildid.\" width=\"595\" height=\"458\" srcset=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/cifar10-classes.png 595w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/cifar10-classes-300x231.png 300w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/cifar10-classes-65x50.png 65w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/cifar10-classes-225x173.png 225w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/cifar10-classes-350x269.png 350w\" sizes=\"auto, (max-width: 595px) 100vw, 595px\" \/><figcaption id=\"caption-attachment-984\" class=\"wp-caption-text\">Pildi allikas: https:\/\/www.cs.toronto.edu\/~kriz\/cifar.html<\/figcaption><\/figure>\n<p>T\u00e4ielikku nimekirja PyTorchi poolt (p\u00f5hiliselt \u00f5ppe- ja uuringeesm\u00e4rkidel) pakutavatest andmekogumitest saab vaadata <a class=\"reference external\" href=\"https:\/\/pytorch.org\/vision\/stable\/datasets.html\">siit<\/a>. CIFAR-10 pildid saab k\u00e4tte (automaatse internetist allalaadimisega) kasutades\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">DataSet<\/span><\/code>-i niiviisi:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">from torchvision import datasets, transforms\r\n\r\ntrain_data = datasets.CIFAR10(\r\n    root='data',  # kaust, kuhu andmed laetakse alla\r\n    train=True,   # kas soovime treeningandmeid (True) v\u00f5i testimisandmeid (False)\r\n    download=True,   # kas laeme vajadusel andmed automaatselt alla internetist\r\n    transform=transforms.ToTensor()  # teisendame pildid tensoriteks\r\n)\r\ntest_data = datasets.CIFAR10(\r\n    root='data',\r\n    train=False,\r\n    download=True,\r\n    transform=transforms.ToTensor()\r\n)\r\n\r\nx, y = next(iter(train_data))  # v\u00f5tame treenimisandmetest esimese pildi\r\nprint(x.shape)  # torch.Size([3, 32, 32]) - 3 v\u00e4rvikanalit (RGB), 32x32 pikslit\r\nprint(y)  # 6 - pildi klass<\/pre>\n<div class=\"textbox textbox--examples\">\n<header class=\"textbox__header\">\n<p class=\"textbox__title\">Lisainfo: Enda piltide laadimine<\/p>\n<\/header>\n<div class=\"textbox__content\">\n<p>Juhul kui meil on olemas oma enda pildid, saame kasutada klassi\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">ImageFolder<\/span><\/code>\u00a0(dokumentatsiooni saab lugeda\u00a0<a class=\"reference external\" href=\"https:\/\/pytorch.org\/vision\/stable\/generated\/torchvision.datasets.ImageFolder.html\">siit<\/a>). Sellele klassile tuleb anda kaust, kus on eraldi kaustad iga klassi jaoks, ning igas klassikaustas on antud klassi (<code>y)<\/code> kuuluvad pildid (<code>x<\/code>). Selles \u00fclesandes piirdume ainult CIFAR-10-ga.<\/p>\n<\/div>\n<\/div>\n<p>Kuigi \u00fcleval on toodud v\u00e4lja n\u00e4ide, kuidas pilte\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">DataSet<\/span><\/code>-ist \u00fche kaupa k\u00e4tte saada, on soovituslik lisaks sellele kasutada PyTorchi poolt pakutavat\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">DataLoader<\/span><\/code>-it, mis v\u00f5imaldab andmeid laadida suvalises j\u00e4rjekorras ja mitme kaupa (miniplokkides, ingl.k. <em>minibatches<\/em>), t\u00e4nu millele muutub treenimisprotsess efektiivsemaks.\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">DataLoader<\/span><\/code>-i kasulikest omadustest saab rohkem lugeda\u00a0<a class=\"reference external\" href=\"https:\/\/pytorch.org\/docs\/stable\/data.html#torch.utils.data.DataLoader\">siit<\/a>.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">from torch.utils.data import DataLoader\r\n\r\ntrain_loader = DataLoader(train_data, batch_size=16, shuffle=True)\r\ntest_loader = DataLoader(test_data, batch_size=16, shuffle=True)\r\n\r\nx, y = next(iter(train_loader))  # v\u00f5tame miniploki\r\nprint(x.shape)  # torch.Size([16, 3, 32, 32]) - 16 pilti, 3 v\u00e4rvikanalit (RGB), 32x32 pikslit\r\nprint(y.shape)  # torch.Size([16]) - 16 m\u00e4rgendit (klassi, kuhu miniploki pildid kuuluvad)<\/pre>\n<p>Mitu pilti korraga \u00fchte miniplokki laetakse on m\u00e4\u00e4ratud l\u00e4bi\u00a0<cite>batch_size<\/cite>\u00a0argumendi; t\u00fc\u00fcpiliselt on see 32 v\u00f5i 64, siin piirdume 16-ga. N\u00e4rviv\u00f5rgu parameetreid uuendatakse vaid \u00fche korra iga miniploki l\u00f5pus l\u00e4bi iga pildi ennustuse summeeritud kahju. Argument\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">shuffle=True<\/span><\/code>\u00a0t\u00e4hendab seda, et andmeid antakse suvalises j\u00e4rjekorras.<\/p>\n<p>&nbsp;<\/p>\n<figure id=\"attachment_985\" aria-describedby=\"caption-attachment-985\" style=\"width: 674px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-985\" src=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/cifar10-loss-landscape-1024x791.png\" alt=\"\" width=\"674\" height=\"521\" srcset=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/cifar10-loss-landscape-1024x791.png 1024w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/cifar10-loss-landscape-300x232.png 300w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/cifar10-loss-landscape-768x593.png 768w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/cifar10-loss-landscape-1536x1187.png 1536w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/cifar10-loss-landscape-65x50.png 65w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/cifar10-loss-landscape-225x174.png 225w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/cifar10-loss-landscape-350x270.png 350w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/cifar10-loss-landscape.png 1811w\" sizes=\"auto, (max-width: 674px) 100vw, 674px\" \/><figcaption id=\"caption-attachment-985\" class=\"wp-caption-text\">\u00dcks v\u00f5imalik CIFAR-10 kahjufunktsioonimaastiku visualisatsioon (interaktiivne versioon on saadaval http:\/\/www.telesens.co\/loss-landscape-viz\/viewer.html). Pildi allikas: https:\/\/www.cs.umd.edu\/~tomg\/projects\/landscapes\/<\/figcaption><\/figure>\n<h2 id=\"pytorchi-narvivorgumoodul\">PyTorchi n\u00e4rviv\u00f5rgumoodul<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"https:\/\/pydoc.pages.taltech.ee\/image_recognition\/v2\/image_classification.html#pytorchi-narvivorgumoodul\">\uf0c1<\/a><\/h2>\n<p>Selles peat\u00fckis v\u00f5tame kokku k\u00f5ik olemasolevad detailid ja hakkame ehitama lihtsat n\u00e4rviv\u00f5rgu, mis oskab klassifitseerida CIFAR-10 pilte. Treenimiseks kasutame PyTorchi poolt pakutavat\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">nn.Module<\/span><\/code>\u00a0klassi, mis lihtsustab n\u00e4rviv\u00f5rkude koostamist. Suurem osa koodist j\u00e4\u00e4b samaks, nagu varasemates n\u00e4idetes.<\/p>\n<div class=\"highlight-python notranslate\">\n<div class=\"highlight\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">from torch import nn\r\n\r\nclass NeuralNetwork(nn.Module):  # anname nn.Module-ile nime \"NeuralNetwork\"\r\n    def __init__(self):\r\n        super().__init__()  # vajalik rida nn.Module-i kasutamiseks!\r\n\r\n    def forward(self, x):\r\n        print(x.shape)  # torch.Size([16, 3, 32, 32]) - 16 pilti, 3 v\u00e4rvikanalit (RGB), 32x32 pikslit<\/pre>\n<p>Et\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">nn.Module<\/span><\/code>-it kasutada, pole vaja teada klassidest palju. Peamine detail on see, et\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">__init__<\/span><\/code>\u00a0meetodis tuleb luua k\u00f5ik kihid ning\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">forward<\/span><\/code>\u00a0meetodis tuleb kirjeldada, kuidas andmed neid kihte l\u00e4bivad.<\/p>\n<p>Deklareerime mooduli kihid pannes kihimuutujate nimede ette\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">self.<\/span><\/code>\u00a0(t\u00e4nu sellele saame neid kasutada\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">forward<\/span><\/code>\u00a0meetodis j\u00e4lle l\u00e4bi\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">self<\/span><\/code>\u00a0parameetri):<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">def __init__(self):\r\n    super().__init__()  # vajalik rida nn.Module-i kasutamiseks!\r\n\r\n    self.hidden_layer = nn.Linear(3 * 32 * 32, 30)  # 32x32x3 pikslit, 30 v\u00e4ljundit\r\n    self.sigmoid = nn.Sigmoid()  # aktivatsioonifunktsioon peidetud kihile\r\n    self.output_layer = nn.Linear(30, 10)  # 30 sisendit, 10 v\u00e4ljundit (\u00fcks iga klassi kohta)\r\n    self.softmax = nn.Softmax(dim=1)  # aktivatsioonifunktsioon v\u00e4ljundkihile<\/pre>\n<p>Viimasel real\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">self.softmax<\/span>\u00a0<span class=\"pre\">=<\/span>\u00a0<span class=\"pre\">nn.Softmax(dim=1)<\/span><\/code>\u00a0m\u00e4\u00e4rab argument\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">dim=1<\/span><\/code>\u00a0\u00e4ra, et\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">softmax<\/span><\/code>\u00a0rakendataks iga pildi kohta eraldi. See on vajalik, kuna me kasutame miniplokke, mille puhul antakse meile\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">x<\/span><\/code>-i 16 pilti korraga. Aktivatsioonifunktsioon\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">nn.Softmax<\/span><\/code>\u00a0viib v\u00e4ljundkihi neuronid kujule kus nende kogusumma on 1, mis v\u00f5imaldab neid v\u00e4\u00e4rtusi interpreteerida piltide klassi kuuluvuse t\u00f5en\u00e4osustena (n\u00e4rviv\u00f5rgu &#8220;ennustuseks&#8221; loeme suurima t\u00f5en\u00e4osusega klassi). N\u00e4iteks:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">data = torch.tensor([5.5, 3.5, 4.2])\r\nactivation_fn = nn.Softmax()\r\nprint(activation_fn(data))  # tensor([0.7103, 0.0961, 0.1936]) - 71%, 10%, 19%<\/pre>\n<p>N\u00fc\u00fcd kui kihimuutujad on deklareeritud, kirjeldame, kuidas andmed neid kihte l\u00e4bivad iga\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">x<\/span><\/code>-i korral:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">def forward(self, x):\r\n    x = x.flatten(start_dim=1)  # 16x3x32x32 -&gt; 16x3072\r\n    z_1 = self.hidden_layer(x)  # 16x3072 -&gt; 16x30\r\n    a_1 = self.sigmoid(z_1)  # 16x30 -&gt; 16x30\r\n    z_2 = self.output_layer(a_1)  # 16x30 -&gt; 16x10\r\n    a_2 = self.softmax(z_2)  # 16x10 -&gt; 16x10\r\n    return a_2<\/pre>\n<p>Esimene\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">x<\/span><\/code>-i transformeeriv rida\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">x<\/span>\u00a0<span class=\"pre\">=<\/span>\u00a0<span class=\"pre\">x.flatten(start_dim=1)<\/span><\/code>\u00a0on vajalik, sest\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">nn.Linear<\/span><\/code>\u00a0ootab sisendandmeid 1D-kujul, v\u00e4lja arvatud esimene miniplokkide dimensioon (indeks 0), millega tegeleb\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">nn.Linear<\/span><\/code>\u00a0automaatselt. Argument\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">start_dim=1<\/span><\/code>\u00a0t\u00e4hendab seda, et tensori lamestamisel esimest dimensiooni ignoreeritakse (lamestamist alustatakse indeks 1 dimensiooniga). N\u00fc\u00fcd kui\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">x<\/span><\/code>\u00a0on \u00f5igel kujul, l\u00e4bivad andmed peidetud kihist v\u00e4ljundkihini, kus l\u00f5plik tulemus tagastatakse.<\/p>\n<figure id=\"attachment_986\" aria-describedby=\"caption-attachment-986\" style=\"width: 719px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-986\" src=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-graph-890x1024.png\" alt=\"N\u00e4rviv\u00f5rgu visualiseeritud struktuur\" width=\"719\" height=\"827\" srcset=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-graph-890x1024.png 890w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-graph-261x300.png 261w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-graph-768x884.png 768w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-graph-1334x1536.png 1334w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-graph-65x75.png 65w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-graph-225x259.png 225w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-graph-350x403.png 350w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-graph.png 1356w\" sizes=\"auto, (max-width: 719px) 100vw, 719px\" \/><figcaption id=\"caption-attachment-986\" class=\"wp-caption-text\">Meie n\u00e4rviv\u00f5rgu visualiseeritud struktuur. Pilt autori koostatud.<\/figcaption><\/figure>\n<h2 id=\"epohhid-ja-narvivorgu-treenimine\">Epohhid ja n\u00e4rviv\u00f5rgu treenimine<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"https:\/\/pydoc.pages.taltech.ee\/image_recognition\/v2\/image_classification.html#epohhid-ja-narvivorgu-treenimine\">\uf0c1<\/a><\/h2>\n<p>N\u00e4rviv\u00f5rgu treenimisprotsess koosneb epohhitest (<em>epoch<\/em>). Epohhi jooksul k\u00e4iakse treeningandmed \u00fche korra l\u00e4bi. Treenimine koosneb tavaliselt rohkem kui \u00fches epohhist. Kui teha liiga palju epohhe, siis tekib \u00fcletreenimise oht, sest n\u00e4rviv\u00f5rgule antakse piisavalt aega andmetele \u00fclesobituda.<\/p>\n<p>Loome n\u00e4rviv\u00f5rgu instantsi, koostame MSE kahjufunktsiooni ja m\u00e4\u00e4rame optimeerija \u00f5pisammuga 0.1:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">net = NeuralNetwork()\r\nloss_fn = nn.MSELoss()\r\noptimizer = torch.optim.SGD(net.parameters(), lr=0.1)<\/pre>\n<div class=\"textbox textbox--examples\">\n<header class=\"textbox__header\">\n<p class=\"textbox__title\">Lisainfo: CUDA kasutajatele<\/p>\n<\/header>\n<div class=\"textbox__content\">\n<p>Juhul kui oled siiamaani kasutanud CUDA-t, tuleb m\u00e4\u00e4rata ka\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">nn.Module<\/span><\/code>-i seadme:\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">net<\/span>\u00a0<span class=\"pre\">=<\/span>\u00a0<span class=\"pre\">NeuralNetwork().to(device)<\/span><\/code>. Seade rakendub k\u00f5ikidele kihtidele, mis on deklareeritud antud <code class=\"docutils literal notranslate\"><span class=\"pre\">class<\/span>\u00a0<span class=\"pre\">NeuralNetwork(nn.Module)<\/span><\/code>-i sees.<\/p>\n<\/div>\n<\/div>\n<p>J\u00e4rgmisena hakkame kirjutama ts\u00fcklit, mis treenib n\u00e4rviv\u00f5rku 10 epohhit:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">for epoch in range(10):\r\n    for x, y in train_loader:  # k\u00e4ime l\u00e4bi k\u00f5ik miniplokid\r\n        y_hat = net(x)  # teeme ennustuse\r\n        print(y.shape, y_hat.shape)  # torch.Size([16]) torch.Size([16, 10])\r\n\r\n        optimizer.zero_grad()  # nullime eelnevad gradiendid\r\n        loss = loss_fn(y_hat, y)  # arvutame kahju\r\n        # error! y ja y_hat on erineva kujuga, seega ei saa kahju arvutada<\/pre>\n<p>Siin tekib probleem! Muutuja\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">y<\/span><\/code>\u00a0on kujul\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">[2,<\/span>\u00a0<span class=\"pre\">5,<\/span>\u00a0<span class=\"pre\">...,<\/span>\u00a0<span class=\"pre\">1,<\/span>\u00a0<span class=\"pre\">5]<\/span><\/code>, muutuja\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">y_hat<\/span><\/code>\u00a0aga kujul\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">[[0.1,<\/span>\u00a0<span class=\"pre\">0.2,<\/span>\u00a0<span class=\"pre\">...,<\/span>\u00a0<span class=\"pre\">0.5],<\/span>\u00a0<span class=\"pre\">[0.4,<\/span>\u00a0<span class=\"pre\">0.1,<\/span>\u00a0<span class=\"pre\">...,<\/span>\u00a0<span class=\"pre\">0.1],<\/span>\u00a0<span class=\"pre\">...,<\/span>\u00a0<span class=\"pre\">[0.0,<\/span>\u00a0<span class=\"pre\">0.7,<\/span>\u00a0<span class=\"pre\">...,<\/span>\u00a0<span class=\"pre\">0.2]]<\/span><\/code>. Esimeses on tegemist \u00f5igete klasside indeksitega, teises aga ennustatud klasside t\u00f5en\u00e4osustega. Et kahjufunktsioon t\u00f6\u00f6taks, peame muutma\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">y<\/span><\/code>\u00a0samale kujule nagu\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">y_hat<\/span><\/code>. N\u00e4iteks kui\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">y[0]<\/span><\/code>\u00a0on\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">2<\/span><\/code>, peaks temast saama vektor\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">[0,<\/span>\u00a0<span class=\"pre\">0,<\/span>\u00a0<span class=\"pre\">1,<\/span>\u00a0<span class=\"pre\">0,<\/span>\u00a0<span class=\"pre\">0,<\/span>\u00a0<span class=\"pre\">0,<\/span>\u00a0<span class=\"pre\">0,<\/span>\u00a0<span class=\"pre\">0,<\/span>\u00a0<span class=\"pre\">0,<\/span>\u00a0<span class=\"pre\">0]<\/span><\/code>. Kuigi saaksime teoorias kirjutada selle probleemi lahendamiseks enda koodi, pakub PyTorch mugavat funktsiooni\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">nn.functional.one_hot<\/span><\/code>, mis teeb terve t\u00f6\u00f6 \u00e4ra meie eest:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">for x, y in train_loader:  # k\u00e4ime l\u00e4bi k\u00f5ik miniplokid\r\n    y_hat = net(x)  # teeme ennustuse\r\n\r\n    y = nn.functional.one_hot(y, 10).to(torch.float)  # muudame y-i \u00f5igele kujule (10 klassi)\r\n    optimizer.zero_grad()  # nullime k\u00f5ik varasemad gradiendid\r\n    loss = loss_fn(y_hat, y)  # arvutame kahju<\/pre>\n<p>N\u00fc\u00fcd kui kahju on arvutatud, peame me ka arvutama, kuidas see kahju m\u00f5jutab iga parameetri (<code>w<\/code>) v\u00e4\u00e4rtust l\u00e4bi funktsiooni\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">loss.backward()<\/span><\/code>. See funktsioon arvutab gradiendid ja salvestab nad kihtide sisse. Ja l\u00f5puks kasutame optimeerijat, et muuta kaalufaktoreid vastavalt gradiendile.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">loss.backward()  # leiame, kui palju peame muutma iga kihi parameetrit, et kahju oleks v\u00e4iksem\r\noptimizer.step()  # teeme sammu optimeerija abil (muudame kaale vastavalt gradiendile)<\/pre>\n<p>Ning sellega on meie treeningprotsess valmis.<\/p>\n<figure id=\"attachment_987\" aria-describedby=\"caption-attachment-987\" style=\"width: 371px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-987 size-full\" src=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/xkcd.png\" alt=\"Machine lerning system\" width=\"371\" height=\"439\" srcset=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/xkcd.png 371w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/xkcd-254x300.png 254w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/xkcd-65x77.png 65w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/xkcd-225x266.png 225w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/xkcd-350x414.png 350w\" sizes=\"auto, (max-width: 371px) 100vw, 371px\" \/><figcaption id=\"caption-attachment-987\" class=\"wp-caption-text\">Pildi allikas: https:\/\/xkcd.com\/1838\/<\/figcaption><\/figure>\n<\/div>\n<h2 id=\"narvivorgu-testimine\">N\u00e4rviv\u00f5rgu testimine<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"https:\/\/pydoc.pages.taltech.ee\/image_recognition\/v2\/image_classification.html#narvivorgu-testimine\">\uf0c1<\/a><\/h2>\n<p>Kui tahame n\u00e4ha, kui h\u00e4sti meie n\u00e4rviv\u00f5rk t\u00f6\u00f6tab, siis saame selleks kasutada testimisandmeid. Igas epohhis k\u00e4ime algselt l\u00e4bi k\u00f5ik treeningandmed (<code class=\"docutils literal notranslate\"><span class=\"pre\">for<\/span>\u00a0<span class=\"pre\">x,<\/span>\u00a0<span class=\"pre\">y<\/span>\u00a0<span class=\"pre\">in<\/span>\u00a0<span class=\"pre\">train_loader<\/span><\/code>) ja siis k\u00f5ik testimisandmed (<code class=\"docutils literal notranslate\"><span class=\"pre\">for<\/span>\u00a0<span class=\"pre\">x,<\/span>\u00a0<span class=\"pre\">y<\/span>\u00a0<span class=\"pre\">in<\/span>\u00a0<span class=\"pre\">test_loader<\/span><\/code>).<\/p>\n<p>Testimisandmete ts\u00fckkel erineb selle poolest, et me ei tee optimeerimist (ei muuda kaale \/ treeni n\u00e4rviv\u00f5rku). T\u00e4nu sellele ei saa n\u00e4rviv\u00f5rk testiandmed &#8220;meelde j\u00e4tta&#8221;: simuleerime reaalse elus olukorda, kus me ei tea, millised andmed meile tulevad. Kui me seda ei teeks, oleks meil v\u00f5imatu teada kui h\u00e4sti n\u00e4rviv\u00f5rk reaalselt oskab meie probleemi mustreid generaliseerida.<\/p>\n<p>Treenimise v\u00e4ltimiseks j\u00e4tame \u00e4ra read\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">loss.backward()<\/span><\/code>,\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">optimizer.zero_grad()<\/span><\/code>\u00a0ja\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">optimizer.step()<\/span><\/code>. Olenedes statistikatest mis meid huvitavad, v\u00f5ime \u00e4ra j\u00e4tta ka l\u00f5pliku kahju arvutamise (<code class=\"docutils literal notranslate\"><span class=\"pre\">loss_fn(y_hat,<\/span>\u00a0<span class=\"pre\">y)<\/span><\/code>).<\/p>\n<p>Lisaks sellele ei pea me ka taustas arvutama gradiente, mida muidu\u00a0<a class=\"reference external\" href=\"https:\/\/pytorch.org\/tutorials\/beginner\/blitz\/autograd_tutorial.html\">tehakse PyTorchis vaikimisi<\/a>. Et gradientide arvutamist v\u00e4ltida, peame panema oma koodi\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">with<\/span>\u00a0<span class=\"pre\">torch.no_grad()<\/span><\/code>\u00a0konteksti:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">for epoch in range(10):\r\n    for x, y in train_loader:\r\n        ...  # sama kood, mis varem\r\n\r\n    with torch.no_grad():  # me ei soovi arvutada gradiente\r\n        for x, y in test_loader:  # k\u00e4ime l\u00e4bi k\u00f5ik miniplokid\r\n            y_hat = net(x)  # teeme ennustuse\r\n\r\n            y = nn.functional.one_hot(y, 10).to(torch.float)  # muudame y-i \u00f5igele kujule (10 klassi)\r\n            loss = loss_fn(y_hat, y)  # arvutame kahju statistika jaoks<\/pre>\n<p>N\u00fc\u00fcd on meie treenimiskood palju kiirem, sest me ei arvuta enam m\u00f5ttetult gradiente.<\/p>\n<p>J\u00e4rgmine samm on hakata arvutama statistikaid nii treenimis- kui ka testimisandmete peal. M\u00f5lema m\u00f5\u00f5tmine on t\u00e4htis selle jaoks, et m\u00e4rgata \u00fclesobitamist. \u00dclesobitamise selgeim s\u00fcmptom on k\u00f5rge \u00f5igsus (\u00f5igete ennustuste protsent; ingl.k.\u00a0<em>accuracy<\/em>) treeningandmete peal, kuid tunduvalt madalamatesse \u00f5igsus testimisandmete peal. Kui n\u00e4rviv\u00f5rk t\u00f6\u00f6tab h\u00e4sti, siis on \u00f5igsus k\u00f5rge nii treening- kui ka testimisandmetel.<\/p>\n<p>Kuna meie koodis tulevad igas epohhis testimisandmed p\u00e4rast treeningandmeid, siis epohhi-keskse n\u00e4rviv\u00f5rgu \u00f5ppimise t\u00f5ttu v\u00f5ib testimisandmete \u00f5igsus tulla k\u00f5rgem kui treeningandmete \u00f5igsus.<\/p>\n<div class=\"textbox textbox--examples\">\n<header class=\"textbox__header\">\n<p class=\"textbox__title\">Lisainfo: \u00dclesobitamise p\u00f5hjused<\/p>\n<\/header>\n<div class=\"textbox__content\">\n<p>\u00dclesobitamist saab tekitada n\u00e4iteks:<\/p>\n<ul class=\"simple\">\n<li>Liiga suur n\u00e4rviv\u00f5rk, mille suur parameetrite arv v\u00f5imaldab tal treeningandmed meelde j\u00e4tta; n\u00e4rviv\u00f5rgul pole m\u00f5tet olla &#8220;kokkuhoidlik&#8221; ja otsida mustreid.<\/li>\n<li>Liiga v\u00e4he treeningandmeid, mis j\u00e4llegi teeb p\u00e4he tuupimise liiga kergeks. Tegu on sisuliselt sama probleemiga nagu eelmises punktis: n\u00e4rviv\u00f5rk on asjatult keeruline treeningandmete koguse suhtes.<\/li>\n<li>Liiga palju epohhe. Tihtipeale \u00f5pib n\u00e4rviv\u00f5rk mingiks epohhiks mustrid selgeks, ning edaspidi hakkab \u00fclesobituma. Seet\u00f5ttu on oluline valida \u00f5ige epohhide arv.<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<h2 id=\"narvivorgu-meetrikad\">N\u00e4rviv\u00f5rgu meetrikad<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"https:\/\/pydoc.pages.taltech.ee\/image_recognition\/v2\/image_classification.html#narvivorgu-meetrikad\">\uf0c1<\/a><\/h2>\n<p>J\u00e4rgmine samm on hakata arvutama n\u00e4rviv\u00f5rgu ennustuste \u00f5igsust (\u00f5igete ennustuste protsenti). Koostame igas epohhis muutujad\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">train_correct<\/span><\/code>\u00a0ja\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">train_total<\/span><\/code>, ning\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">test_correct<\/span><\/code>\u00a0ja\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">test_total<\/span><\/code>. Need muutujad hakkavad hoidma meie \u00f5igete ennustuste ja koguennustuste arvu. Koostame ka\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">train_loss_sum<\/span><\/code>\u00a0ja\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">test_loss_sum<\/span><\/code>\u00a0muutujad, et arvutada keskmist kahju. S\u00e4time k\u00f5igi nelja muutuja algseteks v\u00e4\u00e4rtuseks null. J\u00e4rgmisena k\u00e4ime l\u00e4bi m\u00f5lemad ts\u00fcklid ja liidame\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">train_total<\/span><\/code>\u00a0\/\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">test_total<\/span><\/code>\u00a0muutujatele 1 igas iteratsioonis.<\/p>\n<p>N\u00fc\u00fcd kui see on tehtud, peame me leidma, kas mingi ennustus oli \u00f5ige. Kasutame selleks funktsiooni\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">torch.argmax<\/span><\/code>, mis leiab k\u00f5ige suurema v\u00e4\u00e4rtusega elemendi ja tagastab ta indeksi (ehk klassinumbri). N\u00e4iteks\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">torch.tensor([0.1,<\/span>\u00a0<span class=\"pre\">0.2,<\/span>\u00a0<span class=\"pre\">0.5,<\/span>\u00a0<span class=\"pre\">0.2]).argmax()<\/span><\/code>\u00a0tagastab\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">torch.tensor(2)<\/span><\/code>. Kui ennustatud klass on sama, mis tegelik klass, siis suurendame\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">train_correct<\/span><\/code>\u00a0\/\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">test_correct<\/span><\/code>\u00a0muutujat \u00fche v\u00f5rra. L\u00f5puks arvutame \u00f5igsuse ja kahju keskmiseid.<\/p>\n<p>Siin on n\u00e4idiskood\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">test_loader<\/span><\/code>-i jaoks:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">for x, y in test_loader:\r\n    y_hat = net(x)  # teeme ennustuse\r\n\r\n    for prediction, correct_class in zip(y_hat, y):  # l\u00e4bime paarikaupa k\u00f5ik y\/y_hat v\u00e4\u00e4rtused miniplokis\r\n        prediction_class = torch.argmax(prediction)  # leiame suurima v\u00e4\u00e4rtusega indeksi\r\n        if prediction_class == correct_class:  # kas ennustus oli \u00f5ige?\r\n            test_correct += 1\r\n        test_total += 1\r\n\r\n    y = nn.functional.one_hot(y, 10).to(torch.float)  # muudame y-i \u00f5igele kujule kahju arvutamiseks\r\n    loss = loss_fn(y_hat, y)  # arvutame l\u00f5pliku kahju statistika jaoks\r\n    test_loss_sum += loss.item()  # lisame l\u00f5pliku kahju summale<\/pre>\n<div class=\"textbox textbox--examples\">\n<header class=\"textbox__header\">\n<p class=\"textbox__title\">Lisainfo: Rohkem meetrikaid<\/p>\n<\/header>\n<div class=\"textbox__content\">\n<div class=\"admonition-lisainfo-rohkem-meetrikaid admonition\">\n<p>On olemas\u00a0<a class=\"reference external\" href=\"https:\/\/towardsdatascience.com\/the-5-classification-evaluation-metrics-you-must-know-aa97784ff226\">v\u00e4ga palju teisi meetrikaid<\/a>, mida saab kasutada n\u00e4rviv\u00f5rgu t\u00f6\u00f6 hindamiseks:<\/p>\n<ul class=\"simple\">\n<li><strong>\u00d5igsus<\/strong>\u00a0(<em>accuracy<\/em>) kirjeldab kui suur osa andmetest klassifitseeriti \u00f5igesti. Olukorras, kus \u00fchte klassi on rohkem kui teisi on tulemus kallutatud selle \u00fche klassi poole. V\u00f5tame n\u00e4iteks andmestiku kahe klassiga: 99 koera ja 1 kass. Kui n\u00e4rviv\u00f5rk alati ennustab, et tegu on koeraga, siis \u00f5igsus on 99%, aga ta ei oska tegelikult midagi teha. Kasuta kui klasside osakaal andmetes on v\u00f5rdne (nii see on nt CIFAR-10 puhul).<\/li>\n<li><strong>T\u00e4psus<\/strong>\u00a0(<em>precision<\/em>) kirjeldab kui suurt osa moodustavad \u00f5igesti ennustatud positiivseid (<em>true positive<\/em>) tulemused k\u00f5igist positiivselt ennustatud tulemustest. Kasuta kui tulemuses peab olema v\u00e4ga kindel ja pead v\u00e4ltima valesid positiivseid (<em>false positive<\/em>) tulemusi.<\/li>\n<li><strong>Saagis<\/strong>\u00a0(<em>recall<\/em>) kirjeldab \u00f5igesti ennustatud positiivsete tulemuste osakaalu k\u00f5igist tegelikult t\u00f5estest tulemustest. Kasuta kui eesm\u00e4rgiks on klassifitseerida positiivselt v\u00f5imalikult palju positiivseid tulemusi.<\/li>\n<li><strong>F1 skoor<\/strong>\u00a0kombineerib t\u00e4psuse (<em>precision<\/em>) ja saagise (<em>recall<\/em>), et anda tulemus, mis arvestab m\u00f5lemat.<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<\/div>\n<p>Peale samasuguse koodi kirjutamist ka\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">train_loader<\/span><\/code>-i ts\u00fcklisse, saame printida epohhi l\u00f5pus statistikat. Selle osa v\u00f5ib vabalt kirjutada nii, kuidas soov on. Siin on n\u00e4itena toodud f-stringidega formeeritud versioon:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">print(f'Epoch {epoch + 1:03} train | avg loss: {train_loss_sum \/ train_total:.6f}, '\r\n      f'accuracy: {train_correct \/ train_total:.2%}')\r\nprint(f'           test | avg loss: {test_loss_sum \/ test_total:.6f}, '\r\n      f'accuracy: {test_correct \/ test_total:.2%}')<\/pre>\n<p>Eelneva koodi puhul tuleb v\u00e4ljund selline:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">Epoch 001 train | avg loss: 0.005552, accuracy: 20.98%\r\n           test | avg loss: 0.005468, accuracy: 25.72%\r\nEpoch 002 train | avg loss: 0.005386, accuracy: 26.99%\r\n           test | avg loss: 0.005310, accuracy: 27.91%\r\n...\r\nEpoch 014 train | avg loss: 0.004688, accuracy: 39.11%\r\n           test | avg loss: 0.004678, accuracy: 39.73%\r\nEpoch 015 train | avg loss: 0.004663, accuracy: 39.60%\r\n           test | avg loss: 0.004659, accuracy: 39.53%\r\n...\r\nEpoch 114 train | avg loss: 0.003915, accuracy: 51.45%\r\n           test | avg loss: 0.004193, accuracy: 47.24%\r\n...\r\nEpoch 200 train | avg loss: 0.003642, accuracy: 55.95%\r\n           test | avg loss: 0.004221, accuracy: 47.05%<\/pre>\n<p>Ning ka esimese 250 epohhi graaf:<\/p>\n<figure id=\"attachment_988\" aria-describedby=\"caption-attachment-988\" style=\"width: 1024px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-988 size-large\" src=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-metrics-1024x224.png\" alt=\"N\u00e4rviv\u00f5rgu meetrikute graafid\" width=\"1024\" height=\"224\" srcset=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-metrics-1024x224.png 1024w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-metrics-300x66.png 300w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-metrics-768x168.png 768w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-metrics-1536x336.png 1536w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-metrics-65x14.png 65w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-metrics-225x49.png 225w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-metrics-350x77.png 350w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-metrics.png 1836w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption id=\"caption-attachment-988\" class=\"wp-caption-text\">N\u00e4rviv\u00f5rgu meetrikute graaf, kus x-telg t\u00e4histab m\u00f6\u00f6dunud epohhite arvu ning y-teljed \u00f5igsust ja keskmist kadu. Pilt autori koostatud.<\/figcaption><\/figure>\n<p>Siin n\u00e4eme, et mingil hetkel ei suuda meie n\u00e4rviv\u00f5rk enam \u00f5ppida \u00f5igesti ennustama rohkem kui 47% piltidest. Treenimist\u00e4psus on aga muutumas aina k\u00f5rgemaks, mis v\u00f5ib m\u00f5nel hetkel p\u00f5hjustada isegi m\u00e4rgatavat testimist\u00e4psuste langust.<\/p>\n<div class=\"textbox textbox--examples\">\n<header class=\"textbox__header\">\n<p class=\"textbox__title\">Lisainfo: N\u00e4rviv\u00f5rgu t\u00e4iustamine<\/p>\n<\/header>\n<div class=\"textbox__content\">\n<p>Inimesed suudavad ennustada \u00f5igesti 94% CIFAR-10 klassidest. Parimad n\u00e4rviv\u00f5rgud, mis kasutavad palju suuremaid andmekogumeid (<em>transfer learning<\/em>, loe rohkem\u00a0<a class=\"reference external\" href=\"https:\/\/d2l.ai\/chapter_computer-vision\/fine-tuning.html\">siit<\/a>) ja sadu miljoneid parameetreid, suudavad \u00f5igesti ennustada \u00fcle 99% CIFAR-10 klassidest. \u00dche miljoni parameetriga n\u00e4rviv\u00f5rgud suudavad ennustada \u00fcle 96%. Isegi koduarvutis treenitavatel n\u00e4rviv\u00f5rkudel, millele antakse vaid CIFAR-10 treeningandmeid, on v\u00f5imalik saavutada \u00fcle 80% \u00f5igsuse.<\/p>\n<p>Soovi korral v\u00f5ib \u00fcritada teha paremaks meie olemasolevat n\u00e4rviv\u00f5rku l\u00e4bi meetodite, mida siin peat\u00fckis ei k\u00e4sitleta. Selleks v\u00f5ib proovida:<\/p>\n<ul class=\"simple\">\n<li>Tuunida h\u00fcperparameetreid: mis juhtub, kui valime suurema v\u00f5i v\u00e4iksema \u00f5pisammu\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">lr<\/span><\/code>, epohhite arvu v\u00f5i miniplokkide suuruse\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">batch_size<\/span><\/code>?<\/li>\n<li>Kasutada keerulisemaid kahjufunktsioone: mis juhtub, kui asendada MSE\u00a0<em>cross-entropy<\/em>-ga (loe rohkem\u00a0<a class=\"reference external\" href=\"https:\/\/pytorch.org\/docs\/stable\/generated\/torch.nn.CrossEntropyLoss.html\">siit<\/a>)?<\/li>\n<li>Kasutada keerulisemaid optimisatsioonialgoritme: mis juhtub, kui v\u00f5tta kasutusse <a class=\"reference external\" href=\"https:\/\/pytorch.org\/docs\/stable\/generated\/torch.optim.SGD.html\">SGD-d momentumi ja L2 regularisatsiooniga<\/a>\u00a0v\u00f5i\u00a0<a class=\"reference external\" href=\"https:\/\/pytorch.org\/docs\/stable\/generated\/torch.optim.Adam.html\">Adam<\/a>?<\/li>\n<li>Muuta n\u00e4rviv\u00f5rgu kihte: mis juhtub, kui lisame teise peidetud\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">nn.Linear<\/span><\/code>\u00a0kihi, koostame\u00a0<a class=\"reference external\" href=\"https:\/\/www.youtube.com\/watch?v=bNb2fEVKeEo\">konvolutsioonilised kihid<\/a>, muudame aktivatsioonifunktsioone v\u00f5i lisame olemasolevale rohkem neuroneid?<\/li>\n<li>Moonutada pilte: mis juhtub, kui muudame juhuslikult kontrasti, suurust v\u00f5i v\u00e4rve?<\/li>\n<\/ul>\n<p>Tihtipeale kasutatakse optimaalsete h\u00fcperparameetrite leidmiseks algoritme (nt\u00a0<em>grid search<\/em>), mis proovivad ise l\u00e4bi erinevaid v\u00e4\u00e4rtuste kombinatsioone, et leida parim. Nende kasutamine on aga keerukas ja aegan\u00f5udev. Rohkem saab lugeda\u00a0<a class=\"reference external\" href=\"https:\/\/pytorch.org\/tutorials\/beginner\/hyperparameter_tuning_tutorial.html\">siit<\/a>.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<h2>M\u00f5tle ja nuputa!<\/h2>\n<div id=\"h5p-48\">\n<div class=\"h5p-iframe-wrapper\"><iframe id=\"h5p-iframe-48\" class=\"h5p-iframe\" data-content-id=\"48\" style=\"height:1px\" src=\"about:blank\" frameBorder=\"0\" scrolling=\"no\" title=\"Pildituvastus (m\u00f5isted \u00f5igsus, t\u00e4psus,saagis ja F1-skoor)\"><\/iframe><\/div>\n<\/div>\n<h1 id=\"lisalugemist\">Lisalugemist<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"https:\/\/pydoc.pages.taltech.ee\/image_recognition\/v2\/image_classification.html#lisalugemist\">\uf0c1<\/a><\/h1>\n<ul class=\"simple\">\n<li>&#8220;Neural networks&#8221;, 3Blues1Brown \u2014\u00a0<a class=\"reference external\" href=\"https:\/\/www.youtube.com\/playlist?list=PLZHQObOWTQDNU6R1_67000Dx_ZCJB-3pi\">https:\/\/www.youtube.com\/playlist?list=PLZHQObOWTQDNU6R1_67000Dx_ZCJB-3pi<\/a><\/li>\n<li>&#8220;Tehisintellekti algkursus&#8221;, Tartu \u00dclikool \u2014\u00a0<a class=\"reference external\" href=\"https:\/\/courses.cs.ut.ee\/2020\/Tehisintellekti_algkursus\">https:\/\/courses.cs.ut.ee\/2020\/Tehisintellekti_algkursus<\/a><\/li>\n<li>&#8220;Neural Networks and Deep Learning&#8221;, Michael Nielsen \u2014\u00a0<a class=\"reference external\" href=\"http:\/\/neuralnetworksanddeeplearning.com\/\">http:\/\/neuralnetworksanddeeplearning.com<\/a><\/li>\n<li>&#8220;Learn the Basics&#8221;, PyTorch \u2014\u00a0<a class=\"reference external\" href=\"https:\/\/pytorch.org\/tutorials\/beginner\/basics\/intro.html\">https:\/\/pytorch.org\/tutorials\/beginner\/basics\/intro.html<\/a>\u00a0ja\u00a0<a class=\"reference external\" href=\"https:\/\/www.youtube.com\/playlist?list=PL_lsbAsL_o2CTlGHgMxNrKhzP97BaG9ZN\">https:\/\/www.youtube.com\/playlist?list=PL_lsbAsL_o2CTlGHgMxNrKhzP97BaG9ZN<\/a><\/li>\n<li>&#8220;CS231n: Convolutional Neural Networks for Visual Recognition&#8221;, Stanford University \u2014\u00a0<a class=\"reference external\" href=\"https:\/\/www.youtube.com\/playlist?list=PLC1qU-LWwrF64f4QKQT-Vg5Wr4qEE1Zxk\">https:\/\/www.youtube.com\/playlist?list=PLC1qU-LWwrF64f4QKQT-Vg5Wr4qEE1Zxk<\/a><\/li>\n<li>&#8220;Dive into Deep Learning&#8221; \u2014\u00a0<a class=\"reference external\" href=\"https:\/\/d2l.ai\/\">https:\/\/d2l.ai<\/a>\u00a0ja\u00a0<a class=\"reference external\" href=\"https:\/\/d2l.ai\/d2l-en.pdf\">https:\/\/d2l.ai\/d2l-en.pdf<\/a><\/li>\n<\/ul>\n","protected":false},"author":36,"menu_order":6,"template":"","meta":{"pb_show_title":"on","pb_short_title":"","pb_subtitle":"","pb_authors":[],"pb_section_license":""},"chapter-type":[],"contributor":[],"license":[],"class_list":["post-976","chapter","type-chapter","status-publish","hentry"],"part":784,"_links":{"self":[{"href":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-json\/pressbooks\/v2\/chapters\/976","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-json\/pressbooks\/v2\/chapters"}],"about":[{"href":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-json\/wp\/v2\/types\/chapter"}],"author":[{"embeddable":true,"href":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-json\/wp\/v2\/users\/36"}],"version-history":[{"count":11,"href":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-json\/pressbooks\/v2\/chapters\/976\/revisions"}],"predecessor-version":[{"id":1037,"href":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-json\/pressbooks\/v2\/chapters\/976\/revisions\/1037"}],"part":[{"href":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-json\/pressbooks\/v2\/parts\/784"}],"metadata":[{"href":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-json\/pressbooks\/v2\/chapters\/976\/metadata\/"}],"wp:attachment":[{"href":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-json\/wp\/v2\/media?parent=976"}],"wp:term":[{"taxonomy":"chapter-type","embeddable":true,"href":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-json\/pressbooks\/v2\/chapter-type?post=976"},{"taxonomy":"contributor","embeddable":true,"href":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-json\/wp\/v2\/contributor?post=976"},{"taxonomy":"license","embeddable":true,"href":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-json\/wp\/v2\/license?post=976"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}