Klassisen koneoppimisen haastatteluun valmistautuminen

Klassisen koneoppimisen insinöörin haastattelukysymykset

15 valittua klassisen koneoppimisen haastattelukysymystä ryhmiteltynä kokemustason mukaan. Käytä niitä kertaamaan perusasioita, käytännön kompromisseja ja senioritason tuotantopäätösten logiikkaa.

Aloita klassisen koneoppimisen tekoälyhaastatteluEi vaadi luottokorttia. 1 ilmainen sessio saatavilla.
Teknisten haastattelujen harjoittelu englanniksiTila, jossa ei-äidinkieliset puhujat voivat harjoitella teknisen haastattelun läpäisemistä.

Junioritason kysymykset

1Mitä eroa on malliparametrilla ja hyperparametrilla ohjatussa oppimisessa?

Ohjatussa koneoppimisessa malliparametrit ovat sisäisiä muuttujia, jotka opitaan suoraan harjoitusdatasta optimointialgoritmin (kuten gradienttimenetelmän tai normaalivertailujen) avulla. Esimerkkejä ovat regressiopainot ja harha lineaarisissa malleissa tai jakokynnykset päätöspuissa. Sen sijaan hyperparametrit ovat ulkoisia konfiguraatioasetuksia, jotka määritellään ennen harjoitusta ja jotka ohjaavat oppimisprosessia, mallin kapasiteettia tai arkkitehtuuria. Niitä ei voida oppia suoraan tavanomaisen harjoitushäviön minimoinnin kautta, koska optimoija ylioppisi triviaalisti (esim. asettamalla puun syvyyden äärettömäksi). Esimerkkejä ovat oppimisnopeus, regularisoinnin voimakkuus (lambda/C), puiden määrä metsässä ja maksimipuiden syvyys. Hyperparametrit viritetään validointidatan tai ristivalidoinnin avulla.

from sklearn.linear_model import Ridge
import numpy as np

X = np.array([[1.0], [2.0], [3.0]])
y = np.array([2.0, 4.0, 6.0])

# Hyperparameter: alpha (regularization strength set beforehand)
model = Ridge(alpha=1.0)

# Fitting optimizes internal parameters on training data
model.fit(X, y)

# Learned parameters
print(f"Weight (Parameter): {model.coef_[0]:.4f}")
print(f"Intercept (Parameter): {model.intercept_:.4f}")
Kokeile vastata tähän kysymykseen tekoälyvalmentajan kanssa

2Mitä oletuksia pienimmän neliösumman (OLS) lineaarinen regressio tekee, ja miten jäännösten diagnostiikka paljastaisi oletusten rikkomukset?

Pienimmän neliösumman (OLS) lineaarinen regressio perustuu useisiin ydinoletuksiin: 1. Lineaarisuus: Selittävien muuttujien ja vastemuuttujan välinen suhde on parametrien suhteen lineaarinen. 2. Virhetermien riippumattomuus: Havainnot ja jäännösvirheet ovat keskenään riippumattomia (ei autokorrelaatiota). 3. Homoskedastisuus: Virhetermeillä on vakio varianssi kaikilla selittävien muuttujien tasoilla. 4. Jäännösten normaalijakautuneisuus: Virhetermit ovat normaalijakautuneita (vaaditaan kelvollisia luottamusvälejä ja hypoteesitestejä varten). 5. Ei multikollineaarisuutta: Selittävät muuttujat eivät ole lineaarisesti riippuvaisia (suunnittelumatriisilla on täysi sarakkeiden aste). Jäännösten diagnostiikka paljastaa rikkomukset seuraavasti: - Jäännökset vs. sovitetut arvot -kuvaaja: Kaarevuus tai ei-satunnaiset kuviot paljastavat epälineaarisuuden; suppilo- tai viuhkamainen muoto paljastaa heteroskedastisuuden (epävakion varianssin). - Normaali Q-Q -kuvaaja: Systemaattinen poikkeama suorasta diagonaaliviivasta (esim. S-käyrät tai raskaat hännät) paljastaa epänormaaliuden. - Jäännökset vs. järjestys/aika -kuvaaja: Systemaattiset trendit tai sykliset kuviot paljastavat autokorreloituneet virheet. - Vaikutus / Cookin etäisyys -kuvaaja: Tunnistaa korkean vivun ulkopisteet tai vaikutuspisteet, jotka siirtävät sovitettua mallia suhteettomasti.

import numpy as np
import statsmodels.api as sm

np.random.seed(42)
X = np.linspace(1, 10, 50)
# Quadratic underlying pattern creates a linearity violation
y = 2 * X + 0.5 * (X ** 2) + np.random.normal(0, 2, 50)

X_with_const = sm.add_constant(X)
model = sm.OLS(y, X_with_const).fit()
residuals = model.resid

print(f"Mean Residual: {np.mean(residuals):.4f}")
print(f"Curvature in residuals indicates model misspecification.")
Kokeile vastata tähän kysymykseen tekoälyvalmentajan kanssa

3Miten logistinen regressio mallintaa binääriluokittelua, ja mikä on sigmoidifunktion rooli?

