Градиентный спуск сильно расходится.

Вопрос или проблема

Я вручную создал случайный набор данных вокруг некоторого среднего значения и попытался использовать градиентный спуск для линейной регрессии, чтобы предсказать это простое среднее значение.

Я сделал точно так же, как в руководстве, и по какой-то причине коэффициенты моего предсказателя стремятся к бесконечности, хотя это работало в другом случае.

Почему в этом случае он не может предсказать простое значение 1.4?

clear all;
n=10000;
t=1.4;
sigma_R = t*0.001;
min_value_t = t-sigma_R;
max_value_t = t+sigma_R;
y_data = min_value_t + (max_value_t - min_value_t) * rand(n,1);
x_data=[1:10000]';

m=0
c=0
L=0.0001
epochs=1000 %итерации

for i=1:epochs
   y_pred=m.*x_data+c;
   D_m=(-2/n)*sum(x_data.*(y_data-y_pred));
   D_c=(-2/n)*sum((y_data-y_pred));
   m=m-L*D_m;
   c=c-L*D_c;
end
plot(x_data,y_data,'.')
hold on;
grid;
plot(x_data,y_pred)

Обновление: Я попытался переписать ваш код на языке Matlab, который я лучше знаю. Мои матрицы признаков в виде NX2 [1,X_data] называются Xmat. Я следовал каждому шагу при преобразовании кода, и у меня в обоих Theta получаются NAN. Где я ошибся?

%%начало кода Matlab
n=1000;
t=1.4;
sigma_R = t*0.001;
min_value_t = t-sigma_R;
max_value_t = t+sigma_R;
y_data = min_value_t + (max_value_t - min_value_t) * rand(n,1);
x_data=[1:1000];
L=0.0001; %скорость обучения
%plot(x_data,y_data);
itter=1000;

theta_0=0;
theta_1=0;
theta=[theta_0;theta_1];

itter=1000;
for i=1:itter
onss=ones(1,1000);
x_mat=[onss;x_data]';
pred=x_mat*theta;
residuals = (pred-y_data);
  for k=1:2 %начало цикла theta
  partial=2*dot(residuals,x_mat(:,k));
  theta(k)=theta(k)-L*partial;
  end%конец цикла theta
