Gradiente de perda de dobradiça

25

Estou tentando implementar a descida básica do gradiente e estou testando-a com uma função de perda de dobradiça, ou seja, lhinge=max(0,1y xw) . No entanto, estou confuso sobre o gradiente da perda de dobradiça. Estou com a impressão de que é

wlhinge={y xif y xW<1 10 0E se y xW1 1

Mas isso não retorna uma matriz do mesmo tamanho que x ? Eu pensei que estávamos procurando retornar um vetor de comprimento W ? Claramente, tenho algo confuso em algum lugar. Alguém pode apontar na direção certa aqui?

Incluí algum código básico, caso minha descrição da tarefa não esteja 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)

Atualização: embora a resposta abaixo tenha me ajudado a entender o problema, a saída desse algoritmo ainda está incorreta para os dados fornecidos. A função de perda é reduzida em 0,25 a cada vez, mas converge muito rápido e os pesos resultantes não resultam em uma boa classificação. Atualmente, a saída parece

#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
fonte
O gradiente é um vetor, pois sua função de perda possui valores reais.
Wok
3
sua função não é diferenciável em todos os lugares.
Robin girard
2
Como robin observa, a perda de dobradiça não é diferenciável em x = 1. Isto apenas significa que você precisa usar sub-gradiente de descida algoritmo
Alex Kreimer

Respostas:

27

Para obter o gradiente, diferenciamos a perda em relação ao componente de .wiw

Reescreva a perda de dobradiça em termos de como que 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 a regra da cadeia, obtemos

wif(g(w))=fzgwi

O primeiro termo derivado é avaliado em se tornando quando e 0 quando . O segundo termo derivado torna-se . Portanto, no final, você obtém g(w)=xwyxw<1xw>1xi

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

Como varia sobre os componentes de , você pode ver o acima como uma quantidade vetorial e escrever como abreviação deixw(w1,w2,)

Yaroslav Bulatov
fonte
Obrigado! Isso esclarece as coisas para mim. Agora só preciso acertar em um ambiente prático. Você não tem idéia do motivo pelo qual o código acima não funciona? Parece convergir em 4 iterações com a perda iniciando em 1 e diminuindo 0,25 a cada vez e convergindo em 0. No entanto, os pesos que produz parecem bastante errados.
Br17
11
Você pode verificar quais previsões eles dão aos seus dados de treinamento. Se a perda desce para zero, todas as instâncias devem ser classificados perfeitamente
Yaroslav Bulatov
Este é o caso da classificação binária. Você poderia, por favor, derivar o gradiente da classificação multi classe usando perda de dobradiça?
Shyamkkhadka
12

Está atrasado 3 anos, mas ainda pode ser relevante para alguém ...

Deixe denotar uma amostra dos pontos x iR d e o conjunto de etiquetas correspondentes y i{ - 1 , 1 } . Nós pesquisa para encontrar um hiperplana w que minimizaria a dobradiça-perda total: w * = argmin  w L h i n g e S ( w ) = argmin  w Σ i l h i n g e ( w ,SxiRdyi{1,1}w

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

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
fonte
Este é o caso da classificação binária. Você poderia, por favor, derivar o gradiente da classificação multi classe usando perda de dobradiça?
Shyamkkhadka
1

Eu consertei seu código. O principal problema é a sua definição de funções de dobradiça e d_hinge. Estes devem ser aplicados uma amostra de cada vez. Em vez disso, sua definição agrega todas as amostras antes de obter o 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)

Eu preciso n = 10000 para convergir.

[1] "perda: 0.090000, xw: 1.08999999999995,0.909999999999905, -1.19000000000008, -1.69000000000011" [1] "perda: 0.100000, xw: 1.33999999999995,1.1199999999999, -0.900000000000075, -1.42000000000011:", 1 x 0,939999999999948,0,829999999999905, -1,32000000000007, -1,77000000000011 "[1]" perda: 0,370000, xw: 1,64999999999995,1,2899999999999, -0,630000000000000075, -1,25000000000011 "[1]" perda: 0,00000000009999 ", 990000000099" [1] "perda: 0,240000, xw: 1,49999999999995,1.2099999999999, -0,760000000000075, -1,33000000000011" [1] "perda: 0,080000, xw: 1,09999999999995,0,91999999999999905, -1,18000000000007, -1,680000 x 011: [1] 1.34999999999995,1.1299999999999, -0.890000000000075, -1.41000000000011 "[1] "perda: 0,210000, xw: 0,949999999999948,0,839999999999905, -1,31000000000007, -1,76000000000011" [1] "perda: 0,380000, xw: 1,65999999999995,1,2999999999999, -0,620000000000074, -1,24000000000011" [1] 1.25999999999995,1.0099999999999, -1.04000000000008, -1.59000000000011 "[1]" perda: 0.000000, xw: 1.25999999999995,1.0099999999999, -1.04000000000008, -1.5900000000000011 "

John Jiang
fonte
3
Povos, descida de gradiente é praticamente o pior algoritmo de otimização que existe, e deve ser usado apenas quando não houver escolha. Um algoritmo Quasi-Newton de pesquisa de região ou linha de confiança, usando o valor da função objetivo e o gradiente, soprará a descida do gradiente para fora da água e convergirá de maneira muito mais confiável. E não escreva seu próprio solucionador, a menos que saiba o que está fazendo, o que poucas pessoas fazem.
Mark L. Stone
2
Eu concordo com as duas declarações. No entanto, a descida gradiente com vários sabores é muito mais fácil de implementar em um ambiente distribuído, pelo menos de acordo com as bibliotecas de código aberto disponíveis.
John Jiang