Gradient de perte de charnière

25

J'essaie d'implémenter une descente de gradient de base et je la teste avec une fonction de perte de charnière, c'est-à-dire . Cependant, je suis confus quant au gradient de la perte de charnière. J'ai l'impression que c'estlhinge=max(0,1y xw)

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

Mais cela ne renvoie-t-il pas une matrice de la même taille que x ? Je pensais que nous cherchions à renvoyer un vecteur de longueur w ? De toute évidence, j'ai quelque chose de confus quelque part. Quelqu'un peut-il pointer dans la bonne direction ici?

J'ai inclus un code de base au cas où ma description de la tâche n'était pas claire

#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)

Mise à jour: Alors que la réponse ci-dessous m'a aidé à comprendre le problème, la sortie de cet algorithme est toujours incorrecte pour les données données. La fonction de perte diminue de 0,25 à chaque fois mais converge trop rapidement et les poids résultants n'entraînent pas une bonne classification. Actuellement, la sortie ressemble à

#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"
...  
brcs
la source
Le gradient est un vecteur car votre fonction de perte a des valeurs réelles.
Wok
3
votre fonction n'est pas différenciable partout.
Robin Girard
2
Comme le note Robin, la perte de charnière n'est pas différenciable à x = 1. Cela signifie simplement que vous devez utiliser l'algorithme de descente en sous-gradient
Alex Kreimer

Réponses:

27

Pour obtenir le gradient, nous différencions la perte par rapport à la ème composante de .wiw

Réécrire la perte de charnière en termes de en où etf ( g ( w ) ) f ( z ) = max ( 0 , 1 - y z ) g ( w ) = xwwf(g(w))f(z)=max(0,1y z)g(w)=xw

En utilisant la règle de chaîne, nous obtenons

wif(g(w))=fzgwi

Le premier dérivé est évalué à devenant lorsque , et 0 lorsque . Le second terme dérivé devient . Donc, à la fin, vous obtenez - y xw < 1 xw > 1 x i f ( g ( w ) )g(w)=xwyxw<1xw>1xi

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

Puisque s'étend sur les composants de , vous pouvez voir ce qui précède comme une quantité vectorielle et écrire comme raccourci pourx ix (w(w1,w2,)

Yaroslav Bulatov
la source
Merci! Cela clarifie les choses pour moi. Il ne me reste plus qu'à bien faire les choses dans un cadre pratique. Vous n'auriez aucune idée pourquoi le code ci-dessus ne fonctionne pas? Il semble converger en 4 itérations avec une perte commençant à 1 et descendant à 0,25 à chaque fois et convergeant à 0. Cependant, les poids qu'il produit semblent tout à fait faux.
brcs
1
Vous pouvez vérifier quelles prévisions cela donne à vos données d'entraînement. Si la perte descend à zéro, toutes les instances doivent être parfaitement classées
Yaroslav Bulatov
C'est le cas de la classification binaire. Pourriez-vous s'il vous plaît donner une dérivation pour le gradient de la classification multi-classes en utilisant la perte de charnière?
Shyamkkhadka
12

C'est 3 ans de retard, mais cela peut quand même être pertinent pour quelqu'un ...

Soit un échantillon des points et l'ensemble des étiquettes correspondantes . Nous recherchons un hyperplan qui minimiserait la perte totale de charnière: Pour trouver prendre la dérivée de la perte totale de charnière. Le gradient de chaque composant est: SxiRdyi{1,1}ww l h i n g e

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

Le gradient de la somme est une somme de gradients. Exemple Python, qui utilise GD pour trouver l'hyperplan de séparation optimal de perte de charnière suit (ce n'est probablement pas le code le plus efficace, mais cela fonctionne)

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()
Alex Kreimer
la source
C'est le cas pour la classification binaire. Pourriez-vous s'il vous plaît donner une dérivation pour le gradient de la classification multi-classes en utilisant la perte de charnière?
Shyamkkhadka
1

J'ai corrigé ton code. Le principal problème est votre définition des fonctions charnière et d_hinge. Ceux-ci doivent être appliqués un échantillon à la fois. Au lieu de cela, votre définition regroupe tous les échantillons avant de prendre le maximum.

#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)

J'ai besoin de n = 10000 pour converger.

[1] "perte: 0,090000, xw: 1,08999999999995,0,909999999999905, -1,19000000000008, -1,69000000000011" [1] "perte: 0,100000, xw: 1,33999999999995,1.119999999999999, -0,900000000000075, -1,42000000000011" [1] "perte: 0,23 0.939999999999948,0.829999999999905, -1.32000000000007, -1.77000000000011 "[1]" perte: 0,370000, xw: 1,6499999999999995,1,2899999999999, -0,630000000000075, -1,25000000000011 "[1]" perte: 0,000000, xw: 1,2499999999999999999999999999999999 [1] "perte: 0,240000, xw: 1,499999999999999,1,19999999999999, -0,760000000000075, -1,33000000000011" [1] "perte: 0,080000, xw: 1,09999999999995,0,91999999999999905, -1,18000000000007, -1,6800000000001111" [1] "perte 1.34999999999995,1.1299999999999, -0.890000000000075, -1.41000000000011 "[1] "perte: 0,210000, xw: 0,949999999999948,0,839999999999905, -1,31000000000007, -1,76000000000011" [1] "perte: 0,380000, xw: 1,65999999999995,1,299999999999999, -0,620000000000074, -1,24000000000011:" 1 "," 1 " 1.25999999999995,1.0099999999999, -1.04000000000008, -1.59000000000011 "[1]" perte: 0.000000, xw: 1.25999999999995,1.0099999999999, -1.04000000000008, -1.59000000000011 "

John Jiang
la source
3
Peuples, la descente de gradient est à peu près l'algorithme d'optimisation le plus mauvais qui existe et ne devrait être utilisé que lorsqu'il n'y a pas de choix. Un algorithme de recherche de région ou de ligne de confiance Quasi-Newton, utilisant une valeur de fonction objective et un gradient, fera exploser la descente du gradient hors de l'eau et convergera de manière beaucoup plus fiable. Et n'écrivez pas votre propre solveur à moins de savoir ce que vous faites, ce que très peu de gens font.
Mark L. Stone
2
Je serais d'accord avec les deux déclarations. Cependant, la descente de gradient avec différentes saveurs est beaucoup plus facile à implémenter dans un environnement distribué, du moins selon les bibliothèques open source disponibles.
John Jiang