Logistinen regressio mallintaa binääriluokittelua arvioimalla luokan posterioritodennäköisyyden $P(Y=1|X)$. Jotta ennustetut todennäköisyydet pysyvät rajattuna välille $(0, 1)$, logistinen regressio mallintaa positiivisen luokan log-oddsia (logit) syötteiden lineaarisena funktiona: $\ln\left(\frac{p}{1-p}\right) = w^T x + b$. Sigmoidifunktio (logistinen funktio), $\sigma(z) = \frac{1}{1 + e^{-z}}$, toimii linkkifunktiona, joka muuntaa minkä tahansa reaalilukuarvoisen lineaarisen pistemäärän $z = w^T x + b \in (-\infty, +\infty)$ monotonisesti kelvolliseksi todennäköisyydeksi $p \in (0, 1)$. Diskreettejä luokkapäätöksiä tehdään soveltamalla päätöskynnystä $\tau$ (tyypillisesti 0.5): $\hat{y} = 1$, jos $P(Y=1|X) \ge \tau$, muuten $0$. Koska $\sigma(z) = 0.5$ tapahtuu täsmälleen silloin, kun $z = 0$, päätösraja piirreavaruudessa on lineaarinen hyperkone $w^T x + b = 0$, mikä tekee standardista logistisesta regressiosta lineaarisen luokittelijan.

import numpy as np

def sigmoid(z):
    return 1 / (1 + np.exp(-z))

w = np.array([1.5, -2.0])
b = 0.5
x = np.array([2.0, 1.0])

z = np.dot(w, x) + b
prob = sigmoid(z)
threshold = 0.5
pred = int(prob >= threshold)

print(f"Log-odds (z): {z:.2f}")
print(f"Probability: {prob:.4f}")
print(f"Class Prediction: {pred}")
Kokeile vastata tähän kysymykseen tekoälyvalmentajan kanssa

4Mitä on L2-regularisointi, ja miten Ridge-regressio muuttaa tavoitetta ja kerroinarvioita?

L2-regularisointi (Ridge-regressio) lisää painojen neliösumman suhteessa olevan rangaistustermin pienimmän neliösumman (OLS) häviöfunktioon: $$\min_w \|y - Xw\|_2^2 + \lambda \|w\|_2^2$$ Analyyttisesti Ridge-regressio muokkaa normaaleja yhtälöitä lisäämällä $\lambda I$:n Gram-matriisiin ennen inversiota: $$w_{\text{ridge}} = (X^T X + \lambda I)^{-1} X^T y$$ Keskeiset vaikutukset tavoitteeseen ja kerroinarvioihin: 1. Kutistuminen: Kertoimet kutistuvat kohti nollaa suhteessa piirteiden varianssiin ja korrelaatioon, mikä vähentää mallin kompleksisuutta pakottamatta niitä tarkalleen nollaan. 2. Multikollineaarisuus ja käännettävyys: Kun piirteet ovat kollineaarisia tai $p > N$, $X^T X$ on singulaarinen tai huonosti ehdollistettu. $\lambda I$:n lisääminen varmistaa, että $(X^T X + \lambda I)$ on aidosti positiivisesti definiitti ja käännettävissä, mikä vakauttaa parametriarvioita. 3. Harhan ja varianssin kompromissi: $\lambda$:n kasvattaminen tuo tarkoituksellista harhaa kerroinarvioihin samalla vähentäen merkittävästi varianssia, mikä johtaa pienempään odotettuun yleistysvirheeseen näkemättömällä datalla. 4. Piirteiden skaalausvaatimus: Koska rangaistustermi käsittelee kaikkia painoja samalla tavalla, suuremmilla skaaloilla olevat piirteet regularisoitaisiin suhteettomasti. Piirteet on standardoitava (nollakeskiarvo, yksikkövarianssi) ennen sovitusta.

import numpy as np

def ridge_regression(X, y, alpha):
    X_std = (X - np.mean(X, axis=0)) / np.std(X, axis=0)
    n_features = X_std.shape[1]
    
    I = np.eye(n_features)
    beta = np.linalg.inv(X_std.T @ X_std + alpha * I) @ X_std.T @ y
    return beta

X = np.array([[1.0, 2.0], [2.0, 4.1], [3.0, 5.9], [4.0, 8.2]])
y = np.array([2.1, 4.0, 6.2, 8.1])
weights = ridge_regression(X, y, alpha=1.0)
print('Ridge Weights:', weights)
Kokeile vastata tähän kysymykseen tekoälyvalmentajan kanssa

5Miten päätöspuu (decision tree) jakaa rekursiivisesti ominaisuusavaruuden, ja mitä kriteerejä käytetään luokittelujakojen valitsemiseen?

