Sveučilište u Zagrebu
Fakultet elektrotehnike i računarstva

Strojno učenje 2019/2020

http://www.fer.unizg.hr/predmet/su


Laboratorijska vježba 4: Ansambli i procjena parametara

Verzija: 0.4
Zadnji put ažurirano: 27. rujna 2019.

(c) 2015-2019 Jan Šnajder, Domagoj Alagić

Objavljeno: 30. rujna 2019.
Rok za predaju: 16. prosinca 2019. u 07:00h


Upute

Četvrta laboratorijska vježba sastoji se od četiri zadatka. Kako bi kvalitetnije, ali i na manje zamoran način usvojili gradivo ovog kolegija, potrudili smo se uključiti tri vrste zadataka: 1) implementacija manjih algoritama, modela ili postupaka; 2) eksperimenti s raznim modelima te njihovim hiperparametrima, te 3) primjena modela na (stvarnim) podatcima. Ovim zadatcima pokrivamo dvije paradigme učenja: učenje izgradnjom (engl. learning by building) i učenje eksperimentiranjem (engl. learning by experimenting).

U nastavku slijedite upute navedene u ćelijama s tekstom. Rješavanje vježbe svodi se na dopunjavanje ove bilježnice: umetanja ćelije ili više njih ispod teksta zadatka, pisanja odgovarajućeg kôda te evaluiranja ćelija.

Osigurajte da u potpunosti razumijete kôd koji ste napisali. Kod predaje vježbe, morate biti u stanju na zahtjev asistenta (ili demonstratora) preinačiti i ponovno evaluirati Vaš kôd. Nadalje, morate razumjeti teorijske osnove onoga što radite, u okvirima onoga što smo obradili na predavanju. Ispod nekih zadataka možete naći i pitanja koja služe kao smjernice za bolje razumijevanje gradiva (nemojte pisati odgovore na pitanja u bilježnicu). Stoga se nemojte ograničiti samo na to da riješite zadatak, nego slobodno eksperimentirajte. To upravo i jest svrha ovih vježbi.

Vježbe trebate raditi samostalno. Možete se konzultirati s drugima o načelnom načinu rješavanja, ali u konačnici morate sami odraditi vježbu. U protivnome vježba nema smisla.


In [1]:
# Učitaj osnovne biblioteke...
import sklearn
import mlutils
import numpy as np
import matplotlib.pyplot as plt
%pylab inline


Populating the interactive namespace from numpy and matplotlib

1. Ansambli (glasovanje)

(a)

Vaš je zadatak napisati razred VotingClassifierDIY koji implementira glasački ansambl. Konstruktor razreda ima dva parametra: clfs koji predstavlja listu klasifikatora (objekata iz paketa sklearn) i voting_scheme koji označava radi li se o glasovanju prebrojavanjem (SCHEME_COUNTING) ili usrednjavanjem (SCHEME_AVERAGING). Glasovanje prebrojavanjem jednostavno vraća najčešću oznaku klase, dok glasovanje usrednjavanjem uprosječuje pouzdanosti klasifikacije u neku klasu (po svim klasifikatorima) te vraća onu s najvećom pouzdanošću. Primijetite da svi klasifikatori imaju jednake težine. O komplementarnosti klasifikatora vodimo računa tako da koristimo jednake klasifikatore s različitim hiperparametrima.

Razred sadržava metode fit(X, y) za učenje ansambla i dvije metode za predikciju: predict(X) i predict_proba(X). Prva vraća predviđene oznake klasa, a druga vjerojatnosti pripadanja svakoj od klasa za svaki od danih primjera iz X.

NB: Jedan od razreda koji bi Vam mogao biti koristan jest collections.Counter. Također vrijedi i za funkcije numpy.argmax i numpy.dstack.


In [1]:
from collections import Counter

class VotingClassifierDIY(object):
    
    SCHEME_COUNTING = "counting"
    SCHEME_AVERAGING = "averaging"
    
    def __init__(self, clfs, voting_scheme=SCHEME_COUNTING):
        # Vaš kôd ovdje
        pass
        
    def fit(self, X, y):
        # Vaš kôd ovdje
        pass
    
    def predict_proba(self, X):
        # Vaš kôd ovdje
        pass
    
    def predict(self, X):
        # Vaš kôd ovdje
        pass

