Regressione lineare in Go - Parte 2
Nel post precedente abbiamo esaminato la funzione di ipotesi, ovvero l’espressione matematica che permette di prevedere un valore a partire da un insieme di caratteristiche per un nuovo caso ignoto. In questo articolo costruiremo invece la funzione di costo, uno strumento utile a misurare l’errore della funzione di previsione dato uno specifico insieme di pesi.
Per comodità, riproponiamo la funzione discussa in precedenza:
Come già accennato, la regressione lineare è un algoritmo supervisionato; ciò significa che è necessario addestrarlo mediante una lista di esempi, allo scopo di individuare i valori del vettore in corrispondenza dei quali l’errore medio risulta ridotto al minimo (più avanti affronteremo il problema di una distorsione molto comune nota come overfitting).
La funzione di costo serve a calcolare l’errore associato a un determinato insieme rispetto a un set di addestramento. Nel grafico seguente è rappresentato un sottoinsieme degli esempi precedenti, limitato a sole 5 abitazioni. La linea verde costituisce il tracciato della funzione di ipotesi; le sottili linee rosse indicano invece la differenza tra un valore presente nel set di addestramento e il valore previsto dalla nostra ipotesi:
Per calcolare l’errore si ricorre alla seguente funzione:
Si tratta, in sostanza, della media dei quadrati della differenza tra il valore previsto e il valore effettivo .
Bisogna considerare che ora è una matrice di dimensioni , dove rappresenta il numero di esempi contenuti nel set di addestramento (nel grafico qui riportato abbiamo 5 abitazioni) e indica il numero di caratteristiche (come le dimensioni della casa, il numero di bagni, il numero di stanze da letto e così via).
Ecco dunque la relativa implementazione in Go, realizzata utilizzando le matrici della libreria gonum:
func Cost(x *mat64.Dense, y, theta *mat64.Vector) float64 {
//initialize receivers
m, _ := x.Dims()
h := mat64.NewDense(m, 1, make([]float64, m))
squaredErrors := mat64.NewDense(m, 1, make([]float64, m))
//actual calculus
h.Mul(x, theta)
squaredErrors.Apply(func(r, c int, v float64) float64 {
return math.Pow(h.At(r, c)-y.At(r, c), 2)
}, h)
j := mat64.Sum(squaredErrors) * 1.0 / (2.0 * float64(m))
return j
}
Come di consueto, il codice completo è disponibile qui, mentre a questo indirizzo si trova un test.
Nella terza parte verrà costruito il metodo che consente di minimizzare l’errore scegliendo i valori adeguati di . La terza parte può essere consultata qui.