Päätöspuu jakaa ominaisuusavaruuden ylhäältä alas etenevällä, ahneella algoritmilla, jota kutsutaan **rekursiiviseksi binääriseksi jaoksi**. Algoritmi alkaa juurisolmusta kaiken harjoitusdatan kanssa ja etsii kaikkien ominaisuuksien ja mahdollisten kynnysarvojen yli löytääkseen yksittäisen akselisuuntaisen jaon ($X_j \le t$), joka maksimoi epäpuhtauden vähenemisen. Aineisto jaetaan kahteen lapsisolmuun, ja tätä menettelyä sovelletaan rekursiivisesti jokaiseen lapsisolmuun, kunnes lopetuskriteeri (esim. maksimisyvyys, minimi näytteiden määrä lehteä kohti tai puhtaat solmut) saavutetaan. Koska jaot arvioivat yhtä ominaisuutta kerrallaan kynnysarvoa vastaan, tuloksena olevat päätösrajat ovat ortogonaalisia hypertasoja (akselisuuntaisia suorakulmaisia alueita ominaisuusavaruudessa). Parhaan jaon arvioimiseen ja valitsemiseen luokittelupuissa käytetään kahta pääasiallista epäpuhtauskriteeriä: 1. **Gini-epäpuhtaus (käytetään CARTissa)**: Mittaa todennäköisyyden sille, että satunnaisesti valittu näyte luokiteltaisiin väärin, jos se luokiteltaisiin satunnaisesti solmun luokkajakauman mukaan. Jos $K$ luokkaa ja osuudet $p_k$: $$I_G = 1 - \sum_{k=1}^K p_k^2$$ 2. **Entropia ja informaationlisäys (käytetään ID3:ssa, C4.5:ssä)**: Entropia mittaa solmun epävarmuutta: $H = -\sum_{k=1}^K p_k \log_2(p_k)$. Jako valitaan maksimoimaan **informaationlisäys**, joka on vanhemman solmun entropia miinus lasten solmujen painotettu keskiarvoentropia: $$IG = H(\text{parent}) - \sum_{c \in \{\text{left, right}\}} \frac{N_c}{N} H(c)$$ Molemmat mittarit saavuttavat arvon 0, kun solmu on täysin puhdas (kaikki näytteet kuuluvat yhteen luokkaan), ja maksiminsa, kun luokat ovat tasaisesti jakautuneita.

import numpy as np

def gini(labels):
    _, counts = np.unique(labels, return_counts=True)
    p = counts / len(labels)
    return 1.0 - np.sum(p ** 2)

def entropy(labels):
    _, counts = np.unique(labels, return_counts=True)
    p = counts / len(labels)
    return -np.sum(p * np.log2(p + 1e-12))

# Evenly split node (impure) vs single-class node (pure)
impure_node = np.array([0]*10 + [1]*10)
pure_node = np.array([0]*20)

print(f"Impure - Gini: {gini(impure_node):.2f}, Entropy: {entropy(impure_node):.2f}")
print(f"Pure   - Gini: {gini(pure_node):.2f}, Entropy: {entropy(pure_node):.2f}")
Kokeile vastata tähän kysymykseen tekoälyvalmentajan kanssa

6Mikä on k-lähimmän naapurin (kNN) algoritmi, ja miten se tekee ennusteita luokittelussa ja regressiossa?

k-lähimmän naapurin (kNN) algoritmi on epäparametrinen, tapauspohjainen (laiska) ohjattu oppimisalgoritmi. Se ei harjoita eksplisiittistä parametrinen mallia; sen sijaan se tallentaa harjoitusdatan ja suorittaa kaikki laskutoimitukset päättelyvaiheessa (inference). Ennusteprosessi: 1. **Etäisyyden laskenta:** Kun kyselyinstanssi arvioidaan, algoritmi laskee sen etäisyyden kaikkiin tallennettuihin harjoitusinstansseihin käyttäen annettua mittaria (kuten Euklidinen, Manhattan- tai Minkowski-etäisyys). 2. **Naapurien valinta:** Se valitsee $k$ harjoitusinstanssia, joilla on pienimmät etäisyydet kyselyinstanssiin. 3. **Aggregointi:** * **Luokittelu:** Se määrittää luokan enemmistöäänestyksellä (moodi) $k$ naapurin kesken (tai etäisyyspainotetulla äänestyksellä). * **Regressio:** Se ennustaa jatkuvan kohdearvon ottamalla paikallisen keskiarvon (keskiarvo tai mediaani) $k$ naapurin kohdearvoista (tai etäisyyspainotetun keskiarvon). Koska etäisyyden laskenta riippuu suoraan piirteiden mittakaavasta, piirteiden normalisointi tai standardointi on olennaista estääkseen suuret piirteet hallitsemasta etäisyyden laskentaa.

from sklearn.neighbors import KNeighborsClassifier, KNeighborsRegressor
from sklearn.preprocessing import StandardScaler
import numpy as np

X_train = np.array([[1000.0, 1.0], [2000.0, 2.0], [1500.0, 1.5], [5000.0, 5.0]])
y_cls = np.array([0, 0, 0, 1])
y_reg = np.array([10.0, 20.0, 15.0, 50.0])

# Feature scaling is mandatory for distance-based algorithms
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X_train)

# 1. Classification (Majority Vote)
clf = KNeighborsClassifier(n_neighbors=3)
clf.fit(X_scaled, y_cls)

# 2. Regression (Local Average)
reg = KNeighborsRegressor(n_neighbors=3)
reg.fit(X_scaled, y_reg)

query = scaler.transform([[1800.0, 1.8]])
print('Classification:', clf.predict(query))
print('Regression:', reg.predict(query))
Kokeile vastata tähän kysymykseen tekoälyvalmentajan kanssa