(b)

Uvjerite se da Vaša implementacija radi jednako onoj u razredu ensemble.VotingClassifier, i to pri oba načina glasovanja (parametar voting). Parametar weights ostavite na pretpostavljenoj vrijednosti. Za ovu provjeru koristite tri klasifikatora logističke regresije s različitom stopom regularizacije i brojem iteracija. Koristite skup podataka dan u nastavku. Ekvivalentnost implementacije najlakše je provjeriti usporedbom izlaza funkcije predict (kod prebrojavanja) i funkcije predict_proba (kod usrednjavanja).

NB: Ne koristimo SVM jer njegova ugrađena (probabilistička) implementacija nije posve deterministička, što bi onemogućilo robusnu provjeru Vaše implementacije.


In [3]:
from sklearn.datasets import make_classification
from sklearn.ensemble import VotingClassifier
from sklearn.linear_model import LogisticRegression

X_voting, y_voting = make_classification(n_samples=1000, n_features=4, n_redundant=0, n_informative=3, n_classes=3, n_clusters_per_class=2)

In [2]:
# Vaš kôd ovdje

Q: Kada je prebrojavanje bolje od usrednjavanja? Zašto? A obratno?
Q: Bi li se ovakav algoritam mogao primijeniti na regresiju? Kako?

2. Ansambli (bagging)

U ovom zadatku ćete isprobati tipičnog predstavnika bagging-algoritma, algoritam slučajnih šuma. Pitanje na koje želimo odgovoriti jest kako se ovakvi algoritmi nose s prenaučenošću, odnosno, smanjuje li bagging varijancu modela.

Eksperiment ćete provesti na danom skupu podataka:


In [15]:
from sklearn.model_selection import train_test_split

X_bag, y_bag = make_classification(n_samples=1000, n_features=20, n_redundant=1, n_informative=17, n_classes=3, n_clusters_per_class=2)
X_bag_train, X_bag_test, y_bag_train, y_bag_test = train_test_split(X_bag, y_bag, train_size=0.7, random_state=69)

Razred koji implementira stablo odluke jest tree.DecisionTreeClassifier. Prvo naučite stablo odluke (engl. decision tree) na skupu za učenje, ali tako da je taj model presložen. To možete postići tako da povećate najveću moguću dubinu stabla (parametar max_depth). Ispišite pogrešku na skupu za ispitivanje (pogrešku 0-1; pogledajte paket metrics).


In [3]:
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import zero_one_loss

In [4]:
# Vaš kôd ovdje

Sada isprobajte algoritam slučajnih šuma (dostupan u razredu ensemble.RandomForestClassifier) za različit broj stabala $L \in [1, 30]$. Iscrtajte pogrešku na skupu za učenje i na skupu za ispitivanje u ovisnosti o tom hiperparametru. Ispišite najmanju pogrešku na skupu za ispitivanje.


In [5]:
from sklearn.ensemble import RandomForestClassifier

In [6]:
# Vaš kôd ovdje

Q: Što možete zaključiti iz ovih grafikona?
Q: Kako bagging postiže diverzifikaciju pojedinačnih osnovnih modela?
Q: Koristi li ovaj algoritam složeni ili jednostavni osnovni model? Zašto?

3. Ansambli (boosting)

U ovom zadatku pogledat ćemo klasifikacijski algoritam AdaBoost, koji je implementiran u razredu ensemble.AdaBoostClassifier. Ovaj algoritam tipičan je predstavnik boosting-algoritama.

Najprije ćemo generirati eksperimentalni skup podataka koristeći datasets.make_circles. Ova funkcija stvara dvodimenzijski klasifikacijski problem u kojem su dva razreda podataka raspoređena u obliku kružnica, tako da je jedan razred unutar drugog.


In [19]:
from sklearn.datasets import make_circles

circ_X, circ_y = make_circles(n_samples=400, noise=0.1, factor=0.4)
mlutils.plot_2d_clf_problem(circ_X, circ_y)


(a)

