{"id":913,"date":"2023-01-02T08:00:46","date_gmt":"2023-01-02T08:00:46","guid":{"rendered":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/?post_type=chapter&#038;p=913"},"modified":"2023-01-04T11:58:45","modified_gmt":"2023-01-04T11:58:45","slug":"praktiline-naide-pildi-klassifitseerimise-kohta","status":"publish","type":"chapter","link":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/chapter\/praktiline-naide-pildi-klassifitseerimise-kohta\/","title":{"raw":"Praktiline n\u00e4ide pildi klassifitseerimise kohta","rendered":"Praktiline n\u00e4ide pildi klassifitseerimise kohta"},"content":{"raw":"Kasutame n\u00e4ite jaoks Pythoni pakki <a class=\"reference external\" href=\"https:\/\/docs.fast.ai\/\">fast.ai<\/a>, mis teeb <a class=\"reference external\" href=\"https:\/\/pytorch.org\/\">PyTorch<\/a> kasutamise arendajale mugavamaks. Kogu n\u00e4ite v\u00f5ib proovida k\u00e4ima panna oma arvutis. Aga see eeldab, et vajalikud pakid on vaja installida. PyTorch ise v\u00f5tab v\u00e4hemalt 3 GB kettaruumi. Osade n\u00e4idete jooksutamine vajab arvutis head graafikakaarti v\u00f5i siis palju aega. V\u00f5imalus on n\u00e4iteid jooksutada n\u00e4iteks <a class=\"reference external\" href=\"https:\/\/colab.research.google.com\/\">Google Colab keskkonas<\/a>.\r\n\r\nAastal 2015 ilmus XKCD keskkonnas j\u00e4rgmine pilt:\r\n\r\n[caption id=\"attachment_907\" align=\"alignnone\" width=\"267\"]<img class=\"wp-image-907 size-full\" src=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/01\/tasks.png\" alt=\"Pildituvastus tundus kunagi keeruline probleem\" width=\"267\" height=\"448\" \/> Pildi allikas: <a href=\"https:\/\/xkcd.com\/1425\/\">https:\/\/xkcd.com\/1425\/<\/a>[\/caption]\r\n<figure id=\"id1\" class=\"align-default\"><figcaption><\/figcaption><\/figure>\r\nVaatame siin peat\u00fckis, kuidas tuvastada loomi vaid m\u00f5ne minutiga. Ning selle tegemiseks ei ole vaja v\u00e4ga p\u00f5hjalikke eelteadmisi ega v\u00e4ga n\u00f5udlikku arvutit.\r\n\r\nTeeme l\u00e4bi n\u00e4ite, kuidas eristada kasse ja koeri. Sammud on j\u00e4rgmised:\r\n<ol class=\"arabic simple\">\r\n \t<li>Kasutame DuckDuckGo pildiotsingut, et leida kasside pilte<\/li>\r\n \t<li>Kasutame DuckDuckGo pildiotsingut, et leida koerte pilte<\/li>\r\n \t<li>Kasutame eeltreenitud mudelit, et \u00f5petada seda eristama kasse ja koeri<\/li>\r\n \t<li>Jooksutame saadud mudelit, et kontrollida, kas ta klassifitseerib pilte \u00f5igesti<\/li>\r\n<\/ol>\r\n<section id=\"keskkonna-seadistus\">\r\n<h2 id=\"keskkonna-seadistus\">Keskkonna seadistus<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"#keskkonna-seadistus\">\uf0c1<\/a><\/h2>\r\nVajalikud teegid:\r\n<ul class=\"simple\">\r\n \t<li><a class=\"reference external\" href=\"https:\/\/pytorch.org\/\">PyTorch<\/a><\/li>\r\n \t<li><a class=\"reference external\" href=\"https:\/\/docs.fast.ai\/\">fast.ai<\/a><\/li>\r\n \t<li><a class=\"reference external\" href=\"https:\/\/pypi.org\/project\/duckduckgo-search\/\">duckduckgo_search<\/a><\/li>\r\n<\/ul>\r\nKui jooksutada koodi n\u00e4iteks Google Colab keskkonnas, siis piisab seal j\u00e4rgmisest reast:\r\n<div class=\"highlight-text notranslate\">\r\n<div class=\"highlight\">\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\">!pip install -Uqq fastai duckduckgo_search\r\n<\/pre>\r\n<\/div>\r\n<\/div>\r\nLoome abifunktsiooni, millega pilte otsida:\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">from duckduckgo_search import ddg_images\r\nfrom fastcore.all import *\r\n\r\ndef search_images(term, max_images=30):\r\n    print(f\"Searching for '{term}', count: {max_images}\")\r\n    return L(ddg_images(term, max_results=max_images)).itemgot('image')\r\n<\/pre>\r\nLoodud <code class=\"docutils literal notranslate\"><span class=\"pre\">search_images<\/span><\/code> funktsiooni otsib etteantud otsingutekstiga pilte vastavalt soovitud kogusele. Kui kogust ei m\u00e4\u00e4rata, otsitakse 30 pilti. Funktsiooni sees <code class=\"docutils literal notranslate\"><span class=\"pre\">ddg_images<\/span><\/code> otsib pildid ja tagastab saadud tulemused j\u00e4rjendina (<em>list<\/em>), kus iga element on s\u00f5nastik (<em>dictionary<\/em>). <code class=\"docutils literal notranslate\"><span class=\"pre\">L<\/span><\/code> klass on osa <code class=\"docutils literal notranslate\"><span class=\"pre\">fastcode<\/span><\/code> pakist, mis v\u00f5imaldab mugavalt listist filtreerida v\u00e4lja vaid teatud v\u00f5tmega v\u00e4\u00e4rtused. Ehk siis kuna iga pildi kohta tagastatakse pildi veebiaadress, pealkiri, k\u00f5rgus, laius jne, aga meid huvitab vaid veebiaadress, siis <code class=\"docutils literal notranslate\"><span class=\"pre\">itemgot<\/span><\/code> meetodiga saab listi vaid <code class=\"docutils literal notranslate\"><span class=\"pre\">image<\/span><\/code> v\u00f5tmete v\u00e4\u00e4rtustest.\r\n\r\n<\/section><section id=\"piltide-andmete-otsimine\">\r\n<h2 id=\"piltide-andmete-otsimine\">Piltide (andmete) otsimine<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"#piltide-andmete-otsimine\">\uf0c1<\/a><\/h2>\r\nKasutame eelnevalt loodud funktsiooni, et otsida kasside ja koerte pilte. N\u00e4iteks otsime \u00fche kassi pildi (otsinguks kasutame \"cat photos\"):\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">urls = search_images('cat photos', max_images=1)\r\nurls[0]\r\n<\/pre>\r\nKui eelnev kood k\u00e4ima panna Jupyteris, peaksime n\u00e4gema \u00fche pildi linki. V\u00f5ime selle brauseris avada ja veenduda, kas tegemist on kassiga. Kui jooksutad m\u00f5nes IDE-s, pead aadressi n\u00e4gemiseks selle v\u00e4lja printima (<code class=\"docutils literal notranslate\"><span class=\"pre\">print(url[0])<\/span><\/code>).\r\n\r\nMugavam oleks aga neid pilte kohta n\u00e4ha. Kirjutame selleks j\u00e4rgmise koodi:\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">from fastdownload import download_url\r\ndest = 'cat.jpg'\r\ndownload_url(urls[0], dest, show_progress=False)\r\n\r\nfrom fastai.vision.all import *\r\nim = Image.open(dest)\r\nim.to_thumb(256, 256)\r\n<\/pre>\r\nKood t\u00f5mbab eelnevalt leitud \u00fche pildi (<code class=\"docutils literal notranslate\"><span class=\"pre\">url[0]<\/span><\/code>) alla faili <code class=\"docutils literal notranslate\"><span class=\"pre\">\"cat.jpg\"<\/span><\/code>. Seej\u00e4rel loetakse antud failist pildiobjekt ja kuvatakse selle v\u00e4ike versioon ehk kuvat\u00f5mmis (<em>thumbnail<\/em>, maksimaalselt 256 pikslit k\u00f5rge v\u00f5i lai).\r\n\r\nSelle koodi k\u00e4ivitamisel Jupyteris peaks n\u00e4itama \u00fche kassi pilti. Kui jooksutad koodis IDE-s, pead l\u00f5ppu lisama veel <code class=\"docutils literal notranslate\"><span class=\"pre\">show()<\/span><\/code> v\u00e4ljakutse: <code class=\"docutils literal notranslate\"><span class=\"pre\">im.to_thumb(256,<\/span> <span class=\"pre\">256).show()<\/span><\/code>.\r\n\r\nToimime samamoodi \u00fche koera pildi saamiseks:\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">download_url(search_images('dog photos', max_images=1)[0], 'dog.jpg', show_progress=False)\r\nImage.open('dog.jpg').to_thumb(256,256)\r\n<\/pre>\r\nKood on tegelikult sama. Oleme seda lihtsalt l\u00fchendanud kahe rea peale.\r\n\r\nTreenimiseks on meil aga vaja rohkem pilte. Kirjutame selle j\u00e4rgmise koodi:\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">searches = ('cat', 'dog')\r\n# create a new parent folder\r\npath = Path('cat_or_dog')\r\nfrom time import sleep\r\n\r\nfor search in searches:\r\n    # create a subfolder for current search term\r\n    dest = (path\/search)\r\n    dest.mkdir(exist_ok=True, parents=True)\r\n    # download images\r\n    download_images(dest, urls=search_images(f'{search} photo'))\r\n    sleep(10)  # Pause between searches to avoid over-loading server\r\n    # try some other searches too - sitting animalt\r\n    download_images(dest, urls=search_images(f'{search} sitting photo'))\r\n    sleep(10)\r\n    # let's also try walking animals\r\n    download_images(dest, urls=search_images(f'{search} walking photo'))\r\n    sleep(10)\r\n    # let's resize all the images\r\n    resize_images(path\/search, max_size=400, dest=path\/search)\r\n<\/pre>\r\nVaatame, mida eelnev kood teeb. Alguses defineerime \u00e4ra enniku erinevate loomadega (need on need, keda me tahame piltidelt tuvastama hakata). Seej\u00e4rel m\u00e4\u00e4rame eraldi kausta, kus hakkame otsitud pilte hoidma. <code class=\"docutils literal notranslate\"><span class=\"pre\">time<\/span><\/code> moodulist kasutame <code class=\"docutils literal notranslate\"><span class=\"pre\">sleep<\/span><\/code> funktsiooni selleks, et otsingute vahel v\u00e4ike paus teha (et me ei koormaks otsingumootorit \u00fcle, mis v\u00f5ib p\u00f5hjustada \u00fchenduse piiramise).\r\n\r\nK\u00e4ime l\u00e4bi k\u00f5ik klassid, mida me algselt m\u00e4\u00e4rasime (meie n\u00e4ites kass ja koer). M\u00e4\u00e4rame \u00e4ra sihtkausta, kuhu t\u00f5mmatakse looma pildid. N\u00e4iteks kasside puhul <code class=\"docutils literal notranslate\"><span class=\"pre\">cat_or_dog\/cat<\/span><\/code>. Edasi t\u00f5mbame alla pilte vastavalt m\u00e4\u00e4ratud otsingutekstile. Selleks, et suudaksime looma tuvastada v\u00f5imalikult erinvates tegevustes\/poosides, otsime k\u00f5igepealt lihtsalt looma pilte, seej\u00e4rel istuva looma pilte ja l\u00f5puks k\u00f5ndiva looma pilte. Siia v\u00f5ib vabalat veel lisada otsingutekste, et leida v\u00f5imalikult erinevaid loomapilte. K\u00f5ik need pildid t\u00f5mmatakse m\u00e4\u00e4ratud looma kausta. L\u00f5puks k\u00e4ivitatakse piltide suuruse muutmise funktsiooni, mis k\u00e4ib k\u00f5ik allat\u00f5mmatud pildid l\u00e4bi ja muudab nad vajadusel v\u00e4iksemaks (nii, et pikima k\u00fclje pikkus oleks maksimaalselt 400 pikslit).\r\n\r\n<\/section><section id=\"mudeli-treenimine\">\r\n<h2 id=\"mudeli-treenimine\">Mudeli treenimine<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"#mudeli-treenimine\">\uf0c1<\/a><\/h2>\r\nKuna m\u00f5ned allat\u00f5mmatud pildid ei pruugi olla korrektsed (vigased pildifailid jms), siis eemaldame sellised:\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">failed = verify_images(get_image_files(path))\r\nfailed.map(Path.unlink)\r\n<\/pre>\r\nMudeli treenimiseks peame andmed sobivale kujule viima. Selleks loome <code class=\"docutils literal notranslate\"><span class=\"pre\">DataLoaders<\/span><\/code> objekti. See sisaldab treeningandmeid (<em>training set<\/em>, neid pilte kasutatakse mudeli loomiseks) ja valideerimisandmeid (<em>validation set<\/em>, nende piltidega kontrollitakse mudeli t\u00e4psust ja vajadusel tehakse mudalisse t\u00e4iendusi - treenimisel neid pilte ei kasutata). <code class=\"docutils literal notranslate\"><span class=\"pre\">fastai<\/span><\/code> pakis on selle jaoks olemas abiklass <code class=\"docutils literal notranslate\"><span class=\"pre\">DataBlock<\/span><\/code>.\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">dls = DataBlock(\r\n    blocks=(ImageBlock, CategoryBlock), \r\n    get_items=get_image_files, \r\n    splitter=RandomSplitter(valid_pct=0.2, seed=42),\r\n    get_y=parent_label,\r\n    item_tfms=[Resize(192, method='squish')]\r\n).dataloaders(path, bs=32)\r\n\r\ndls.show_batch(max_n=6)\r\n<\/pre>\r\nVaatame, mida <code class=\"docutils literal notranslate\"><span class=\"pre\">DataBlock<\/span><\/code> konstruktori argumendid t\u00e4hendavad:\r\n<ul class=\"simple\">\r\n \t<li><code class=\"docutils literal notranslate\"><span class=\"pre\">blocks=(ImageBlock,<\/span> <span class=\"pre\">CategoryBlock)<\/span><\/code> - meie mudeli sisendiks on pildid ja v\u00e4ljundiks on kategooria (kass v\u00f5i koer).<\/li>\r\n \t<li><code class=\"docutils literal notranslate\"><span class=\"pre\">get_items=get_image_files<\/span><\/code> - sisendi saamiseks kasutatakse <code class=\"docutils literal notranslate\"><span class=\"pre\">get_image_files<\/span><\/code> funktsiooni, mis leiab pildifailid kaustast.<\/li>\r\n \t<li><code class=\"docutils literal notranslate\"><span class=\"pre\">splitter=RandomSplitter(valid_pct=0.2,<\/span> <span class=\"pre\">seed=42)<\/span><\/code> - sisendandmed jagatakse juhuslikult treening- ja valideerimisandmeteks, kusjuures valideerimiseks j\u00e4\u00e4b 20% sisenditest (piltidest).<\/li>\r\n \t<li><code class=\"docutils literal notranslate\"><span class=\"pre\">get_y=parent_label<\/span><\/code> - m\u00e4rgend (kas kass v\u00f5i koer) iga sisendi (pildi) kohta saadakse kasuta nimest.<\/li>\r\n \t<li><code class=\"docutils literal notranslate\"><span class=\"pre\">item_tfms=[Resize(192,<\/span> <span class=\"pre\">method='squish')]<\/span><\/code> - k\u00f5ik pildid tehakse v\u00e4iksemaks nii, et need mahuks 192 x 192 piksli sisse \u00e4ra kasutades v\u00e4hendamiseks meetodit \"squish\" (pilt venitatakse vajadusel ruudukujuliseks). V\u00f5imalik on ka meetod \"crop\", aga selle puhul v\u00f5ib m\u00f5ni oluline osa pildilt v\u00e4lja j\u00e4\u00e4da (n\u00e4iteks laia pildi puhul j\u00e4\u00e4b looma sama v\u00e4hendatud pildilt v\u00e4lja).<\/li>\r\n<\/ul>\r\nSelle koodi tulemusena peaks Jupyteris n\u00e4gema 6 pilti koos kategooriaga (kass v\u00f5i koer). Kuna need 6 pilti valitakse juhuslikult, v\u00f5ivad k\u00f5ik olla n\u00e4iteks kassid. V\u00f5ib proovida m\u00e4ngida <code class=\"docutils literal notranslate\"><span class=\"pre\">max_n<\/span><\/code> v\u00e4\u00e4rtusega, et n\u00e4ha rohkem\/v\u00e4hem pilte.\r\n\r\nLoodud <code class=\"docutils literal notranslate\"><span class=\"pre\">dls<\/span><\/code> muutujat saame n\u00fc\u00fcd kasutada mudeli treenimiseks:\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">learn = vision_learner(dls, resnet18, metrics=error_rate)\r\nlearn.fine_tune(3)\r\n<\/pre>\r\nKasutame treenimiseks eeltreenitud mudelit <code class=\"docutils literal notranslate\"><span class=\"pre\">resnet18<\/span><\/code>. T\u00e4psemalt saab eeltreenitud mudelite kohta lugeda siit: <a class=\"reference external\" href=\"https:\/\/pytorch.org\/vision\/main\/models.html\">https:\/\/pytorch.org\/vision\/main\/models.html<\/a>. Valitud <code class=\"docutils literal notranslate\"><span class=\"pre\">resnet18<\/span><\/code> on piisavalt kiire ja t\u00e4pne sellist t\u00fc\u00fcpi klassifitseerimis\u00fclesande jaoks.\r\n\r\n<code class=\"docutils literal notranslate\"><span class=\"pre\">fine_tune<\/span><\/code> meetod oskab \u00e4ra kasutada parimad praktikaid tulemuste parandamiseks.\r\n\r\nKui see kood k\u00e4ima panna, peaks n\u00e4gema treenimise tulemusi. N\u00e4iteks:\r\n\r\n[caption id=\"attachment_908\" align=\"alignnone\" width=\"700\"]<img class=\"size-full wp-image-908\" src=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/01\/training_results.png\" alt=\"Treeningu tulemused\" width=\"700\" height=\"354\" \/> \u00dcks v\u00f5imalik n\u00e4ide treeningutulemustest Jupyter Notebooksis (jooksutatud Google Colab keskkonnas).[\/caption]\r\n\r\n<\/section><section id=\"kontrollime-mudelit\">\r\n<h2 id=\"kontrollime-mudelit\">Kontrollime mudelit<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"#kontrollime-mudelit\">\uf0c1<\/a><\/h2>\r\nProovime treenitud mudeliga ennustada m\u00f5ne pildi klassi. N\u00e4iteks proovime j\u00e4rgmist koodi:\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">download_url(search_images('cat 2022 photos', max_images=1)[0], 'cat1.jpg', show_progress=False)\r\ncategory, _, probs = learn.predict(PILImage.create('cat1.jpg'))\r\nprint(f\"This is a: {category}.\")\r\nprint(f\"Probability it's a cat: {probs[0]:.4f}\")\r\nImage.open('cat1.jpg').to_thumb(256, 256)\r\n<\/pre>\r\nEelneva koodi puhul oleme kasutanud erinevat otsinguteksti (\"cat 2022 photos\"). Vastasel korral leiaksime t\u00e4pselt samad pildid, mida treenimisel kasutati. Testimiseks tuleks kasutada andmeid, mida treenimisel ei ole kasutatud. Eelneva koodi tulemusena peaks kassi tuvastamise t\u00e4psus olema 100%-l\u00e4hedane. \u00dchtlasi peaks koodi tulemusena v\u00e4lja kuvatama kass pilt, mida tuvastati. <code class=\"docutils literal notranslate\"><span class=\"pre\">probs<\/span><\/code> muutujasse pannakse t\u00f5en\u00e4osus iga kategooria kohta. <code class=\"docutils literal notranslate\"><span class=\"pre\">probs[0]<\/span><\/code> viitab esimese kategooria peale (kassid), kuna see oli meie eelnevas koodis eespool.\r\n\r\nProovi sama teha koera pildiga. N\u00e4iteks selline pilt:\r\n\r\n[caption id=\"attachment_903\" align=\"alignnone\" width=\"640\"]<img class=\"wp-image-903 size-full\" src=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/01\/dog1.jpg\" alt=\"Pilt koerast, millega testime mudeli \u00f5igsust\" width=\"640\" height=\"427\" \/> Pildi autor <a href=\"https:\/\/pixabay.com\/users\/vizslafotozas-9868721\/\">P\u00e9ter G\u00f6bly\u00f6s<\/a> (allikas <a href=\"https:\/\/pixabay.com\/\">Pixabay<\/a>)[\/caption]\r\n\r\n<\/section><section id=\"viiteid\">\r\n<h2 id=\"viiteid\">Viiteid<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"#viiteid\">\uf0c1<\/a><\/h2>\r\n<ul class=\"simple\">\r\n \t<li><a class=\"reference external\" href=\"https:\/\/course.fast.ai\/\">https:\/\/course.fast.ai\/<\/a> - Tasuta kursus n\u00e4rviv\u00f5rkudega \u00f5ppimise jaoks. Ka siinne n\u00e4ide on paljuski selle peale \u00fcles ehitatud.<\/li>\r\n \t<li><a class=\"reference external\" href=\"https:\/\/playground.tensorflow.org\/\">https:\/\/playground.tensorflow.org\/<\/a> - Siin saab visuaalselt j\u00e4lgida, kuidas n\u00e4rviv\u00f5rk v\u00f5iks toimida erinevate \u00fclesannete puhul.<\/li>\r\n<\/ul>\r\n<\/section>","rendered":"<p>Kasutame n\u00e4ite jaoks Pythoni pakki <a class=\"reference external\" href=\"https:\/\/docs.fast.ai\/\">fast.ai<\/a>, mis teeb <a class=\"reference external\" href=\"https:\/\/pytorch.org\/\">PyTorch<\/a> kasutamise arendajale mugavamaks. Kogu n\u00e4ite v\u00f5ib proovida k\u00e4ima panna oma arvutis. Aga see eeldab, et vajalikud pakid on vaja installida. PyTorch ise v\u00f5tab v\u00e4hemalt 3 GB kettaruumi. Osade n\u00e4idete jooksutamine vajab arvutis head graafikakaarti v\u00f5i siis palju aega. V\u00f5imalus on n\u00e4iteid jooksutada n\u00e4iteks <a class=\"reference external\" href=\"https:\/\/colab.research.google.com\/\">Google Colab keskkonas<\/a>.<\/p>\n<p>Aastal 2015 ilmus XKCD keskkonnas j\u00e4rgmine pilt:<\/p>\n<figure id=\"attachment_907\" aria-describedby=\"caption-attachment-907\" style=\"width: 267px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-907 size-full\" src=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/01\/tasks.png\" alt=\"Pildituvastus tundus kunagi keeruline probleem\" width=\"267\" height=\"448\" srcset=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/01\/tasks.png 267w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/01\/tasks-179x300.png 179w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/01\/tasks-65x109.png 65w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/01\/tasks-225x378.png 225w\" sizes=\"auto, (max-width: 267px) 100vw, 267px\" \/><figcaption id=\"caption-attachment-907\" class=\"wp-caption-text\">Pildi allikas: <a href=\"https:\/\/xkcd.com\/1425\/\">https:\/\/xkcd.com\/1425\/<\/a><\/figcaption><\/figure>\n<figure id=\"id1\" class=\"align-default\"><figcaption><\/figcaption><\/figure>\n<p>Vaatame siin peat\u00fckis, kuidas tuvastada loomi vaid m\u00f5ne minutiga. Ning selle tegemiseks ei ole vaja v\u00e4ga p\u00f5hjalikke eelteadmisi ega v\u00e4ga n\u00f5udlikku arvutit.<\/p>\n<p>Teeme l\u00e4bi n\u00e4ite, kuidas eristada kasse ja koeri. Sammud on j\u00e4rgmised:<\/p>\n<ol class=\"arabic simple\">\n<li>Kasutame DuckDuckGo pildiotsingut, et leida kasside pilte<\/li>\n<li>Kasutame DuckDuckGo pildiotsingut, et leida koerte pilte<\/li>\n<li>Kasutame eeltreenitud mudelit, et \u00f5petada seda eristama kasse ja koeri<\/li>\n<li>Jooksutame saadud mudelit, et kontrollida, kas ta klassifitseerib pilte \u00f5igesti<\/li>\n<\/ol>\n<section id=\"keskkonna-seadistus\">\n<h2 id=\"keskkonna-seadistus\">Keskkonna seadistus<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"#keskkonna-seadistus\">\uf0c1<\/a><\/h2>\n<p>Vajalikud teegid:<\/p>\n<ul class=\"simple\">\n<li><a class=\"reference external\" href=\"https:\/\/pytorch.org\/\">PyTorch<\/a><\/li>\n<li><a class=\"reference external\" href=\"https:\/\/docs.fast.ai\/\">fast.ai<\/a><\/li>\n<li><a class=\"reference external\" href=\"https:\/\/pypi.org\/project\/duckduckgo-search\/\">duckduckgo_search<\/a><\/li>\n<\/ul>\n<p>Kui jooksutada koodi n\u00e4iteks Google Colab keskkonnas, siis piisab seal j\u00e4rgmisest reast:<\/p>\n<div class=\"highlight-text notranslate\">\n<div class=\"highlight\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\">!pip install -Uqq fastai duckduckgo_search\r\n<\/pre>\n<\/div>\n<\/div>\n<p>Loome abifunktsiooni, millega pilte otsida:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">from duckduckgo_search import ddg_images\r\nfrom fastcore.all import *\r\n\r\ndef search_images(term, max_images=30):\r\n    print(f\"Searching for '{term}', count: {max_images}\")\r\n    return L(ddg_images(term, max_results=max_images)).itemgot('image')\r\n<\/pre>\n<p>Loodud <code class=\"docutils literal notranslate\"><span class=\"pre\">search_images<\/span><\/code> funktsiooni otsib etteantud otsingutekstiga pilte vastavalt soovitud kogusele. Kui kogust ei m\u00e4\u00e4rata, otsitakse 30 pilti. Funktsiooni sees <code class=\"docutils literal notranslate\"><span class=\"pre\">ddg_images<\/span><\/code> otsib pildid ja tagastab saadud tulemused j\u00e4rjendina (<em>list<\/em>), kus iga element on s\u00f5nastik (<em>dictionary<\/em>). <code class=\"docutils literal notranslate\"><span class=\"pre\">L<\/span><\/code> klass on osa <code class=\"docutils literal notranslate\"><span class=\"pre\">fastcode<\/span><\/code> pakist, mis v\u00f5imaldab mugavalt listist filtreerida v\u00e4lja vaid teatud v\u00f5tmega v\u00e4\u00e4rtused. Ehk siis kuna iga pildi kohta tagastatakse pildi veebiaadress, pealkiri, k\u00f5rgus, laius jne, aga meid huvitab vaid veebiaadress, siis <code class=\"docutils literal notranslate\"><span class=\"pre\">itemgot<\/span><\/code> meetodiga saab listi vaid <code class=\"docutils literal notranslate\"><span class=\"pre\">image<\/span><\/code> v\u00f5tmete v\u00e4\u00e4rtustest.<\/p>\n<\/section>\n<section id=\"piltide-andmete-otsimine\">\n<h2 id=\"piltide-andmete-otsimine\">Piltide (andmete) otsimine<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"#piltide-andmete-otsimine\">\uf0c1<\/a><\/h2>\n<p>Kasutame eelnevalt loodud funktsiooni, et otsida kasside ja koerte pilte. N\u00e4iteks otsime \u00fche kassi pildi (otsinguks kasutame &#8220;cat photos&#8221;):<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">urls = search_images('cat photos', max_images=1)\r\nurls[0]\r\n<\/pre>\n<p>Kui eelnev kood k\u00e4ima panna Jupyteris, peaksime n\u00e4gema \u00fche pildi linki. V\u00f5ime selle brauseris avada ja veenduda, kas tegemist on kassiga. Kui jooksutad m\u00f5nes IDE-s, pead aadressi n\u00e4gemiseks selle v\u00e4lja printima (<code class=\"docutils literal notranslate\"><span class=\"pre\">print(url[0])<\/span><\/code>).<\/p>\n<p>Mugavam oleks aga neid pilte kohta n\u00e4ha. Kirjutame selleks j\u00e4rgmise koodi:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">from fastdownload import download_url\r\ndest = 'cat.jpg'\r\ndownload_url(urls[0], dest, show_progress=False)\r\n\r\nfrom fastai.vision.all import *\r\nim = Image.open(dest)\r\nim.to_thumb(256, 256)\r\n<\/pre>\n<p>Kood t\u00f5mbab eelnevalt leitud \u00fche pildi (<code class=\"docutils literal notranslate\"><span class=\"pre\">url[0]<\/span><\/code>) alla faili <code class=\"docutils literal notranslate\"><span class=\"pre\">\"cat.jpg\"<\/span><\/code>. Seej\u00e4rel loetakse antud failist pildiobjekt ja kuvatakse selle v\u00e4ike versioon ehk kuvat\u00f5mmis (<em>thumbnail<\/em>, maksimaalselt 256 pikslit k\u00f5rge v\u00f5i lai).<\/p>\n<p>Selle koodi k\u00e4ivitamisel Jupyteris peaks n\u00e4itama \u00fche kassi pilti. Kui jooksutad koodis IDE-s, pead l\u00f5ppu lisama veel <code class=\"docutils literal notranslate\"><span class=\"pre\">show()<\/span><\/code> v\u00e4ljakutse: <code class=\"docutils literal notranslate\"><span class=\"pre\">im.to_thumb(256,<\/span> <span class=\"pre\">256).show()<\/span><\/code>.<\/p>\n<p>Toimime samamoodi \u00fche koera pildi saamiseks:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">download_url(search_images('dog photos', max_images=1)[0], 'dog.jpg', show_progress=False)\r\nImage.open('dog.jpg').to_thumb(256,256)\r\n<\/pre>\n<p>Kood on tegelikult sama. Oleme seda lihtsalt l\u00fchendanud kahe rea peale.<\/p>\n<p>Treenimiseks on meil aga vaja rohkem pilte. Kirjutame selle j\u00e4rgmise koodi:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">searches = ('cat', 'dog')\r\n# create a new parent folder\r\npath = Path('cat_or_dog')\r\nfrom time import sleep\r\n\r\nfor search in searches:\r\n    # create a subfolder for current search term\r\n    dest = (path\/search)\r\n    dest.mkdir(exist_ok=True, parents=True)\r\n    # download images\r\n    download_images(dest, urls=search_images(f'{search} photo'))\r\n    sleep(10)  # Pause between searches to avoid over-loading server\r\n    # try some other searches too - sitting animalt\r\n    download_images(dest, urls=search_images(f'{search} sitting photo'))\r\n    sleep(10)\r\n    # let's also try walking animals\r\n    download_images(dest, urls=search_images(f'{search} walking photo'))\r\n    sleep(10)\r\n    # let's resize all the images\r\n    resize_images(path\/search, max_size=400, dest=path\/search)\r\n<\/pre>\n<p>Vaatame, mida eelnev kood teeb. Alguses defineerime \u00e4ra enniku erinevate loomadega (need on need, keda me tahame piltidelt tuvastama hakata). Seej\u00e4rel m\u00e4\u00e4rame eraldi kausta, kus hakkame otsitud pilte hoidma. <code class=\"docutils literal notranslate\"><span class=\"pre\">time<\/span><\/code> moodulist kasutame <code class=\"docutils literal notranslate\"><span class=\"pre\">sleep<\/span><\/code> funktsiooni selleks, et otsingute vahel v\u00e4ike paus teha (et me ei koormaks otsingumootorit \u00fcle, mis v\u00f5ib p\u00f5hjustada \u00fchenduse piiramise).<\/p>\n<p>K\u00e4ime l\u00e4bi k\u00f5ik klassid, mida me algselt m\u00e4\u00e4rasime (meie n\u00e4ites kass ja koer). M\u00e4\u00e4rame \u00e4ra sihtkausta, kuhu t\u00f5mmatakse looma pildid. N\u00e4iteks kasside puhul <code class=\"docutils literal notranslate\"><span class=\"pre\">cat_or_dog\/cat<\/span><\/code>. Edasi t\u00f5mbame alla pilte vastavalt m\u00e4\u00e4ratud otsingutekstile. Selleks, et suudaksime looma tuvastada v\u00f5imalikult erinvates tegevustes\/poosides, otsime k\u00f5igepealt lihtsalt looma pilte, seej\u00e4rel istuva looma pilte ja l\u00f5puks k\u00f5ndiva looma pilte. Siia v\u00f5ib vabalat veel lisada otsingutekste, et leida v\u00f5imalikult erinevaid loomapilte. K\u00f5ik need pildid t\u00f5mmatakse m\u00e4\u00e4ratud looma kausta. L\u00f5puks k\u00e4ivitatakse piltide suuruse muutmise funktsiooni, mis k\u00e4ib k\u00f5ik allat\u00f5mmatud pildid l\u00e4bi ja muudab nad vajadusel v\u00e4iksemaks (nii, et pikima k\u00fclje pikkus oleks maksimaalselt 400 pikslit).<\/p>\n<\/section>\n<section id=\"mudeli-treenimine\">\n<h2 id=\"mudeli-treenimine\">Mudeli treenimine<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"#mudeli-treenimine\">\uf0c1<\/a><\/h2>\n<p>Kuna m\u00f5ned allat\u00f5mmatud pildid ei pruugi olla korrektsed (vigased pildifailid jms), siis eemaldame sellised:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">failed = verify_images(get_image_files(path))\r\nfailed.map(Path.unlink)\r\n<\/pre>\n<p>Mudeli treenimiseks peame andmed sobivale kujule viima. Selleks loome <code class=\"docutils literal notranslate\"><span class=\"pre\">DataLoaders<\/span><\/code> objekti. See sisaldab treeningandmeid (<em>training set<\/em>, neid pilte kasutatakse mudeli loomiseks) ja valideerimisandmeid (<em>validation set<\/em>, nende piltidega kontrollitakse mudeli t\u00e4psust ja vajadusel tehakse mudalisse t\u00e4iendusi &#8211; treenimisel neid pilte ei kasutata). <code class=\"docutils literal notranslate\"><span class=\"pre\">fastai<\/span><\/code> pakis on selle jaoks olemas abiklass <code class=\"docutils literal notranslate\"><span class=\"pre\">DataBlock<\/span><\/code>.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">dls = DataBlock(\r\n    blocks=(ImageBlock, CategoryBlock), \r\n    get_items=get_image_files, \r\n    splitter=RandomSplitter(valid_pct=0.2, seed=42),\r\n    get_y=parent_label,\r\n    item_tfms=[Resize(192, method='squish')]\r\n).dataloaders(path, bs=32)\r\n\r\ndls.show_batch(max_n=6)\r\n<\/pre>\n<p>Vaatame, mida <code class=\"docutils literal notranslate\"><span class=\"pre\">DataBlock<\/span><\/code> konstruktori argumendid t\u00e4hendavad:<\/p>\n<ul class=\"simple\">\n<li><code class=\"docutils literal notranslate\"><span class=\"pre\">blocks=(ImageBlock,<\/span> <span class=\"pre\">CategoryBlock)<\/span><\/code> &#8211; meie mudeli sisendiks on pildid ja v\u00e4ljundiks on kategooria (kass v\u00f5i koer).<\/li>\n<li><code class=\"docutils literal notranslate\"><span class=\"pre\">get_items=get_image_files<\/span><\/code> &#8211; sisendi saamiseks kasutatakse <code class=\"docutils literal notranslate\"><span class=\"pre\">get_image_files<\/span><\/code> funktsiooni, mis leiab pildifailid kaustast.<\/li>\n<li><code class=\"docutils literal notranslate\"><span class=\"pre\">splitter=RandomSplitter(valid_pct=0.2,<\/span> <span class=\"pre\">seed=42)<\/span><\/code> &#8211; sisendandmed jagatakse juhuslikult treening- ja valideerimisandmeteks, kusjuures valideerimiseks j\u00e4\u00e4b 20% sisenditest (piltidest).<\/li>\n<li><code class=\"docutils literal notranslate\"><span class=\"pre\">get_y=parent_label<\/span><\/code> &#8211; m\u00e4rgend (kas kass v\u00f5i koer) iga sisendi (pildi) kohta saadakse kasuta nimest.<\/li>\n<li><code class=\"docutils literal notranslate\"><span class=\"pre\">item_tfms=[Resize(192,<\/span> <span class=\"pre\">method='squish')]<\/span><\/code> &#8211; k\u00f5ik pildid tehakse v\u00e4iksemaks nii, et need mahuks 192 x 192 piksli sisse \u00e4ra kasutades v\u00e4hendamiseks meetodit &#8220;squish&#8221; (pilt venitatakse vajadusel ruudukujuliseks). V\u00f5imalik on ka meetod &#8220;crop&#8221;, aga selle puhul v\u00f5ib m\u00f5ni oluline osa pildilt v\u00e4lja j\u00e4\u00e4da (n\u00e4iteks laia pildi puhul j\u00e4\u00e4b looma sama v\u00e4hendatud pildilt v\u00e4lja).<\/li>\n<\/ul>\n<p>Selle koodi tulemusena peaks Jupyteris n\u00e4gema 6 pilti koos kategooriaga (kass v\u00f5i koer). Kuna need 6 pilti valitakse juhuslikult, v\u00f5ivad k\u00f5ik olla n\u00e4iteks kassid. V\u00f5ib proovida m\u00e4ngida <code class=\"docutils literal notranslate\"><span class=\"pre\">max_n<\/span><\/code> v\u00e4\u00e4rtusega, et n\u00e4ha rohkem\/v\u00e4hem pilte.<\/p>\n<p>Loodud <code class=\"docutils literal notranslate\"><span class=\"pre\">dls<\/span><\/code> muutujat saame n\u00fc\u00fcd kasutada mudeli treenimiseks:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">learn = vision_learner(dls, resnet18, metrics=error_rate)\r\nlearn.fine_tune(3)\r\n<\/pre>\n<p>Kasutame treenimiseks eeltreenitud mudelit <code class=\"docutils literal notranslate\"><span class=\"pre\">resnet18<\/span><\/code>. T\u00e4psemalt saab eeltreenitud mudelite kohta lugeda siit: <a class=\"reference external\" href=\"https:\/\/pytorch.org\/vision\/main\/models.html\">https:\/\/pytorch.org\/vision\/main\/models.html<\/a>. Valitud <code class=\"docutils literal notranslate\"><span class=\"pre\">resnet18<\/span><\/code> on piisavalt kiire ja t\u00e4pne sellist t\u00fc\u00fcpi klassifitseerimis\u00fclesande jaoks.<\/p>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">fine_tune<\/span><\/code> meetod oskab \u00e4ra kasutada parimad praktikaid tulemuste parandamiseks.<\/p>\n<p>Kui see kood k\u00e4ima panna, peaks n\u00e4gema treenimise tulemusi. N\u00e4iteks:<\/p>\n<figure id=\"attachment_908\" aria-describedby=\"caption-attachment-908\" style=\"width: 700px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-908\" src=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/01\/training_results.png\" alt=\"Treeningu tulemused\" width=\"700\" height=\"354\" srcset=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/01\/training_results.png 700w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/01\/training_results-300x152.png 300w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/01\/training_results-65x33.png 65w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/01\/training_results-225x114.png 225w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/01\/training_results-350x177.png 350w\" sizes=\"auto, (max-width: 700px) 100vw, 700px\" \/><figcaption id=\"caption-attachment-908\" class=\"wp-caption-text\">\u00dcks v\u00f5imalik n\u00e4ide treeningutulemustest Jupyter Notebooksis (jooksutatud Google Colab keskkonnas).<\/figcaption><\/figure>\n<\/section>\n<section id=\"kontrollime-mudelit\">\n<h2 id=\"kontrollime-mudelit\">Kontrollime mudelit<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"#kontrollime-mudelit\">\uf0c1<\/a><\/h2>\n<p>Proovime treenitud mudeliga ennustada m\u00f5ne pildi klassi. N\u00e4iteks proovime j\u00e4rgmist koodi:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">download_url(search_images('cat 2022 photos', max_images=1)[0], 'cat1.jpg', show_progress=False)\r\ncategory, _, probs = learn.predict(PILImage.create('cat1.jpg'))\r\nprint(f\"This is a: {category}.\")\r\nprint(f\"Probability it's a cat: {probs[0]:.4f}\")\r\nImage.open('cat1.jpg').to_thumb(256, 256)\r\n<\/pre>\n<p>Eelneva koodi puhul oleme kasutanud erinevat otsinguteksti (&#8220;cat 2022 photos&#8221;). Vastasel korral leiaksime t\u00e4pselt samad pildid, mida treenimisel kasutati. Testimiseks tuleks kasutada andmeid, mida treenimisel ei ole kasutatud. Eelneva koodi tulemusena peaks kassi tuvastamise t\u00e4psus olema 100%-l\u00e4hedane. \u00dchtlasi peaks koodi tulemusena v\u00e4lja kuvatama kass pilt, mida tuvastati. <code class=\"docutils literal notranslate\"><span class=\"pre\">probs<\/span><\/code> muutujasse pannakse t\u00f5en\u00e4osus iga kategooria kohta. <code class=\"docutils literal notranslate\"><span class=\"pre\">probs[0]<\/span><\/code> viitab esimese kategooria peale (kassid), kuna see oli meie eelnevas koodis eespool.<\/p>\n<p>Proovi sama teha koera pildiga. N\u00e4iteks selline pilt:<\/p>\n<figure id=\"attachment_903\" aria-describedby=\"caption-attachment-903\" style=\"width: 640px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-903 size-full\" src=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/01\/dog1.jpg\" alt=\"Pilt koerast, millega testime mudeli \u00f5igsust\" width=\"640\" height=\"427\" srcset=\"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/01\/dog1.jpg 640w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/01\/dog1-300x200.jpg 300w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/01\/dog1-65x43.jpg 65w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/01\/dog1-225x150.jpg 225w, https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-content\/uploads\/sites\/18\/2023\/01\/dog1-350x234.jpg 350w\" sizes=\"auto, (max-width: 640px) 100vw, 640px\" \/><figcaption id=\"caption-attachment-903\" class=\"wp-caption-text\">Pildi autor <a href=\"https:\/\/pixabay.com\/users\/vizslafotozas-9868721\/\">P\u00e9ter G\u00f6bly\u00f6s<\/a> (allikas <a href=\"https:\/\/pixabay.com\/\">Pixabay<\/a>)<\/figcaption><\/figure>\n<\/section>\n<section id=\"viiteid\">\n<h2 id=\"viiteid\">Viiteid<a class=\"headerlink\" title=\"Permalink to this heading\" href=\"#viiteid\">\uf0c1<\/a><\/h2>\n<ul class=\"simple\">\n<li><a class=\"reference external\" href=\"https:\/\/course.fast.ai\/\">https:\/\/course.fast.ai\/<\/a> &#8211; Tasuta kursus n\u00e4rviv\u00f5rkudega \u00f5ppimise jaoks. Ka siinne n\u00e4ide on paljuski selle peale \u00fcles ehitatud.<\/li>\n<li><a class=\"reference external\" href=\"https:\/\/playground.tensorflow.org\/\">https:\/\/playground.tensorflow.org\/<\/a> &#8211; Siin saab visuaalselt j\u00e4lgida, kuidas n\u00e4rviv\u00f5rk v\u00f5iks toimida erinevate \u00fclesannete puhul.<\/li>\n<\/ul>\n<\/section>\n","protected":false},"author":36,"menu_order":5,"template":"","meta":{"pb_show_title":"on","pb_short_title":"","pb_subtitle":"","pb_authors":[],"pb_section_license":""},"chapter-type":[],"contributor":[],"license":[],"class_list":["post-913","chapter","type-chapter","status-publish","hentry"],"part":784,"_links":{"self":[{"href":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-json\/pressbooks\/v2\/chapters\/913","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":3,"href":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-json\/pressbooks\/v2\/chapters\/913\/revisions"}],"predecessor-version":[{"id":954,"href":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-json\/pressbooks\/v2\/chapters\/913\/revisions\/954"}],"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\/913\/metadata\/"}],"wp:attachment":[{"href":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-json\/wp\/v2\/media?parent=913"}],"wp:term":[{"taxonomy":"chapter-type","embeddable":true,"href":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-json\/pressbooks\/v2\/chapter-type?post=913"},{"taxonomy":"contributor","embeddable":true,"href":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-json\/wp\/v2\/contributor?post=913"},{"taxonomy":"license","embeddable":true,"href":"https:\/\/web.htk.tlu.ee\/digitaru\/tarkvara2\/wp-json\/wp\/v2\/license?post=913"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}