7Määrittele naiivin Bayesin ehdollinen riippumattomuusoletus ja selitä, miksi naiivi Bayes voi silti toimia hyvin, vaikka se rikottaisiin.

Naiivin Bayesin ehdollinen riippumattomuusoletus sanoo, että annettaessa luokkamerkintä Y = y, kaikki piirteet X_1, X_2, ..., X_d ovat keskenään riippumattomia: P(X_1, ..., X_d | Y = y) = \prod_{j=1}^d P(X_j | Y = y). Bayesin teoreemaa käyttäen posteriori-todennäköisyys on: P(Y = y | X) \propto P(Y = y) \prod_{j=1}^d P(X_j | Y = y), missä P(Y = y) on luokan prior-todennäköisyys ja P(X_j | Y = y) on luokkakohtainen todennäköisyysfunktio (esim. Gaussin jakauma jatkuville piirteille, Multinomiaalinen jakauma laskureille). Naiivi Bayes toimii usein hyvin käytännössä riippumattomuuden rikkoutumisesta huolimatta, koska luokittelu perustuu argmax-päätössääntöön (argmax_y P(Y=y | X)) eikä tarkkaan todennäköisyyden kalibrointiin. Vaikka piirteiden korrelaatiot aiheuttaisivat ennustettujen todennäköisyyksien muuttumisen ylivarmaksi tai vääristyneeksi, oikea luokka säilyttää usein korkeimman suhteellisen järjestyksen. Niin kauan kuin korrelaatio ei muuta luokkakohtaisten todennäköisyyksien järjestystä, 0-1-luokittelupäätös pysyy tarkkana.

from sklearn.naive_bayes import GaussianNB
import numpy as np

X = np.array([[1.0, 1.1], [1.2, 0.9], [-1.0, -1.2], [-0.8, -1.1]])
y = np.array([1, 1, 0, 0])

model = GaussianNB()
model.fit(X, y)
# Prediction uses argmax over class posterior scores
print("Predicted class:", model.predict([[1.1, 1.0]]))
Kokeile vastata tähän kysymykseen tekoälyvalmentajan kanssa

Keskitason kysymykset

8Johda tai selitä suljetun muodon OLS (Ordinary Least Squares) -ratkaisu ja kerro, milloin se on yksikäsitteinen.

OLS (Ordinary Least Squares) -tavoite minimoi jäännösneliösumman: $S(\beta) = \|y - X\beta\|^2 = (y - X\beta)^T (y - X\beta) = y^T y - 2\beta^T X^T y + \beta^T X^T X \beta$. Asetettaessa gradientti $\beta$:n suhteen nollaksi: $$\nabla_\beta S(\beta) = -2 X^T y + 2 X^T X \beta = 0 \implies X^T X \beta = X^T y$$ Nämä ovat normaaliequations. Kun $X^T X$ on epäsingulaarinen (käännettävä), yksikäsitteinen suljetun muodon ratkaisu on: $$\hat{\beta} = (X^T X)^{-1} X^T y$$ Geometrisesti $\hat{y} = X\hat{\beta} = X(X^T X)^{-1} X^T y = H y$ edustaa kohdevektorin $y$ ortogonaalista projektiota design-matriisin $X$ sarakeavaruuteen, missä $H$ on projektio- (hattu-) matriisi. Ratkaisu on yksikäsitteinen vain ja ainoastaan, jos $X^T X$ on käännettävä, mikä edellyttää, että $N \times P$ design-matriisilla $X$ on täysi sarakeaste ($Rank(X) = P$). Tämä vaatii $N \ge P$ ja ettei ole eksaktia multikollineaarisuutta (mikään ominaisuus ei ole toisten lineaarinen yhdistelmä). Jos $X$ on asteeltaan vajaa (rank-deficient), $X^T X$ on singulaarinen, mikä johtaa äärettömän moniin ratkaisuihin. Tämä ratkaistaan usein regularisoinnilla tai Moore-Penrosen pseudoinverssillä $X^+ y$.

import numpy as np

# Design matrix X (with intercept column) and target y
X = np.array([[1, 1], [1, 2], [1, 3], [1, 4]])
y = np.array([2.1, 3.9, 6.2, 8.0])

# Normal equations: (X^T X)^(-1) X^T y
beta_hat = np.linalg.inv(X.T @ X) @ X.T @ y
H = X @ np.linalg.inv(X.T @ X) @ X.T
y_hat = H @ y

print(f"Beta: {beta_hat}")
print(f"Predictions: {y_hat}")
Kokeile vastata tähän kysymykseen tekoälyvalmentajan kanssa

9Mikä on suurimman uskottavuuden estimointi (Maximum Likelihood Estimation, MLE), ja miten se johtaa logistisen regression ristientropia-objektiivifunktioon?

