{"id":979,"date":"2023-02-01T09:27:59","date_gmt":"2023-02-01T09:27:59","guid":{"rendered":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/?post_type=chapter&#038;p=979"},"modified":"2023-02-02T10:07:35","modified_gmt":"2023-02-02T10:07:35","slug":"narvivorgud-ja-pytorch","status":"publish","type":"chapter","link":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/chapter\/narvivorgud-ja-pytorch\/","title":{"raw":"N\u00e4rviv\u00f5rgud ja PyTorch","rendered":"N\u00e4rviv\u00f5rgud ja PyTorch"},"content":{"raw":"<h2 id=\"narvivorkude-alused\">N\u00e4rviv\u00f5rkude alused<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"https:\/\/pydoc.pages.taltech.ee\/image_recognition\/v2\/intro.html#narvivorkude-alused\">\uf0c1<\/a><\/h2>\r\nN\u00e4rviv\u00f5rgud on tehisintellekti valdkonnas \u00fcks olulisemaid teemasid. Nad on v\u00f5imelised \u00f5ppima automaatselt ja avastama andmetes mustreid, mida inimestel k\u00e4sitsi m\u00f5ista ja programmeerida oleks olnud v\u00f5imatu.\r\n\r\nN\u00e4rviv\u00f5rgud koosnevad erinevatest kihtidest. K\u00f5ige esimene kiht on sisendkiht, kuhu antakse sisendandmed. N\u00e4iteks kui sisendiks on mustvalge pilt, siis iga\u00fcks sisendkihi neuronitest vastab \u00fche piksli v\u00e4\u00e4rtusele (madal arv on must, k\u00f5rge arv on valge, vahepeal on hallid). Juhul kui pilt on v\u00e4rviline, siis sisendkihi neuronite arv on kolm korda suurem, kuna iga\u00fcks sisendkihi neuronitest vastab \u00fchele RGB v\u00e4rvikanalile (punane, roheline ja sinine). N\u00e4iteks v\u00e4rviline pilt suurusega 32x32 pikslit vajaks 32 * 32 * 3 = 3072 pikslit. Sisendandmeteks v\u00f5ivad muidugi olla ka muud andmed, nagu tekst, heli, video, numbrid, jne.\r\n\r\nSisendile j\u00e4rgneb mingi arv peidetud kihte, mis samamoodi koosnevad neuronitest. Iga neuron on \u00fcks lineaarne funktsioon, mis t\u00fc\u00fcpiliselt v\u00f5tab sisendiks k\u00f5ik eelneva kihi neuronid ja annab v\u00e4ljundiks mingi v\u00e4\u00e4rtuse, mida j\u00e4llegi edastatakse j\u00e4rgmisele kihile. Iga neuroni funktsioon n\u00e4eb v\u00e4lja selline:\r\n\r\n<math display=\"block\" xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><mrow><mi>z<\/mi><mo>=<\/mo><mrow><mrow><msub><mi>w<\/mi><mn>1<\/mn><\/msub><mo>\u2062<\/mo><msub><mi>x<\/mi><mn>1<\/mn><\/msub><\/mrow><mo>+<\/mo><mrow><mrow><msub><mi>w<\/mi><mn>2<\/mn><\/msub><mo>\u2062<\/mo><msub><mi>x<\/mi><mn>2<\/mn><\/msub><\/mrow><mo>+<\/mo><\/mrow><\/mrow><\/mrow><mo>.<\/mo><mo>.<\/mo><mo>.<\/mo><mrow><mrow><mo>+<\/mo><mrow><msub><mi>w<\/mi><mi>n<\/mi><\/msub><mo>\u2062<\/mo><msub><mi>x<\/mi><mi>n<\/mi><\/msub><\/mrow><\/mrow><mo>+<\/mo><mi>b<\/mi><\/mrow><\/math>\r\n\r\n[latex]z[\/latex]\u00a0on neuroni v\u00e4ljundv\u00e4\u00e4rtus.\u00a0<span class=\"math notranslate nohighlight\">[latex]x[\/latex] <\/span>on sisendandmete v\u00e4\u00e4rtused. [latex]w[\/latex] on sisendandmetele vastavad kaalud, mis m\u00e4\u00e4ravad, kui palju iga sisendneuroni v\u00e4\u00e4rtus m\u00f5jutab neuroni v\u00e4ljundv\u00e4\u00e4rtust. <span class=\"math notranslate nohighlight\">[latex]w[\/latex] <\/span>v\u00e4\u00e4rtused on tavaliselt algselt suvaliselt valitud ja millele leitakse algoritmiliselt optimaalsed v\u00e4\u00e4rtused alles n\u00e4rviv\u00f5rgu treenimise k\u00e4igus. L\u00f5pus on vabaliige <span class=\"math notranslate nohighlight\">[latex]b[\/latex]<\/span>\u00a0(<em>bias<\/em>), mis alati nihutab neuroni v\u00e4\u00e4rtust mingi suuruse v\u00f5rra. K\u00f5iki n\u00e4rviv\u00f5rgu kaale koos kutsutakse ka n\u00e4rviv\u00f5rgu parameetriteks.\r\n\r\nTavaliselt rakendatakse neuronitele aktivatsioonifunktsiooni, mis transformeerib neuronite v\u00e4ljundv\u00e4\u00e4rtuseid mingiks uueks v\u00e4\u00e4rtuseks enne nende edasi saatmist.\r\n\r\n<math display=\"block\" xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><mi>a<\/mi><mo>=<\/mo><mrow><mi>\u03d5<\/mi><mo>\u2061<\/mo><mrow><mo stretchy=\"false\">(<\/mo><mi>z<\/mi><mo stretchy=\"false\">)<\/mo><\/mrow><\/mrow><mo>=<\/mo><mrow><mi>\u03d5<\/mi><mo>\u2061<\/mo><mrow><mo stretchy=\"false\">(<\/mo><mrow><mrow><msub><mi>w<\/mi><mn>1<\/mn><\/msub><mo>\u2062<\/mo><mrow><msub><mi>x<\/mi><mn>1<\/mn><\/msub><mo>+<\/mo><\/mrow><\/mrow><mo>.<\/mo><mo>.<\/mo><mo>.<\/mo><mrow><mrow><mo>+<\/mo><mrow><msub><mi>w<\/mi><mi>n<\/mi><\/msub><mo>\u2062<\/mo><msub><mi>x<\/mi><mi>n<\/mi><\/msub><\/mrow><\/mrow><mo>+<\/mo><mi>b<\/mi><\/mrow><\/mrow><mo stretchy=\"false\">)<\/mo><\/mrow><\/mrow><\/math>\r\n\r\nS\u00fcmbol\u00a0<span class=\"math notranslate nohighlight\">[latex]\u03d5[\/latex]<\/span>\u00a0t\u00e4histab aktivatsioonifunktsiooni. V\u00e4\u00e4rtus\u00a0<span class=\"math notranslate nohighlight\">[latex]a[\/latex]<\/span> on neuroni l\u00f5plik v\u00e4ljundv\u00e4\u00e4rtus, mis antakse edasi j\u00e4rgmise kihi neuronitele (v\u00f5i viimase kihi puhul antakse meile tagasi). Aktivatsioonifunktsioonid pole kohustuslikud, mispuhul [latex]a = z[\/latex].\r\n\r\n&nbsp;\r\n\r\nK\u00fcll aga on nad olulised, sest ilma nendeta pole v\u00f5imalik lahendada mittelineaarseid probleeme.\r\n\r\n[caption id=\"attachment_995\" align=\"alignnone\" width=\"602\"]<img class=\"wp-image-995 size-full\" src=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/nonlinear-classification.png\" alt=\"\" width=\"602\" height=\"252\" \/> Joonisel A on andmed, mida on v\u00f5imalik eraldada kahte klassi lineaarselt (joonistades sirge). Joonisel B on keerulisem klassifikatsiooni probleem, mille andmete eraldamiseks on vaja mittelineaarset funktsiooni. Autor: Sebastian Raschka. Creative Commons Attribution-Share Alike 4.0 International. Pildi allikas: https:\/\/commons.wikimedia.org\/wiki\/File:Main-qimg-48d5bd214e53d440fa32fc9e5300c894.png[\/caption]\r\n\r\nSigmoidifunktsioon, mille v\u00e4ljundv\u00e4\u00e4rtused on 0-1 vahel, n\u00e4eb v\u00e4lja selline:\r\n\r\n[caption id=\"attachment_996\" align=\"alignnone\" width=\"1024\"]<img class=\"wp-image-996 size-large\" src=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/sigmoid-1024x471.png\" alt=\"\" width=\"1024\" height=\"471\" \/> Sigmoidifunktsioon. Pildi allikas: https:\/\/commons.wikimedia.org\/wiki\/File:Sigmoid-function-2.svg[\/caption]\r\n\r\n<math display=\"block\" xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><mi>a<\/mi><mo>=<\/mo><mrow><mi>\u03c3<\/mi><mo>\u2061<\/mo><mrow><mo stretchy=\"false\">(<\/mo><mi>z<\/mi><mo stretchy=\"false\">)<\/mo><\/mrow><\/mrow><mo>=<\/mo><mfrac><mn>1<\/mn><mrow><mn>1<\/mn><mo>+<\/mo><msup><mi>e<\/mi><mrow><mo>\u2212<\/mo><mi>z<\/mi><\/mrow><\/msup><\/mrow><\/mfrac><\/math>\r\n\r\nMuutuja\u00a0<span class=\"math notranslate nohighlight\">[latex]a[\/latex]<\/span>\u00a0on l\u00f5plik neuroni v\u00e4ljundv\u00e4\u00e4rtus, mis antakse edasi j\u00e4rgneva kihi neuronitesse [latex]x[\/latex]<sub>[latex]i[\/latex] <\/sub>kujul, kus\u00a0<span class=\"math notranslate nohighlight\">[latex]i[\/latex]<\/span>\u00a0on neuroni indeks kihis. Igal aktivatsioonifunktsioonil on oma eelised ja puudused. N\u00e4iteks ReLU aktivatsioonifunktsioon n\u00e4eb v\u00e4lja selline:\r\n\r\n<math display=\"block\" xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><mrow><mi><\/mi><\/mrow><\/math> <math display=\"block\" xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><mrow><mi>a<\/mi><mo>=<\/mo><mrow><\/mrow><\/mrow><mtext>ReLU<\/mtext><mrow><mrow><mo stretchy=\"false\">(<\/mo><mi>z<\/mi><mo stretchy=\"false\">)<\/mo><\/mrow><mo>=<\/mo><mrow><mo movablelimits=\"true\">max<\/mo><mo>\u2061<\/mo><mrow><mo stretchy=\"false\">(<\/mo><mrow><mi>z<\/mi><mo>,<\/mo><mn>0<\/mn><\/mrow><mo stretchy=\"false\">)<\/mo><\/mrow><\/mrow><\/mrow><\/math>\r\n\r\nReLU funktsioon on kiirem kui sigmoidifunktsioon ta arvutuslihtsuse t\u00f5ttu, kuid tal on ka m\u00f5ningaid puudusi, nagu see, et ReLU funktsioon ei tagasta v\u00e4\u00e4rtusi (0, 1) piirkonnas, vaid [0,\u221e), mis v\u00f5ib olla m\u00f5nedes olukordades oluline.\r\n\r\n[caption id=\"attachment_997\" align=\"alignnone\" width=\"1024\"]<img class=\"wp-image-997 size-large\" src=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-formula-1024x542.png\" alt=\"\" width=\"1024\" height=\"542\" \/> \u00dche neuroni arvutused visualiseerituna. Autor: BrunelloN. Pildi allikas: https:\/\/commons.wikimedia.org\/wiki\/File:Example_of_a_neural_network%27s_neural_unit.png[\/caption]\r\n\r\nN\u00e4rviv\u00f5rgu viimast kihti kutsutakse v\u00e4ljundkihiks, mis sisaldab l\u00f5ppandmetega neuroneid. Klassifitseerimis\u00fclesande v\u00e4ljundiks on klass, millesse antud sisend kuulub. N\u00e4iteks n\u00e4rviv\u00f5rk, mis tuvastab loomi, omaks \u00fchte v\u00e4ljundneuronit iga looma kohta, mida ta on v\u00f5imeline tuvastama. K\u00f5ige eredamat v\u00e4ljundneuronit (ehk neuronit, mille funktsioon tagastab suurima v\u00e4ljundi) loetaks n\u00e4rviv\u00f5rgu vastuseks.\r\n\r\n[caption id=\"attachment_998\" align=\"alignnone\" width=\"791\"]<img class=\"wp-image-998 size-full\" src=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net.jpeg\" alt=\"\" width=\"791\" height=\"388\" \/> N\u00e4rviv\u00f5rk, kus vasakul on sisendkiht (input layer), sellele j\u00e4rgnevad kaks peidetud kihti (hidden layer) ja k\u00f5ige paremal on v\u00e4ljundkiht (output layer). Info sisestatakse sisendkihti ja liigub seej\u00e4rel vasakult paremale. L\u00f5puks on v\u00e4ljundiks \u00fcks number, nt kas pildil on kass (1.0) v\u00f5i mitte (0.0). Pildi allikas: https:\/\/cs231n.github.io\/convolutional-networks\/[\/caption]\r\n\r\n<div class=\"textbox textbox--examples\"><header class=\"textbox__header\">\r\n<p class=\"textbox__title\">Lisainfo: Klassid ja v\u00e4ljundneuronid<\/p>\r\n\r\n<\/header>\r\n<div class=\"textbox__content\">\r\n\r\nKuigi teoorias oleks v\u00f5imalik k\u00f5ikide klassifitseerimis\u00fclesannete lahendamiseks kasutada v\u00e4ljundkihis vaid \u00fchte neuronit, mille oodatud v\u00e4\u00e4rtus oleneks klassinumbrist (nt 1 on koer, 2 on kass, 3 on j\u00e4nes, jne), praktikas on selliseid n\u00e4rviv\u00f5rke ekstreemselt keeruline treenida. Seep\u00e4rast eelistatakse disaini, kus v\u00e4ljundkihis on olemas \u00fcks neuron igale klassile. N\u00e4iteks kui klassifitseerimis\u00fclesandes on 10 erinevat klassi, siis on v\u00e4ljundkihis 10 neuronit.\r\n\r\n<\/div>\r\n<\/div>\r\n<h2>M\u00f5tle ja nuputa!<\/h2>\r\n[h5p id=\"49\"]\r\n<h2 id=\"pytorchi-seadistamine\">PyTorchi seadistamine<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"https:\/\/pydoc.pages.taltech.ee\/image_recognition\/v2\/intro.html#pytorchi-seadistamine\">\uf0c1<\/a><\/h2>\r\nKasutame n\u00e4rviv\u00f5rkude koostamiseks Pythoni raamistikku\u00a0<a class=\"reference external\" href=\"https:\/\/pytorch.org\/\">PyTorch<\/a>, mida on v\u00f5imalik alla laadida\u00a0<a class=\"reference external\" href=\"https:\/\/pytorch.org\/get-started\/locally\/\">siit<\/a>.\r\n\r\n[caption id=\"attachment_999\" align=\"alignnone\" width=\"1003\"]<img class=\"wp-image-999 size-full\" src=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/pytorch-installation.png\" alt=\"\" width=\"1003\" height=\"384\" \/> PyTorchi installeerimine:<br \/>1. Vali oma operatsioonis\u00fcsteem.<br \/>2. Vali paketihalduriks Pip.<br \/>3. Vali vaikimisi \"CPU\". V\u00f5ib valida ka \"CUDA\" juhul kui arvutis on olemas NVIDIA graafikakaarti ja selle poolt pakutav CUDA tarkvara, mida saab alla laadida siit. CUDA kiirendab oluliselt n\u00e4rviv\u00f5rkude treenimist, aga selle seadistamine v\u00f5ib olla ajakulukas. Olemasolevat CUDA versiooni saab kontrollida k\u00e4suga nvcc --version.<br \/>4. Genereeritakse l\u00f5plik k\u00e4sk, mida tuleb k\u00e4ivitada oma arvuti terminalis.<br \/>Pilt autori koostatud.[\/caption]\r\n\r\nK\u00f5iki n\u00e4iteid v\u00f5ib proovida k\u00e4ima panna oma arvutis, mis eeldab, et vajalikud Pythoni pakid on installeeritud. PyTorch ise v\u00f5tab v\u00e4hemalt 3 GB kettaruumi. Osade n\u00e4idete jooksutamine vajab arvutis head graafikakaarti v\u00f5i siis palju aega. Lokaalse installeerimise asemel on v\u00f5imalus n\u00e4iteid jooksutada n\u00e4iteks\u00a0<a class=\"reference external\" href=\"https:\/\/colab.research.google.com\/\">Google Colab keskkonnas<\/a>.\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\nMuutujat\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">device<\/span><\/code>\u00a0on hea luua siis, kui on v\u00f5imalus, et koodi k\u00e4ivitavates arvutites on olemas CUDA. Sel juhul peaks k\u00f5ikide tensorite (nendest rohkem j\u00e4rgmises peat\u00fckis) ning kihtide\/moodulite loomisel m\u00e4\u00e4rama, millise seadme (CPU v\u00f5i GPU) peal me neid luua soovime.\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">import torch\r\n\r\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\r\nprint(device)  # 'cpu' v\u00f5i 'cuda'\r\n\r\n# loome tensoreid GPU-l v\u00f5i CPU-l\r\na = torch.tensor([1, 2, 3], device=device)\r\nb = torch.rand(2, 2, 2, device=device)<\/pre>\r\nJuhul kui kood k\u00e4ivitub kindlalt ainult CPU-del, v\u00f5ib k\u00f5ik\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">device<\/span><\/code>-iga seotud koodi \u00e4ra j\u00e4tta. Edaspidi lihtsuse huvides seda ka teeme.\r\n\r\n<\/div>\r\n<\/div>\r\n<h2 id=\"tensorid-ja-kihid\">Tensorid ja kihid<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"https:\/\/pydoc.pages.taltech.ee\/image_recognition\/v2\/intro.html#tensorid-ja-kihid\">\uf0c1<\/a><\/h2>\r\nPyTorchi kasutamiseks tuleb importida\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">torch<\/span><\/code>\u00a0moodul (k\u00f5ikides j\u00e4rgnevates n\u00e4idetes on see eeldatud). Tensor on PyTorchi peamine andmestruktuur, mis saab olla skalaar (arv), vektor (massiiv), maatriks (2D massiiv) v\u00f5i muu k\u00f5rgema-dimensiooniline struktuur. Praktikas sarnaneb ta mitmem\u00f5\u00f5tmeliste NumPy massiividega. Tensorite loomiseks on mitmeid v\u00f5imalusi, millest paljud sarnanevad NumPy s\u00fcntaksile. N\u00e4iteks:\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">import torch  # edaspidistes n\u00e4idetes alati eeldame, et torch on imporditud!\r\n\r\na = torch.tensor([[1, 2], [3, 4]])  # loome 2D tensori l\u00e4bi listi\r\nprint(a)\r\n# tensor([[1, 2],\r\n#         [3, 4]])\r\n\r\nb = torch.rand(3, 4)  # nt siia v\u00f5ib lisada ka device=device\r\nprint(b)\r\n# tensor([[0.5436, 0.6467, 0.9512, 0.6870],\r\n#         [0.4695, 0.0687, 0.1261, 0.5235],\r\n#         [0.7589, 0.3855, 0.2477, 0.0452]])\r\n\r\nprint(b.shape)         # torch.Size([3, 4])\r\nprint(b[0])            # tensor([0.5436, 0.6467, 0.9512, 0.6870])\r\nprint(b[0, 0])         # tensor(0.5436)\r\nprint(b[0, 0].item())  # 0.5436\r\nprint(b[:, -1])        # tensor([0.6870, 0.5235, 0.0452])<\/pre>\r\nTensoritel on olemas ka palju dimensioonidega seotud operatsioone (mis ei muuda tensoreid ise, vaid tagastavad uue tensori):\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">c = torch.zeros(4)\r\nprint(c)  # tensor([0.,  0.,  0.,  0.])\r\n\r\n# lisame dimensiooni positsiooni 0\r\nprint(c.unsqueeze(0))  # tensor([[0.,  0.,  0.,  0.]])\r\n\r\n# lisame dimensiooni positsiooni 1\r\nprint(c.unsqueeze(1))  # tensor([[0.], [0.], [0.], [0.]])\r\n\r\nd = torch.tensor([[[[1], [2]], [[3], [4]]]])\r\n\r\n# kustutame \u00fcleliigsed dimensioonid\r\nprint(d.squeeze())  # tensor([[1, 2], [3, 4]])\r\n\r\n# teisendame tensori 1D massiiviks\r\nprint(d.flatten())  # tensor([1, 2, 3, 4])<\/pre>\r\nIga n\u00e4rviv\u00f5rgu peidetud kihi sisendiks ja v\u00e4ljundiks on tensor. Esimese peidetud kihi sisendiks saaks n\u00e4iteks olla pilt, mille pikslid on esitatud vektorina. V\u00e4ljundiks v\u00f5iks olla n\u00e4iteks 10-kohaline vektor, mille iga element vastab mingile pildikategooriale.\r\n\r\nKoostame kihid n\u00e4rviv\u00f5rgule, mis ennustab koosinusfunktsiooni [latex]f(x) = cos(x)[\/latex]\u00a0v\u00e4\u00e4rtusi (mist\u00f5ttu vajab ta vaid \u00fchte sisendneuronit\u00a0<span class=\"math notranslate nohighlight\">[latex]x[\/latex]<\/span> ja \u00fchte v\u00e4ljundneuronit [latex]f(x)[\/latex]ennustatud v\u00e4\u00e4rtusele):\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">from torch import nn\r\n\r\nhidden_layer = nn.Linear(1, 7)  # 1 sisendneuron, 7 v\u00e4ljundneuronit\r\nactivation_fn = nn.ReLU()       # ReLU aktivatsioonifunktsioon\r\noutput_layer = nn.Linear(7, 1)  # 7 sisendneuronit peidetud kihist, 1 v\u00e4ljundneuron\r\n\r\nx = torch.tensor([50.0])  # kihi sisendiks oodatakse alati listi\r\ny = torch.cos(x)          # arvutame ette \u00f5ige vastuse, mida AI peab ennustama\r\nprint(x, y)               # tensor([50.]) tensor([0.9650])\r\n\r\nz = hidden_layer(x)   # z on 10-elemendine vektor\r\na = activation_fn(z)  # a on transformeeritud 10-elemendine vektor\r\n\r\nprint(z, '\\n', a)\r\n# tensor([29.3417, -37.906, -39.440, 21.4118, 21.6470, 17.2586, -2.5133])\r\n# tensor([29.3417,  0.0000,  0.0000, 21.4118, 21.6470, 17.2586,  0.0000])<\/pre>\r\nNagu n\u00e4eme, ReLU aktivatsioonifunktsioon [latex]a = max(z, 0)[\/latex] muudab k\u00f5ik negatiivsed v\u00e4\u00e4rtused nulliks. Muutuja\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">a<\/span><\/code> on n\u00fc\u00fcd meie j\u00e4rgmise kihi sisend, mis annab v\u00e4ljundiks \u00fche neuroni. See v\u00e4ljundneuron peaks peale treenimist oskama ennustada, et [latex]cos(50) = 0.9650[\/latex].\r\n\r\nV\u00e4ljundneuronile me aktivatsioonifunktsiooni ei rakenda, sest me tegeleme regressiooniga (mingi v\u00e4\u00e4rtuste, mitte klassi, ennustamine).\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">y_hat = output_layer(a)  # n\u00e4rviv\u00f5rgu ennustus\r\nprint(y_hat)             # tensor([13.5124])\r\nprint(y)                 # tensor([0.9650])<\/pre>\r\nMuutuja\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">y_hat<\/span><\/code> on meie ennustus, mille matemaatiline notatsioon on [latex]\\hat{y}[\/latex]. Kuna n\u00e4rviv\u00f5rku pole veel treenitud ja seet\u00f5ttu ta ei tea mida teha, annab ta meile t\u00e4ielikult suvalise vastuse. \u00d5nneks on PyTorchis treenimise matemaatiline pool suuresti automatiseeritud: me peame vaid \u00fctlema n\u00e4rviv\u00f5rgule, kui vale ta ennustus on. Seej\u00e4rel oskab n\u00e4rviv\u00f5rk ise ennast korrigeerida, et j\u00e4rgmine kord ta ennustus oleks t\u00e4psem.\r\n\r\nV\u00f5ime n\u00e4iteks vaadata peidetud kihi parameetreid\u00a0<span class=\"math notranslate nohighlight\">[latex]w[\/latex]<\/span>\u00a0niiviisi:\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"prolog\">print(hidden_layer.weight)\r\n# tensor([[0.5847], [-0.7390], [-0.7715], [0.4361], [0.4337], [0.3365], [-0.0392]])<\/pre>\r\n<h2 id=\"kahjufunktsioonid\">Kahjufunktsioonid<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"https:\/\/pydoc.pages.taltech.ee\/image_recognition\/v2\/intro.html#kahjufunktsioonid\">\uf0c1<\/a><\/h2>\r\nEt treenimist alustada, tuleb meil v\u00f5tta n\u00e4idisandmete komplekt\u00a0<span class=\"math notranslate nohighlight\">[latex]x[\/latex]<\/span>\u00a0ja\u00a0<span class=\"math notranslate nohighlight\">[latex]y[\/latex]<\/span> ning alustuseks arvutada sellele ennustus [latex]\\hat{y}[\/latex], mida me eelmises peat\u00fckis juba ka tegime.\r\n\r\nJ\u00e4rgmine samm on arvutada l\u00e4bi [latex]y[\/latex] ja [latex]\\hat{y}[\/latex] v\u00e4\u00e4rtuste v\u00e4lja kahju (<em>loss<\/em>). Selleks kasutame kahjufunktsiooni (<em>loss function<\/em>), mis leiab, kui suur on meie ennustuse ja tegeliku v\u00e4\u00e4rtuse vahe. Sellised kahjufunktsioone on palju, aga meile piisab populaarsest valikust\u00a0<a class=\"reference external\" href=\"https:\/\/en.wikipedia.org\/wiki\/Mean_squared_error#Loss_function\">MSE<\/a>\u00a0(<em>mean squared error<\/em>):\r\n\r\n<math display=\"block\" xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><mtext>MSE<\/mtext><mrow><mrow><\/mrow><mo>=<\/mo><mfrac><mrow><mrow><msup><mrow><mo stretchy=\"false\">(<\/mo><mrow><msub><mi>y<\/mi><mn>1<\/mn><\/msub><mo>\u2212<\/mo><msub><mrow><mover><mi>y<\/mi><mo stretchy=\"false\">^<\/mo><\/mover><\/mrow><mn>1<\/mn><\/msub><\/mrow><mo stretchy=\"false\">)<\/mo><\/mrow><mn>2<\/mn><\/msup><mo>+<\/mo><\/mrow><mo>.<\/mo><mo>.<\/mo><mo>.<\/mo><mrow><mo>+<\/mo><msup><mrow><mo stretchy=\"false\">(<\/mo><mrow><msub><mi>y<\/mi><mi>n<\/mi><\/msub><mo>\u2212<\/mo><msub><mrow><mover><mi>y<\/mi><mo stretchy=\"false\">^<\/mo><\/mover><\/mrow><mi>n<\/mi><\/msub><\/mrow><mo stretchy=\"false\">)<\/mo><\/mrow><mn>2<\/mn><\/msup><\/mrow><\/mrow><mi>n<\/mi><\/mfrac><\/mrow><\/math>\r\n\r\nSiin j\u00e4tkame eelmise peat\u00fcki n\u00e4ite l\u00f5pust:\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">loss_fn = nn.MSELoss()  # loome 'Mean Squared Error' funktsiooni\r\nloss = loss_fn(y_hat, y)  # arvutame kahju: mida v\u00e4iksem on tulemus, seda \u00f5igem on ennustus\r\nprint(loss)  # tensor(157.4369)<\/pre>\r\nTulemuse peal\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">loss.backward()<\/span><\/code> meetodi v\u00e4lja kutsumine leiab vajalikud muutused (ehk gradiendid) kihtide [latex]w[\/latex] parameetritele, et tulevikus antud\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">x<\/span><\/code>-ile sarnase v\u00e4\u00e4rtuse korral teha parem ennustus. Parameetrite gradiendid salvestatakse tensorite\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">.grad<\/span><\/code>\u00a0atribuuti.\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\n\r\n# gradiendid n\u00e4itavad, kui palju iga kaaluparameeter peaks muutuma\r\nprint(hidden_layer.weight.grad)  # tensor([[142.13], [0.], [0.], [241.06], [294.88], [69.76], [0.]])<\/pre>\r\n<div class=\"textbox textbox--examples\"><header class=\"textbox__header\">\r\n<p class=\"textbox__title\">Lisainfo: Gradiendid<\/p>\r\n\r\n<\/header>\r\n<div class=\"textbox__content\">\r\n\r\nGradientide leidmiseks kasutatakse kalkulusel p\u00f5hinevat\u00a0<a class=\"reference external\" href=\"https:\/\/et.wikipedia.org\/wiki\/Tehisn%C3%A4rviv%C3%B5rk#Tagasilevi\">tagasilevi algoritmi<\/a>\u00a0(<em>backpropagation<\/em>). Soovi korral saab vaadata p\u00f5hjalikumat YouTube videot tagasilevist\u00a0<a class=\"reference external\" href=\"https:\/\/www.youtube.com\/watch?v=Ilg3gGewQ5U\">siit<\/a>.\r\n\r\nPyTorchis on implementeeritud\u00a0<a class=\"reference external\" href=\"https:\/\/en.wikipedia.org\/wiki\/Automatic_differentiation\">automaatne diferentseerimine<\/a>\u00a0(<em>automatic differentiation<\/em>). See t\u00e4hendab, et gradientide leidmiseks ei ole vaja ise kirjutada gradientide v\u00e4\u00e4rtusi arvutavat koodi, vaid PyTorch teeb seda automaatselt. Kui on soov kardinate taha piiluda, siis v\u00f5ib vaadelda tensorite\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">.grad_fn<\/span><\/code>\u00a0atribuuti. Rohkem saab lugeda\u00a0<a class=\"reference external\" href=\"https:\/\/pytorch.org\/tutorials\/beginner\/blitz\/autograd_tutorial.html\">siit<\/a>.\r\n\r\n<\/div>\r\n<\/div>\r\n<h2 id=\"optimeerijad\">Optimeerijad<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"https:\/\/pydoc.pages.taltech.ee\/image_recognition\/v2\/intro.html#optimeerijad\">\uf0c1<\/a><\/h2>\r\nN\u00fc\u00fcd kui oleme leidnud gradiendid, peame koostama optimeerija, mis neid muudatusi ka tegelikult parameetritele rakendab. Selles n\u00e4ites kasutame lihtsat SGD (<em>stochastic gradient descent<\/em>) optimeerijat. Optimeerijale peame andma ka \u00f5pisammu (<em>learning rate<\/em>), mis m\u00e4\u00e4rab, kui kiiresti meie kihtide parameetrid muutuvad relatiivselt gradientide suurustele. Optimaalse \u00f5pisammu h\u00fcperparameetri\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">lr<\/span><\/code>\u00a0v\u00e4\u00e4rtuse leiame l\u00e4bi\u00a0<a class=\"reference external\" href=\"https:\/\/sciencedryad.wordpress.com\/2014\/01\/25\/grad-student-descent\/\">katsetamise<\/a>, aga tavaliselt alustatakse v\u00e4\u00e4rtustega\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">0.1<\/span><\/code>,\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">0.01<\/span><\/code>\u00a0v\u00f5i\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">0.001<\/span><\/code>. Liiga madal arv v\u00f5ib panna n\u00e4rviv\u00f5rgu \u00f5ppima m\u00f5ttetult aeglaselt, liiga kiire paneb aga parameetrid muutuma liiga kiirelt ja v\u00f5ib juhtuda, et n\u00e4rviv\u00f5rk ei \u00f5pigi \u00fcldse midagi.\r\n\r\nJ\u00e4tkame n\u00e4idet:\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\"># loome optimeerija, mis teeb muudatusi hidden_layer-i parameetrites vastavalt kahjufunktsiooni tulemustele\r\noptimizer = torch.optim.SGD(hidden_layer.parameters(), lr=0.1)  # kasutame \u00f5pisammu 0.1\r\n\r\n# vaatame uuesti algseid kaale ja gradiente\r\nprint(hidden_layer.weight)       # tensor([[0.584], [-0.739], [-0.771], [0.436], [0.433], [0.336], [-0.03]])\r\nprint(hidden_layer.weight.grad)  # tensor([[142.13], [0.], [0.], [241.06], [294.88], [69.76], [0.]])\r\n\r\noptimizer.step()  # teeme muudatused\r\n\r\nprint(hidden_layer.weight)  # vaatame, kuidas kaalud muutusid\r\n# tensor([[-13.6292], [-0.7390], [-0.7715], [-23.6709], [-29.0547], [-6.6397], [-0.0392]])\r\n\r\n# juhul kui soovime uuesti muudatusi teha, peame alguses eelmised gradiendid nullima\r\n# kui me seda ei teeks, sis j\u00e4rgmise iteratsiooni gradiendid oleksid liidetud juurde eelnevatele\r\noptimizer.zero_grad()  # kustutame hidden_layer-i parameetrite gradiendid\r\nprint(hidden_layer.weight.grad)  # tensor([[0.], [0.], [0.], [0.], [0.], [0.], [0.]])<\/pre>\r\nL\u00f5puks v\u00f5ime lasta n\u00e4rviv\u00f5rgul teha ennustuse uuesti sama\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">x<\/span><\/code>\u00a0v\u00e4\u00e4rtusega:\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">z = hidden_layer(x)\r\na = activation_fn(z)\r\ny_hat = output_layer(a)\r\nloss = loss_fn(y_hat, y)\r\n\r\nprint(y_hat, y)  # tensor([0.0277]) tensor([0.9650])\r\nprint(loss)  # tensor(0.8784)<\/pre>\r\nN\u00e4eme, et meie\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">loss<\/span><\/code> on muutunud v\u00e4iksemaks, ehk meie ennustus on seekord t\u00e4psem. Eelmine kord ennustas n\u00e4rviv\u00f5rk [latex]\\hat{y} = 13.5124[\/latex], see kord aga [latex]\\hat{y}= 0.0277[\/latex]. Kuna \u00f5ige vastus on [latex]y = 0.965[\/latex], j\u00f5udsime \u00f5igele vastusele tunduvalt l\u00e4hemale. Kuna n\u00e4rviv\u00f5rk oskab funktsioone ainult j\u00e4ljendada, siis t\u00e4pselt \u00f5ige vastuseni me arvatavasti kunagi ei j\u00f5uagi. Lisaks sellele, praegune n\u00e4rviv\u00f5rk ikka veel annaks suvalisi vastusi paljude teiste <code class=\"docutils literal notranslate\"><span class=\"pre\">x<\/span><\/code>\u00a0v\u00e4\u00e4rtuste puhul, sest me pole talle veel nende kohta n\u00e4iteid andnud.\r\n<div class=\"textbox textbox--examples\"><header class=\"textbox__header\">\r\n<p class=\"admonition-title\">Lisainfo: Gradiendi laskumine (<em>gradient descent<\/em>)<\/p>\r\n\r\n<\/header>\r\n<div class=\"textbox__content\">\r\n\r\nGradiendi laskumise algoritmi p\u00f5hiolemus on see, et ta liigub kahjufunktsiooni k\u00f5rgematest kohtadest madalamatesse, kuni j\u00f5uab mingi piirini, kus muutusi enam ei toimu. Kalkuluse terminites, otsitakse kahjufunktsiooni lokaalset miinimumpunkti. Laskumiskiirus oleneb\u00a0<em>learning rate (LR)<\/em> h\u00fcperparameetrist. Liiga k\u00f5rge laskumiskiirus tekitab olukorra, kus algoritm hakkab lendama optimaalsest madalaimast punktist m\u00f6\u00f6da.\r\n\r\n[caption id=\"attachment_1000\" align=\"alignnone\" width=\"300\"]<img class=\"size-medium wp-image-1000\" src=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/gradient-descent-300x243.gif\" alt=\"\" width=\"300\" height=\"243\" \/> Gradiendi laskumise (gradient descent, GD\/SGD) algoritmi visualisatsioon 3D ruumis, kus x- ja y-telg on kaaluparameetrid ning z-telg (k\u00f5rgus) on kahjufunktsiooni v\u00e4\u00e4rtus. Mida v\u00e4iksem on mingis x\/y punktis kahjufunktsiooni v\u00e4\u00e4rtus, seda t\u00e4psemad on seal n\u00e4rviv\u00f5rgu ennustused. Autor: Jacopo Bertolotti. Pildi allikas: https:\/\/commons.wikimedia.org\/wiki\/File:Gradient_descent.gif[\/caption]\r\n\r\nKahjufunktsiooni maastikud on tegelikkuses h\u00e4sti keerulised ja mitte\u00fchtlased, ning koosnevad tavaliselt tuhandetest v\u00f5i miljonitest dimensioonidest.\r\n\r\n<\/div>\r\n<\/div>\r\n&nbsp;","rendered":"<h2 id=\"narvivorkude-alused\">N\u00e4rviv\u00f5rkude alused<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"https:\/\/pydoc.pages.taltech.ee\/image_recognition\/v2\/intro.html#narvivorkude-alused\">\uf0c1<\/a><\/h2>\n<p>N\u00e4rviv\u00f5rgud on tehisintellekti valdkonnas \u00fcks olulisemaid teemasid. Nad on v\u00f5imelised \u00f5ppima automaatselt ja avastama andmetes mustreid, mida inimestel k\u00e4sitsi m\u00f5ista ja programmeerida oleks olnud v\u00f5imatu.<\/p>\n<p>N\u00e4rviv\u00f5rgud koosnevad erinevatest kihtidest. K\u00f5ige esimene kiht on sisendkiht, kuhu antakse sisendandmed. N\u00e4iteks kui sisendiks on mustvalge pilt, siis iga\u00fcks sisendkihi neuronitest vastab \u00fche piksli v\u00e4\u00e4rtusele (madal arv on must, k\u00f5rge arv on valge, vahepeal on hallid). Juhul kui pilt on v\u00e4rviline, siis sisendkihi neuronite arv on kolm korda suurem, kuna iga\u00fcks sisendkihi neuronitest vastab \u00fchele RGB v\u00e4rvikanalile (punane, roheline ja sinine). N\u00e4iteks v\u00e4rviline pilt suurusega 32&#215;32 pikslit vajaks 32 * 32 * 3 = 3072 pikslit. Sisendandmeteks v\u00f5ivad muidugi olla ka muud andmed, nagu tekst, heli, video, numbrid, jne.<\/p>\n<p>Sisendile j\u00e4rgneb mingi arv peidetud kihte, mis samamoodi koosnevad neuronitest. Iga neuron on \u00fcks lineaarne funktsioon, mis t\u00fc\u00fcpiliselt v\u00f5tab sisendiks k\u00f5ik eelneva kihi neuronid ja annab v\u00e4ljundiks mingi v\u00e4\u00e4rtuse, mida j\u00e4llegi edastatakse j\u00e4rgmisele kihile. Iga neuroni funktsioon n\u00e4eb v\u00e4lja selline:<\/p>\n<p><math display=\"block\" xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><mrow><mi>z<\/mi><mo>=<\/mo><mrow><mrow><msub><mi>w<\/mi><mn>1<\/mn><\/msub><mo>\u2062<\/mo><msub><mi>x<\/mi><mn>1<\/mn><\/msub><\/mrow><mo>+<\/mo><mrow><mrow><msub><mi>w<\/mi><mn>2<\/mn><\/msub><mo>\u2062<\/mo><msub><mi>x<\/mi><mn>2<\/mn><\/msub><\/mrow><mo>+<\/mo><\/mrow><\/mrow><\/mrow><mo>.<\/mo><mo>.<\/mo><mo>.<\/mo><mrow><mrow><mo>+<\/mo><mrow><msub><mi>w<\/mi><mi>n<\/mi><\/msub><mo>\u2062<\/mo><msub><mi>x<\/mi><mi>n<\/mi><\/msub><\/mrow><\/mrow><mo>+<\/mo><mi>b<\/mi><\/mrow><\/math><\/p>\n<p>[latex]z[\/latex]\u00a0on neuroni v\u00e4ljundv\u00e4\u00e4rtus.\u00a0<span class=\"math notranslate nohighlight\">[latex]x[\/latex] <\/span>on sisendandmete v\u00e4\u00e4rtused. [latex]w[\/latex] on sisendandmetele vastavad kaalud, mis m\u00e4\u00e4ravad, kui palju iga sisendneuroni v\u00e4\u00e4rtus m\u00f5jutab neuroni v\u00e4ljundv\u00e4\u00e4rtust. <span class=\"math notranslate nohighlight\">[latex]w[\/latex] <\/span>v\u00e4\u00e4rtused on tavaliselt algselt suvaliselt valitud ja millele leitakse algoritmiliselt optimaalsed v\u00e4\u00e4rtused alles n\u00e4rviv\u00f5rgu treenimise k\u00e4igus. L\u00f5pus on vabaliige <span class=\"math notranslate nohighlight\">[latex]b[\/latex]<\/span>\u00a0(<em>bias<\/em>), mis alati nihutab neuroni v\u00e4\u00e4rtust mingi suuruse v\u00f5rra. K\u00f5iki n\u00e4rviv\u00f5rgu kaale koos kutsutakse ka n\u00e4rviv\u00f5rgu parameetriteks.<\/p>\n<p>Tavaliselt rakendatakse neuronitele aktivatsioonifunktsiooni, mis transformeerib neuronite v\u00e4ljundv\u00e4\u00e4rtuseid mingiks uueks v\u00e4\u00e4rtuseks enne nende edasi saatmist.<\/p>\n<p><math display=\"block\" xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><mi>a<\/mi><mo>=<\/mo><mrow><mi>\u03d5<\/mi><mo>\u2061<\/mo><mrow><mo stretchy=\"false\">(<\/mo><mi>z<\/mi><mo stretchy=\"false\">)<\/mo><\/mrow><\/mrow><mo>=<\/mo><mrow><mi>\u03d5<\/mi><mo>\u2061<\/mo><mrow><mo stretchy=\"false\">(<\/mo><mrow><mrow><msub><mi>w<\/mi><mn>1<\/mn><\/msub><mo>\u2062<\/mo><mrow><msub><mi>x<\/mi><mn>1<\/mn><\/msub><mo>+<\/mo><\/mrow><\/mrow><mo>.<\/mo><mo>.<\/mo><mo>.<\/mo><mrow><mrow><mo>+<\/mo><mrow><msub><mi>w<\/mi><mi>n<\/mi><\/msub><mo>\u2062<\/mo><msub><mi>x<\/mi><mi>n<\/mi><\/msub><\/mrow><\/mrow><mo>+<\/mo><mi>b<\/mi><\/mrow><\/mrow><mo stretchy=\"false\">)<\/mo><\/mrow><\/mrow><\/math><\/p>\n<p>S\u00fcmbol\u00a0<span class=\"math notranslate nohighlight\">[latex]\u03d5[\/latex]<\/span>\u00a0t\u00e4histab aktivatsioonifunktsiooni. V\u00e4\u00e4rtus\u00a0<span class=\"math notranslate nohighlight\">[latex]a[\/latex]<\/span> on neuroni l\u00f5plik v\u00e4ljundv\u00e4\u00e4rtus, mis antakse edasi j\u00e4rgmise kihi neuronitele (v\u00f5i viimase kihi puhul antakse meile tagasi). Aktivatsioonifunktsioonid pole kohustuslikud, mispuhul [latex]a = z[\/latex].<\/p>\n<p>&nbsp;<\/p>\n<p>K\u00fcll aga on nad olulised, sest ilma nendeta pole v\u00f5imalik lahendada mittelineaarseid probleeme.<\/p>\n<figure id=\"attachment_995\" aria-describedby=\"caption-attachment-995\" style=\"width: 602px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-995 size-full\" src=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/nonlinear-classification.png\" alt=\"\" width=\"602\" height=\"252\" srcset=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/nonlinear-classification.png 602w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/nonlinear-classification-300x126.png 300w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/nonlinear-classification-65x27.png 65w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/nonlinear-classification-225x94.png 225w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/nonlinear-classification-350x147.png 350w\" sizes=\"auto, (max-width: 602px) 100vw, 602px\" \/><figcaption id=\"caption-attachment-995\" class=\"wp-caption-text\">Joonisel A on andmed, mida on v\u00f5imalik eraldada kahte klassi lineaarselt (joonistades sirge). Joonisel B on keerulisem klassifikatsiooni probleem, mille andmete eraldamiseks on vaja mittelineaarset funktsiooni. Autor: Sebastian Raschka. Creative Commons Attribution-Share Alike 4.0 International. Pildi allikas: https:\/\/commons.wikimedia.org\/wiki\/File:Main-qimg-48d5bd214e53d440fa32fc9e5300c894.png<\/figcaption><\/figure>\n<p>Sigmoidifunktsioon, mille v\u00e4ljundv\u00e4\u00e4rtused on 0-1 vahel, n\u00e4eb v\u00e4lja selline:<\/p>\n<figure id=\"attachment_996\" aria-describedby=\"caption-attachment-996\" style=\"width: 1024px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-996 size-large\" src=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/sigmoid-1024x471.png\" alt=\"\" width=\"1024\" height=\"471\" srcset=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/sigmoid-1024x471.png 1024w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/sigmoid-300x138.png 300w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/sigmoid-768x353.png 768w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/sigmoid-65x30.png 65w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/sigmoid-225x104.png 225w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/sigmoid-350x161.png 350w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/sigmoid.png 1200w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption id=\"caption-attachment-996\" class=\"wp-caption-text\">Sigmoidifunktsioon. Pildi allikas: https:\/\/commons.wikimedia.org\/wiki\/File:Sigmoid-function-2.svg<\/figcaption><\/figure>\n<p><math display=\"block\" xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><mi>a<\/mi><mo>=<\/mo><mrow><mi>\u03c3<\/mi><mo>\u2061<\/mo><mrow><mo stretchy=\"false\">(<\/mo><mi>z<\/mi><mo stretchy=\"false\">)<\/mo><\/mrow><\/mrow><mo>=<\/mo><mfrac><mn>1<\/mn><mrow><mn>1<\/mn><mo>+<\/mo><msup><mi>e<\/mi><mrow><mo>\u2212<\/mo><mi>z<\/mi><\/mrow><\/msup><\/mrow><\/mfrac><\/math><\/p>\n<p>Muutuja\u00a0<span class=\"math notranslate nohighlight\">[latex]a[\/latex]<\/span>\u00a0on l\u00f5plik neuroni v\u00e4ljundv\u00e4\u00e4rtus, mis antakse edasi j\u00e4rgneva kihi neuronitesse [latex]x[\/latex]<sub>[latex]i[\/latex] <\/sub>kujul, kus\u00a0<span class=\"math notranslate nohighlight\">[latex]i[\/latex]<\/span>\u00a0on neuroni indeks kihis. Igal aktivatsioonifunktsioonil on oma eelised ja puudused. N\u00e4iteks ReLU aktivatsioonifunktsioon n\u00e4eb v\u00e4lja selline:<\/p>\n<p><math display=\"block\" xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><mrow><mi><\/mi><\/mrow><\/math> <math display=\"block\" xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><mrow><mi>a<\/mi><mo>=<\/mo><mrow><\/mrow><\/mrow><mtext>ReLU<\/mtext><mrow><mrow><mo stretchy=\"false\">(<\/mo><mi>z<\/mi><mo stretchy=\"false\">)<\/mo><\/mrow><mo>=<\/mo><mrow><mo movablelimits=\"true\">max<\/mo><mo>\u2061<\/mo><mrow><mo stretchy=\"false\">(<\/mo><mrow><mi>z<\/mi><mo>,<\/mo><mn>0<\/mn><\/mrow><mo stretchy=\"false\">)<\/mo><\/mrow><\/mrow><\/mrow><\/math><\/p>\n<p>ReLU funktsioon on kiirem kui sigmoidifunktsioon ta arvutuslihtsuse t\u00f5ttu, kuid tal on ka m\u00f5ningaid puudusi, nagu see, et ReLU funktsioon ei tagasta v\u00e4\u00e4rtusi (0, 1) piirkonnas, vaid [0,\u221e), mis v\u00f5ib olla m\u00f5nedes olukordades oluline.<\/p>\n<figure id=\"attachment_997\" aria-describedby=\"caption-attachment-997\" style=\"width: 1024px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-997 size-large\" src=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-formula-1024x542.png\" alt=\"\" width=\"1024\" height=\"542\" srcset=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-formula-1024x542.png 1024w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-formula-300x159.png 300w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-formula-768x407.png 768w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-formula-65x34.png 65w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-formula-225x119.png 225w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-formula-350x185.png 350w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-formula.png 1420w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption id=\"caption-attachment-997\" class=\"wp-caption-text\">\u00dche neuroni arvutused visualiseerituna. Autor: BrunelloN. Pildi allikas: https:\/\/commons.wikimedia.org\/wiki\/File:Example_of_a_neural_network%27s_neural_unit.png<\/figcaption><\/figure>\n<p>N\u00e4rviv\u00f5rgu viimast kihti kutsutakse v\u00e4ljundkihiks, mis sisaldab l\u00f5ppandmetega neuroneid. Klassifitseerimis\u00fclesande v\u00e4ljundiks on klass, millesse antud sisend kuulub. N\u00e4iteks n\u00e4rviv\u00f5rk, mis tuvastab loomi, omaks \u00fchte v\u00e4ljundneuronit iga looma kohta, mida ta on v\u00f5imeline tuvastama. K\u00f5ige eredamat v\u00e4ljundneuronit (ehk neuronit, mille funktsioon tagastab suurima v\u00e4ljundi) loetaks n\u00e4rviv\u00f5rgu vastuseks.<\/p>\n<figure id=\"attachment_998\" aria-describedby=\"caption-attachment-998\" style=\"width: 791px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-998 size-full\" src=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net.jpeg\" alt=\"\" width=\"791\" height=\"388\" srcset=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net.jpeg 791w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-300x147.jpeg 300w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-768x377.jpeg 768w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-65x32.jpeg 65w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-225x110.jpeg 225w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/neural-net-350x172.jpeg 350w\" sizes=\"auto, (max-width: 791px) 100vw, 791px\" \/><figcaption id=\"caption-attachment-998\" class=\"wp-caption-text\">N\u00e4rviv\u00f5rk, kus vasakul on sisendkiht (input layer), sellele j\u00e4rgnevad kaks peidetud kihti (hidden layer) ja k\u00f5ige paremal on v\u00e4ljundkiht (output layer). Info sisestatakse sisendkihti ja liigub seej\u00e4rel vasakult paremale. L\u00f5puks on v\u00e4ljundiks \u00fcks number, nt kas pildil on kass (1.0) v\u00f5i mitte (0.0). Pildi allikas: https:\/\/cs231n.github.io\/convolutional-networks\/<\/figcaption><\/figure>\n<div class=\"textbox textbox--examples\">\n<header class=\"textbox__header\">\n<p class=\"textbox__title\">Lisainfo: Klassid ja v\u00e4ljundneuronid<\/p>\n<\/header>\n<div class=\"textbox__content\">\n<p>Kuigi teoorias oleks v\u00f5imalik k\u00f5ikide klassifitseerimis\u00fclesannete lahendamiseks kasutada v\u00e4ljundkihis vaid \u00fchte neuronit, mille oodatud v\u00e4\u00e4rtus oleneks klassinumbrist (nt 1 on koer, 2 on kass, 3 on j\u00e4nes, jne), praktikas on selliseid n\u00e4rviv\u00f5rke ekstreemselt keeruline treenida. Seep\u00e4rast eelistatakse disaini, kus v\u00e4ljundkihis on olemas \u00fcks neuron igale klassile. N\u00e4iteks kui klassifitseerimis\u00fclesandes on 10 erinevat klassi, siis on v\u00e4ljundkihis 10 neuronit.<\/p>\n<\/div>\n<\/div>\n<h2>M\u00f5tle ja nuputa!<\/h2>\n<div id=\"h5p-49\">\n<div class=\"h5p-iframe-wrapper\"><iframe id=\"h5p-iframe-49\" class=\"h5p-iframe\" data-content-id=\"49\" style=\"height:1px\" src=\"about:blank\" frameBorder=\"0\" scrolling=\"no\" title=\"Pildituvastus (n\u00e4rviv\u00f5rgu kihid)\"><\/iframe><\/div>\n<\/div>\n<h2 id=\"pytorchi-seadistamine\">PyTorchi seadistamine<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"https:\/\/pydoc.pages.taltech.ee\/image_recognition\/v2\/intro.html#pytorchi-seadistamine\">\uf0c1<\/a><\/h2>\n<p>Kasutame n\u00e4rviv\u00f5rkude koostamiseks Pythoni raamistikku\u00a0<a class=\"reference external\" href=\"https:\/\/pytorch.org\/\">PyTorch<\/a>, mida on v\u00f5imalik alla laadida\u00a0<a class=\"reference external\" href=\"https:\/\/pytorch.org\/get-started\/locally\/\">siit<\/a>.<\/p>\n<figure id=\"attachment_999\" aria-describedby=\"caption-attachment-999\" style=\"width: 1003px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-999 size-full\" src=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/pytorch-installation.png\" alt=\"\" width=\"1003\" height=\"384\" srcset=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/pytorch-installation.png 1003w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/pytorch-installation-300x115.png 300w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/pytorch-installation-768x294.png 768w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/pytorch-installation-65x25.png 65w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/pytorch-installation-225x86.png 225w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/pytorch-installation-350x134.png 350w\" sizes=\"auto, (max-width: 1003px) 100vw, 1003px\" \/><figcaption id=\"caption-attachment-999\" class=\"wp-caption-text\">PyTorchi installeerimine:<br \/>1. Vali oma operatsioonis\u00fcsteem.<br \/>2. Vali paketihalduriks Pip.<br \/>3. Vali vaikimisi &#8220;CPU&#8221;. V\u00f5ib valida ka &#8220;CUDA&#8221; juhul kui arvutis on olemas NVIDIA graafikakaarti ja selle poolt pakutav CUDA tarkvara, mida saab alla laadida siit. CUDA kiirendab oluliselt n\u00e4rviv\u00f5rkude treenimist, aga selle seadistamine v\u00f5ib olla ajakulukas. Olemasolevat CUDA versiooni saab kontrollida k\u00e4suga nvcc &#8211;version.<br \/>4. Genereeritakse l\u00f5plik k\u00e4sk, mida tuleb k\u00e4ivitada oma arvuti terminalis.<br \/>Pilt autori koostatud.<\/figcaption><\/figure>\n<p>K\u00f5iki n\u00e4iteid v\u00f5ib proovida k\u00e4ima panna oma arvutis, mis eeldab, et vajalikud Pythoni pakid on installeeritud. PyTorch ise v\u00f5tab v\u00e4hemalt 3 GB kettaruumi. Osade n\u00e4idete jooksutamine vajab arvutis head graafikakaarti v\u00f5i siis palju aega. Lokaalse installeerimise asemel on v\u00f5imalus n\u00e4iteid jooksutada n\u00e4iteks\u00a0<a class=\"reference external\" href=\"https:\/\/colab.research.google.com\/\">Google Colab keskkonnas<\/a>.<\/p>\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>Muutujat\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">device<\/span><\/code>\u00a0on hea luua siis, kui on v\u00f5imalus, et koodi k\u00e4ivitavates arvutites on olemas CUDA. Sel juhul peaks k\u00f5ikide tensorite (nendest rohkem j\u00e4rgmises peat\u00fckis) ning kihtide\/moodulite loomisel m\u00e4\u00e4rama, millise seadme (CPU v\u00f5i GPU) peal me neid luua soovime.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">import torch\r\n\r\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\r\nprint(device)  # 'cpu' v\u00f5i 'cuda'\r\n\r\n# loome tensoreid GPU-l v\u00f5i CPU-l\r\na = torch.tensor([1, 2, 3], device=device)\r\nb = torch.rand(2, 2, 2, device=device)<\/pre>\n<p>Juhul kui kood k\u00e4ivitub kindlalt ainult CPU-del, v\u00f5ib k\u00f5ik\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">device<\/span><\/code>-iga seotud koodi \u00e4ra j\u00e4tta. Edaspidi lihtsuse huvides seda ka teeme.<\/p>\n<\/div>\n<\/div>\n<h2 id=\"tensorid-ja-kihid\">Tensorid ja kihid<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"https:\/\/pydoc.pages.taltech.ee\/image_recognition\/v2\/intro.html#tensorid-ja-kihid\">\uf0c1<\/a><\/h2>\n<p>PyTorchi kasutamiseks tuleb importida\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">torch<\/span><\/code>\u00a0moodul (k\u00f5ikides j\u00e4rgnevates n\u00e4idetes on see eeldatud). Tensor on PyTorchi peamine andmestruktuur, mis saab olla skalaar (arv), vektor (massiiv), maatriks (2D massiiv) v\u00f5i muu k\u00f5rgema-dimensiooniline struktuur. Praktikas sarnaneb ta mitmem\u00f5\u00f5tmeliste NumPy massiividega. Tensorite loomiseks on mitmeid v\u00f5imalusi, millest paljud sarnanevad NumPy s\u00fcntaksile. N\u00e4iteks:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">import torch  # edaspidistes n\u00e4idetes alati eeldame, et torch on imporditud!\r\n\r\na = torch.tensor([[1, 2], [3, 4]])  # loome 2D tensori l\u00e4bi listi\r\nprint(a)\r\n# tensor([[1, 2],\r\n#         [3, 4]])\r\n\r\nb = torch.rand(3, 4)  # nt siia v\u00f5ib lisada ka device=device\r\nprint(b)\r\n# tensor([[0.5436, 0.6467, 0.9512, 0.6870],\r\n#         [0.4695, 0.0687, 0.1261, 0.5235],\r\n#         [0.7589, 0.3855, 0.2477, 0.0452]])\r\n\r\nprint(b.shape)         # torch.Size([3, 4])\r\nprint(b[0])            # tensor([0.5436, 0.6467, 0.9512, 0.6870])\r\nprint(b[0, 0])         # tensor(0.5436)\r\nprint(b[0, 0].item())  # 0.5436\r\nprint(b[:, -1])        # tensor([0.6870, 0.5235, 0.0452])<\/pre>\n<p>Tensoritel on olemas ka palju dimensioonidega seotud operatsioone (mis ei muuda tensoreid ise, vaid tagastavad uue tensori):<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">c = torch.zeros(4)\r\nprint(c)  # tensor([0.,  0.,  0.,  0.])\r\n\r\n# lisame dimensiooni positsiooni 0\r\nprint(c.unsqueeze(0))  # tensor([[0.,  0.,  0.,  0.]])\r\n\r\n# lisame dimensiooni positsiooni 1\r\nprint(c.unsqueeze(1))  # tensor([[0.], [0.], [0.], [0.]])\r\n\r\nd = torch.tensor([[[[1], [2]], [[3], [4]]]])\r\n\r\n# kustutame \u00fcleliigsed dimensioonid\r\nprint(d.squeeze())  # tensor([[1, 2], [3, 4]])\r\n\r\n# teisendame tensori 1D massiiviks\r\nprint(d.flatten())  # tensor([1, 2, 3, 4])<\/pre>\n<p>Iga n\u00e4rviv\u00f5rgu peidetud kihi sisendiks ja v\u00e4ljundiks on tensor. Esimese peidetud kihi sisendiks saaks n\u00e4iteks olla pilt, mille pikslid on esitatud vektorina. V\u00e4ljundiks v\u00f5iks olla n\u00e4iteks 10-kohaline vektor, mille iga element vastab mingile pildikategooriale.<\/p>\n<p>Koostame kihid n\u00e4rviv\u00f5rgule, mis ennustab koosinusfunktsiooni [latex]f(x) = cos(x)[\/latex]\u00a0v\u00e4\u00e4rtusi (mist\u00f5ttu vajab ta vaid \u00fchte sisendneuronit\u00a0<span class=\"math notranslate nohighlight\">[latex]x[\/latex]<\/span> ja \u00fchte v\u00e4ljundneuronit [latex]f(x)[\/latex]ennustatud v\u00e4\u00e4rtusele):<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">from torch import nn\r\n\r\nhidden_layer = nn.Linear(1, 7)  # 1 sisendneuron, 7 v\u00e4ljundneuronit\r\nactivation_fn = nn.ReLU()       # ReLU aktivatsioonifunktsioon\r\noutput_layer = nn.Linear(7, 1)  # 7 sisendneuronit peidetud kihist, 1 v\u00e4ljundneuron\r\n\r\nx = torch.tensor([50.0])  # kihi sisendiks oodatakse alati listi\r\ny = torch.cos(x)          # arvutame ette \u00f5ige vastuse, mida AI peab ennustama\r\nprint(x, y)               # tensor([50.]) tensor([0.9650])\r\n\r\nz = hidden_layer(x)   # z on 10-elemendine vektor\r\na = activation_fn(z)  # a on transformeeritud 10-elemendine vektor\r\n\r\nprint(z, '\\n', a)\r\n# tensor([29.3417, -37.906, -39.440, 21.4118, 21.6470, 17.2586, -2.5133])\r\n# tensor([29.3417,  0.0000,  0.0000, 21.4118, 21.6470, 17.2586,  0.0000])<\/pre>\n<p>Nagu n\u00e4eme, ReLU aktivatsioonifunktsioon [latex]a = max(z, 0)[\/latex] muudab k\u00f5ik negatiivsed v\u00e4\u00e4rtused nulliks. Muutuja\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">a<\/span><\/code> on n\u00fc\u00fcd meie j\u00e4rgmise kihi sisend, mis annab v\u00e4ljundiks \u00fche neuroni. See v\u00e4ljundneuron peaks peale treenimist oskama ennustada, et [latex]cos(50) = 0.9650[\/latex].<\/p>\n<p>V\u00e4ljundneuronile me aktivatsioonifunktsiooni ei rakenda, sest me tegeleme regressiooniga (mingi v\u00e4\u00e4rtuste, mitte klassi, ennustamine).<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">y_hat = output_layer(a)  # n\u00e4rviv\u00f5rgu ennustus\r\nprint(y_hat)             # tensor([13.5124])\r\nprint(y)                 # tensor([0.9650])<\/pre>\n<p>Muutuja\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">y_hat<\/span><\/code> on meie ennustus, mille matemaatiline notatsioon on [latex]\\hat{y}[\/latex]. Kuna n\u00e4rviv\u00f5rku pole veel treenitud ja seet\u00f5ttu ta ei tea mida teha, annab ta meile t\u00e4ielikult suvalise vastuse. \u00d5nneks on PyTorchis treenimise matemaatiline pool suuresti automatiseeritud: me peame vaid \u00fctlema n\u00e4rviv\u00f5rgule, kui vale ta ennustus on. Seej\u00e4rel oskab n\u00e4rviv\u00f5rk ise ennast korrigeerida, et j\u00e4rgmine kord ta ennustus oleks t\u00e4psem.<\/p>\n<p>V\u00f5ime n\u00e4iteks vaadata peidetud kihi parameetreid\u00a0<span class=\"math notranslate nohighlight\">[latex]w[\/latex]<\/span>\u00a0niiviisi:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"prolog\">print(hidden_layer.weight)\r\n# tensor([[0.5847], [-0.7390], [-0.7715], [0.4361], [0.4337], [0.3365], [-0.0392]])<\/pre>\n<h2 id=\"kahjufunktsioonid\">Kahjufunktsioonid<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"https:\/\/pydoc.pages.taltech.ee\/image_recognition\/v2\/intro.html#kahjufunktsioonid\">\uf0c1<\/a><\/h2>\n<p>Et treenimist alustada, tuleb meil v\u00f5tta n\u00e4idisandmete komplekt\u00a0<span class=\"math notranslate nohighlight\">[latex]x[\/latex]<\/span>\u00a0ja\u00a0<span class=\"math notranslate nohighlight\">[latex]y[\/latex]<\/span> ning alustuseks arvutada sellele ennustus [latex]\\hat{y}[\/latex], mida me eelmises peat\u00fckis juba ka tegime.<\/p>\n<p>J\u00e4rgmine samm on arvutada l\u00e4bi [latex]y[\/latex] ja [latex]\\hat{y}[\/latex] v\u00e4\u00e4rtuste v\u00e4lja kahju (<em>loss<\/em>). Selleks kasutame kahjufunktsiooni (<em>loss function<\/em>), mis leiab, kui suur on meie ennustuse ja tegeliku v\u00e4\u00e4rtuse vahe. Sellised kahjufunktsioone on palju, aga meile piisab populaarsest valikust\u00a0<a class=\"reference external\" href=\"https:\/\/en.wikipedia.org\/wiki\/Mean_squared_error#Loss_function\">MSE<\/a>\u00a0(<em>mean squared error<\/em>):<\/p>\n<p><math display=\"block\" xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><mtext>MSE<\/mtext><mrow><mrow><\/mrow><mo>=<\/mo><mfrac><mrow><mrow><msup><mrow><mo stretchy=\"false\">(<\/mo><mrow><msub><mi>y<\/mi><mn>1<\/mn><\/msub><mo>\u2212<\/mo><msub><mrow><mover><mi>y<\/mi><mo stretchy=\"false\">^<\/mo><\/mover><\/mrow><mn>1<\/mn><\/msub><\/mrow><mo stretchy=\"false\">)<\/mo><\/mrow><mn>2<\/mn><\/msup><mo>+<\/mo><\/mrow><mo>.<\/mo><mo>.<\/mo><mo>.<\/mo><mrow><mo>+<\/mo><msup><mrow><mo stretchy=\"false\">(<\/mo><mrow><msub><mi>y<\/mi><mi>n<\/mi><\/msub><mo>\u2212<\/mo><msub><mrow><mover><mi>y<\/mi><mo stretchy=\"false\">^<\/mo><\/mover><\/mrow><mi>n<\/mi><\/msub><\/mrow><mo stretchy=\"false\">)<\/mo><\/mrow><mn>2<\/mn><\/msup><\/mrow><\/mrow><mi>n<\/mi><\/mfrac><\/mrow><\/math><\/p>\n<p>Siin j\u00e4tkame eelmise peat\u00fcki n\u00e4ite l\u00f5pust:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">loss_fn = nn.MSELoss()  # loome 'Mean Squared Error' funktsiooni\r\nloss = loss_fn(y_hat, y)  # arvutame kahju: mida v\u00e4iksem on tulemus, seda \u00f5igem on ennustus\r\nprint(loss)  # tensor(157.4369)<\/pre>\n<p>Tulemuse peal\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">loss.backward()<\/span><\/code> meetodi v\u00e4lja kutsumine leiab vajalikud muutused (ehk gradiendid) kihtide [latex]w[\/latex] parameetritele, et tulevikus antud\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">x<\/span><\/code>-ile sarnase v\u00e4\u00e4rtuse korral teha parem ennustus. Parameetrite gradiendid salvestatakse tensorite\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">.grad<\/span><\/code>\u00a0atribuuti.<\/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\n\r\n# gradiendid n\u00e4itavad, kui palju iga kaaluparameeter peaks muutuma\r\nprint(hidden_layer.weight.grad)  # tensor([[142.13], [0.], [0.], [241.06], [294.88], [69.76], [0.]])<\/pre>\n<div class=\"textbox textbox--examples\">\n<header class=\"textbox__header\">\n<p class=\"textbox__title\">Lisainfo: Gradiendid<\/p>\n<\/header>\n<div class=\"textbox__content\">\n<p>Gradientide leidmiseks kasutatakse kalkulusel p\u00f5hinevat\u00a0<a class=\"reference external\" href=\"https:\/\/et.wikipedia.org\/wiki\/Tehisn%C3%A4rviv%C3%B5rk#Tagasilevi\">tagasilevi algoritmi<\/a>\u00a0(<em>backpropagation<\/em>). Soovi korral saab vaadata p\u00f5hjalikumat YouTube videot tagasilevist\u00a0<a class=\"reference external\" href=\"https:\/\/www.youtube.com\/watch?v=Ilg3gGewQ5U\">siit<\/a>.<\/p>\n<p>PyTorchis on implementeeritud\u00a0<a class=\"reference external\" href=\"https:\/\/en.wikipedia.org\/wiki\/Automatic_differentiation\">automaatne diferentseerimine<\/a>\u00a0(<em>automatic differentiation<\/em>). See t\u00e4hendab, et gradientide leidmiseks ei ole vaja ise kirjutada gradientide v\u00e4\u00e4rtusi arvutavat koodi, vaid PyTorch teeb seda automaatselt. Kui on soov kardinate taha piiluda, siis v\u00f5ib vaadelda tensorite\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">.grad_fn<\/span><\/code>\u00a0atribuuti. Rohkem saab lugeda\u00a0<a class=\"reference external\" href=\"https:\/\/pytorch.org\/tutorials\/beginner\/blitz\/autograd_tutorial.html\">siit<\/a>.<\/p>\n<\/div>\n<\/div>\n<h2 id=\"optimeerijad\">Optimeerijad<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"https:\/\/pydoc.pages.taltech.ee\/image_recognition\/v2\/intro.html#optimeerijad\">\uf0c1<\/a><\/h2>\n<p>N\u00fc\u00fcd kui oleme leidnud gradiendid, peame koostama optimeerija, mis neid muudatusi ka tegelikult parameetritele rakendab. Selles n\u00e4ites kasutame lihtsat SGD (<em>stochastic gradient descent<\/em>) optimeerijat. Optimeerijale peame andma ka \u00f5pisammu (<em>learning rate<\/em>), mis m\u00e4\u00e4rab, kui kiiresti meie kihtide parameetrid muutuvad relatiivselt gradientide suurustele. Optimaalse \u00f5pisammu h\u00fcperparameetri\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">lr<\/span><\/code>\u00a0v\u00e4\u00e4rtuse leiame l\u00e4bi\u00a0<a class=\"reference external\" href=\"https:\/\/sciencedryad.wordpress.com\/2014\/01\/25\/grad-student-descent\/\">katsetamise<\/a>, aga tavaliselt alustatakse v\u00e4\u00e4rtustega\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">0.1<\/span><\/code>,\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">0.01<\/span><\/code>\u00a0v\u00f5i\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">0.001<\/span><\/code>. Liiga madal arv v\u00f5ib panna n\u00e4rviv\u00f5rgu \u00f5ppima m\u00f5ttetult aeglaselt, liiga kiire paneb aga parameetrid muutuma liiga kiirelt ja v\u00f5ib juhtuda, et n\u00e4rviv\u00f5rk ei \u00f5pigi \u00fcldse midagi.<\/p>\n<p>J\u00e4tkame n\u00e4idet:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\"># loome optimeerija, mis teeb muudatusi hidden_layer-i parameetrites vastavalt kahjufunktsiooni tulemustele\r\noptimizer = torch.optim.SGD(hidden_layer.parameters(), lr=0.1)  # kasutame \u00f5pisammu 0.1\r\n\r\n# vaatame uuesti algseid kaale ja gradiente\r\nprint(hidden_layer.weight)       # tensor([[0.584], [-0.739], [-0.771], [0.436], [0.433], [0.336], [-0.03]])\r\nprint(hidden_layer.weight.grad)  # tensor([[142.13], [0.], [0.], [241.06], [294.88], [69.76], [0.]])\r\n\r\noptimizer.step()  # teeme muudatused\r\n\r\nprint(hidden_layer.weight)  # vaatame, kuidas kaalud muutusid\r\n# tensor([[-13.6292], [-0.7390], [-0.7715], [-23.6709], [-29.0547], [-6.6397], [-0.0392]])\r\n\r\n# juhul kui soovime uuesti muudatusi teha, peame alguses eelmised gradiendid nullima\r\n# kui me seda ei teeks, sis j\u00e4rgmise iteratsiooni gradiendid oleksid liidetud juurde eelnevatele\r\noptimizer.zero_grad()  # kustutame hidden_layer-i parameetrite gradiendid\r\nprint(hidden_layer.weight.grad)  # tensor([[0.], [0.], [0.], [0.], [0.], [0.], [0.]])<\/pre>\n<p>L\u00f5puks v\u00f5ime lasta n\u00e4rviv\u00f5rgul teha ennustuse uuesti sama\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">x<\/span><\/code>\u00a0v\u00e4\u00e4rtusega:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">z = hidden_layer(x)\r\na = activation_fn(z)\r\ny_hat = output_layer(a)\r\nloss = loss_fn(y_hat, y)\r\n\r\nprint(y_hat, y)  # tensor([0.0277]) tensor([0.9650])\r\nprint(loss)  # tensor(0.8784)<\/pre>\n<p>N\u00e4eme, et meie\u00a0<code class=\"docutils literal notranslate\"><span class=\"pre\">loss<\/span><\/code> on muutunud v\u00e4iksemaks, ehk meie ennustus on seekord t\u00e4psem. Eelmine kord ennustas n\u00e4rviv\u00f5rk [latex]\\hat{y} = 13.5124[\/latex], see kord aga [latex]\\hat{y}= 0.0277[\/latex]. Kuna \u00f5ige vastus on [latex]y = 0.965[\/latex], j\u00f5udsime \u00f5igele vastusele tunduvalt l\u00e4hemale. Kuna n\u00e4rviv\u00f5rk oskab funktsioone ainult j\u00e4ljendada, siis t\u00e4pselt \u00f5ige vastuseni me arvatavasti kunagi ei j\u00f5uagi. Lisaks sellele, praegune n\u00e4rviv\u00f5rk ikka veel annaks suvalisi vastusi paljude teiste <code class=\"docutils literal notranslate\"><span class=\"pre\">x<\/span><\/code>\u00a0v\u00e4\u00e4rtuste puhul, sest me pole talle veel nende kohta n\u00e4iteid andnud.<\/p>\n<div class=\"textbox textbox--examples\">\n<header class=\"textbox__header\">\n<p class=\"admonition-title\">Lisainfo: Gradiendi laskumine (<em>gradient descent<\/em>)<\/p>\n<\/header>\n<div class=\"textbox__content\">\n<p>Gradiendi laskumise algoritmi p\u00f5hiolemus on see, et ta liigub kahjufunktsiooni k\u00f5rgematest kohtadest madalamatesse, kuni j\u00f5uab mingi piirini, kus muutusi enam ei toimu. Kalkuluse terminites, otsitakse kahjufunktsiooni lokaalset miinimumpunkti. Laskumiskiirus oleneb\u00a0<em>learning rate (LR)<\/em> h\u00fcperparameetrist. Liiga k\u00f5rge laskumiskiirus tekitab olukorra, kus algoritm hakkab lendama optimaalsest madalaimast punktist m\u00f6\u00f6da.<\/p>\n<figure id=\"attachment_1000\" aria-describedby=\"caption-attachment-1000\" style=\"width: 300px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"size-medium wp-image-1000\" src=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/gradient-descent-300x243.gif\" alt=\"\" width=\"300\" height=\"243\" srcset=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/gradient-descent-300x243.gif 300w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/gradient-descent-65x53.gif 65w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/gradient-descent-225x183.gif 225w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/02\/gradient-descent-350x284.gif 350w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><figcaption id=\"caption-attachment-1000\" class=\"wp-caption-text\">Gradiendi laskumise (gradient descent, GD\/SGD) algoritmi visualisatsioon 3D ruumis, kus x- ja y-telg on kaaluparameetrid ning z-telg (k\u00f5rgus) on kahjufunktsiooni v\u00e4\u00e4rtus. Mida v\u00e4iksem on mingis x\/y punktis kahjufunktsiooni v\u00e4\u00e4rtus, seda t\u00e4psemad on seal n\u00e4rviv\u00f5rgu ennustused. Autor: Jacopo Bertolotti. Pildi allikas: https:\/\/commons.wikimedia.org\/wiki\/File:Gradient_descent.gif<\/figcaption><\/figure>\n<p>Kahjufunktsiooni maastikud on tegelikkuses h\u00e4sti keerulised ja mitte\u00fchtlased, ning koosnevad tavaliselt tuhandetest v\u00f5i miljonitest dimensioonidest.<\/p>\n<\/div>\n<\/div>\n<p>&nbsp;<\/p>\n","protected":false},"author":36,"menu_order":3,"template":"","meta":{"pb_show_title":"on","pb_short_title":"","pb_subtitle":"","pb_authors":[],"pb_section_license":""},"chapter-type":[],"contributor":[],"license":[],"class_list":["post-979","chapter","type-chapter","status-publish","hentry"],"part":784,"_links":{"self":[{"href":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-json\/pressbooks\/v2\/chapters\/979","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":22,"href":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-json\/pressbooks\/v2\/chapters\/979\/revisions"}],"predecessor-version":[{"id":1036,"href":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-json\/pressbooks\/v2\/chapters\/979\/revisions\/1036"}],"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\/979\/metadata\/"}],"wp:attachment":[{"href":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-json\/wp\/v2\/media?parent=979"}],"wp:term":[{"taxonomy":"chapter-type","embeddable":true,"href":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-json\/pressbooks\/v2\/chapter-type?post=979"},{"taxonomy":"contributor","embeddable":true,"href":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-json\/wp\/v2\/contributor?post=979"},{"taxonomy":"license","embeddable":true,"href":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-json\/wp\/v2\/license?post=979"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}