Стохастический градиентный спуск — различия между версиями
Nikitaevg (обсуждение | вклад) м |
м (rollbackEdits.php mass rollback) |
||
(не показано 11 промежуточных версий 7 участников) | |||
Строка 5: | Строка 5: | ||
== Стохастический градиентный спуск == | == Стохастический градиентный спуск == | ||
− | Проблема предыдущего алгоритма заключается в том, что чтобы определить новое приближение вектора весов необходимо вычислить градиент от каждого элемента выборки, что может сильно замедлять алгоритм. Идея ускорения алгоритма заключается в использовании только одного элемента, либо некоторой подвыборки для подсчета нового приближения весов. То есть теперь новое приближение будет вычисляться как ${\bf w}^{(t+1)}={\bf w}^{(t)} | + | Проблема предыдущего алгоритма заключается в том, что чтобы определить новое приближение вектора весов необходимо вычислить градиент от каждого элемента выборки, что может сильно замедлять алгоритм. Идея ускорения алгоритма заключается в использовании только одного элемента, либо некоторой подвыборки для подсчета нового приближения весов. То есть теперь новое приближение будет вычисляться как ${\bf w}^{(t+1)}={\bf w}^{(t)} - h\nabla \mathscr{L}_i({\bf w}^{(t)})$, где $i$ $-$ случайно выбранный индекс. Так как теперь направление изменения $\bf w$ будет определяться за $O(1)$, подсчет $Q$ на каждом шаге будет слишком дорогостоящим. Для того, чтобы ускорить оценку $Q$, будем использовать приближенную рекуррентную формулу. Можно выбрать одну из следующих формул: |
− | * среднее арифметическое: $\overline{Q}_m = \dfrac{1}{m}\varepsilon_m + \dfrac{1}{m}\varepsilon_{m - 1} + \dfrac{1}{m}\varepsilon_{m - 2} + \dots = \dfrac{1}{m}\varepsilon_m + (1 - \dfrac{1}{m})\overline{Q}_{m-1}$ | + | * среднее арифметическое: $\overline{Q}_m = \dfrac{1}{m}\varepsilon_m + \dfrac{1}{m}\varepsilon_{m - 1} + \dfrac{1}{m}\varepsilon_{m - 2} + \dots = \dfrac{1}{m}\varepsilon_m + (1 - \dfrac{1}{m})\overline{Q}_{m-1}$; |
* экспоненциальное скользящее среднее: $\overline{Q}_m = \lambda\varepsilon_m + (1 - \lambda)\varepsilon_{m - 1} + (1 - \lambda)^2\varepsilon_{m - 2} + \dots = \lambda\varepsilon_m + (1-\lambda)\overline{Q}_{m - 1},$ где $\lambda$ $-$ темп забывания предыстории ряда. | * экспоненциальное скользящее среднее: $\overline{Q}_m = \lambda\varepsilon_m + (1 - \lambda)\varepsilon_{m - 1} + (1 - \lambda)^2\varepsilon_{m - 2} + \dots = \lambda\varepsilon_m + (1-\lambda)\overline{Q}_{m - 1},$ где $\lambda$ $-$ темп забывания предыстории ряда. | ||
Строка 16: | Строка 16: | ||
$i =$ rand() % $l$ <font color=green> # случайно выбрать элемент, по которому будет считаться градиент </font> | $i =$ rand() % $l$ <font color=green> # случайно выбрать элемент, по которому будет считаться градиент </font> | ||
$\varepsilon = \mathscr{L}_i({\bf w})$ <font color=green> # вычислить потерю </font> | $\varepsilon = \mathscr{L}_i({\bf w})$ <font color=green> # вычислить потерю </font> | ||
− | ${\bf w} = {\bf w} - h \nabla \mathscr{L}_i({\bf w})$ <font color=green># обновить вектор весов в направлении | + | ${\bf w} = {\bf w} - h \nabla \mathscr{L}_i({\bf w})$ <font color=green># обновить вектор весов в направлении антиградиента</font> |
$\overline{Q} = \lambda\varepsilon + (1 - \lambda)\overline{Q}$ <font color=green># оценить функционал</font> | $\overline{Q} = \lambda\varepsilon + (1 - \lambda)\overline{Q}$ <font color=green># оценить функционал</font> | ||
'''return''' w | '''return''' w | ||
Строка 22: | Строка 22: | ||
== Эвристики == | == Эвристики == | ||
Существует несколько способов инициализировать веса: | Существует несколько способов инициализировать веса: | ||
− | * ${\bf w} = {\bf 0}$ | + | * ${\bf w} = {\bf 0}$; |
− | * $w_j = random(-\dfrac{1}{2n}, \dfrac{1}{2n})$. Стоит брать небольшие случайные веса, так как если выбрать их слишком большими, в некоторых случаях (к примеру в случае нейрона с [[Нейронные_сети,_перцептрон|функцией активациии]] равной арктангенсу) большие начальные значения веса могут привести в область с малыми по модулю производными, в связи с чем из такой области будет трудно выбраться | + | * $w_j = random(-\dfrac{1}{2n}, \dfrac{1}{2n})$. Стоит брать небольшие случайные веса, так как если выбрать их слишком большими, в некоторых случаях (к примеру в случае нейрона с [[Нейронные_сети,_перцептрон|функцией активациии]] равной арктангенсу) большие начальные значения веса могут привести в область с малыми по модулю производными, в связи с чем из такой области будет трудно выбраться; |
− | * $w_j = \dfrac{\langle y, f_j \rangle}{\langle f_j, f_j \rangle}$, где $f_j = (f_j(x_i))_{i=1}^l$. Оценка оптимальная в случае, если функция потерь квадратична и признаки нескоррелированы, то есть $\langle f_j, f_k \rangle = 0, j \neq k$ | + | * $w_j = \dfrac{\langle y, f_j \rangle}{\langle f_j, f_j \rangle}$, где $f_j = (f_j(x_i))_{i=1}^l$. Оценка оптимальная в случае, если функция потерь квадратична и признаки нескоррелированы, то есть $\langle f_j, f_k \rangle = 0, j \neq k$. |
Так же можно запустить спуск несколько раз с разными начальными приближениями и выбрать лучшее решение. | Так же можно запустить спуск несколько раз с разными начальными приближениями и выбрать лучшее решение. | ||
При выборе случайного элемента можно использовать следующие эвристики: | При выборе случайного элемента можно использовать следующие эвристики: | ||
− | * брать объекты из разных классов | + | * брать объекты из разных классов; |
− | * брать объекты, на которых ошибка больше, то есть чем меньше отступ i-го объекта $M_i$, тем больше вероятность взять этот объект | + | * брать объекты, на которых ошибка больше, то есть чем меньше отступ (в метрических классификаторах расстояние от разделяющей поверхности до объекта) i-го объекта $M_i$, тем больше вероятность взять этот объект; |
− | * брать объекты, на которых уверенность меньше, то есть чем меньше $|M_i|$, тем больше вероятность взять этот объект | + | * брать объекты, на которых уверенность меньше, то есть чем меньше $|M_i|$, тем больше вероятность взять этот объект; |
− | * не брать объекты, на которых уже высокая уверенность ($M_i > \mu_+$) либо не брать объекты-выбросы ($M_i<\mu_i$) | + | * не брать объекты, на которых уже высокая уверенность ($M_i > \mu_+$) либо не брать объекты-выбросы ($M_i<\mu_i$); |
Выбирать величину градиентного шага можно следующими способами: | Выбирать величину градиентного шага можно следующими способами: | ||
− | * $h_t = \dfrac{1}{t}$ | + | * $h_t = \dfrac{1}{t}$; |
− | * метод скорейшего градиентного спуска: $\mathscr{L}_i({\bf w} - h\nabla \mathscr{L}_i({\bf w})) \rightarrow \min\limits_h$ | + | * метод скорейшего градиентного спуска: $\mathscr{L}_i({\bf w} - h\nabla \mathscr{L}_i({\bf w})) \rightarrow \min\limits_h$; |
− | * при квадратичной функции потерь можно использовать $h = ||x_i||^2$ | + | * при квадратичной функции потерь можно использовать $h = ||x_i||^2$; |
− | * иногда можно выполнять пробные шаги, а именно увеличивать $h$ для выбивания процесса из локальных минимумов | + | * иногда можно выполнять пробные шаги, а именно увеличивать $h$ для выбивания процесса из локальных минимумов; |
− | * [https://ru.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9B%D0%B5%D0%B2%D0%B5%D0%BD%D0%B1%D0%B5%D1%80%D0%B3%D0%B0_%E2%80%94_%D0%9C%D0%B0%D1%80%D0%BA%D0%B2%D0%B0%D1%80%D0%B4%D1%82%D0%B0 метод Левенберга-Марквардта] | + | * [https://ru.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9B%D0%B5%D0%B2%D0%B5%D0%BD%D0%B1%D0%B5%D1%80%D0%B3%D0%B0_%E2%80%94_%D0%9C%D0%B0%D1%80%D0%BA%D0%B2%D0%B0%D1%80%D0%B4%D1%82%D0%B0 метод Левенберга-Марквардта]; |
== Регуляризация == | == Регуляризация == | ||
− | Основным способом уменьшить переобучение является [[ | + | Основным способом уменьшить переобучение является [[Регуляризация|регуляризация]], т.е. сокращение весов. Будем штрафовать за увеличение нормы вектора весов, для этого перепишем функцию потерь $\tilde{\mathscr{L}}_i({\bf w}) = \mathscr{L}_i({\bf w}) + \dfrac{\tau}{2}||w||^2 = \mathscr{L}_i({\bf w}) + \dfrac{\tau}{2} \sum\limits_{j=1}^nw_j^2 \rightarrow \min\limits_w$, где $\tau$ $-$ коэффициент регуляризации. |
Тогда градиент будет следующим: $\nabla \tilde{\mathscr{L}}_i({\bf w}) = \nabla \mathscr{L}_i({\bf w}) + \tau {\bf w}$, а градиентный шаг будет выглядеть так: ${\bf w} = {\bf w}(1 - h\tau) - h\nabla \mathscr{L}_i({\bf w})$. | Тогда градиент будет следующим: $\nabla \tilde{\mathscr{L}}_i({\bf w}) = \nabla \mathscr{L}_i({\bf w}) + \tau {\bf w}$, а градиентный шаг будет выглядеть так: ${\bf w} = {\bf w}(1 - h\tau) - h\nabla \mathscr{L}_i({\bf w})$. | ||
Строка 48: | Строка 48: | ||
== Достоинства и недостатки == | == Достоинства и недостатки == | ||
'''Достониства:''' | '''Достониства:''' | ||
− | * легко реализуется | + | * легко реализуется; |
− | * функция потерь и семейство алгоритмов могут быть любыми (если функция потерь не дифференцируема, ее можно аппроксимировать дифференцируемой) | + | * функция потерь и семейство алгоритмов могут быть любыми (если функция потерь не дифференцируема, ее можно аппроксимировать дифференцируемой); |
− | * легко добавить регуляризацию | + | * легко добавить регуляризацию; |
− | * возможно потоковое обучение | + | * возможно потоковое обучение; |
− | * подходит для задач с большими данными, иногда можно получить решение даже не обработав всю выборку | + | * подходит для задач с большими данными, иногда можно получить решение даже не обработав всю выборку; |
'''Недостатки''' | '''Недостатки''' | ||
− | * нет универсального набора эвристик, их нужно выбирать для конкретной задачи отдельно | + | * нет универсального набора эвристик, их нужно выбирать для конкретной задачи отдельно; |
Строка 60: | Строка 60: | ||
Классификатор [https://scikit-learn.org/stable/modules/sgd.html sklearn.linear_model.'''SGDClassifier'''] имеет несколько параметров, например: | Классификатор [https://scikit-learn.org/stable/modules/sgd.html sklearn.linear_model.'''SGDClassifier'''] имеет несколько параметров, например: | ||
− | '''loss''' $-$ функция потерь. По умолчанию используется "hinge", дающая алгоритм линейного SVM | + | '''loss''' $-$ функция потерь. По умолчанию используется "hinge", дающая алгоритм линейного SVM; |
− | '''penalty''' $-$ метод регуляризации. По умолчанию "l2" | + | '''penalty''' $-$ метод регуляризации. По умолчанию "l2"; |
− | '''alpha''' $-$ $\tau$, коэффициент регуляризации | + | '''alpha''' $-$ $\tau$, коэффициент регуляризации; |
− | '''learning_rate''' $-$ алгоритм изменения градиентного шага | + | '''learning_rate''' $-$ алгоритм изменения градиентного шага; |
− | '''eta0''' $-$ начальный градиентный шаг | + | '''eta0''' $-$ начальный градиентный шаг; |
− | '''shuffle''' перемешивать тренировочную выборку после каждой итерации | + | '''shuffle''' перемешивать тренировочную выборку после каждой итерации; |
− | * Импортируем нужные библиотеки | + | * Импортируем нужные библиотеки: |
'''from''' sklearn.linear_model '''import''' SGDClassifier | '''from''' sklearn.linear_model '''import''' SGDClassifier | ||
'''from''' sklearn '''import''' datasets | '''from''' sklearn '''import''' datasets | ||
'''from''' sklearn.model_selection '''import''' train_test_split | '''from''' sklearn.model_selection '''import''' train_test_split | ||
− | * Выберем тренировочное и тестовое множества | + | * Выберем тренировочное и тестовое множества: |
iris = datasets.'''load_iris()''' | iris = datasets.'''load_iris()''' | ||
Строка 84: | Строка 84: | ||
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size='''0.3''') | X_train, X_test, y_train, y_test = train_test_split(X, y, test_size='''0.3''') | ||
− | * Обучение | + | * Обучение: |
clf = SGDClassifier(shuffle = True) | clf = SGDClassifier(shuffle = True) | ||
model = clf.'''fit'''(X_train, y_train) | model = clf.'''fit'''(X_train, y_train) | ||
− | * Предсказание | + | * Предсказание: |
y_pred = model.'''predict'''(X_test) | y_pred = model.'''predict'''(X_test) | ||
model.'''score'''(X_test, y_test) | model.'''score'''(X_test, y_test) | ||
Строка 97: | Строка 97: | ||
== Источники информации == | == Источники информации == | ||
− | + | *[http://www.machinelearning.ru/wiki/images/5/53/Voron-ML-Lin-SG.pdf К.В. Воронцов {{---}} Линейные методы классификации и регрессии: метод стохастического градиента] | |
− | + | *[https://www.youtube.com/watch?v=4BKQ3GZR32w&list=PLJOzdkh8T5kp99tGTEFjH_b9zqEQiiBtC&index=4 Курс "Машинное обучение" {{---}} Линейные методы классификации: метод стохастического градиента {{---}} К.В. Воронцов] | |
− | + | *[https://en.wikipedia.org/wiki/Logistic_regression Wikipedia {{---}} Logistic regression] | |
− | + | *[https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.SGDClassifier.html#sklearn.linear_model.SGDClassifier.decision_function sklearn.linear_model.SGDClassifier {{---}} описание алгоритма] | |
[[Категория: Машинное обучение]] | [[Категория: Машинное обучение]] |
Текущая версия на 19:44, 4 сентября 2022
Стохастический градиентный спуск (англ. stochastic gradient descent) $-$ оптимизационный алгоритм, отличающийся от обычного градиентного спуска тем, что градиент оптимизируемой функции считается на каждом шаге не как сумма градиентов от каждого элемента выборки, а как градиент от одного, случайно выбранного элемента.
Содержание
Обычный градиентный спуск
Для начала вспомним, как работает обычный градиентный спуск. Пусть объекты задаются $n$ числовыми признаками $f_j : X \to R, j = 1 ... n$ и пространство признаковых описаний в таком случае $X = R^n$. Пусть $Y$ $-$ конечное множество меток классов и задана обучающая выборка пар «объект-ответ»
Пусть семейство алгоритмов $a(x, {\bf w})$ имеет параметр вектор весов $\bf w$. И пускай мы выбрали какую-нибудь функцию потерь. Для $i$-го объекта выборки для алгоритма с весами ${\bf w}$ обозначим ее . Необходимо минимизировать эмпирический риск, т.е. . Если функция потерь принадлежит классу $C_1(X)$, то можно применить метод градиентного спуска. Выберем ${\bf w}^{(0)}$ $-$ начальное приближение. Тогда каждый следующий вектор параметров будет вычисляться как ${\bf w}^{(t+1)}={\bf w}^{(t)} - h\sum\limits_{i=1}^{l}\nabla \mathscr{L}_i({\bf w}^{(t)})$, где $h$ - градиентный шаг, смысл которого заключается в том, насколько сильно менять вектор весов в направлении градиента. Остановка алгоритма будет определяться сходимостью $Q$ или $\bf w$.Стохастический градиентный спуск
Проблема предыдущего алгоритма заключается в том, что чтобы определить новое приближение вектора весов необходимо вычислить градиент от каждого элемента выборки, что может сильно замедлять алгоритм. Идея ускорения алгоритма заключается в использовании только одного элемента, либо некоторой подвыборки для подсчета нового приближения весов. То есть теперь новое приближение будет вычисляться как ${\bf w}^{(t+1)}={\bf w}^{(t)} - h\nabla \mathscr{L}_i({\bf w}^{(t)})$, где $i$ $-$ случайно выбранный индекс. Так как теперь направление изменения $\bf w$ будет определяться за $O(1)$, подсчет $Q$ на каждом шаге будет слишком дорогостоящим. Для того, чтобы ускорить оценку $Q$, будем использовать приближенную рекуррентную формулу. Можно выбрать одну из следующих формул:
- среднее арифметическое: $\overline{Q}_m = \dfrac{1}{m}\varepsilon_m + \dfrac{1}{m}\varepsilon_{m - 1} + \dfrac{1}{m}\varepsilon_{m - 2} + \dots = \dfrac{1}{m}\varepsilon_m + (1 - \dfrac{1}{m})\overline{Q}_{m-1}$;
- экспоненциальное скользящее среднее: $\overline{Q}_m = \lambda\varepsilon_m + (1 - \lambda)\varepsilon_{m - 1} + (1 - \lambda)^2\varepsilon_{m - 2} + \dots = \lambda\varepsilon_m + (1-\lambda)\overline{Q}_{m - 1},$ где $\lambda$ $-$ темп забывания предыстории ряда.
Псевдокод
def SGD(X, h, $\lambda$): # где X $-$ выборка, h $-$ градиентный шаг, а $\lambda$ $-$ темп забывания ${\bf w} =$ initialize_weights() # инициализировать веса $\overline{Q} = \frac{1}{l} \sum_{i=1}^{l}\mathscr{L}_i({\bf w})$ # инициализировать оценку функционала while $Q$ not converges or ${\bf w}$ not converges: $i =$ rand() % $l$ # случайно выбрать элемент, по которому будет считаться градиент $\varepsilon = \mathscr{L}_i({\bf w})$ # вычислить потерю ${\bf w} = {\bf w} - h \nabla \mathscr{L}_i({\bf w})$ # обновить вектор весов в направлении антиградиента $\overline{Q} = \lambda\varepsilon + (1 - \lambda)\overline{Q}$ # оценить функционал return w
Эвристики
Существует несколько способов инициализировать веса:
- ${\bf w} = {\bf 0}$;
- $w_j = random(-\dfrac{1}{2n}, \dfrac{1}{2n})$. Стоит брать небольшие случайные веса, так как если выбрать их слишком большими, в некоторых случаях (к примеру в случае нейрона с функцией активациии равной арктангенсу) большие начальные значения веса могут привести в область с малыми по модулю производными, в связи с чем из такой области будет трудно выбраться;
- $w_j = \dfrac{\langle y, f_j \rangle}{\langle f_j, f_j \rangle}$, где $f_j = (f_j(x_i))_{i=1}^l$. Оценка оптимальная в случае, если функция потерь квадратична и признаки нескоррелированы, то есть $\langle f_j, f_k \rangle = 0, j \neq k$.
Так же можно запустить спуск несколько раз с разными начальными приближениями и выбрать лучшее решение.
При выборе случайного элемента можно использовать следующие эвристики:
- брать объекты из разных классов;
- брать объекты, на которых ошибка больше, то есть чем меньше отступ (в метрических классификаторах расстояние от разделяющей поверхности до объекта) i-го объекта $M_i$, тем больше вероятность взять этот объект;
- брать объекты, на которых уверенность меньше, то есть чем меньше $|M_i|$, тем больше вероятность взять этот объект;
- не брать объекты, на которых уже высокая уверенность ($M_i > \mu_+$) либо не брать объекты-выбросы ($M_i<\mu_i$);
Выбирать величину градиентного шага можно следующими способами:
- $h_t = \dfrac{1}{t}$;
- метод скорейшего градиентного спуска: $\mathscr{L}_i({\bf w} - h\nabla \mathscr{L}_i({\bf w})) \rightarrow \min\limits_h$;
- при квадратичной функции потерь можно использовать $h = ||x_i||^2$;
- иногда можно выполнять пробные шаги, а именно увеличивать $h$ для выбивания процесса из локальных минимумов;
- метод Левенберга-Марквардта;
Регуляризация
Основным способом уменьшить переобучение является регуляризация, т.е. сокращение весов. Будем штрафовать за увеличение нормы вектора весов, для этого перепишем функцию потерь $\tilde{\mathscr{L}}_i({\bf w}) = \mathscr{L}_i({\bf w}) + \dfrac{\tau}{2}||w||^2 = \mathscr{L}_i({\bf w}) + \dfrac{\tau}{2} \sum\limits_{j=1}^nw_j^2 \rightarrow \min\limits_w$, где $\tau$ $-$ коэффициент регуляризации.
Тогда градиент будет следующим: $\nabla \tilde{\mathscr{L}}_i({\bf w}) = \nabla \mathscr{L}_i({\bf w}) + \tau {\bf w}$, а градиентный шаг будет выглядеть так: ${\bf w} = {\bf w}(1 - h\tau) - h\nabla \mathscr{L}_i({\bf w})$.
Достоинства и недостатки
Достониства:
- легко реализуется;
- функция потерь и семейство алгоритмов могут быть любыми (если функция потерь не дифференцируема, ее можно аппроксимировать дифференцируемой);
- легко добавить регуляризацию;
- возможно потоковое обучение;
- подходит для задач с большими данными, иногда можно получить решение даже не обработав всю выборку;
Недостатки
- нет универсального набора эвристик, их нужно выбирать для конкретной задачи отдельно;
Пример кода scikit-learn
Классификатор sklearn.linear_model.SGDClassifier имеет несколько параметров, например:
loss $-$ функция потерь. По умолчанию используется "hinge", дающая алгоритм линейного SVM;
penalty $-$ метод регуляризации. По умолчанию "l2";
alpha $-$ $\tau$, коэффициент регуляризации;
learning_rate $-$ алгоритм изменения градиентного шага;
eta0 $-$ начальный градиентный шаг;
shuffle перемешивать тренировочную выборку после каждой итерации;
- Импортируем нужные библиотеки:
from sklearn.linear_model import SGDClassifier from sklearn import datasets from sklearn.model_selection import train_test_split
- Выберем тренировочное и тестовое множества:
iris = datasets.load_iris() X = iris.data y = iris.target X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
- Обучение:
clf = SGDClassifier(shuffle = True) model = clf.fit(X_train, y_train)
- Предсказание:
y_pred = model.predict(X_test) model.score(X_test, y_test)