Suurimman uskottavuuden estimointi (MLE) on menetelmä malliparametrien $\theta$ estimoimiseksi valitsemalla arvot, jotka maksimoivat havaitun datajoukon uskottavuuden $L(\theta) = P(\mathcal{D}|\theta)$. Binaarisessa logistisessa regressiossa jokainen luokka $y_i \in \{0, 1\}$ mallinnetaan riippumattomana Bernoullin satunnaismuuttujana ehdollistettuna $x_i$:lle, onnistumistodennäköisyydellä $p_i = \sigma(w^T x_i + b)$. Havainnon $i$ todennäköisyysmassafunktio on $P(y_i|x_i) = p_i^{y_i} (1 - p_i)^{1 - y_i}$. Olettaen i.i.d. (independent and identically distributed) -näytteet, yhteinen uskottavuus on: $$L(w, b) = \prod_{i=1}^N p_i^{y_i} (1 - p_i)^{1 - y_i}$$ Luonnollisen logaritmin ottaminen muuntaa tulon laskennallisesti hallittavaksi log-uskottavuuksien summaksi: $$\ell(w, b) = \sum_{i=1}^N \left[ y_i \ln(p_i) + (1 - y_i) \ln(1 - p_i) \right]$$ Koska optimointialgoritmit on standardisti muotoiltu minimointiongelmiksi, negaatioimme log-uskottavuuden ja normalisoimme näytekoon $N$ mukaan, jolloin saadaan negatiivinen log-uskottavuus (Negative Log-Likelihood, NLL), joka on tarkalleen Binary Cross-Entropy (log loss) -objektiivifunktio: $$J(w, b) = -\frac{1}{N} \sum_{i=1}^N \left[ y_i \ln(p_i) + (1 - y_i) \ln(1 - p_i) \right]$$ Tämä objektiivifunktio on konveksi lineaaristen logiikkojen/painojen suhteen, joten asianmukaiset numeeriset ratkaisijat optimoivat globaalin tavoitteen. Tarkka konveksisuus ja äärellinen yksilöllinen MLE vaativat lisäehtoja, kuten riittävän piirrearvon (feature rank), regularisoinnin ja täydellisen luokkajaon puuttumisen.

import numpy as np

y_true = np.array([1, 0, 1, 1])
y_prob = np.array([0.9, 0.2, 0.8, 0.4])

# Binary cross-entropy (Negative Log-Likelihood)
epsilon = 1e-15  # prevent log(0)
y_prob = np.clip(y_prob, epsilon, 1 - epsilon)
bce_loss = -np.mean(y_true * np.log(y_prob) + (1 - y_true) * np.log(1 - y_prob))

print(f"Binary Cross-Entropy Loss: {bce_loss:.4f}")
Kokeile vastata tähän kysymykseen tekoälyvalmentajan kanssa

10Miten gradienttimenetelmä (gradient descent) optimoi klassisen koneoppimisen tavoitefunktion, ja miten oppimisvauhti, suppeneminen ja konveksisuus vaikuttavat harjoitteluun?

Gradienttimenetelmä minimoi empiirisen häviöfunktion päivittämällä mallin parametreja iteroivasti tavoitefunktion gradientin vastakkaiseen suuntaan näiden parametrien suhteen: $\theta_{t+1} = \theta_t - \eta \nabla L(\theta_t)$. Keskeisiä harjoitteluun vaikuttavia tekijöitä ovat: 1. **Oppimisvauhti** ($\eta$): Määrittää askelkoon. Jos se on liian pieni, suppeneminen on äärimmäisen hidasta ja harjoittelu voi pysähtyä. Jos se on liian suuri, päivitykset ylittävät minimikohdan, mikä aiheuttaa oskillaatiota tai numeerista hajaantumista. 2. **Suppeneminen:** Määräytyy pysäytyskriteerien (kuten pienen gradientin normi ($||\nabla L(\theta)|| \le \epsilon$), minimaalinen parametrissiirtymä tai häviön tasaantuminen peräkkäisissä iteraatioissa) seurannalla. 3. **Konveksisuus:** Konvekseissa tavoitefunktioissa (esim. standardi OLS-lineaarinen regressio tai logistinen regressio) mikä tahansa lokaali minimi on taattu olevan globaali minimi, mikä mahdollistaa gradienttimenetelmän luotettavan suppenemisen sopivilla askelkoilla. Epäkonvekseissa tavoitefunktioissa (esim. monikerroksiset neuroverkot) häviöfunktio sisältää useita lokaaleja minimeitä, satulapisteitä ja tasa-alueita, mikä tekee lopullisesta ratkaisusta herkän alustukselle. 4. **Optimointi vs. yleistettävyys:** Harjoitushäviön suppeneminen kuvastaa optimoinnin onnistumista, kun taas validointihäviö arvioi yleistettävyyttä. Alhainen harjoitushäviö korkealla validointivirheellä viittaa ylisovittumiseen optimoinnin epäonnistumisen sijaan.

import numpy as np

def gradient_descent(X, y, lr=0.01, max_iters=1000, tol=1e-6):
    n_samples, n_features = X.shape
    theta = np.zeros(n_features)
    prev_loss = float('inf')
    
    for i in range(max_iters):
        predictions = X @ theta
        error = predictions - y
        loss = (1 / (2 * n_samples)) * np.dot(error, error)
        
        if abs(prev_loss - loss) < tol:
            print(f'Converged at iteration {i}')
            break
        prev_loss = loss
        
        grad = (1 / n_samples) * (X.T @ error)
        theta -= lr * grad
        
    return theta
Kokeile vastata tähän kysymykseen tekoälyvalmentajan kanssa

11Vertaa L1-, L2- ja ElasticNet-regularisointia harvuuden, korreloituneiden piirteiden ja käytännön mallinvalinnan kannalta.

