Skip to content

Commit

Permalink
ct0509: Add notes
Browse files Browse the repository at this point in the history
  • Loading branch information
alek3y committed Sep 28, 2024
1 parent ca4c8fa commit 8a4ec4c
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@

# Terzo anno

- [Data e web mining](./ct0509/README.md)
- [Supervised learning](./ct0509/01/README.md)
- [k-NN](./ct0509/01/01/README.md)
- [Decision tree](./ct0509/01/02/README.md)

- [Calcolabilità e linguaggi formali](./ct0374/README.md)
- [Linguaggi regolari](./ct0374/01/README.md)

Expand Down
41 changes: 41 additions & 0 deletions src/ct0509/01/01/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# k-NN

L'algoritmo di **$k$-nearest neighbors** prevede la classe di appartenenza semplicemente scegliendo quella **più frequente** tra i $k$ _feature vector_ (i.e. input) più vicini, secondo la loro [distanza euclidea](https://en.wikipedia.org/wiki/Euclidean_distance).

Si vuole quindi **bilanciare** $k$: **non troppo piccolo** per evitare sensibilità al rumore, e **non troppo grande** per evitare costi computazionali elevati.
È anche possibile dare un **peso alla distanza** con $w = \frac{1}{d^2}$.

Uno **svantaggio** della _distanza euclidea_ è che assume che ogni _feature_ (i.e. elemento del vettore) abbia lo stesso peso.
Questo si può aggirare **scalando** ogni colonna $j$ in modo che il suo intervallo
$$
\left[\min_i A_{ij}, \max_i A_{ij}\right] \to [0, 1]
$$
con `StandardScaler` o `MinMaxScaling`, che sono però entrambi sensibili agli _outliers_.

Per esempio, usando dati casuali
```python
from sklearn.datasets import make_blobs
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
from sklearn.neighbors import KNeighborsClassifier

X, X_labels = make_blobs(
n_samples=1000, centers=4,
random_state=12345
)
X_train, X_test, y_train, y_test = train_test_split(
X, X_labels,
test_size=0.33,
random_state=42
)

scaler = MinMaxScaler()
scaler.fit(X_train)

kNN = KNeighborsClassifier(n_neighbors=10)
kNN.fit(scaler.transform(X_train), y_train)
y_pred = kNN.predict(scaler.transform(X_test))
```
si ottiene la seguente classificazione, con le `X` normalizzate in $[0, 1]$:

![Scatter plot dei confini dei data point normalizzati](assets/01.png)
Binary file added src/ct0509/01/01/assets/01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
97 changes: 97 additions & 0 deletions src/ct0509/01/02/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Decision tree


Attraverso gli _alberi decisionali_ è possibile **partizionare** il dominio dei _feature vector_ in base ai valori delle singole _feature_ (i.e. componenti), e a dei **threshold** che rappresentano il confine di suddivisione.

Per esempio, dallo spazio con _feature vector_ $(x, y)$

![Dati in cluster opposti](assets/01.png)

si può ricavare un albero simile a:

```dot process
digraph {
node [shape=box]
edge [arrowsize=0.8]
0 [label="x ≤ 1.4" style=rounded]
1 [label="y ≤ 1.5" style=rounded]
2 [label="y ≤ 1.45" style=rounded]
3 [label="Purple"]
4 [label="Yellow"]
5 [label="Yellow"]
6 [label="Purple"]
{
rank=same
_0 [shape=point width=0 height=inf style=invis]
1 -> _0 -> 2 [style=invis]
}
{
rank=same
_1, _2 [shape=point width=0 height=inf style=invis]
3 -> _1 -> 4 [style=invis]
5 -> _2 -> 6 [style=invis]
}
0 -> _0 [style=invis weight=100]
1 -> _1 [style=invis weight=100]
2 -> _2 [style=invis weight=100]
0 -> 1 [label="True"]
0 -> 2 [label="False"]
1 -> 3 [label="True"]
1 -> 4 [label="False"]
2 -> 5 [label="True"]
2 -> 6 [label="False"]
}
```

## Classificazione

Un algoritmo per la costruzione dell'albero è l'**algoritmo di Hunt** ricorsivo, che prende un _dataset_ $\mathcal{D}$:
```c
build_tree(D)
best_split, best_gain = null
for each feature f
for each threshold t
gain = split_goodness(f, t) // Riduzione dell'errore partizionando per (f <= t)
if gain >= best_gain
best_gain = gain
best_split = (f, t)

if best_gain = 0 or other_stopping_criterion
μ = best_prediction(D) // Media/moda delle y di D per regressione/classificazione
return Leaf(μ)

f, t = best_split
L = build_tree(filter(D, x : x[f] <= t))
R = build_tree(filter(D, x : x[f] > t))
return Node(L, R)
```
### Foglie
La `best_prediction(D)` è definita come la moda delle $y$ di $(x, y) \in \mathcal{D}$, ovvero il **label più frequente**:
$$
\argmin_\mu \mathrm{Error}(\mathcal{D}, \mu) =
\argmin_\mu \frac{1}{|\mathcal{D}|} \sum_{(x, y) \in \mathcal{D}}
\begin{cases}
0 & \text{se } \mu = y \\
1 & \text{altrimenti}
\end{cases}
$$
Per semplicità, d'ora in poi si definisce $\mathrm{Error}(\mathcal{D})$ come l'errore della miglior previsione $\mu$ di $\mathcal{D}$.
### Nodi interni
La qualità del taglio $(f, t)$ è dato dal **guadagno** dell'errore che si ha tagliando $\mathcal{D}$ rispetto a non tagliare:
$$
\mathrm{Gain}((f, t), \mathcal{D}) = \mathrm{Error}(\mathcal{D}) -
\left(
\frac{|\mathcal{D}_L|}{|\mathcal{D}|} \mathrm{Error}(\mathcal{D}_L) +
\frac{|\mathcal{D}_R|}{|\mathcal{D}|} \mathrm{Error}(\mathcal{D}_R)
\right)
$$
Binary file added src/ct0509/01/02/assets/01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions src/ct0509/01/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Supervised learning

L'_apprendimento supervisionato_ avviene istruendo un modello **conoscendo gli output** voluti a priori.

Tra le due principali forme di apprendimento, ci sono:
- **Classificazione**: per predire l'appartenenza a delle categorie
- **Analisi di regressione**: per stimare una funzione numerica sui dati

Il **dataset** viene suddiviso in **training** e **test** per poter **valutare** successivamente il modello, per esempio:
```python
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=42
)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
accuracy = accuracy_score(y_true=y_test, y_pred=y_pred)
```
1 change: 1 addition & 0 deletions src/ct0509/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Data e web mining

0 comments on commit 8a4ec4c

Please sign in to comment.