Вручную обучить модель методами 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)
→ Ссылка