Prva laboratorijska vježba sastoji se od deset 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 [1]:
# Učitaj osnovne biblioteke...
import numpy as np
import sklearn
import matplotlib.pyplot as plt
%pylab inline
Zadan je skup primjera $\mathcal{D}=\{(x^{(i)},y^{(i)})\}_{i=1}^4 = \{(0,4),(1,1),(2,2),(4,5)\}$. Primjere predstavite matrixom $\mathbf{X}$ dimenzija $N\times n$ (u ovom slučaju $4\times 1$) i vektorom oznaka $\textbf{y}$, dimenzija $N\times 1$ (u ovom slučaju $4\times 1$), na sljedeći način:
In [2]:
X = np.array([[0],[1],[2],[4]])
y = np.array([4,1,2,5])
X1 = X
y1 = y
Proučite funkciju PolynomialFeatures
iz biblioteke sklearn
i upotrijebite je za generiranje matrice dizajna $\mathbf{\Phi}$ koja ne koristi preslikavanje u prostor više dimenzije (samo će svakom primjeru biti dodane dummy jedinice; $m=n+1$).
In [3]:
from sklearn.preprocessing import PolynomialFeatures
In [4]:
poly = PolynomialFeatures(1)
phi = poly.fit_transform(X)
print(phi)
# Vaš kôd ovdje
In [ ]:
Upoznajte se s modulom linalg
. Izračunajte težine $\mathbf{w}$ modela linearne regresije kao $\mathbf{w}=(\mathbf{\Phi}^\intercal\mathbf{\Phi})^{-1}\mathbf{\Phi}^\intercal\mathbf{y}$. Zatim se uvjerite da isti rezultat možete dobiti izračunom pseudoinverza $\mathbf{\Phi}^+$ matrice dizajna, tj. $\mathbf{w}=\mathbf{\Phi}^+\mathbf{y}$, korištenjem funkcije pinv
.
In [5]:
from numpy import linalg
In [6]:
pinverse1 = pinv(phi)
pinverse2 = matmul(inv(matmul(transpose(phi), phi)), transpose(phi))
#print(pinverse1)
#print(pinverse2)
w = matmul(pinverse2, y)
print(w)
# Vaš kôd ovdje
Radi jasnoće, u nastavku je vektor $\mathbf{x}$ s dodanom dummy jedinicom $x_0=1$ označen kao $\tilde{\mathbf{x}}$.
Prikažite primjere iz $\mathcal{D}$ i funkciju $h(\tilde{\mathbf{x}})=\mathbf{w}^\intercal\tilde{\mathbf{x}}$. Izračunajte pogrešku učenja prema izrazu $E(h|\mathcal{D})=\frac{1}{2}\sum_{i=1}^N(\tilde{\mathbf{y}}^{(i)} - h(\tilde{\mathbf{x}}))^2$. Možete koristiti funkciju srednje kvadratne pogreške mean_squared_error
iz modula sklearn.metrics
.
Q: Gore definirana funkcija pogreške $E(h|\mathcal{D})$ i funkcija srednje kvadratne pogreške nisu posve identične. U čemu je razlika? Koja je "realnija"?
In [7]:
import sklearn.metrics as mt
wt = w #(np.array([w]))
print(wt)
print(phi)
hx = np.dot(phi, w)
E = mt.mean_squared_error(hx, y)
print(E)
# Vaš kôd ovdje
Uvjerite se da za primjere iz $\mathcal{D}$ težine $\mathbf{w}$ ne možemo naći rješavanjem sustava $\mathbf{w}=\mathbf{\Phi}^{-1}\mathbf{y}$, već da nam doista treba pseudoinverz.
Q: Zašto je to slučaj? Bi li se problem mogao riješiti preslikavanjem primjera u višu dimenziju? Ako da, bi li to uvijek funkcioniralo, neovisno o skupu primjera $\mathcal{D}$? Pokažite na primjeru.
In [8]:
# Vaš kôd ovdje
w = matmul(inv(phi), y)
print(w)
Proučite klasu LinearRegression
iz modula sklearn.linear_model
. Uvjerite se da su težine koje izračunava ta funkcija (dostupne pomoću atributa coef_
i intercept_
) jednake onima koje ste izračunali gore. Izračunajte predikcije modela (metoda predict
) i uvjerite se da je pogreška učenja identična onoj koju ste ranije izračunali.
In [9]:
from sklearn.linear_model import LinearRegression
In [10]:
# Vaš kôd ovdje
lr = LinearRegression().fit(X, y)
#print(lr.score(X, y))
#print(lr.coef_)
#print(lr.intercept_)
print([lr.intercept_, lr.coef_[0]])
print(wt)
pr = lr.predict(X)
E = mt.mean_squared_error(pr, y)
print(E)
Razmotrimo sada regresiju na većem broju primjera. Definirajte funkciju make_labels(X, f, noise=0)
koja uzima matricu neoznačenih primjera $\mathbf{X}_{N\times n}$ te generira vektor njihovih oznaka $\mathbf{y}_{N\times 1}$. Oznake se generiraju kao $y^{(i)} = f(x^{(i)})+\mathcal{N}(0,\sigma^2)$, gdje je $f:\mathbb{R}^n\to\mathbb{R}$ stvarna funkcija koja je generirala podatke (koja nam je u stvarnosti nepoznata), a $\sigma$ je standardna devijacija Gaussovog šuma, definirana parametrom noise
. Za generiranje šuma možete koristiti funkciju numpy.random.normal
.
Generirajte skup za učenje od $N=50$ primjera uniformno distribuiranih u intervalu $[-5,5]$ pomoću funkcije $f(x) = 5 + x -2 x^2 -5 x^3$ uz šum $\sigma=200$:
In [11]:
from numpy.random import normal
def make_labels(X, f, noise=0) :
# Vaš kôd ovdje
N = numpy.random.normal
fx = f(X)
#nois = [N(0, noise) for _ in range(X.shape[0])]
#print(nois)
#y = f(X) + nois
y = [ f(x) + N(0, noise) for x in X ]
return y
In [12]:
def make_instances(x1, x2, N) :
return np.array([np.array([x]) for x in np.linspace(x1,x2,N)])
Prikažite taj skup funkcijom scatter
.
In [13]:
# Vaš kôd ovdje
N = 50
def f(x):
return 5 + x - 2*x*x - 5*x*x*x
noise = 200
X2 = make_instances(-5, 5, N)
y2 = make_labels(X2, f, noise)
#print(X)
#print(y)
s = scatter(X2, y2)
Trenirajte model polinomijalne regresije stupnja $d=3$. Na istom grafikonu prikažite naučeni model $h(\mathbf{x})=\mathbf{w}^\intercal\tilde{\mathbf{x}}$ i primjere za učenje. Izračunajte pogrešku učenja modela.
In [14]:
# Vaš kôd ovdje
import sklearn.linear_model as lm
def polyX(d):
p3 = PolynomialFeatures(d).fit_transform(X2)
l2 = LinearRegression().fit(p3, y2)
h2 = l2.predict(p3)
E = mt.mean_squared_error(h2, y2)
print('d: ' + str(d) + ' E: ' + str(E))
#print(p3)
plot(X2, h2, label = str(d))
scatter(X2, y2)
polyX(3)
Na skupu podataka iz zadatka 2 trenirajte pet modela linearne regresije $\mathcal{H}_d$ različite složenosti, gdje je $d$ stupanj polinoma, $d\in\{1,3,5,10,20\}$. Prikažite na istome grafikonu skup za učenje i funkcije $h_d(\mathbf{x})$ za svih pet modela (preporučujemo koristiti plot
unutar for
petlje). Izračunajte pogrešku učenja svakog od modela.
Q: Koji model ima najmanju pogrešku učenja i zašto?
In [15]:
# Vaš kôd ovdje
figure(figsize=(15,10))
scatter(X2, y2)
polyX(1)
polyX(3)
polyX(5)
polyX(10)
polyX(20)
s = plt.legend(loc="center right")
Razdvojite skup primjera iz zadatka 2 pomoću funkcije model_selection.train_test_split
na skup za učenja i skup za ispitivanje u omjeru 1:1. Prikažite na jednom grafikonu pogrešku učenja i ispitnu pogrešku za modele polinomijalne regresije $\mathcal{H}_d$, sa stupnjem polinoma $d$ u rasponu $d\in [1,2,\ldots,20]$. Budući da kvadratna pogreška brzo raste za veće stupnjeve polinoma, umjesto da iscrtate izravno iznose pogrešaka, iscrtajte njihove logaritme.
NB: Podjela na skupa za učenje i skup za ispitivanje mora za svih pet modela biti identična.
Q: Je li rezultat u skladu s očekivanjima? Koji biste model odabrali i zašto?
Q: Pokrenite iscrtavanje više puta. U čemu je problem? Bi li problem bio jednako izražen kad bismo imali više primjera? Zašto?
In [16]:
from sklearn.model_selection import train_test_split
In [17]:
# Vaš kôd ovdje
xTr, xTest, yTr, yTest = train_test_split(X2, y2, test_size=0.5)
testError = []
for d in range(1,33):
polyXTrain = PolynomialFeatures(d).fit_transform(xTr)
polyXTest = PolynomialFeatures(d).fit_transform(xTest)
l2 = LinearRegression().fit(polyXTrain, yTr)
h2 = l2.predict(polyXTest)
E = mt.mean_squared_error(h2, yTest)
print('d: ' + str(d) + ' E: ' + str(E))
testError.append(E)
#print(p3)
#plot(polyXTest, h2, label = str(d))
plot(numpy.log(numpy.array(testError)))
Out[17]:
Točnost modela ovisi o (1) njegovoj složenosti (stupanj $d$ polinoma), (2) broju primjera $N$, i (3) količini šuma. Kako biste to analizirali, nacrtajte grafikone pogrešaka kao u 3b, ali za sve kombinacija broja primjera $N\in\{100,200,1000\}$ i količine šuma $\sigma\in\{100,200,500\}$ (ukupno 9 grafikona). Upotrijebite funkciju subplots
kako biste pregledno posložili grafikone u tablicu $3\times 3$. Podatci se generiraju na isti način kao u zadatku 2.
NB: Pobrinite se da svi grafikoni budu generirani nad usporedivim skupovima podataka, na sljedeći način. Generirajte najprije svih 1000 primjera, podijelite ih na skupove za učenje i skupove za ispitivanje (dva skupa od po 500 primjera). Zatim i od skupa za učenje i od skupa za ispitivanje načinite tri različite verzije, svaka s drugačijom količinom šuma (ukupno 2x3=6 verzija podataka). Kako bi simulirali veličinu skupa podataka, od tih dobivenih 6 skupova podataka uzorkujte trećinu, dvije trećine i sve podatke. Time ste dobili 18 skupova podataka -- skup za učenje i za testiranje za svaki od devet grafova.
In [18]:
# Vaš kôd ovdje
# Vaš kôd ovdje
figure(figsize=(15,15))
N = 1000
def f(x):
return 5 + x - 2*x*x - 5*x*x*x
X3 = make_instances(-5, 5, N)
xAllTrain, xAllTest = train_test_split(X3, test_size=0.5)
i = 0
j = 0
for N in [100, 200, 1000]:
for noise in [100, 200, 500]:
j += 1
xTrain = xAllTrain[:N]
xTest = xAllTest[:N]
yTrain = make_labels(xTrain, f, noise)
yTest = make_labels(xTest, f, noise)
trainError = []
testError = []
for d in range(1,21):
polyXTrain = PolynomialFeatures(d).fit_transform(xTrain)
polyXTest = PolynomialFeatures(d).fit_transform(xTest)
l2 = LinearRegression().fit(polyXTrain, yTrain)
h2 = l2.predict(polyXTest)
testE = mt.mean_squared_error(h2, yTest)
testError.append(testE)
h2 = l2.predict(polyXTrain)
trainE = mt.mean_squared_error(h2, yTrain)
trainError.append(trainE)
#print('d: ' + str(d) + ' E: ' + str(E))
#print(p3)
#plot(polyXTest, h2, label = str(d))
subplot(3,3,j, title = "N: " + str(N) + " noise: " + str(noise))
plot(numpy.log(numpy.array(trainError)), label = 'train')
plot(numpy.log(numpy.array(testError)), label = 'test')
plt.legend(loc="center right")
#print(X)
#print(y)
#s = scatter(X2, y2)
Q: Jesu li rezultati očekivani? Obrazložite.
U gornjim eksperimentima nismo koristili regularizaciju. Vratimo se najprije na primjer iz zadatka 1. Na primjerima iz tog zadatka izračunajte težine $\mathbf{w}$ za polinomijalni regresijski model stupnja $d=3$ uz L2-regularizaciju (tzv. ridge regression), prema izrazu $\mathbf{w}=(\mathbf{\Phi}^\intercal\mathbf{\Phi}+\lambda\mathbf{I})^{-1}\mathbf{\Phi}^\intercal\mathbf{y}$. Napravite izračun težina za regularizacijske faktore $\lambda=0$, $\lambda=1$ i $\lambda=10$ te usporedite dobivene težine.
Q: Kojih je dimenzija matrica koju treba invertirati?
Q: Po čemu se razlikuju dobivene težine i je li ta razlika očekivana? Obrazložite.
In [19]:
# Vaš kôd ovdje
def reg2(lambd):
phi4 = PolynomialFeatures(3).fit_transform(X1)
w = matmul( matmul(inv( matmul(transpose(phi4), phi4) + lambd * identity(len(phi4))), transpose(phi4)), y1)
print(w)
reg2(0)
reg2(1)
reg2(10)
Proučite klasu Ridge
iz modula sklearn.linear_model
, koja implementira L2-regularizirani regresijski model. Parametar $\alpha$ odgovara parametru $\lambda$. Primijenite model na istim primjerima kao u prethodnom zadatku i ispišite težine $\mathbf{w}$ (atributi coef_
i intercept_
).
Q: Jesu li težine identične onima iz zadatka 4a? Ako nisu, objasnite zašto je to tako i kako biste to popravili.
In [20]:
from sklearn.linear_model import Ridge
In [32]:
phi4 = PolynomialFeatures(3).fit_transform(X1)
r = Ridge(0).fit(phi4, y1)
print(r.coef_)
print(r.intercept_)
# Vaš kôd ovdje
Vratimo se na slučaj $N=50$ slučajno generiranih primjera iz zadatka 2. Trenirajte modele polinomijalne regresije $\mathcal{H}_{\lambda,d}$ za $\lambda\in\{0,100\}$ i $d\in\{2,10\}$ (ukupno četiri modela). Skicirajte pripadne funkcije $h(\mathbf{x})$ i primjere (na jednom grafikonu; preporučujemo koristiti plot
unutar for
petlje).
Q: Jesu li rezultati očekivani? Obrazložite.
In [46]:
# Vaš kôd ovdje
N = 50
figure(figsize = (15, 15))
x123 = scatter(X2, y2)
for lambd in [0, 100]:
for d in [2, 10]:
phi2 = PolynomialFeatures(d).fit_transform(X2)
r = Ridge(lambd).fit(phi2, y2)
h2 = r.predict(phi2)
#print(d)
plot(X2, h2, label="lambda " + str(lambd) + " d " + str(d))
x321 = plt.legend(loc="center right")
Kao u zadataku 3b, razdvojite primjere na skup za učenje i skup za ispitivanje u omjeru 1:1. Prikažite krivulje logaritama pogreške učenja i ispitne pogreške u ovisnosti za model $\mathcal{H}_{d=10,\lambda}$, podešavajući faktor regularizacije $\lambda$ u rasponu $\lambda\in\{0,1,\dots,50\}$.
Q: Kojoj strani na grafikonu odgovara područje prenaučenosti, a kojoj podnaučenosti? Zašto?
Q: Koju biste vrijednosti za $\lambda$ izabrali na temelju ovih grafikona i zašto?
In [64]:
# Vaš kôd ovdje
xTr, xTest, yTr, yTest = train_test_split(X2, y2, test_size=0.5)
figure(figsize=(10,10))
trainError = []
testError = []
for lambd in range(0,51):
polyXTrain = PolynomialFeatures(10).fit_transform(xTr)
polyXTest = PolynomialFeatures(10).fit_transform(xTest)
l2 = Ridge(lambd).fit(polyXTrain, yTr)
h2 = l2.predict(polyXTest)
E = mt.mean_squared_error(h2, yTest)
#print('d: ' + str(d) + ' E: ' + str(E))
testError.append(log( E))
h2 = l2.predict(polyXTrain)
E = mt.mean_squared_error(h2, yTr)
trainError.append(log(E))
#print(p3)
#plot(polyXTest, h2, label = str(d))
plot(numpy.log(numpy.array(testError)), label="test")
plot(numpy.log(numpy.array(trainError)), label="train")
legend()
Out[64]:
Svrha regularizacije jest potiskivanje težina modela $\mathbf{w}$ prema nuli, kako bi model bio što jednostavniji. Složenost modela može se okarakterizirati normom pripadnog vektora težina $\mathbf{w}$, i to tipično L2-normom ili L1-normom. Za jednom trenirani model možemo izračunati i broj ne-nul značajki, ili L0-normu, pomoću sljedeće funkcije koja prima vektor težina $\mathbf{w}$:
In [73]:
def nonzeroes(coef, tol=1e-6):
return len(coef) - len(coef[np.isclose(0, coef, atol=tol)])
Za ovaj zadatak upotrijebite skup za učenje i skup za testiranje iz zadatka 3b. Trenirajte modele L2-regularizirane polinomijalne regresije stupnja $d=10$, mijenjajući hiperparametar $\lambda$ u rasponu $\{1,2,\dots,100\}$. Za svaki od treniranih modela izračunajte L{0,1,2}-norme vektora težina $\mathbf{w}$ te ih prikažite kao funkciju od $\lambda$. Pripazite što točno šaljete u funkciju za izračun normi.
Q: Objasnite oblik obiju krivulja. Hoće li krivulja za $\|\mathbf{w}\|_2$ doseći nulu? Zašto? Je li to problem? Zašto?
Q: Za $\lambda=100$, koliki je postotak težina modela jednak nuli, odnosno koliko je model rijedak?
In [76]:
# Vaš kôd ovdje
d = 10
l0 = []
l1 = []
l2 = []
xTr, xTest, yTr, yTest = train_test_split(X2, y2, test_size=0.5)
for lambd in range(0,101):
polyXTrain = PolynomialFeatures(10).fit_transform(xTr)
polyXTest = PolynomialFeatures(10).fit_transform(xTest)
r = Ridge(lambd).fit(polyXTrain, yTr)
r.coef_[0] = r.intercept_
l0.append(nonzeroes(r.coef_))
#print(r.coef_)
l1.append(numpy.linalg.norm(r.coef_, ord=1))
l2.append(numpy.linalg.norm(r.coef_, ord=2))
figure(figsize=(10,10))
plot(l0, label="l0")
legend()
figure(figsize=(10,10))
plot(l1, label="l1")
legend()
figure(figsize=(10,10))
plot(l2, label="l2")
legend()
Out[76]:
Glavna prednost L1-regularizirane regresije (ili LASSO regression) nad L2-regulariziranom regresijom jest u tome što L1-regularizirana regresija rezultira rijetkim modelima (engl. sparse models), odnosno modelima kod kojih su mnoge težine pritegnute na nulu. Pokažite da je to doista tako, ponovivši gornji eksperiment s L1-regulariziranom regresijom, implementiranom u klasi Lasso
u modulu sklearn.linear_model
. Zanemarite upozorenja.
In [26]:
# Vaš kôd ovdje
Često se u praksi možemo susreti sa podatcima u kojima sve značajke nisu jednakih magnituda. Primjer jednog takvog skupa je regresijski skup podataka grades
u kojem se predviđa prosjek ocjena studenta na studiju (1--5) na temelju dvije značajke: bodova na prijamnom ispitu (1--3000) i prosjeka ocjena u srednjoj školi. Prosjek ocjena na studiju izračunat je kao težinska suma ove dvije značajke uz dodani šum.
Koristite sljedeći kôd kako biste generirali ovaj skup podataka.
In [27]:
n_data_points = 500
np.random.seed(69)
# Generiraj podatke o bodovima na prijamnom ispitu koristeći normalnu razdiobu i ograniči ih na interval [1, 3000].
exam_score = np.random.normal(loc=1500.0, scale = 500.0, size = n_data_points)
exam_score = np.round(exam_score)
exam_score[exam_score > 3000] = 3000
exam_score[exam_score < 0] = 0
# Generiraj podatke o ocjenama iz srednje škole koristeći normalnu razdiobu i ograniči ih na interval [1, 5].
grade_in_highschool = np.random.normal(loc=3, scale = 2.0, size = n_data_points)
grade_in_highschool[grade_in_highschool > 5] = 5
grade_in_highschool[grade_in_highschool < 1] = 1
# Matrica dizajna.
grades_X = np.array([exam_score,grade_in_highschool]).T
# Završno, generiraj izlazne vrijednosti.
rand_noise = np.random.normal(loc=0.0, scale = 0.5, size = n_data_points)
exam_influence = 0.9
grades_y = ((exam_score / 3000.0) * (exam_influence) + (grade_in_highschool / 5.0) \
* (1.0 - exam_influence)) * 5.0 + rand_noise
grades_y[grades_y < 1] = 1
grades_y[grades_y > 5] = 5
a) Iscrtajte ovisnost ciljne vrijednosti (y-os) o prvoj i o drugoj značajki (x-os). Iscrtajte dva odvojena grafa.
In [28]:
# Vaš kôd ovdje
b) Naučite model L2-regularizirane regresije ($\lambda = 0.01$), na podacima grades_X
i grades_y
:
In [29]:
# Vaš kôd ovdje
Sada ponovite gornji eksperiment, ali prvo skalirajte podatke grades_X
i grades_y
i spremite ih u varijable grades_X_fixed
i grades_y_fixed
. Za tu svrhu, koristite StandardScaler
.
In [30]:
from sklearn.preprocessing import StandardScaler
In [31]:
# Vaš kôd ovdje
Q: Gledajući grafikone iz podzadatka (a), koja značajka bi trebala imati veću magnitudu, odnosno važnost pri predikciji prosjeka na studiju? Odgovaraju li težine Vašoj intuiciji? Objasnite.
a) Izradite skup podataka grades_X_fixed_colinear
tako što ćete u skupu grades_X_fixed
iz
zadatka 7b duplicirati zadnji stupac (ocjenu iz srednje škole). Time smo efektivno uveli savršenu multikolinearnost.
In [ ]:
# Vaš kôd ovdje
Ponovno, naučite na ovom skupu L2-regularizirani model regresije ($\lambda = 0.01$).
In [ ]:
# Vaš kôd ovdje
Q: Usporedite iznose težina s onima koje ste dobili u zadatku 7b. Što se dogodilo?
b) Slučajno uzorkujte 50% elemenata iz skupa grades_X_fixed_colinear
i naučite dva modela L2-regularizirane regresije, jedan s $\lambda=0.01$ i jedan s $\lambda=1000$). Ponovite ovaj pokus 10 puta (svaki put s drugim podskupom od 50% elemenata). Za svaki model, ispišite dobiveni vektor težina u svih 10 ponavljanja te ispišite standardnu devijaciju vrijednosti svake od težina (ukupno šest standardnih devijacija, svaka dobivena nad 10 vrijednosti).
In [ ]:
# Vaš kôd ovdje
Q: Kako regularizacija utječe na stabilnost težina?
Q: Jesu li koeficijenti jednakih magnituda kao u prethodnom pokusu? Objasnite zašto.
c) Koristeći numpy.linalg.cond
izračunajte kondicijski broj matrice $\mathbf{\Phi}^\intercal\mathbf{\Phi}+\lambda\mathbf{I}$, gdje je $\mathbf{\Phi}$ matrica dizajna (grades_X_fixed_colinear
). Ponovite i za $\lambda=0.01$ i za $\lambda=10$.
In [ ]:
# Vaš kôd ovdje
Q: Kako regularizacija utječe na kondicijski broj matrice $\mathbf{\Phi}^\intercal\mathbf{\Phi}+\lambda\mathbf{I}$?