I OSA sisukord |
1.4 MASINÕPE
Rasked ülesanded
Kursusel Programmeerimise alused ja sel kursusel oleme lahendanud juba mitmeid ülesandeid: kuidas koostada peo eelarvet, kuidas tunda ära, kas perekonnanimi kuulub leedulannale või kuidas arvutada kala pikkuse järgi tema kaalu. Nende ülesannete jaoks on olemas olnud (või me oleme välja mõelnud) algoritmid ehk programmeerimiskeeles kirjapandavad formaalsed juhendid, mis määravad, kuidas programm peab käituma. Küll aga leidub ülesandeid, mida oleks tore programselt lahendada, kuid mille jaoks on "tavapärast" algoritmi väga keeruline kirja panna.
Olgu meie programmi ülesanne lugeda failist pilt ja leida sõnastikust seda pilti kõige paremini kirjeldav sõna. Failist pilti lugedes saab programm teada iga piksli värvikoodi (näiteks kuueteistkümnendsüsteemis). Kuidas leida sõnastikust sõna, mille tähendus vastab kõige paremini nende pikslite poolt kirjeldatud pildile?
[['#474747', '#4c4b4b', '#474545', ... ], ['#494646', '#444141', '#444242', ... ], --> 'kiisu' ['#444242', '#444141', '#444242', ... ], ...]
Mida saaksime pikslitega teha? Näiteks saaks arvutada nende pikslite keskmise värvi. See lahendus võib töötada piiratud maailmas. Näiteks Robotexi robotid võivadki kasutada oma kaamera pildistatud pildi mingi ala keskmist värvi, et tuvastada näiteks, kas nende ees on oranž pall, vastase kollane värav või hoopis hall sein. Kui aga soovime tuvastada, kas foto peal on kiisu või kutsu, siis jääb see meetod hätta. Üldine probleem peitub selles, et üksikud pikslid ei ütle suurt midagi pildi tervikliku sisu kohta. Isegi kui me suudaks kirjeldada algoritmi, mis uurib pildi piksleid ja püüab nende põhjal ennustada, kas pildil on kass, siis ei tuleks kuidagi kõne alla teha seda kõikvõimalike sõnade ja objektide jaoks.
Programm õpib
Kui me ei suuda programmi algoritmi sisse kirjutada tarkusi, kuidas üht objekti teisest eristada, siis äkki suudame kirjutada algoritmi, mille abil saaks programm neid teadmisi ise ammutada olemasolevatest treeningnäidetest. Just sellist algoritmi, mis oskab etteantud sisendite ja väljundite põhjal õppida sooritama mingit teisendust, nimetatakse masinõppe algoritmiks.
Seda, mis masinõppe algoritmi juures määrab, kuidas teisendus sisendist väljundiks tehakse, nimetatakse algoritmi mudeliks (mõnikord ka meetodiks või algoritmiks). Igas mudelis on defineeritud mudeli parameetrid (või kaalud), mis hoiavadki treeningnäidetest õpitud "teadmisi". Need mudeli parameetrid on tavalised ujukomaarvud, mida kasutatakse mõnes valemis mudeli sees.
Järgneb üks väga lihtne masinõppe algoritm, mis oskab näidete põhjal õppida sooritama lihtsaid aritmeetilisi tehteid. Mudeli sisendiks on üks ujukomaarv ja väljundiks üks ujukomaarv. Täpsemalt oskab mudel õppida selgeks mistahes lineaarteisenduse. See tähendab, et sisendist saab väljundi, kasutades valemit, mis sisaldab vaid liitmist-lahutamist ja arvuga (mitte sisendväärtusega) korrutamist-jagamist. Näiteks üks lineaarteisendus on selline:
väljund = (42 * sisend - 4) * 2 + 1
ehk lihtsustatult:
väljund = 84 * sisend - 7
Programm loeb failist treeningandmed.txt
treenimiseks kasutatavad sisend- ja väljundväärtused, treenib nende põhjal mudelit, küsib kasutajalt uue sisendväärtuse ning ennustab sellele vastava väljundi. Failis on igal real üks treeningnäide, kus sisend ja väljund (sellises järjekorras) on eraldatud komaga. Selle mudeli treenimiseks on vaja ainult kahte treeningnäidet, seega failis peab olema vähemalt kaks rida näidetega. Kuna rohkem näiteid pole vaja, siis arvestataksegi ainult kahte esimest rida.
Proovige välja mõelda üks selline valem, koostage treeningandmete fail ja katsetage, kas programm oskab valemi ära arvata. Vajadusel võite malliks võtta eelneva näiteteisenduse kohta loodud faili treeningandmed.txt.
def opi(x_list, y_list): # Modelleerime sõltuvust sisendi ja väljundi vahel # lineaarfunktsiooni (sirge) abil: valjund = a * sisend + b, kus # a ja b on kordajad ehk kaalud ehk mudeli parameetrid. # Kordajate arvutamiseks kasutame sirge võrrandit kahe punktiga. # Kuna sirge on määratud kahe punktiga, siis õppimiseks rakendamegi # ainult kahte esimest näidet isegi siis, kui neid on antud rohkem. a = (y_list[1] - y_list[0]) / (x_list[1] - x_list[0]) b = (y_list[0] * x_list[1] - y_list[1] * x_list[0]) / (x_list[1] - x_list[0]) return [a, b] # Tagastame kordajad listina def ennusta(x, kaalud): # Ennustamiseks on vaja arvutada lineaarfunktsiooni väärtus # kohal x, mis vastab kasutaja sisendile, kasutades õpitud kordajaid. a = kaalud[0] b = kaalud[1] return a * x + b # Loeme treeningandmed failist. f = open('treeningandmed.txt') sisendvaartused = [] valjundvaartused = [] for rida in f: jupid = rida.split(',') sisendvaartused.append(float(jupid[0])) valjundvaartused.append(float(jupid[1])) f.close() # Õpime treeningandmete põhjal selgeks kaalud ehk mudeli parameetrid. kaalud = opi(sisendvaartused, valjundvaartused) # Ennustame uue sisendi väärtust, kasutades õpitud kaale. kasutaja_sisend = float(input('Sisend: ')) ennustus = ennusta(kasutaja_sisend, kaalud) print('Ennustus: ' + str(ennustus))
Selle algoritmi mudeliks on lineaarfunktsioon, millel on kaks treenitavat parameetrit. Mudeli treenimiseks on vaja ka vaid kahte näidet sisendist ja väljundist, sest nagu koolist teate, siis iga sirge määramiseks tasandil on piisav fikseerida kaks erinevat punkti, mida see sirge läbib (vt kahe punktiga määratud sirge võrrand). Täpselt seda seal mudelis tehaksegi: eeldatakse, et sisendi ja väljundi vahel on lineaarne sõltuvus ning joonestatakse kahe antud punkti põhjal sirge. Uuele sisendile arvutatakse väljund selle leitud sirge (funktsiooni) abil.
Masinõpe päris rakendustes
Masinõppe rakendustes kasutatakse sageli palju keerulisemaid sõltuvusi sisendi ja väljundi vahel. Samuti on selle arvelt treenitavaid parameetreid rohkem ning mudelite treenimiseks on vaja väga palju treeningandmeid. Ent siiski on kõik masinõppe rakendused oma põhimõtetelt väga sarnased eelnevale näitele.
Märkus. Tegelikult räägime siin täpsemalt juhendatud masinõppest, kus treenitakse (juhendatakse) algoritm käituma meile sobivalt. On olemas ka juhendamata masinõpe, mille eesmärk on leida sisendandmetest struktuure ilma, et talle oleks midagi ette öeldud selle kohta, mis väljundiks peaks olema.
Pildituvastuse või masintõlke valdkondades on tavaline kasutada mudeleid, millel on kümneid (või isegi sadu) miljoneid treenitavaid parameetreid ning mida treenitakse kümnete miljonite treeningnäidete peal. Samuti ei leidu enamasti parameetrite jaoks valemeid, millega nende väärtust täpselt määrata (n-ö analüütiline lahendus), või need lahendused on liiga keerulised. Sel juhul leitakse parameetrite umbkaudsed väärtused mõne optimeerimisalgoritmiga (nt gradientlaskumine - ingl gradient descent), mis püüab väikeste sammudega liikuda "õigemate" väärtuste poole nii kaua, kuni tulemus sellest paraneb.
Internetti kasutades puutute tõenäoliselt iga päev kokku masinõppega. Kui kasutate Google'i, Facebooki või Apple'i teenuseid, näidatakse teile sisu, mis on kujundatud masinõppe meetodite abiga ning samuti kasutatakse teie käitumist ja andmeid uute masinõppemudelite treenimiseks. Ülesanded, mida masinõppemeetodid lahendavad, võivad tunduda programmeerimise seisukohast hoomamatud ja nende lahendused lausa maagilised. Kuid tegelikult peitub iga sellise programmi taga kavalate matemaatiliste-statistiliste nippidega algoritm, mis suudab õppida teisendusi sisendi ja väljundi vahel. Võibolla on inimese geenides defineeritud sarnane algoritm, mis õpib teisendama meelte abil saadud sensatsioone liikumiseks, rääkimiseks, söömiseks ja programmeerimiseks?
I OSA sisukord |