Коротка відповідь:
- У багатьох великих параметрах даних (скажімо, кілька мільйонів точок даних) обчислення вартості або градієнта займає дуже багато часу, тому що нам потрібно підсумовувати всі точки даних.
- Нам НЕ потрібно мати точний градієнт, щоб зменшити вартість в заданій ітерації. Деякі наближення градієнта спрацює нормально.
- Стохастичний градієнт пристойний (SGD) наближає градієнт, використовуючи лише одну точку даних. Отже, оцінка градієнта економить багато часу в порівнянні з підбиттям даних за всіма даними.
- При "розумній" кількості ітерацій (ця кількість може бути пару тисяч і набагато менша, ніж кількість точок даних, яка може бути мільйонами), стохастичний градієнт пристойний може отримати розумне хороше рішення.
Довга відповідь:
Моє позначення слідує за курсом машинного навчання Ендрю Н.Г. Якщо ви не знайомі з цим, ви можете переглянути серію лекцій тут .
Припустимо, регрес на збиток у квадраті, функція витрат така
J( θ ) = 12 м∑i = 1м( годθ( х( i )) - у( i ))2
а градієнт -
гJ( θ )гθ= 1м∑i = 1м( годθ( х( i )) - у( i )) х( i )
для градієнта пристойного (GD), ми оновлюємо параметр на
θн е ш= θo l d- α 1м∑i = 1м( годθ( х( i )) - у( i )) х( i )
1 / мх( i ), у( i )
θн е ш= θo l d- α ⋅ ( годθ( х( 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_approx
3001000