Como a descida estocástica do gradiente poderia economizar tempo em comparação com a descida padrão do gradiente?

15

A Descida de gradiente padrão calcularia o gradiente para todo o conjunto de dados de treinamento.

for i in range(nb_epochs):
  params_grad = evaluate_gradient(loss_function, data, params)
  params = params - learning_rate * params_grad

Para um número predefinido de épocas, primeiro calculamos o vetor de gradiente weights_grad da função de perda para todo o conjunto de dados, usando nossos parâmetros de vetor de parâmetros.

A descida estocástica do gradiente, por outro lado, executa uma atualização de parâmetro para cada exemplo de treinamento x (i) e o rótulo y (i).

for i in range(nb_epochs):
  np.random.shuffle(data)
  for example in data:
    params_grad = evaluate_gradient(loss_function, example, params)
    params = params - learning_rate * params_grad

Diz-se que o SGD é muito mais rápido. No entanto, não entendo como pode ser muito mais rápido se ainda tivermos um loop sobre todos os pontos de dados. O cálculo do gradiente em GD é muito mais lento que o cálculo de GD para cada ponto de dados separadamente?

O código vem daqui .

Alina
fonte
11
No segundo caso, você levaria um pequeno lote para aproximar todo o conjunto de dados. Isso geralmente funciona muito bem. Portanto, a parte confusa provavelmente é que parece que o número de épocas é o mesmo nos dois casos, mas você não precisaria de tantas épocas no caso 2. Os "hiperparâmetros" seriam diferentes para esses dois métodos: GD nb_epochs! = SGD nb_epochs. Digamos, para o propósito do argumento: GD nb_epochs = exemplos de SGD * nb_epochs, para que o número total de loops seja o mesmo, mas o cálculo do gradiente é muito mais rápido no SGD.
Nima Mousavi
Esta resposta no CV é boa e relacionada.
Zhubarb

Respostas:

23

Resposta curta:

  • Em muitas configurações de big data (digamos vários milhões de pontos de dados), o cálculo do custo ou do gradiente leva muito tempo, porque precisamos somar todos os pontos de dados.
  • Nós não precisa ter inclinação exata para reduzir o custo de uma determinada iteração. Alguma aproximação do gradiente funcionaria bem.
  • O gradiente estocástico decente (SGD) aproxima o gradiente usando apenas um ponto de dados. Portanto, avaliar o gradiente economiza muito tempo em comparação com a soma de todos os dados.
  • Com um número "razoável" de iterações (esse número pode ser alguns milhares e muito menor que o número de pontos de dados, que podem ser milhões), o gradiente estocástico decente pode obter uma boa solução razoável.

Resposta longa:

Minha anotação segue o curso Coursera de aprendizado de máquina de Andrew NG. Se você não estiver familiarizado com isso, pode rever a série de palestras aqui .

Vamos assumir a regressão na perda ao quadrado, a função de custo é

J(θ)=1 12mEu=1 1m(hθ(x(Eu))-y(Eu))2

e o gradiente é

dJ(θ)dθ=1 1mEu=1 1m(hθ(x(Eu))-y(Eu))x(Eu)

para gradiente decente (GD), atualizamos o parâmetro por

θneW=θoeud-α1 1mEu=1 1m(hθ(x(Eu))-y(Eu))x(Eu)

1 1/mx(Eu),y(Eu)

θneW=θoeud-α(hθ(x(Eu))-y(Eu))x(Eu)

Eis por que estamos economizando tempo:

Suponha que tenhamos 1 bilhão de pontos de dados.

  • No GD, para atualizar os parâmetros uma vez, precisamos ter o gradiente (exato). Isso requer a soma desses 1 bilhão de pontos de dados para executar 1 atualização.

  • No SGD, podemos pensar nisso como tentar obter um gradiente aproximado em vez do gradiente exato . A aproximação é proveniente de um ponto de dados (ou vários pontos de dados chamados mini lote). Portanto, no SGD, podemos atualizar os parâmetros muito rapidamente. Além disso, se "repetirmos" todos os dados (chamados de uma época), na verdade teremos 1 bilhão de atualizações.

