Вручную обучить модель методами nesterov momentum и rmsprop на Python
Есть готовые методы TensorFlow, которые реализуют тот и другой функционал. Однако пытаюсь посмотреть, как это можно сделать руками для модели логистической регрессии (классификация). Использую датасет Iris, оставив 2 класса.
Реализовал обучение методом градиентного спуска, однако более сложные эвристики (nesterov momentum и rmsprop) не поддаются. Для их реализации мне необходимо поменять функцию learn_sgd из кода ниже
Градиентный спуск сделал следующим образом
import pandas as pd
import numpy as np
from sklearn.datasets import load_iris
X,y = load_iris(return_X_y=True)
filter = y != 2 # накладываем фильтр - только 2 признака
X = X[filter]
y = y[filter]
COEFS = np.random.randn(5) # генерируем случайные коэффициенты
def predict_proba(coefs, x):
# формула логистической регрессии:
return 1. / (1. + np.exp( -(x.dot(coefs[:4]) + coefs[-1]) ) )
# теперь на основе модели предсказываем класс (но модель пока не обучена:
# это сделаем градиентным спуском чуть позже)
def predict_class(coefs, x):
probas = predict_proba(coefs, x)
return (probas > 0.5).astype(np.float)
# явно прописываем функцию потерь на основе ее формулы
def bce_loss(coefs, x, y):
probas = predict_proba(coefs, x)
filter_ones = y == 1
loss = -1. * (np.sum(np.log(probas[filter_ones])) + np.sum(np.log(1. - probas[~filter_ones]))) / len(y)
return loss
# расчет градиента:
# он зависит от двух сущностей: от модели и функции потерь
def grad(coefs, x, y):
probas = predict_proba(coefs, x)
delta = probas - y
modified_x = x.T * delta
deltas = np.mean(modified_x, axis=1)
return deltas, np.mean(delta)
# обучение модели методом градиентного спуска
def learn_sgd(coefs, x, y, num_epochs=20, learning_rate=0.0001):
losses = []
for e in range(num_epochs):
grad_coefs, grad_bias = grad(coefs, x, y)
coefs[:-1] = coefs[:-1] - learning_rate * grad_coefs
coefs[-1] = coefs[-1] - learning_rate * grad_bias
loss = bce_loss(coefs, x, y)
losses.append(loss)
return losses, coefs
Ответы (1 шт):
Общее направление про nesterov momentum определил. Напомню, что метод заключается в импульсе (momentum) и расчете градиента для следующей точки графика. Вот общая формула: Nesterov Momentum v_t = momentum* v_t{t-1} + learning_rate*grad(coefs - momentum v_t{t-1}), где coefs = coefs{t-1} - v_t. Видим, что скорость в точке t соотносится со скоростью в точке t-1.
Есть трудности. Интерпретатор мне ругается на строку coefs[i] -= v_t[i]. Пишет следующее: setting an array element with a sequence.
def learn_nesterov(coefs, x, y, num_epochs=20, momentum=0.9, learning_rate=0.0001):
v_t = [0 for _ in range(len(coefs))]
losses =[]
for it in range(num_epochs):
pr_coefs = [coefs[i] - momentum*v_t[i] for i in range(len(coefs))]
gr_coefs = grad(pr_coefs, x, y)
for i in range(len(coefs)):
v_t[i]=momentum*v_t[i] + learning_rate*gr_coefs[i]
coefs[i] = coefs[i]-v_t[i]
losses.append(bce_loss(x, y, coefs))
return losses, coefs
learn_nesterov(COEFS, X, y)
Второй метод RMSprop сделал так (код см. ниже). Ошибки с размерностью. Выводится следующее резюме: shapes (100,) and (4,4) not aligned: 100 (dim 0) != 4 (dim 0)
def learn_RMSprop(coefs, x, y, num_epochs=20, momentum=0.9, learning_rate=0.0001):
e = 10**(-8)
S = [0 for _ in range(len(coefs))]
losses =[]
for it in range(num_epochs):
gr_coefs = grad(x, y, coefs)
gr_coefs_2 = [x**2 for x in gr_coefs]
for i in range(len(coefs)):
S[i]=momentum*S[i] + (1-momentum)*gr_coefs_2[i]
coefs[i] = coefs[i]-learning_rate* (gr_coefs[i]/(math.sqrt(S[i])+e))
losses.append(bce_loss(coefs, x, y))
return losses, coefs
learn_RMSprop(COEFS, X, y)