Boosting, kao vrsta ansambla, također se temelji na kombinaciji više klasifikatora s ciljem boljih prediktivnih sposobnosti. Međutim, ono što ovakav tip ansambla čini zanimljivim jest to da za osnovni klasifikator traži slabi klasifikator (engl. weak classifier), odnosno klasifikator koji radi tek malo bolje od nasumičnog pogađanja. Često korišteni klasifikator za tu svrhu jest panj odluke (engl. decision stump), koji radi predikciju na temelju samo jedne značajke ulaznih primjera. Panj odluke specijalizacija je stabla odluke (engl. decision tree) koje smo već spomenuli. Panj odluke stablo je dubine 1. Stabla odluke implementirana su u razredu tree.DecisionTreeClassifier.

Radi ilustracije, naučite ansambl (AdaBoost) koristeći panj odluke kao osnovni klasifikator, ali pritom isprobavajući različit broj klasifikatora u ansamblu iz skupa $L \in \{1, 2, 3, 50\}$. Prikažite decizijske granice na danom skupu podataka za svaku od vrijednosti korištenjem pomoćne funkcije mlutils.plot_2d_clf_problem.

NB: Još jedan dokaz da hrvatska terminologija zaista može biti smiješna. :)


In [7]:
from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier

In [8]:
# Vaš kôd ovdje

Q: Kako AdaBoost radi? Ovise li izlazi pojedinih osnovnih modela o onima drugih?
Q: Je li AdaBoost linearan klasifikator? Pojasnite.

(b)

Kao što je i za očekivati, broj klasifikatora $L$ u ansamblu predstavlja hiperparametar algoritma AdaBoost. U ovom zadatku proučit ćete kako on utječe na generalizacijsku sposobnost Vašeg ansambla. Ponovno, koristite panj odluke kao osnovni klasifikator.

Poslužite se skupom podataka koji je dan niže.


In [23]:
from sklearn.model_selection import train_test_split

X_boost, y_boost = make_classification(n_samples=1000, n_features=20, n_redundant=0, n_informative=18, n_classes=3, n_clusters_per_class=1)
X_boost_train, X_boost_test, y_boost_train, y_boost_test = train_test_split(X_boost, y_boost, train_size=0.7, random_state=69)

Iscrtajte krivulje pogrešaka na skupu za učenje i ispitivanje u ovisnosti o hiperparametru $L \in [1,80]$. Koristite pogrešku 0-1 iz paketa metrics. Ispišite najmanju ostvarenu pogrešku na skupu za ispitivanje, te pripadajuću vrijednost hiperparametra $L$.


In [9]:
# Vaš kôd ovdje

Q: Može li uopće doći do prenaučenosti pri korištenju boosting-algoritama?

(c)

Kao što je rečeno na početku, boosting-algoritmi traže slabe klasifikatore kako bi bili najefikasniji što mogu biti. Međutim, kako se takav ansambl mjeri s jednim jakim klasifikatorom (engl. strong classifier)? To ćemo isprobati na istom primjeru, ali korištenjem jednog optimalno naučenog stabla odluke.

Ispišite pogrešku ispitivanja optimalnog stabla odluke. Glavni hiperparametar stabala odluka jest njihova maksimalna dubina $d$ (parametar max_depth). Iscrtajte krivulje pogrešaka na skupu za učenje i ispitivanje u ovisnosti o dubini stabla $d \in [1,20]$.


In [10]:
# Vaš kôd ovdje

Q: Isplati li se koristiti ansambl u obliku boostinga? Idu li grafikoni tome u prilog?
Q: Koja je prednost boostinga nad korištenjem jednog jakog klasifikatora?

4. Procjena maksimalne izglednosti i procjena maksimalne aposteriorne vjerojatnosti

(a)

Definirajte funkciju izglednosti $\mathcal{L}(\mu|\mathcal{D})$ za skup $\mathcal{D}=\{x^{(i)}\}_{i=1}^N$ Bernoullijevih varijabli. Neka od $N$ varijabli njih $m$ ima vrijednost 1 (npr. od $N$ bacanja novčića, $m$ puta smo dobili glavu). Definirajte funkciju izglednosti tako da je parametrizirana s $N$ i $m$, dakle definirajte funkciju $\mathcal{L}(\mu|N,m)$.


In [11]:
# Vaš kôd ovdje

