Prva laboratorijska vježba sastoji se od šest zadataka. 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 [2]:
# Učitaj osnovne biblioteke...
import numpy as np
import sklearn
import mlutils
import matplotlib.pyplot as plt
%pylab inline
U prvoj laboratorijskoj vježbi koristili smo model linearne regresije za, naravno, regresiju. Međutim, model linearne regresije može se koristiti i za klasifikaciju. Iako zvuči pomalo kontraintuitivno, zapravo je dosta jednostavno. Naime, cilj je naučiti funkciju $f(\mathbf{x})$ koja za negativne primjere predviđa vrijednost $1$, dok za pozitivne primjere predviđa vrijednost $0$. U tom slučaju, funkcija $f(\mathbf{x})=0.5$ predstavlja granicu između klasa, tj. primjeri za koje vrijedi $h(\mathbf{x})\geq 0.5$ klasificiraju se kao pozitivni, dok se ostali klasificiraju kao negativni.
Klasifikacija pomoću linearne regresije implementirana je u razredu RidgeClassifier
. U sljedećim podzadatcima istrenirajte taj model na danim podatcima i prikažite dobivenu granicu između klasa. Pritom isključite regularizaciju ($\alpha = 0$, odnosno alpha=0
). Također i ispišite točnost vašeg klasifikacijskog modela (smijete koristiti funkciju metrics.accuracy_score
). Skupove podataka vizualizirajte korištenjem pomoćne funkcije plot_clf_problem(X, y, h=None)
koja je dostupna u pomoćnom paketu mlutils
(datoteku mlutils.py
možete preuzeti sa stranice kolegija). X
i y
predstavljaju ulazne primjere i oznake, dok h
predstavlja funkciju predikcije modela (npr. model.predict
).
U ovom zadatku cilj je razmotriti kako se klasifikacijski model linearne regresije ponaša na linearno odvojim i neodvojivim podatcima.
In [3]:
from sklearn.linear_model import LinearRegression, RidgeClassifier
from sklearn.metrics import accuracy_score
In [4]:
seven_X = np.array([[2,1], [2,3], [1,2], [3,2], [5,2], [5,4], [6,3]])
seven_y = np.array([1, 1, 1, 1, 0, 0, 0])
In [7]:
# Vaš kôd ovdje
Kako bi se uvjerili da se u isprobanoj implementaciji ne radi o ničemu doli o običnoj linearnoj regresiji, napišite kôd koji dolazi do jednakog rješenja korištenjem isključivo razreda LinearRegression
. Funkciju za predikciju, koju predajete kao treći argument h
funkciji plot_2d_clf_problem
, možete definirati lambda-izrazom: lambda x : model.predict(x) >= 0.5
.
In [8]:
# Vaš kôd ovdje
Q: Kako bi bila definirana granica između klasa ako bismo koristili oznake klasa $-1$ i $1$ umjesto $0$ i $1$?
Probajte isto na linearno odvojivom skupu podataka outlier
($N=8$):
In [9]:
outlier_X = np.append(seven_X, [[12,8]], axis=0)
outlier_y = np.append(seven_y, 0)
In [10]:
# Vaš kôd ovdje
Q: Zašto model ne ostvaruje potpunu točnost iako su podatci linearno odvojivi?
Završno, probajte isto na linearno neodvojivom skupu podataka unsep
($N=8$):
In [11]:
unsep_X = np.append(seven_X, [[2,2]], axis=0)
unsep_y = np.append(seven_y, 0)
In [12]:
# Vaš kôd ovdje
Q: Očito je zašto model nije u mogućnosti postići potpunu točnost na ovom skupu podataka. Međutim, smatrate li da je problem u modelu ili u podacima? Argumentirajte svoj stav.
Postoji više načina kako se binarni klasifikatori mogu se upotrijebiti za višeklasnu klasifikaciju. Najčešće se koristi shema tzv. jedan-naspram-ostali (engl. one-vs-rest, OVR), u kojoj se trenira po jedan klasifikator $h_j$ za svaku od $K$ klasa. Svaki klasifikator $h_j$ trenira se da razdvaja primjere klase $j$ od primjera svih drugih klasa, a primjer se klasificira u klasu $j$ za koju je $h_j(\mathbf{x})$ maksimalan.
Pomoću funkcije datasets.make_classification
generirajte slučajan dvodimenzijski skup podataka od tri klase i prikažite ga koristeći funkciju plot_2d_clf_problem
. Radi jednostavnosti, pretpostavite da nema redundantnih značajki te da je svaka od klasa "zbijena" upravo u jednu grupu.
In [13]:
from sklearn.datasets import make_classification
In [14]:
# Vaš kôd ovdje
Trenirajte tri binarna klasifikatora, $h_1$, $h_2$ i $h_3$ te prikažite granice između klasa (tri grafikona). Zatim definirajte $h(\mathbf{x})=\mathrm{argmax}_j h_j(\mathbf{x})$ (napišite svoju funkciju predict
koja to radi) i prikažite granice između klasa za taj model. Zatim se uvjerite da biste identičan rezultat dobili izravno primjenom modela RidgeClassifier
, budući da taj model za višeklasan problem zapravo interno implementira shemu jedan-naspram-ostali.
Q: Alternativna shema jest ona zvana jedan-naspram-jedan (engl, one-vs-one, OVO). Koja je prednost sheme OVR nad shemom OVO? A obratno?
In [15]:
# Vaš kôd ovdje
Ovaj zadatak bavi se probabilističkim diskriminativnim modelom, logističkom regresijom, koja je, unatoč nazivu, klasifikacijski model.
Logistička regresija tipičan je predstavnik tzv. poopćenih linearnih modela koji su oblika: $h(\mathbf{x})=f(\mathbf{w}^\intercal\tilde{\mathbf{x}})$. Logistička funkcija za funkciju $f$ koristi tzv. logističku (sigmoidalnu) funkciju $\sigma (x) = \frac{1}{1 + \textit{exp}(-x)}$.
Definirajte logističku (sigmoidalnu) funkciju $\mathrm{sigm}(x)=\frac{1}{1+\exp(-\alpha x)}$ i prikažite je za $\alpha\in\{1,2,4\}$.
In [16]:
# Vaš kôd ovdje
Q: Zašto je sigmoidalna funkcija prikladan izbor za aktivacijsku funkciju poopćenoga linearnog modela? </br>
Q: Kakav utjecaj ima faktor $\alpha$ na oblik sigmoide? Što to znači za model logističke regresije (tj. kako izlaz modela ovisi o normi vektora težina $\mathbf{w}$)?
Implementirajte funkciju
lr_train(X, y, eta=0.01, max_iter=2000, alpha=0, epsilon=0.0001, trace=False)
za treniranje modela logističke regresije gradijentnim spustom (batch izvedba). Funkcija uzima označeni skup primjera za učenje (matrica primjera X
i vektor oznaka y
) te vraća $(n+1)$-dimenzijski vektor težina tipa ndarray
. Ako je trace=True
, funkcija dodatno vraća listu (ili matricu) vektora težina $\mathbf{w}^0,\mathbf{w}^1,\dots,\mathbf{w}^k$ generiranih kroz sve iteracije optimizacije, od 0 do $k$. Optimizaciju treba provoditi dok se ne dosegne max_iter
iteracija, ili kada razlika u pogrešci unakrsne entropije između dviju iteracija padne ispod vrijednosti epsilon
. Parametar alpha
predstavlja faktor L2-regularizacije.
Preporučamo definiranje pomoćne funkcije lr_h(x,w)
koja daje predikciju za primjer x
uz zadane težine w
. Također, preporučamo i funkciju cross_entropy_error(X,y,w)
koja izračunava pogrešku unakrsne entropije modela na označenom skupu (X,y)
uz te iste težine.
NB: Obratite pozornost na to da je način kako su definirane oznake ($\{+1,-1\}$ ili $\{1,0\}$) kompatibilan s izračunom funkcije gubitka u optimizacijskome algoritmu.
In [17]:
from numpy import linalg
def lr_h(x, w):
# Vaš kôd ovdje
pass
def cross_entropy_error(X, y, w):
# Vaš kôd ovdje
pass
def lr_train(X, y, eta=0.01, max_iter=2000, trace=False, alpha=0, epsilon=0.000001):
# Vaš kôd ovdje
pass
Koristeći funkciju lr_train
, trenirajte model logističke regresije na skupu seven
, prikažite dobivenu granicu između klasa te izračunajte pogrešku unakrsne entropije.
NB: Pripazite da modelu date dovoljan broj iteracija.
In [19]:
# Vaš kôd ovdje
Q: Koji kriterij zaustavljanja je aktiviran?
Q: Zašto dobivena pogreška unakrsne entropije nije jednaka nuli?
Q: Kako biste utvrdili da je optimizacijski postupak doista pronašao hipotezu koja minimizira pogrešku učenja? O čemu to ovisi?
Q: Na koji način biste preinačili kôd ako biste htjeli da se optimizacija izvodi stohastičkim gradijentnim spustom (online learning)?
Prikažite na jednom grafikonu pogrešku unakrsne entropije (očekivanje logističkog gubitka) i pogrešku klasifikacije (očekivanje gubitka 0-1) na skupu seven
kroz iteracije optimizacijskog postupka. Koristite trag težina funkcije lr_train
iz zadatka (b) (opcija trace=True
). Na drugom grafikonu prikažite pogrešku unakrsne entropije kao funkciju broja iteracija za različite stope učenja, $\eta\in\{0.005,0.01,0.05,0.1\}$.
In [20]:
# Vaš kôd ovdje
Q: Zašto je pogreška unakrsne entropije veća od pogreške klasifikacije? Je li to uvijek slučaj kod logističke regresije i zašto?
Q: Koju stopu učenja $\eta$ biste odabrali i zašto?
Upoznajte se s klasom linear_model.LogisticRegression
koja implementira logističku regresiju. Usporedite rezultat modela na skupu seven
s rezultatom koji dobivate pomoću vlastite implementacije algoritma.
NB: Kako ugrađena implementacija koristi naprednije verzije optimizacije funkcije, vrlo je vjerojatno da Vam se rješenja neće poklapati, ali generalne performanse modela bi trebale. Ponovno, pripazite na broj iteracija i snagu regularizacije.
In [21]:
from sklearn.linear_model import LogisticRegression
In [22]:
# Vaš kôd ovdje
Koristeći ugrađenu implementaciju logističke regresije, provjerite kako se logistička regresija nosi s vrijednostima koje odskaču. Iskoristite skup outlier
iz prvog zadatka. Prikažite granicu između klasa.
Q: Zašto se rezultat razlikuje od onog koji je dobio model klasifikacije linearnom regresijom iz prvog zadatka?
In [23]:
# Vaš kôd ovdje
Trenirajte model logističke regresije na skupu seven
te na dva odvojena grafikona prikažite, kroz iteracije optimizacijskoga algoritma, (1) izlaz modela $h(\mathbf{x})$ za svih sedam primjera te (2) vrijednosti težina $w_0$, $w_1$, $w_2$.
In [24]:
# Vaš kôd ovdje
Ponovite eksperiment iz podzadatka (b) koristeći linearno neodvojiv skup podataka unsep
iz prvog zadatka.
Q: Usporedite grafikone za slučaj linearno odvojivih i linearno neodvojivih primjera te komentirajte razliku.
In [25]:
# Vaš kôd ovdje
Trenirajte model logističke regresije na skupu seven
s različitim faktorima L2-regularizacije, $\alpha\in\{0,1,10,100\}$. Prikažite na dva odvojena grafikona (1) pogrešku unakrsne entropije te (2) L2-normu vektora $\mathbf{w}$ kroz iteracije optimizacijskog algoritma.
Q: Jesu li izgledi krivulja očekivani i zašto?
Q: Koju biste vrijednost za $\alpha$ odabrali i zašto?
In [31]:
from numpy.linalg import norm
In [26]:
# Vaš kôd ovdje
Proučite funkciju datasets.make_classification
. Generirajte i prikažite dvoklasan skup podataka s ukupno $N=100$ dvodimenzijskih ($n=2)$ primjera, i to sa dvije grupe po klasi (n_clusters_per_class=2
). Malo je izgledno da će tako generiran skup biti linearno odvojiv, međutim to nije problem jer primjere možemo preslikati u višedimenzijski prostor značajki pomoću klase preprocessing.PolynomialFeatures
, kao što smo to učinili kod linearne regresije u prvoj laboratorijskoj vježbi. Trenirajte model logističke regresije koristeći za preslikavanje u prostor značajki polinomijalnu funkciju stupnja $d=2$ i stupnja $d=3$. Prikažite dobivene granice između klasa. Možete koristiti svoju implementaciju, ali se radi brzine preporuča koristiti linear_model.LogisticRegression
. Regularizacijski faktor odaberite po želji.
NB: Kao i ranije, za prikaz granice između klasa koristite funkciju plot_2d_clf_problem
. Funkciji kao argumente predajte izvorni skup podataka, a preslikavanje u prostor značajki napravite unutar poziva funkcije h
koja čini predikciju, na sljedeći način:
In [27]:
from sklearn.preprocessing import PolynomialFeatures
#poly = PolynomialFeatures(2)
#...
#mlutils.plot_2d_clf_problem(X, y, lambda x : model.predict(poly.transform(x))
In [28]:
# Vaš kôd ovdje
Q: Koji biste stupanj polinoma upotrijebili i zašto? Je li taj odabir povezan s odabirom regularizacijskog faktora $\alpha$? Zašto?