Č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 scipy as sp
import matplotlib.pyplot as plt
%pylab inline
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 [33]:
from collections import Counter
class VotingClassifierDIY(object):
SCHEME_COUNTING = "counting"
SCHEME_AVERAGING = "averaging"
def __init__(self, clfs, voting_scheme=SCHEME_COUNTING):
self.clfs = clfs
self.voting_scheme = voting_scheme
def fit(self, X, y):
[ clf.fit(X,y) for clf in self.clfs ]
def predict_proba(self, X):
if self.voting_scheme == self.SCHEME_AVERAGING:
pred = [ clf.predict_proba(X) for clf in self.clfs ]
pred_avg = []
for i in range(0, len(pred[0])):
prob = [ p[i] for p in pred ]
pred_avg.append(mean(prod, axis = 0))
return pred_avg
else:
raise Exception("Nemoze")
def predict(self, X):
if self.voting_scheme == self.SCHEME_COUNTING:
pred_cnt = []; pred = []; predd = [];
for i in range(0, len(self.clfs)):
predd.append(self.clfs[i].predict(X))
for i in range(0, shape(predd)[1]): # 0 - 1000
for j in range(0, len(predd)): # 0 - 3
pred.append(predd[j][i])
for i in range(0, len(pred), 3):
pred_cnt.append((Counter(pred[i:i+3]).most_common()[0])[0])
return pred_cnt
elif self.voting_scheme == self.SCHEME_AVERAGING:
avg = self.predict_proba(X)
pred_avg = []
for i in range(0, len(avg)):
pred_avg.append(argmax(avg[i]))
return pred_avg
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 [34]:
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 [35]:
Logisticka1 = LogisticRegression(multi_class = 'auto', solver = 'lbfgs', C = 1, max_iter = 1000).fit(X_voting, y_voting)
Logisticka2 = LogisticRegression(multi_class = 'auto', solver = 'lbfgs', C = 100, max_iter = 1000).fit(X_voting, y_voting)
Logisticka3 = LogisticRegression(multi_class = 'auto', solver = 'lbfgs', C = 1000, max_iter = 1000).fit(X_voting, y_voting)
clfs = [Logisticka1, Logisticka2, Logisticka3]
moj = VotingClassifierDIY(clfs = clfs)
njihov = VotingClassifier(estimators= [('lr1', Logisticka1), ('lr2', Logisticka2), ('lr3', Logisticka3)])
a = moj.fit(X_voting, y_voting)
a = njihov.fit(X_voting, y_voting)
moj.voting_scheme = moj.SCHEME_COUNTING
print(moj.voting_scheme)
njihov.voting = 'hard'
print(moj.predict(X_voting))
print(njihov.predict(X_voting).all())
moj.voting_scheme = moj.SCHEME_AVERAGING
njihov.voting = 'soft'
print(moj.predict_proba(X_voting) == njihov.predict_proba(X_voting).all)
Q: Kada je prebrojavanje bolje od usrednjavanja? Zašto? A obratno?
Q: Bi li se ovakav algoritam mogao primijeniti na regresiju? Kako?
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 [111]:
from sklearn.cross_validation 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 [2]:
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import zero_one_loss
# 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 [3]:
from sklearn.ensemble import RandomForestClassifier
# 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?
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 [114]:
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)
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 [4]:
from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier
# Vaš kôd ovdje...
Q: Kako AdaBoost radi? Ovise li izlazi pojedinih osnovnih modela o onima drugih?
Q: Je li AdaBoost linearan klasifikator? Pojasnite.
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 [116]:
from sklearn.cross_validation 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 [5]:
from sklearn.metrics import zero_one_loss
# Vaš kôd ovdje...
Q: Može li uopće doći do prenaučenosti pri korištenju boosting-algoritama?
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 [6]:
# 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?
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 [7]:
# Vaš kôd ovdje...
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 [8]:
# Vaš kôd ovdje...
Q: Koja vrijednost odgovara ML-procjenama i zašto?
In [9]:
# Vaš kôd ovdje...
Q: Koja je ML-procjena za $\mu$ i što je problem s takvom procjenom u ovome slučaju?
In [11]:
# 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?
In [12]:
# Vaš kôd ovdje...
Q: Koje vrijednosti odgovaraju MAP-procjeni za $\mu$? Usporedite ih sa ML-procjenama.
In [13]:
# Vaš kôd ovdje...
Pročitajte ove upute o učitavanju oglednih skupova podataka u SciPy. 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 [14]:
from sklearn.datasets import load_iris
import itertools as it
# Vaš kôd ovdje...
In [16]:
# Vaš kôd ovdje...
In [17]:
# Vaš kôd ovdje...
Q: Možete li, na temelju dobivenih log-izglednosti, zaključiti koja se značajka najbolje pokorava normalnoj distribuciji?
Proučite funkciju pearsonr
za izračun Pearsonovog koeficijenta korelacije. Izračunajte koeficijente korelacije između svih četiri značajki u skupu Iris.
In [18]:
from scipy.stats import pearsonr
# Vaš kôd ovdje...
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 [19]:
# Vaš kôd ovdje...