end %конец цикла итераций
%%конец кода matlab
```

Хотя данный ответ показывает код на Python, теория остается точно такой же. Два совета при запуске линейной регрессии с использованием градиентного спуска с нуля:

  • используйте матричную нотацию для ваших данных признаков (т.е. входных данных), чтобы вы могли применять это к n-мерным наборам данных (а не только 1-D, как в этом случае)
  • нормализуйте данные вашей матрицы признаков (хотя в этом случае у вас на самом деле 1-D, это хорошая практика, которая всегда работает)

Ваши данные, построенные с помощью matplotlib:

import numpy as np 
from numpy.random import randn, seed

x_data = 20 * randn(1000) + 100
y_data = x_data + (10 * randn(1000) + 50)
n=1000
t=1.4
sigma_R = t*0.001
min_value_t = t-sigma_R
max_value_t = t+sigma_R
y_data = min_value_t + (max_value_t - min_value_t) * np.random.rand(n,1)
x_data=np.array(range(1000))

pyplot.scatter(x_data, y_data)
pyplot.show()

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

Создание вашего набора данных для обучения модели:

final_df_DICT = {'X': x_data}
import pandas as pd
H = pd.DataFrame(final_df_DICT)
feature_matrix = np.zeros(n*2) 
feature_matrix.shape = (n, 2) 
feature_matrix[:,0] = 1 
feature_matrix[:,1] = H['X'] 
#нормализация признаков
feature_matrix = (feature_matrix - feature_matrix.mean()) / feature_matrix.std()
target_data = y_data.reshape(len(y_data), )

Посмотрите на матрицу признаков, в которой первый столбец заполнен единицами, а второй столбец содержит реальные x_data:
введите описание изображения здесь

Обучение модели с вашими гиперпараметрами:

w = [0, 0]
L=0.0001
epochs=1000
iteration = 0
cost=[]
while iteration < epochs:
    pred = np.dot(feature_matrix, w)
    residuals = pred-target_data
    #мы вычисляем градиент для 2 коэффициентов при помощи скалярного произведения 
    for i in range(len(w)):
        partial = 2*np.dot(residuals, feature_matrix[:, i])
        w[i] = w[i] - L*partial

    iteration += 1
    computed_cost = np.sum(np.power((pred - target_data), 2)) / n

    cost.append(computed_cost)

print('коэффициенты: {}'.format(w))
print('стоимость: {}'.format(cost[-1]))

Результат:

коэффициенты: [-1.80963253e+00 -6.15189807e-06] стоимость: 6.466287828899486e-07

Давайте нарисуем предсказания модели линейной регрессии на исходном наборе данных (мы обучались на всем наборе данных, не принимая во внимание никакой валидационный набор в этом случае…):

my_predictions = np.dot(feature_matrix, w)
pyplot.scatter(feature_matrix[:, 1], target_data)
pyplot.scatter(feature_matrix[:, 1], my_predictions, color="r")

pyplot.show()

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

Ответ или решение

Почему градиентный спуск расходится: технический анализ проблемы

Градиентный спуск — это мощный метод оптимизации, широко используемый в задачах регрессии и машинного обучения. Однако его основные параметры, такие как скорость обучения и начальные условия, значительно влияют на его производительность. В вашем случае результаты градиентного спуска divergируют, что требует более глубокого анализа.

1. Характеристика ваших данных

Вы создадите набор данных с использованием 1.4 как целевой средней величины, добавляя небольшие случайные колебания, что теоретически должно привести к концу меньшего стандартного отклонения. Однако, несмотря на это, результаты предсказания уходят к бесконечности. Рассмотрим несколько причин этого поведения.

2. Проблема с размером шага (Learning Rate)

Скорость обучения (L) — один из критически важных параметров gradient descent. В вашем случае вы указали L=0.0001. Эта скорость может быть слишком низкой, но также существует вероятность, что она слишком высока для входящих данных, что может привести к осциллированию и расходимости.

Рекомендации:

  • Попробуйте использовать более высокие значения скорости обучения, например, L=0.001 или L=0.01, и наблюдайте за поведением алгоритма.
  • Однако, если используете высокую скорость, внимательно следите за значениями theta; они могут нарастать слишком быстро и начать выдавать NaN.

3. Инициализация параметров

Инициализация коэффициентов также имеет значение. Если параметры инициализируются неправильно (например, затирание нулями слишком близко к вашему целевому значению), это может привести к проблемам при минимизации функции потерь.

Рекомендации:

  • Попробуйте инициализировать параметры случайными значениями или небольшими ненулевыми числами.

4. Масштабирование данных

Большое значение входных переменных, таких как x_data, может привести к значительному увеличению величины градиентов, что может вызвать нестабильность алгоритма. Стандартизация или нормализация фичей может помочь избежать этой проблемы.

Рекомендации:

  • Примените стандартизацию ваших данных, чтобы нормализовать ваши признаки. Например, вы можете сместить и разделить данные, чтобы их среднее значение было равно 0 и стандартное отклонение — 1.

5. Динамика чередования градиентного спуска

В вашем примере используется for-цикл для расчета вашего градиента для каждого коэффициента. Алгоритм в данном случае проходит через два цикла, что может дополнительно усложнить процесс. Попробуйте заменить это на матричные операции для оптимизации производительности.

Рекомендации:

  • Рассмотрите возможность реализации матричных вычислений для обновления theta, что позволит избежать двух вложенных циклов и повысит скорость обучения.

Рассмотрим комбинацию рекомендаций

Применение рекомендаций выше вероятно решение проблемы расходимости. Пример изменения кода может выглядеть следующим образом:

L = 0.001; % Измененное значение скорости обучения
theta = rand(2, 1); % Случайная инициализация параметров

for i = 1:itter
    pred = x_mat * theta;
    residuals = (pred - y_data);

    % Применение векторизованного расчета градиента
    gradient = (2/n) * (x_mat' * residuals);
    theta = theta - L * gradient;
end

Заключение

Оптимизация градиентного спуска требует тщательной настройки различных параметров. Правильный выбор скорости обучения, инициализация параметров, предварительная обработка данных и оптимизированный расчет градиента — все эти элементы могут значительно повлиять на результат. Следуя предложенным рекомендациям, вы сможете решить проблему расходимости вашей модели и достичь стабильных и предсказуемых результатов.

Оцените материал
Добавить комментарий

Капча загружается...