Gradiente de pérdida de bisagra

25

Estoy tratando de implementar un descenso de gradiente básico y lo estoy probando con una función de pérdida de bisagra, es decir, lhinge=max(0,1y xw) . Sin embargo, estoy confundido sobre el gradiente de la pérdida de la bisagra. Tengo la impresión de que es

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

Pero, ¿no devuelve esto una matriz del mismo tamaño que x ? Pensé que estábamos buscando devolver un vector de longitud w ? Claramente, tengo algo confundido en alguna parte. ¿Alguien puede apuntar en la dirección correcta aquí?

He incluido un código básico en caso de que mi descripción de la tarea no estuviera clara

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

Actualización: Si bien la respuesta a continuación me ayudó a comprender el problema, la salida de este algoritmo sigue siendo incorrecta para los datos dados. La función de pérdida se reduce en 0.25 cada vez, pero converge demasiado rápido y los pesos resultantes no resultan en una buena clasificación. Actualmente la salida se ve así

#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
fuente
El gradiente es un vector ya que su función de pérdida tiene valores reales.
Wok
3
Su función no es diferenciable en todas partes.
robin girard
2
Como Robin observa que la pérdida de bisagra no es diferenciable en x = 1. Esto solo significa que necesita usar el algoritmo de descenso de subdegradado
Alex Kreimer

Respuestas:

27

Para obtener el gradiente, diferenciamos la pérdida con respecto al componente de .wiw

Reescribe la pérdida de la bisagra en términos de como donde yf ( 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 regla de la cadena obtenemos

wif(g(w))=fzgwi

El primer término derivado se evalúa en convirtiéndose en cuando , y 0 cuando . El segundo término derivado se convierte en . Entonces, al final obtienes - 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

Como extiende sobre los componentes de , puede ver lo anterior como una cantidad vectorial y escribir como abreviatura dex ix (w(w1,w2,)

Yaroslav Bulatov
fuente
¡Gracias! Eso me aclara las cosas. Ahora solo tengo que hacerlo bien en un entorno práctico. ¿No tiene idea de por qué el código anterior no funciona? Parece converger en 4 iteraciones con la pérdida comenzando en 1 y bajando 0.25 cada vez y convergiendo en 0. Sin embargo, los pesos que produce parecen bastante erróneos.
brcs
1
Puede verificar qué predicciones da a sus datos de entrenamiento. Si la pérdida se reduce a cero, todas las instancias deben clasificarse perfectamente
Yaroslav Bulatov
Este es el caso de la clasificación binaria. ¿Podría dar una derivación para el gradiente de clasificación de múltiples clases utilizando la pérdida de bisagra?
Shyamkkhadka
12

Esto lleva 3 años de retraso, pero aún puede ser relevante para alguien ...

Supongamos que denota una muestra de puntos y el conjunto de etiquetas correspondientes . Buscamos encontrar un hiperplano que minimice la pérdida total de la bisagra: Para encontrar tome la derivada de la pérdida total de la bisagra. El gradiente de cada componente es: x iR d y i{ - 1 , 1 } w w = argmin  w L h i n g e S ( w ) = argmin  w i l h i n g e ( w , x i , y i ) = argmin  w i max { 0 ,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

El gradiente de la suma es una suma de gradientes. Ejemplo de Python, que usa GD para encontrar sigue el hiperplano de separación óptimo de pérdida de bisagra (probablemente no sea el código más eficiente, pero funciona)

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
fuente
Este es el caso de la clasificación binaria. ¿Podría dar una derivación para el gradiente de clasificación de múltiples clases utilizando la pérdida de bisagra?
Shyamkkhadka
1

Arregle tu código. El principal problema es su definición de funciones de bisagra y d_hinge. Estos deben aplicarse una muestra a la vez. En cambio, su definición agrega todas las muestras antes de tomar el máximo.

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

Necesito n = 10000 para converger.

[1] "pérdida: 0.090000, xw: 1.08999999999995,0.909999999999905, -1,19000000000008, -1,69000000000011" [1] "pérdida: 0.100000, xw: 1.33999999999995,1.1199999999999, -0,900000000000075, -1,42000000000011" [1] "pérdida: 0.230000, xw: .109. [1] "pérdida: 0.240000, xw: 1.49999999999995,1.2099999999999, -0.760000000000075, -1.33000000000011" [1] "pérdida: 0.080000, xw: 1.09999999999995,0.919999999999905, -1.18000000000007, -1.6800", 1: 800 ", 1: 800" 1.34999999999995,1.1299999999999, -0.890000000000075, -1.41000000000011 "[1] "pérdida: 0.210000, xw: 0.949999999999948,0.839999999999905, -1,31000000000007, -1,76000000000011" [1] "pérdida: 0.380000, xw: 1.65999999999995,1.2999999999999, -0,620000000000074, -1,24000000000011" [1] "pérdida: 0.000000, xw: 1.25999999999995,1.0099999999999, -1.04000000000008, -1.59000000000011 "[1]" pérdida: 0.000000, xw: 1.25999999999995,1.0099999999999, -1.04000000000008, -1.59000000000011 "

John Jiang
fuente
3
Pueblos, el descenso de gradiente es casi el PEOR algoritmo de optimización que existe, y debe usarse solo cuando no hay otra opción. Un algoritmo Cuasi-Newton de búsqueda de región o línea de confianza, que utiliza el valor de función objetivo y el gradiente, expulsará el descenso del gradiente fuera del agua y convergerá de manera mucho más confiable. Y no escriba su propio solucionador a menos que sepa lo que está haciendo, que muy pocas personas saben.
Mark L. Stone
2
Estoy de acuerdo con ambas declaraciones. Sin embargo, el descenso de gradiente con varios sabores es mucho más fácil de implementar en un entorno distribuido, al menos de acuerdo con las bibliotecas de código abierto disponibles.
John Jiang