Як стохастичний градієнтний спуск може заощадити час порівняно зі звичайним градієнтним спуском?


16

Стандартний градієнт спуск обчислює градієнт для всього навчального набору даних.

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

Для заздалегідь визначеної кількості епох спочатку обчислюємо градієнтний вектор 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. "Гіперпараметри" були б різними для цих двох методів: GD nb_epochs! = SGD nb_epochs. Скажімо для цілей аргументу: GD nb_epochs = приклади SGD * nb_epochs, так що загальна кількість циклів однакова, але обчислення градієнта в SGD швидше.
Німа Мусаві

Ця відповідь на резюме хороша і споріднена.
Жубарб

Відповіді:


24

Коротка відповідь:

  • У багатьох великих параметрах даних (скажімо, кілька мільйонів точок даних) обчислення вартості або градієнта займає дуже багато часу, тому що нам потрібно підсумовувати всі точки даних.
  • Нам НЕ потрібно мати точний градієнт, щоб зменшити вартість в заданій ітерації. Деякі наближення градієнта спрацює нормально.
  • Стохастичний градієнт пристойний (SGD) наближає градієнт, використовуючи лише одну точку даних. Отже, оцінка градієнта економить багато часу в порівнянні з підбиттям даних за всіма даними.
  • При "розумній" кількості ітерацій (ця кількість може бути пару тисяч і набагато менша, ніж кількість точок даних, яка може бути мільйонами), стохастичний градієнт пристойний може отримати розумне хороше рішення.

Довга відповідь:

Моє позначення слідує за курсом машинного навчання Ендрю Н.Г. Якщо ви не знайомі з цим, ви можете переглянути серію лекцій тут .

Припустимо, регрес на збиток у квадраті, функція витрат така

J(θ)=12мi=1м(годθ(х(i))-у(i))2

а градієнт -

гJ(θ)гθ=1мi=1м(годθ(х(i))-у(i))х(i)

для градієнта пристойного (GD), ми оновлюємо параметр на

θнеш=θолг-α1мi=1м(годθ(х(i))-у(i))х(i)

1/мх(i),у(i)

θнеш=θолг-α(годθ(х(i))-у(i))х(i)

Ось чому ми економимо час:

Припустимо, у нас є 1 мільярд точок даних.

  • У GD, для того, щоб оновити параметри один раз, нам потрібно мати (точний) градієнт. Для цього потрібно підсумувати ці 1 мільярд точок даних, щоб виконати 1 оновлення.

  • У SGD ми можемо вважати це намаганням отримати приблизний градієнт замість точного градієнта . Наближення відбувається від однієї точки даних (або декількох точок даних, званих міні-пакет). Тому в SGD ми можемо дуже швидко оновлювати параметри. Окрім того, якщо ми "прокручуємо" всі дані (звані однією епохою), ми фактично маємо 1 мільярд оновлень.

Хитрість полягає в тому, що в SGD вам не потрібно мати 1 мільярд ітерацій / оновлень, але набагато менше ітерацій / оновлень, скажімо, 1 мільйон, і ви будете мати «достатньо хорошу» модель для використання.


Я пишу код для демонстрації ідеї. Спочатку розв'язуємо лінійну систему за звичайним рівнянням, потім розв'язуємо її за допомогою 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.1343123.0355

Ось значення вартості функції за ітераціями, ми бачимо, що це може ефективно зменшити втрати, що ілюструє ідею: ми можемо використовувати підмножину даних для наближення градієнта та отримання «досить хороших» результатів.

введіть тут опис зображення

введіть тут опис зображення

1000sq_loss_gr_approx3001000


Я думав, що аргумент про "швидкість" - це більше про те, скільки операцій / ітерацій потрібно, щоб сходити до локального оптимуму? (А також те, що стохастичний градієнтний спуск має тенденцію до сходу до кращої оптими.)
GeoMatt22

Наскільки я зрозумів, в коді python я вказав "data" -змінний той самий. Міні-пакетний градієнт пристойний - код відрізняється від SDG (і саме там він використовує лише невелику частину даних). Крім того, у наданому вами поясненні, хоча ми позбавляємося суми в SDG, ми все ще обчислюємо оновлення для кожної точки даних. Я досі не розумію, як оновлювати параметр під час циклу за кожною точкою даних швидше, ніж просто взяти суму за всі точки даних одразу.
Аліна

@ GeoMatt22 У посиланні, яке я надав, зазначено: "З іншого боку, це в кінцевому рахунку ускладнює конвергенцію до точного мінімуму, оскільки SGD буде продовжувати перевищувати". Це означає, що вона не сходить до кращої оптими. Або я помилився?
Аліна

@Tonja Я не експерт, але, наприклад, ця дуже впливова робота в глибокому навчанні дає аргумент "швидше і надійніше навчання" для стохастичного градієнтного спуску. Зауважте, що він не використовує "сировинну" версію, але використовує різні оцінки кривизни для встановлення (залежно від координати) швидкості навчання.
GeoMatt22

1
@Tonja, так. будь-яке "слабке" наближення градієнта спрацює. Ви можете перевірити "збільшення градієнта", що схожа ідея. З іншого боку, я пишу якийсь код, щоб демонструвати ідею. Я опублікую його, коли він буде готовий.
Хайтао Дю
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.