O truque é que, no SGD, você não precisa ter 1 bilhão de iterações / atualizações, mas muito menos iterações / atualizações, digamos 1 milhão, e você terá um modelo "bom o suficiente" para usar.


Estou escrevendo um código para demonstrar a ideia. Primeiro resolvemos o sistema linear pela equação normal, depois resolvemos com SGD. Em seguida, comparamos os resultados em termos de valores de parâmetros e valores da função objetivo final. Para visualizá-lo mais tarde, teremos 2 parâmetros para ajustar.

set.seed(0);n_data=1e3;n_feature=2;
A=matrix(runif(n_data*n_feature),ncol=n_feature)
b=runif(n_data)
res1=solve(t(A) %*% A, t(A) %*% b)

sq_loss<-function(A,b,x){
  e=A %*% x -b
  v=crossprod(e)
  return(v[1])
}

sq_loss_gr_approx<-function(A,b,x){
  # note, in GD, we need to sum over all data
  # here i is just one random index sample
  i=sample(1:n_data, 1)
  gr=2*(crossprod(A[i,],x)-b[i])*A[i,]
  return(gr)
}

x=runif(n_feature)
alpha=0.01
N_iter=300
loss=rep(0,N_iter)

for (i in 1:N_iter){
  x=x-alpha*sq_loss_gr_approx(A,b,x)
  loss[i]=sq_loss(A,b,x)
}

Os resultados:

as.vector(res1)
[1] 0.4368427 0.3991028
x
[1] 0.3580121 0.4782659

Observe que, embora os parâmetros não estejam muito próximos, os valores de perda são 124.1343 e 123.0355 que estão muito perto.

Aqui estão os valores da função de custo nas iterações, podemos ver que ela pode efetivamente diminuir a perda, o que ilustra a idéia: podemos usar um subconjunto de dados para aproximar o gradiente e obter resultados "bons o suficiente".

insira a descrição da imagem aqui

insira a descrição da imagem aqui

Agora vamos verificar os esforços computacionais entre duas abordagens. No experimento, temos1000os pontos de dados, usando SD, avaliam o gradiente uma vez que precisa somar os dados. MAS, no SGD, a sq_loss_gr_approxfunção resume apenas 1 ponto de dados e, em geral, o algoritmo converge menos que300 iterações (observe, não 1000 iterações.) Essa é a economia computacional.

Haitao Du
fonte
Eu pensei que o argumento sobre "velocidade" é mais sobre quantas operações / iterações são necessárias para convergir para um local ideal? (E também que a descida gradiente estocástico tende a convergir para uma melhor optima.)
GeoMatt22
Tanto quanto eu entendi, no código python eu forneci a variável "data" é a mesma. O código decente do mini lote gradiente é diferente do SDG (e exatamente lá ele usa apenas uma pequena fração dos dados). Além disso, na explicação que você forneceu, embora nos livramos da soma no SDG, ainda calculamos a atualização para cada ponto de dados. Ainda não entendo como a atualização de um parâmetro durante um loop sobre cada ponto de dados é mais rápida do que apenas obter uma soma de todos os pontos de dados de uma só vez.
Alina
@ GeoMatt22 No link que forneci, declara: "Por outro lado, isso acaba complicando a convergência ao mínimo exato, pois a SGD continuará ultrapassando". Isso significa que não converge para melhores otimizações. Ou eu entendi errado?
Alina
@Tonja Não sou especialista, mas, por exemplo, este artigo altamente influente no aprendizado profundo fornece o argumento "treinamento mais rápido e confiável" para descida estocástica do gradiente. Observe que ele não usa a versão "bruta", mas usa várias estimativas de curvatura para definir a taxa de aprendizado (dependente de coordenadas).
GeoMatt22
11
@ Tonja, sim. qualquer aproximação "fraca" do gradiente funcionaria. Você pode verificar "aumento de gradiente", que é uma idéia semelhante. Por outro lado, estou escrevendo algum código para demonstrar a ideia. Vou publicá-lo quando estiver pronto.
Haitao Du