Алгоритм градиентного спуска не сходится
Я пытаюсь написать немного кода для алгоритма градиентного спуска, описанного в лекции Стэнфордского машинного обучения ( лекция 2 около 25:00). Ниже приведена реализация, которую я использовал вначале, и я думаю, что она правильно скопирована из лекции, но она не сходится, когда я добавляю большие числа (>8
) к тренировочному набору.
Я ввожу число X
и point (X,X)
добавлен в тренировочный набор, так что на данный момент я только пытаюсь заставить его сходиться к y=ax+b
где a=1=theta\[1\]
а также b=0=theta\[0\]
, Учебный набор - это массив x
а также y
, где (x[i],y[i])
это точка.
void train()
{
double delta;
for (int i = 0; i < x.size(); i++)
{
delta = y[i]-hypothesis(x[i]);
theta[1] += alpha*delta*x[i];
theta[0] += alpha*delta*1;
}
}
void C_Approx::display()
{
std::cout<<theta[1]<<"x + "<<theta[0]<<" \t "<<"f(x)="<<hypothesis(1)<<std::endl;
}
некоторые из результатов, которые я получаю: я ввожу число, оно работает train()
затем несколько раз display()
1
0.33616x + 0.33616 f(x)=0.67232
1
0.482408x + 0.482408 f(x)=0.964816
1
0.499381x + 0.499381 f(x)=0.998762
1
0.499993x + 0.499993 f(x)=0.999986
1
0.5x + 0.5 f(x)=1
Пример этого расходится после того, как он прошел 8
:
1
0.33616x + 0.33616 f(x)=0.67232
2
0.705508x + 0.509914 f(x)=1.21542
3
0.850024x + 0.449928 f(x)=1.29995
4
0.936062x + 0.330346 f(x)=1.26641
5
0.951346x + 0.231295 f(x)=1.18264
6
0.992876x + 0.137739 f(x)=1.13062
7
0.932206x + 0.127372 f(x)=1.05958
8
1.00077x + 0.000493063 f(x)=1.00126
9
-0.689325x + -0.0714712 f(x)=-0.760797
10
4.10321e+08x + 4.365e+07 f(x)=4.53971e+08
11
1.79968e+22x + 1.61125e+21 f(x)=1.9608e+22
12
-3.9452e+41x + -3.26957e+40 f(x)=-4.27216e+41
Я попробовал решение, предложенное здесь для масштабирования шага, и в итоге получил схожие результаты. Что я делаю неправильно?
6 ответов
Ваша реализация хороша. Обычно стохастический градиентный спуск может расходиться, когда α слишком велик. Что вы бы сделали с большим набором данных, это возьмите случайную выборку разумного размера, найдите α, который дает вам наилучшие результаты, а затем используйте его для остальных.
Я столкнулся с той же проблемой (хотя и в Java), потому что моя скорость обучения была слишком большой.
Короче говоря, я использовал α = 0.001
и я должен был подтолкнуть его к 0.000001
чтобы увидеть фактическую конвергенцию.
Конечно, эти значения связаны с вашим набором данных.
Когда ваша функция затрат увеличивается или циклически развивается, вы обычно получаете слишком большое значение для alpha
, Какие alpha
ты используешь?
Начните с alpha = 0.001
и посмотреть, сходится ли это? Если не попробовать различные alphas
(0.003, 0.01, 0.03, 0.1, 0.3, 1)
и найти тот, который быстро сходится.
Масштабирование данных (нормализация) не поможет вам только с одной функцией (ваш theta[1]
) как нормализация относится только к 2+
особенности (многомерная линейная регрессия).
Также имейте в виду, что для небольшого числа функций вы можете использовать Нормальное уравнение, чтобы получить правильный ответ.
Используйте возвратный поиск строки, чтобы гарантировать конвергенцию. Это очень просто реализовать. См. Стивена Бойда, выпуклая оптимизация для справки. Вы можете выбрать некоторые стандартные значения альфа, бета для поиска по линии возврата, например 0,3 и 0,8.
Из вашего описания не ясно, какую проблему вы решаете. Также очень опасно размещать ссылки на внешние ресурсы - вы можете быть заблокированы в stackru.
В любом случае - метод градиентного спуска и (субградиентный спуск тоже) с фиксированным размером шага (сообщество ML называет это скоростью обучения) не должны сходиться.
Сообщество машинного обучения не заинтересовано в "условии сходимости" и "сходимости к чему-либо" - оно заинтересовано в создании "чего-то", которое проходит перекрестную проверку с хорошим результатом.
Если вас интересует оптимизация - начните искать выпуклую оптимизацию. К сожалению, на ней трудно найти работу, но она добавляет четкое представление о том, что происходит в различных вещах по математической оптимизации.
Вот исходный код, который демонстрирует это для простой квадратичной цели:
#!/usr/bin/env python
# Gradiend descend method (without stepping) is not converged for convex
# objective
alpha = 0.1
#k = 10.0 # jumping around minimum
k = 20.0 # diverge
#k = 0.001 # algorithm converged but gap to the optimal is big
def f(x): return k*x*x
def g(x): return 2*k*x
x0 = 12
xNext = x0
i = 0
threshold = 0.01
while True:
i += 1
xNext = xNext + alpha*(-1)*(g(xNext))
obj = (xNext)
print "Iteration: %i, Iterate: %f, Objective: %f, Optimality Gap: %f" % (i, xNext, obj, obj - f(0.0))
if (abs(g(xNext)) < threshold):
break
if i > 50:
break
print "\nYou launched application with x0=%f,threshold=%f" % (x0, threshold)
Если я вас правильно понимаю, у вашего тренировочного набора только ненулевой градиент на краю линии? Если вы не начнете с линии (фактически начинайте точно с одной из ваших тренировочных точек), вы не найдете линию. Вы всегда на локальном минимуме.