Gradiente di perdita della cerniera


25

Sto cercando di implementare la discesa gradiente di base e la sto testando con una funzione di perdita della cerniera, ad esempio . Tuttavia, sono confuso sul gradiente della perdita della cerniera. Ho l'impressione che lo sialhinge=max(0,1y xw)

wlhinge={y xif y xw<10if y xw1

Ma questo non restituisce una matrice delle stesse dimensioni di x ? Pensavo che stessimo cercando di restituire un vettore di lunghezza w ? Chiaramente, ho qualcosa di confuso da qualche parte. Qualcuno può puntare nella giusta direzione qui?

Ho incluso un codice di base nel caso in cui la mia descrizione dell'attività non fosse chiara

#Run standard gradient descent
gradient_descent<-function(fw, dfw, n, lr=0.01)
{
    #Date to be used
    x<-t(matrix(c(1,3,6,1,4,2,1,5,4,1,6,1), nrow=3))
    y<-c(1,1,-1,-1)
    w<-matrix(0, nrow=ncol(x))

    print(sprintf("loss: %f,x.w: %s",sum(fw(w,x,y)),paste(x%*%w, collapse=',')))
    #update the weights 'n' times
    for (i in 1:n)
    {
      w<-w-lr*dfw(w,x,y)
      print(sprintf("loss: %f,x.w: %s",sum(fw(w,x,y)),paste(x%*%w,collapse=',')))
    }
}
#Hinge loss
hinge<-function(w,x,y) max(1-y%*%x%*%w, 0)
d_hinge<-function(w,x,y){ dw<-t(-y%*%x); dw[y%*%x%*%w>=1]<-0; dw}
gradient_descent(hinge, d_hinge, 100, lr=0.01)

Aggiornamento: mentre la risposta di seguito ha aiutato la mia comprensione del problema, l'output di questo algoritmo è ancora errato per i dati forniti. La funzione di perdita si riduce di 0,25 ogni volta ma converge troppo velocemente e i pesi risultanti non determinano una buona classificazione. Attualmente l'output è simile

#y=1,1,-1,-1
"loss: 1.000000, x.w: 0,0,0,0"
"loss: 0.750000, x.w: 0.06,-0.1,-0.08,-0.21"
"loss: 0.500000, x.w: 0.12,-0.2,-0.16,-0.42"
"loss: 0.250000, x.w: 0.18,-0.3,-0.24,-0.63"
"loss: 0.000000, x.w: 0.24,-0.4,-0.32,-0.84"
"loss: 0.000000, x.w: 0.24,-0.4,-0.32,-0.84"
"loss: 0.000000, x.w: 0.24,-0.4,-0.32,-0.84"
...  

Il gradiente è un vettore poiché la tua funzione di perdita ha valori reali.
Wok,

3
la tua funzione non è differenziabile ovunque.
Robin Girard,

2
Come nota il pettirosso, la perdita della cerniera non è differenziabile in x = 1. Questo significa solo che è necessario utilizzare l'algoritmo di discesa sub-gradiente
Alex Kreimer

Risposte:


27

Per ottenere il gradiente differenziamo la perdita rispetto al ° componente del .wiw

Riscrivere perdita cerniera in termini di come dove ef ( g ( w ) ) f ( z ) = max ( 0 , 1 - y z ) g ( w ) = xwwf(g(w))f(z)=max(0,1y z)g(w)=xw

Usando la regola della catena otteniamo

wif(g(w))=fzgwio

Il primo termine derivato viene valutato in diventando quando e 0 quando . Il secondo termine derivato diventa . Quindi alla fine ottieni g(w)=xwyxw<1xw>1xi

f(g(w))wi={y xiif y xw<10if y xw>1

Poiché spazia tra i componenti di , puoi visualizzare quanto sopra come quantità vettoriale e scrivere come scorciatoia perixw(w1,w2,)


Grazie! Questo mi chiarisce le cose. Ora devo solo farlo bene in un ambiente pratico. Non hai idea del perché il codice sopra non funziona? Sembra convergere in 4 iterazioni con la perdita che inizia da 1 e scende ogni volta a 0,25 e converge a 0. Tuttavia, i pesi che produce sembrano piuttosto sbagliati.
brcs,

1
È possibile verificare quali previsioni fornisce ai dati di allenamento. Se la perdita scende a zero, tutte le istanze dovrebbero essere classificate perfettamente
Yaroslav Bulatov,

Questo è il caso della classificazione binaria. Potresti fornire la derivazione per il gradiente della classificazione multi classe usando la perdita della cerniera?
Shyamkkhadka,

12

È in ritardo di 3 anni, ma potrebbe essere ancora rilevante per qualcuno ...

Sia un campione di punti x iR d e l'insieme delle etichette corrispondenti y i{ - 1 , 1 } . Cerchiamo di trovare un iperpiano w che minimizzi la perdita totale della cerniera: w = argmin  w L h i n g e S ( w ) =SxiRdyi{1,1}w Per trovare w prendere la derivata della perdita totale della cerniera. Il gradiente di ciascun componente è: l h i n g e

w=argmin wLShinge(w)=argmin wilhinge(w,xi,yi)=argmin wimax{0,1yiwx}
w
lhingew={0yiwx1yixyiwx<1

Il gradiente della somma è una somma di gradienti. Esempio Python, che utilizza GD per trovare l'iperpiano di separazione ottimale per perdita di cerniera (probabilmente non è il codice più efficiente, ma funziona)

LShingew=ilhingew
import numpy as np
import matplotlib.pyplot as plt

def hinge_loss(w,x,y):
    """ evaluates hinge loss and its gradient at w

    rows of x are data points
    y is a vector of labels
    """
    loss,grad = 0,0
    for (x_,y_) in zip(x,y):
        v = y_*np.dot(w,x_)
        loss += max(0,1-v)
        grad += 0 if v > 1 else -y_*x_
    return (loss,grad)

def grad_descent(x,y,w,step,thresh=0.001):
    grad = np.inf
    ws = np.zeros((2,0))
    ws = np.hstack((ws,w.reshape(2,1)))
    step_num = 1
    delta = np.inf
    loss0 = np.inf
    while np.abs(delta)>thresh:
        loss,grad = hinge_loss(w,x,y)
        delta = loss0-loss
        loss0 = loss
        grad_dir = grad/np.linalg.norm(grad)
        w = w-step*grad_dir/step_num
        ws = np.hstack((ws,w.reshape((2,1))))
        step_num += 1
    return np.sum(ws,1)/np.size(ws,1)

def test1():
    # sample data points
    x1 = np.array((0,1,3,4,1))
    x2 = np.array((1,2,0,1,1))
    x  = np.vstack((x1,x2)).T
    # sample labels
    y = np.array((1,1,-1,-1,-1))
    w = grad_descent(x,y,np.array((0,0)),0.1)
    loss, grad = hinge_loss(w,x,y)
    plot_test(x,y,w)

def plot_test(x,y,w):
    plt.figure()
    x1, x2 = x[:,0], x[:,1]
    x1_min, x1_max = np.min(x1)*.7, np.max(x1)*1.3
    x2_min, x2_max = np.min(x2)*.7, np.max(x2)*1.3
    gridpoints = 2000
    x1s = np.linspace(x1_min, x1_max, gridpoints)
    x2s = np.linspace(x2_min, x2_max, gridpoints)
    gridx1, gridx2 = np.meshgrid(x1s,x2s)
    grid_pts = np.c_[gridx1.ravel(), gridx2.ravel()]
    predictions = np.array([np.sign(np.dot(w,x_)) for x_ in grid_pts]).reshape((gridpoints,gridpoints))
    plt.contourf(gridx1, gridx2, predictions, cmap=plt.cm.Paired)
    plt.scatter(x[:, 0], x[:, 1], c=y, cmap=plt.cm.Paired)
    plt.title('total hinge loss: %g' % hinge_loss(w,x,y)[0])
    plt.show()

if __name__ == '__main__':
    np.set_printoptions(precision=3)
    test1()

Questo è il caso della classificazione binaria. Potresti fornire la derivazione per il gradiente della classificazione multi classe usando la perdita della cerniera?
Shyamkkhadka,

1

Ho corretto il tuo codice. Il problema principale è la definizione delle funzioni hinge e d_hinge. Questi dovrebbero essere applicati un campione alla volta. Invece la tua definizione aggrega tutti i campioni prima di ottenere il massimo.

#Run standard gradient descent
gradient_descent<-function(fw, dfw, n, lr=0.01)
{
    #Date to be used
    x<-t(matrix(c(1,3,6,1,4,2,1,5,4,1,6,1), nrow=3))
    y<-t(t(c(1,1,-1,-1)))
    w<-matrix(0, nrow=ncol(x))


    print(sprintf("loss: %f,x.w: %s",sum(mapply(function(xr,yr) fw(w,xr,yr), split(x,row(x)),split(y,row(y)))),paste(x%*%w, collapse=',')))
    #update the weights 'n' times
    for (i in 1:n)
    {
      w<-w-lr*dfw(w,x,y)
      print(sprintf("loss: %f,x.w: %s",sum(mapply(function(xr,yr) fw(w,xr,yr), split(x,row(x)),split(y,row(y)))),paste(x%*%w,collapse=',')))
    }
}

#Hinge loss
hinge<-function(w,xr,yr) max(1-yr*xr%*%w, 0)
d_hinge<-function(w,x,y){ dw<- apply(mapply(function(xr,yr) -yr * xr * (yr * xr %*% w < 1),split(x,row(x)),split(y,row(y))),1,sum); dw}
gradient_descent(hinge, d_hinge, 100, lr=0.01)

Ho bisogno di n = 10000 per convergere.

[1] "perdita: 0,090000, xw: 1,08999999999995,0,909999999999905, -1,19000000000008, -1,69000000000011" perdita [1] ": 0,100000, xw: 1,33999999999995,1,119999999999999995, 110000000000, -1,4200 0.939999999999948,0.829999999999905, -1,32000000000007, -1,77000000000011" [1] "perdita: 0,370 mila, XW: 1.64999999999995,1.2899999999999, -,630000000000075, -1,25000000000011" [1] "perdita: 0.000000, XW: 1.24999999999995,0.999999999999905, -1,05000000000008, -1,60000000000011" [1] "perdita: 0,240000, xw: 1,49999999999995,1,2099999999999, -0,760000000000075, -1,33000000000011" [1] "perdita: 0,080000, xw: 1,09999999999995,0,91999999999990, -1,18000000000000000000000000 1.34999999999995,1.1299999999999, -,890000000000075, -1,41000000000011"[1] "perdita: 0,210000, xw: 0,949999999999948,0,839999999999905, -1,31000000000007, -1,76000000000011" [1] "perdita: 0,380000, xw: 1,65999999999995,1,29999999999, -0,60000000000 1, 0,000000 1 1.25999999999995,1.0099999999999, -1.04000000000008, -1.5900000000000011 Perdita "[1]": 0.000000, xw: 1.25999999999995,1.0099999999999, -1.04000000000008, -1.59000000000011 "


3
Popoli, la discesa gradiente è quasi l'algoritmo di ottimizzazione peggiore che ci sia e dovrebbe essere usato solo quando non c'è scelta. Un algoritmo Quasi-Newton per la ricerca di una regione di fiducia o di una linea, utilizzando il valore e il gradiente della funzione oggettiva, farà esplodere la discesa del gradiente dall'acqua e convergere in modo molto più affidabile. E non scrivere il tuo solutore a meno che tu non sappia cosa stai facendo, cosa che fanno pochissime persone.
Mark L. Stone,

2
Sono d'accordo con entrambe le dichiarazioni. Tuttavia la discesa gradiente con vari gusti è molto più semplice da implementare in un ambiente distribuito, almeno secondo le librerie open source disponibili là fuori.
John Jiang,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.