確率的勾配降下は、標準の勾配降下と比較して、どのように時間を節約できますか?


15

標準勾配降下法は、トレーニングデータセット全体の勾配を計算します。

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

事前に定義された数のエポックの場合、最初にパラメーターベクトルparamsに対するデータセット全体の損失関数の勾配ベクトルweights_gradを計算します。

対照的に、確率的勾配降下法は、各トレーニング例x(i)およびラベル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

SGDははるかに高速であると言われています。ただし、まだすべてのデータポイントでループが発生している場合、それがはるかに高速になる方法はわかりません。GDの勾配の計算は、各データポイントのGDの計算よりもはるかに遅いですか?

コードはここから来ます


1
2番目のケースでは、小さなバッチを使用してデータセット全体を概算します。これは通常かなりうまくいきます。混乱を招く部分は、おそらく両方のケースでエポックの数が同じであるように見えることですが、ケース2でエポックを多く必要とすることはないでしょう。 nb_epochs。引数の目的で、GD nb_epochs = SGD examples * nb_epochsとすると、ループの総数は同じになりますが、勾配の計算はSGDの方が高速です。
ニマムサビ

CVに関するこの回答は、適切で関連性のあるものです。
ジュバル

回答:


23

短い答え:

  • 多くのビッグデータ設定(たとえば数百万のデータポイント)では、すべてのデータポイントを合計する必要があるため、コストまたは勾配の計算に非常に長い時間がかかります。
  • 特定の反復でコストを削減するために、正確な勾配を持つ必要はありません。勾配の近似は問題なく機能します。
  • 確率的勾配降下(SGD)は、1つのデータポイントのみを使用して勾配を近似します。そのため、勾配を評価すると、すべてのデータを合計するのに比べて時間を大幅に節約できます。
  • 「合理的な」反復回数(この回数は数千で、データポイントの数(数百万)よりもはるかに少ない可能性があります)では、確率的勾配まともな妥当な解決策が得られます。

長い答え:

私の表記法はAndrew NGの機械学習Courseraコースに従います。よく知らない場合は、こちらで講義シリーズを確認できます。

二乗損失の回帰を想定してみましょう、コスト関数は

Jθ=12m=1mhθバツy2

勾配は

dJθdθ=1m=1mhθバツyバツ

勾配まとも(GD)の場合、次のようにパラメータを更新します

θnew=θoldα1m=1mhθバツyバツ

1/mバツy

θnew=θoldαhθバツyバツ

これが時間を節約する理由です。

10億のデータポイントがあるとします。

  • GDでは、パラメーターを1回更新するために、(正確な)勾配が必要です。これには、1つの更新を実行するためにこれらの10億のデータポイントを合計する必要があります。

  • SGD では、厳密な勾配の代わりに近似勾配を取得しようとしていると考えることができます。近似は、1つのデータポイント(またはミニバッチと呼ばれる複数のデータポイント)からのものです。したがって、SGDでは、パラメーターを非常に迅速に更新できます。さらに、すべてのデータ(1エポックと呼ばれる)を「ループ」すると、実際には10億の更新があります。

秘trickは、SGDでは10億回の反復/更新は必要ないが、反復/更新ははるかに少ない、たとえば100万であり、使用するのに十分なモデルがあるということです。


アイデアをデモするためのコードを書いています。最初に正規方程式で線形システムを解き、次にSGDで解きます。次に、パラメータ値と最終目的関数値に関して結果を比較します。後で視覚化するために、調整する2つのパラメーターがあります。

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

結果:

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

パラメーターは近すぎませんが、損失値は 124.1343 そして 123.0355 とても近いです。

反復にわたるコスト関数の値は次のとおりです。損失を効果的に減らすことができることがわかります。これは、データのサブセットを使用して勾配を近似し、「十分な」結果を得ることができるという考えを示しています。

ここに画像の説明を入力してください

ここに画像の説明を入力してください

次に、2つのアプローチ間の計算の労力を確認しましょう。実験では、1000データポイントは、SDを使用して、データを合計する必要がある場合に勾配を評価します。sq_loss_gr_approxただし、SGDでは、関数は1データポイントのみを合計します。全体的に見て、アルゴリズムは以下よりも収束します300 繰り返し(注意、ではありません 1000 反復。)これは計算上の節約です。


「速度」についての議論は、ローカル最適に収束するために必要な操作/反復の数に関するものだと思いましたか?(また、その確率的勾配降下はより良い最適値に収束する傾向があります。)
GeoMatt22

私が理解した限りでは、Pythonコードで「データ」変数が同じであることを提供しました。適切なミニバッチグラジエント-コードはSDGとは異なります(正確には、データのごく一部のみを使用しています)。また、提供された説明では、SDGで合計を取り除きましたが、各データポイントの更新を計算しています。すべてのデータポイントを一度に合計するよりも、各データポイントをループしながらパラメータを更新する方法が速いことはまだわかりません。
アリーナ

@ GeoMatt22私が提供したリンクでは、「一方で、SGDがオーバーシュートし続けるため、これにより最終的に正確な最小値への収束が複雑になります。」つまり、最適な最適値に収束しません。または、私はそれを間違えましたか?
アリーナ

@Tonja私は専門家ではありませんが、たとえば、この深層学習の非常に影響力のある論文は、確率的勾配降下の「より高速で信頼性の高いトレーニング」引数を与えます。「未加工」バージョンを使用せず、さまざまな曲率推定値を使用して(座標依存の)学習率を設定することに注意してください。
GeoMatt22

1
@トンジャ、はい。勾配の「弱い」近似は機能します。同様の考え方である「勾配ブースティング」を確認できます。一方、私はアイデアをデモするためのコードを書いています。準備ができたら投稿します。
ハイタオデュ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.