L1 (Lasso)-, L2 (Ridge)- ja ElasticNet-regularisoinnit eroavat toisistaan rangaistusfunktion muotoilun, rajoitteen geometrian, harvuuden ja korreloituneiden selittävien muuttujien käsittelyn osalta: 1. **Harvuus ja geometria:** * **L1** käyttää itseisarvorangaistusta ($\lambda \|w\|_1$). Sen rajoitepinta on timantin/polytoopin muotoinen, ja siinä on teräviä kulmia koordinaattiakseleilla. Kun virhefunktion ääriviivat leikkaavat näitä kulmia, painokertoimet ajetaan tarkkaan nollaan, mikä suorittaa automaattisen piirteiden valinnan. * **L2** käyttää neliöityä euklidista normirangaistusta ($\lambda \|w\|_2^2$). Sen rajoitepinta on sileä hypersfääri ilman kulmia, joka kutistaa painokertoimia kohti nollaa asymptoottisesti, mutta harvoin asettaa niitä tarkkaan nollaan. 2. **Korreloituneet piirteet:** * Vahvan kollineaarisuuden vallitessa **L1** pyrkii mielivaltaisesti valitsemaan yhden piirteen ryhmästä korreloituneita selittäviä muuttujia ja asettamaan muiden kertoimet nollaan, mikä johtaa epävakaisiin estimaatteihin eri otoksissa. * **L2** säilyttää kaikki korreloituneet piirteet, jakaen painokertoimet niiden kesken ja kutistaen niitä yhdessä. 3. **ElasticNet:** * Yhdistää molemmat rangaistukset: $\lambda_1 \|w\|_1 + \lambda_2 \|w\|_2^2$ (usein parametrisoitu $\alpha$:lla ja $l_1\_\text{ratio}$:lla). * Tarjoaa L1-Lasson harvuuden ja piirteiden valinnan säilyttäen samalla L2-Ridgen ryhmittelyefektin, valiten klustereita korreloituneita selittäviä muuttujia yhdessä. Se on erityisen hyödyllinen, kun $p > N$ tai vakavan multikollineaarisuuden vallitessa.

from sklearn.linear_model import Ridge, Lasso, ElasticNet
import numpy as np

np.random.seed(42)
X1 = np.random.randn(100, 1)
X2 = X1 + np.random.randn(100, 1) * 0.01  # highly correlated
X = np.hstack([X1, X2])
y = 3 * X1.ravel() + np.random.randn(100) * 0.1

ridge = Ridge(alpha=1.0).fit(X, y)
lasso = Lasso(alpha=0.1).fit(X, y)
elastic = ElasticNet(alpha=0.1, l1_ratio=0.5).fit(X, y)

print('Ridge coefs:', ridge.coef_)
print('Lasso coefs:', lasso.coef_)
print('ElasticNet coefs:', elastic.coef_)
Kokeile vastata tähän kysymykseen tekoälyvalmentajan kanssa

12Mitä eroa on Ridge-regressiolla, pääkomponenttiregressiolla (PCR) ja osittaisin pienimmän neliösumman menetelmällä (PLS) yleisellä tasolla?

Ridge-regressio, pääkomponenttiregressio (PCR) ja osittaisin pienimmän neliösumman menetelmä (PLS) ovat kolme lineaarista tekniikkaa, joita käytetään multikollineaarisuuden ja korkean ulotteisuuden käsittelyyn, mutta ne eroavat toisistaan siinä, miten ne vähentävät varianssia ja onko vähennys jatkuvaa vai ohjattua: 1. **Ridge-regressio:** Säilyttää kaikki alkuperäiset $p$ piirrettä ja soveltaa jatkuvaa kutistusta kertoimien suuruuksiin L2-rangaistuksen kautta. Se ei muodosta alimittaisia latentteja komponentteja tai hylkää piirteiden ulottuvuuksia; pikemminkin se kutistaa varianssia $X^T X$:n matalan ominaisarvon suuntien mukaisesti. 2. **Pääkomponenttiregressio (PCR):** Kaksivaiheinen, ohjaamaton ulotteisuuden vähennysmenetelmä. Se soveltaa ensin pääkomponenttianalyysiä (PCA) tiukasti selittäjien matriisiin $X$ löytääkseen ortogonaaliset maksimaalisen varianssin suunnat, säilyttää $k$ ensimmäistä pääkomponenttia ja sovittaa OLS-regressioanalyysin (Ordinary Least Squares) näille $k$ komponentille. Koska PCA jättää huomiotta kohdemuuttujan $y$, PCR riskillä hylkää komponentteja, joilla on matala varianssi $X$:ssä, mutta korkea ennustuskyky $y$:lle. 3. **Osittaisin pienimmän neliösumman menetelmä (PLS):** Ohjattu ulotteisuuden vähennysmenetelmä. Se rakentaa $k$ ortogonaalista latenttia komponenttia löytämällä $X$:n lineaarisia yhdistelmiä, jotka maksimoivat kovarianssin $X$:n ja vastemuuttujan $y$ välillä. Sisällyttämällä kohdetiedot eksplisiittisesti, PLS tunnistaa komponentteja, jotka selittävät sekä piirteiden varianssia että vastemuuttujan vaihtelua.

