#004 Slučajni brojevi i njihova uloga u ML
Šta su slučajni brojevi?
U prethodnom primeru naš dataset kreirali smo ručno. Često se pretpostavlja da su sami podaci u mašinskom učenju generisani nekom slučajnom statističkom raspodelom. Uzimajući ovu pretpostavku u obzir, postaje lako da generišemo klase podataka koje sadrže proizvoljno veliki broj elemenata.
Ukoliko do sada niste pohađali kurseve verovatnoće, zamislite bubanj za loto izvlačenje. To je generator slučajnih celih brojeva od 1 do 39 (ili već kako je definisano).
Prva raspodela koja će za nas biti interesantna je uniformna raspodela. Često se definiše na intervalu [0, 1], a bilo koji broj može u slučaju uniformne raspodele da bude generisan sa istom verovatnoćom. Za uniformnu raspodelu možemo posmatrati i ekvidistantne intervale. Ukoliko interval podelimo na deset delova, a generišemo 1000 brojeva uniformne raspodele očekuje se da će približno po 100 brojeva biti iz svakog intervala. Hajde da ovo proverimo u Python-u i da se upoznamo sa korišćenjem histograma.
Dobijeni histogram je podeljen na 5 intervala (parametar bins=5). Vidimo da je broj elemenata u ovom intervalu približno 200. Tačni brojevi, i tačne granice ovih intervala su nam ispisane odmah iznad samog histograma. Recimo, u prvom intervalu se nalaze 210 generisanih elemenata, u drugom 212, trećem 208 i tako dalje.
Druga, svakako i najvažnija raspodela, je Gausova (normalna) raspodela. To je i kriva koja se nalazila na novčanici od 10 nemačkih maraka.
Ona je definisana sa dva parametra, čije su standardne vrednosti: 0-srednja vrednost, i 1 – standardno odstupanje (kvadratni koren varijanse-odstupanja od srednje vrednosti).
Na slici je prikazan primer histograma gausovske funkcije. Vidimo da je verovatnoća mnogo veća u centralnom delu, dok ostali intervali imaju sve manje i manje verovatnoće da budu zastupljeni.
Jednostavnim množenjem elemenata raspodele možemo modifikovati histogram. Ukoliko je broj kojim množimo sve elemente veći od 1 dobićemo ,,širu” raspodelu, a ukoliko je manji od 1 ,,užu”. Dodatno, ukoliko svaki broj saberemo sa konstantom, raspodelu možemo translirati.
Sada možemo da pravimo razne vrste 1D Gausovskih raspodela i da ih dodeljujemo odgovarajućim koordinatama x1 i x2. Na taj način možemo definisati podatke koji podsećaju na krugove, odnosno elipse. Na engleskom ovakve oblike zovemo ,,blob”.
Sledeći kod nam pokazuje kako možemo da napravimo dve klase podataka koje dolaze od različitih raspodela. Primeri su namerno izabrani tako da dolazi do preklapanja. Stoga, naše klase neće biti linearno separabilne, jer je to čest slučaj u praksi (ako može ovo da se tvrdi).
U prethodnom delu koda korišćene su naredbe np.hstack() i np.vstack() koje dva nd-niza spajaju po horizontali, odnosno vertikali. Prvo su nizovi x10 i x11 dimenzija 1000 x 1, pretvoreni u matricu dimenzija 1000 x 2. Nadalje je vektor X, dobijen spajanjem matrica X1 i X2 kreirajući na kraju matricu koja ima 2000 x 2 elementa. Za ovaj skup od 2000 elemenata potrebno je definisati vektor pripadnosti klasama y. Nadalje, koristićemo kod iz bloga #003 gde je primenjen LDA klasifikator.
Trening vs test skup
Kada dizajniramo ML (Machine Learning) modele potrebno je izvršiti optimizaciju parametara. Ova optimimzacija vrši se na trening skupu. Sa druge strane, testiranje i provera tačnosti samog ML modela obavlja se na test skupu. Dakle, veoma je važno da test skup nikada ne bude prisutan u toku samog treniranja. Za naš prethodni primer kreiraćemo test skup koristeći identične raspodele. Generisaćemo još po 100 elemenata iz obe klase tako što ćemo prethodni kod neznatno modifikovati.
Rezultat predikcije predstavljen je uz pomoć naredbe stem(). Ona nam jasno pokazuje da je od ukupno 200 elemenata veliki broj uspešno klasifikovan. Samo nekoliko elemenata iz klase 1 je klasifikovano da pripadaju klasi 2. Takođe, 1 element klase 2 klasifikovan je kao da pripada klasi 1.
Ovo možemo i eksplicitno pokazati sledećim kodom. Ukoliko proverimo jednakost između predviđenih i stvarnih vrednosti dobićemo niz koji proverava ,,element po element” i generiše niz elemenata tipa boolean. Ukoliko je došlo do tačnog predviđanja imaćemo vrednost True, u suprotnom False. Na ovaj niz od 200 boolean elemenata primenićemo jedan trik. Naime, sve ćemo ih sabrati. U tom slučaju Python će izvršiti promenu tipa podataka iz boolean-a u racionalne/cele brojeve tako da se True pretvori u 1 (logička jedinica), a False u 0 (logička nula). Tada će nam naredba sum() prikazati koliko je elemenata tačno klasifikovano. Ukoliko ovaj broj podelimo sa ukupnim brojem elemenata test niza (200) dobićemo tačnost klasifikacije izraženu u procentima. Ostale mere o oceni performanse rada klasifikatora analiziraćemo u narednim blog postovima.
Vizuelizacija linearne diskriminacione funkcije
Zanimljivo je na kraju pogledati i same koeficijente koje je LDA algoritam izračunao.
Oni se nalaze u promenljivama clf.coef_ i clf.intercept_. Možemo nacrtati samu linearnu funkciju, a možemo i okolinu tačaka oko naših blob – ova diskretizovati u 2D grid mrežu i svaku od tih tačaka koristiti kao ulazni podatak u naredbi .predict(). Sledeći kod ilustruje ovo.