(b)

Prikažite funkciju $\mathcal{L}(\mu|N,m)$ za (1) $N=10$ i $m=1,2,5,9$ te za (2) $N=100$ i $m=1,10,50,90$ (dva zasebna grafikona).


In [12]:
# Vaš kôd ovdje

Q: Koja vrijednost odgovara ML-procjenama i zašto?

(c)

Prikažite funkciju $\mathcal{L}(\mu|N,m)$ za $N=10$ i $m=\{0,9\}$.


In [13]:
# Vaš kôd ovdje

Q: Koja je ML-procjena za $\mu$ i što je problem s takvom procjenom u ovome slučaju?

(d)

Prikažite beta-distribuciju $B(\mu|\alpha,\beta)$ za različite kombinacije parametara $\alpha$ i $\beta$, uključivo $\alpha=\beta=1$ te $\alpha=\beta=2$.


In [14]:
from scipy.stats import beta

In [15]:
# Vaš kôd ovdje

Q: Koje parametere biste odabrali za modeliranje apriornog znanja o parametru $\mu$ za novčić za koji mislite da je "donekle pravedan, ali malo češće pada na glavu"? Koje biste parametre odabrali za novčić za koji držite da je posve pravedan? Zašto uopće koristimo beta-distribuciju, a ne neku drugu?

(e)

Definirajte funkciju za izračun zajedničke vjerojatnosti $P(\mu,\mathcal{D}) = P(\mathcal{D}|\mu) \cdot P(\mu|\alpha,\beta)$ te prikažite tu funkciju za $N=10$ i $m=9$ i nekolicinu kombinacija parametara $\alpha$ i $\beta$.


In [16]:
# Vaš kôd ovdje

Q: Koje vrijednosti odgovaraju MAP-procjeni za $\mu$? Usporedite ih sa ML-procjenama.

(f)

Za $N=10$ i $m=1$, na jednome grafikonu prikažite sve tri distribucije: $P(\mu,\mathcal{D})$, $P(\mu|\alpha,\beta)$ i $\mathcal{L}(\mu|\mathcal{D})$.


In [17]:
# Vaš kôd ovdje

(g)

Pročitajte ove upute o učitavanju oglednih skupova podataka. Učitajte skup podataka Iris. Taj skup sadrži $n=4$ značajke i $K=3$ klase. Odaberite jednu klasu i odaberite sve primjere iz te klase, dok ostale primjere zanemarite (u nastavku radite isključivo s primjerima iz te jedne klase). Vizualizirajte podatke tako da načinite 2D-prikaze za svaki par značajki (šest grafikona; za prikaz je najjednostavnije koristiti funkciju scatter).

NB: Mogla bi Vam dobro dući funkcija itertools.combinations.


In [18]:
from sklearn.datasets import load_iris
import itertools as it

In [19]:
# Vaš kôd ovdje

(h)

Pogledajte opis modul stats te proučite funkciju norm. Implementirajte funkciju log-izglednosti za parametre $\mu$ i $\sigma^2$ normalne distribucije.


In [20]:
from scipy.stats import norm

In [21]:
# Vaš kôd ovdje

(i)

Izračunajte ML-procjene za $(\mu, \sigma^2)$ za svaku od $n=4$ značajki iz skupa Iris. Ispišite log-izglednosti tih ML-procjena.


In [22]:
# Vaš kôd ovdje

Q: Možete li, na temelju dobivenih log-izglednosti, zaključiti koja se značajka najbolje pokorava normalnoj distribuciji?

(j)

Proučite funkciju pearsonr za izračun Pearsonovog koeficijenta korelacije. Izračunajte koeficijente korelacije između svih četiri značajki u skupu Iris.


In [23]:
from scipy.stats import pearsonr

In [24]:
# Vaš kôd ovdje

(k)

Proučite funkciju cov te izračunajte ML-procjenu za kovarijacijsku matricu za skup Iris. Usporedite pristranu i nepristranu procjenu. Pokažite da se razlika (srednja apsolutna i kvadratna) smanjuje s brojem primjera (npr. isprobajte za $N/4$ i $N/2$ i $N$ primjera).


In [25]:
# Vaš kôd ovdje