from sklearn.linear_model import Ridge, LinearRegression
from sklearn.decomposition import PCA
from sklearn.cross_decomposition import PLSRegression
from sklearn.pipeline import make_pipeline

# 1. Ridge: Regularized full feature space
ridge = Ridge(alpha=1.0)

# 2. PCR: Unsupervised PCA followed by OLS
pcr = make_pipeline(PCA(n_components=2), LinearRegression())

# 3. PLS: Supervised latent component projection and regression
pls = PLSRegression(n_components=2)
Kokeile vastata tähän kysymykseen tekoälyvalmentajan kanssa

Senioritason kysymykset

13Miten modernit gradienttiparannuksen (gradient boosting) toteutukset, kuten XGBoost, LightGBM ja CatBoost, optimoivat koulutusta tai käsittelevät taulukkomuotoisia piirteitä (tabular features) eri tavoin?

Modernit GBDT (Gradient Boosting Decision Tree) -kehitysympäristöt eroavat merkittävästi jaonetsintäalgoritmeissaan (split-finding algorithms), puun kasvatusstrategioissaan ja taulukkomuotoisten/kategoristen piirteiden käsittelyssä: 1. **XGBoost:** Perinteisesti luottaa tarkkaan ahneeseen tai likimääräiseen kvantiililuonnos-jaonetsintään (quantile sketch) (ja myöhemmin Fast Hist), käyttää tasoittain (level-wise/depth-wise) tapahtuvaa puun kasvua ja käsittelee puuttuvia arvoja oppimalla optimaalisen oletushaaran suunnan jaon etsinnän aikana. 2. **LightGBM:** Käyttää histogrammiin perustuvaa jaonetsintää (muuntaen jatkuvat piirteet diskreeteiksi ryhmiksi, tyypillisesti 256), lehti-kohtaisesti (leaf-wise/best-first) tapahtuvaa puun kasvua nopeamman häviön (loss) vähentämiseksi, gradienttipohjaista yksipuolista otantaa (Gradient-based One-Side Sampling, GOSS) suurigradienttisten esiintymien säilyttämiseksi samalla kun pientä gradienttia omaavat otostetaan, ja eksklusiivista piirteiden yhdistelyä (Exclusive Feature Bundling, EFB) keskenään poissulkevien harvojen piirteiden yhdistämiseksi. Kategoristen muuttujien osalta se löytää optimaaliset jaot lajittelemalla kategoriset histogrammiryhmät ($O(K \log K)$). 3. **CatBoost:** Käyttää "tiedostamattomia" (oblivious/symmetric) päätöspuita, joissa kaikki tietyllä syvyydellä olevat solmut jakavat täsmälleen saman jaon, mikä mahdollistaa nopean vektoroidun CPU/GPU-pisteytyksen. Sen ensisijainen innovaatio on järjestetyt kohdetilastot (Ordered Target Statistics) ja järjestetty tehostus (ordered boosting), joka laskee kohdetilastot satunnaisten harjoitusdatan permutaatioiden yli estääkseen kohdevuotoa (target leakage) ja ennusteen siirtymää (prediction shift).

from catboost import CatBoostClassifier
import lightgbm as lgb
import pandas as pd

df = pd.DataFrame({
    'city': ['NY', 'LDN', 'NY', 'PAR', 'LDN', 'TOK'],
    'age': [25, 42, 30, 22, 55, 38],
    'target': [1, 0, 1, 0, 1, 0]
})
cat_cols = ['city']
df['city'] = df['city'].astype('category')

# LightGBM handles pandas 'category' dtype natively via integer binning
lgb_clf = lgb.LGBMClassifier(max_depth=3, n_estimators=10)
lgb_clf.fit(df[['city', 'age']], df['target'])

# CatBoost handles categorical column names natively with ordered TS
cb_clf = CatBoostClassifier(iterations=10, cat_features=cat_cols, verbose=False)
cb_clf.fit(df[['city', 'age']], df['target'])
Kokeile vastata tähän kysymykseen tekoälyvalmentajan kanssa

14Miten päättäisit, onko mukautettu häviöfunktio (custom loss) sopiva gradienttivahvistusmallille (gradient boosting model) epäsymmetristen liiketoimintakustannusten vallitessa?

Päätettäessä, tuleeko toteuttaa mukautettu häviöfunktio (custom loss function) gradienttivahvistuksessa (gradient boosting) epäsymmetristen liiketoimintakustannusten vallitessa, on arvioitava, voidaanko epäsymmetriaa käsitellä myöhemmässä vaiheessa todennäköisyyden kalibroinnilla ja kynnysarvon säädöllä, vai muuttaako se perusteellisesti optimointimaisemaa puun rakentamisen aikana: 1. **Kynnysarvon säätö vs. mukautettu häviöfunktio:** Luokittelutehtävissä, joissa virhekustannukset ovat epäsymmetrisiä (esim. virheelliset negatiiviset tulokset maksavat 10 kertaa enemmän kuin virheelliset positiiviset), standardi ristientropia on asianmukainen pisteytyssääntö, jonka tarkoituksena on arvioida posterioritodennäköisyyksiä $P(y=1|x)$. Kalibrointi tulisi tarkistaa ja tarvittaessa korjata validointidatalla. Luokittelun päätöskynnysarvon siirtäminen liiketoiminnan kustannusmatriisin $\tau = \frac{C_{FP}}{C_{FP} + C_{FN}}$ perusteella tai otospainojen soveltaminen on usein selkeämpää ja välttää mukautettuja derivaattoja. Kuitenkin, epäsymmetrisen regression (esim. epäsymmetrinen pinball-häviö varaston kysynnälle) tai epälineaaristen liiketoiminnan rangaistusten tapauksessa, joissa vakiotavoitteet eivät voi ohjata jakamisen löytämistä, mukautettu häviöfunktio on perusteltu. 2. **Matemaattiset vaatimukset GBDT:lle (Gradient Boosting Decision Tree):** Toisen kertaluvun tehostimissa (kuten XGBoost, LightGBM) mukautettu häviöfunktio $L(y, \hat{y})$ tarvitsee yleensä laskettavissa olevat ensimmäisen kertaluvun gradientit ($g_i = \partial L / \partial \hat{y}_i$) ja kelvolliset toisen kertaluvun kaarevuus-/Hessian-arvot ($h_i = \partial^2 L / \partial \hat{y}_i^2$) jakamisen hyödyn ja lehtien painojen laskemiseen ($w^* = -\sum g_i / (\sum h_i + \lambda)$). Hessianien tulee olla ei-negatiivisia tai turvallisesti approksimoituja/rajattuja numeerisen vakauden vuoksi; jotkin toteutukset tukevat ensimmäisen kertaluvun tai approksimoituja tavoitteita, joten vaatimus on kehyskohtainen. Epäderivoituvia tai epäjatkuvia liiketoimintamittareita tulisi korvata sileillä surrogaattilähennyksillä (esim. Huber- tai log-cosh-versiot).

import numpy as np
import xgboost as xgb

def asymmetric_mse_objective(preds, dtrain):
    labels = dtrain.get_label()
    residual = preds - labels
    # Penalize underestimation (residual < 0) 5x more heavily than overestimation
    penalty = np.where(residual < 0, 5.0, 1.0)
    grad = 2.0 * penalty * residual
    hess = 2.0 * penalty
    return grad, hess

# Usage:
# model = xgb.train(params, dtrain, obj=asymmetric_mse_objective)
Kokeile vastata tähän kysymykseen tekoälyvalmentajan kanssa

15Mikä on LambdaMART, ja miten se soveltaa gradienttipotkurointia (gradient boosting) oppimiseen järjestämistavoitteita (learning-to-rank objectives) varten?

LambdaMART on Learning-to-Rank (LTR) -algoritmi, joka yhdistää MART:n (Multiple Additive Regression Trees / Gradient Boosting) LambdaRank-viitekehykseen. Järjestämisessä kohdemetriikat kuten NDCG (Normalized Discounted Cumulative Gain) ja MAP riippuvat diskreetistä järjestysjärjestyksestä (sijoituksista), mikä tekee niistä lähes kaikkialla tasaisia ja ei-derivoituvia jatkuvien mallipisteiden suhteen. LambdaMART ohittaa tämän rakentamalla virtuaalisia gradientteja, joita kutsutaan 'lambda-gradienteiksi' ($\lambda_{ij}$), pareittain kohteille $(i, j)$ saman kyselyn sisällä. Perusparittainen gradientti tulee logistisesta häviöfunktiosta pisteytyserojen ($s_i - s_j$) perusteella. LambdaMART skaalaa tätä gradienttia kohdejärjestysmetriikan tarkan muutoksen ($|\Delta \text{NDCG}_{ij}|$) avulla, joka tapahtuisi, jos dokumenttien $i$ ja $j$ paikat vaihdettaisiin: $$\lambda_{ij} = \frac{-\sigma}{1 + e^{\sigma(s_i - s_j)}} |\Delta \text{NDCG}_{ij}|$$ Jokaiselle yksittäiselle dokumentille $i$ lasketaan kokonaisgradientti yhdistämällä parittaiset lambdat kaikkien dokumenttiin $i$ liittyvien parien yli: $\lambda_i = \sum_{j: j \succ i} \lambda_{ij} - \sum_{k: i \succ k} \lambda_{ki}$. Standardit regressiopuut potkurointikokoonpanossa sovittavat sitten näitä yhdistettyjä dokumenttikohtaisia lambda-gradientteja jokaisella potkurointikierroksella, optimoiden suoraan listaperustaisia järjestysmetriikoita.

import lightgbm as lgb
import numpy as np

# Simulated query-grouped data: 2 queries with 3 docs each
X = np.random.randn(6, 10)
y = np.array([3, 1, 0, 2, 0, 1])  # Relevance grades (0-3)
group = [3, 3]                     # Query group sizes

train_data = lgb.Dataset(X, label=y, group=group)
params = {
    'objective': 'lambdarank',
    'metric': 'ndcg',
    'ndcg_eval_at': [1, 3],
    'learning_rate': 0.1,
    'n_estimators': 50
}

ranker = lgb.train(params, train_data)
Kokeile vastata tähän kysymykseen tekoälyvalmentajan kanssa