coding utf8 references factorization machines http www csie ntu edu twb97053paperrendle2010fm pdf simplified factorization machines implementation using sgd optimizer self regw0 regw0 self regw regw self regv regv self ncomponents ncomponents self lr learningrate self initstdev initstdev self maxiter maxiter self loss none self lossgrad none def fitself x ynone self setupinputx y bias self wo 0 0 feature weights self w np zerosself nfeatures factor weights self v np random normalscaleself initstdev sizeself nfeatures self ncomponents self train def trainself for epoch in rangeself maxiter ypred self predictself x loss self lossgradself y ypred wgrad np dotloss self x floatself nsamples self wo self lr loss mean 2 self regw0 self wo self w self lr wgrad 2 self regw self w self factorsteploss def factorstepself loss for ix x in enumerateself x for i in rangeself nfeatures vgrad lossix x dotself v dotxi0 self vi xi 2 self vi self lr vgrad 2 self regv self vi def predictself xnone linearoutput np dotx self w factorsoutput np sumnp dotx self v 2 np dotx 2 self v 2 axis1 2 0 return self wo linearoutput factorsoutput class fmregressorbasefm def fitself x ynone superfmregressor self fitx y self loss meansquarederror self lossgrad elementwisegradmeansquarederror class fmclassifierbasefm def fitself x ynone superfmclassifier self fitx y self loss binarycrossentropy self lossgrad elementwisegradbinarycrossentropy def predictself xnone predictions self predictx return np signpredictions coding utf 8 references factorization machines http www csie ntu edu tw b97053 paper rendle2010fm pdf simplified factorization machines implementation using sgd optimizer bias feature weights factor weights
import autograd.numpy as np
from autograd import elementwise_grad

from mla.base import BaseEstimator
from mla.metrics import mean_squared_error, binary_crossentropy

np.random.seed(9999)




class BaseFM(BaseEstimator):
    def __init__(
        self, n_components=10, max_iter=100, init_stdev=0.1, learning_rate=0.01, reg_v=0.1, reg_w=0.5, reg_w0=0.0
    ):
        
        self.reg_w0 = reg_w0
        self.reg_w = reg_w
        self.reg_v = reg_v
        self.n_components = n_components
        self.lr = learning_rate
        self.init_stdev = init_stdev
        self.max_iter = max_iter
        self.loss = None
        self.loss_grad = None

    def fit(self, X, y=None):
        self._setup_input(X, y)
        
        self.wo = 0.0
        
        self.w = np.zeros(self.n_features)
        
        self.v = np.random.normal(scale=self.init_stdev, size=(self.n_features, self.n_components))
        self._train()

    def _train(self):
        for epoch in range(self.max_iter):
            y_pred = self._predict(self.X)
            loss = self.loss_grad(self.y, y_pred)
            w_grad = np.dot(loss, self.X) / float(self.n_samples)
            self.wo -= self.lr * (loss.mean() + 2 * self.reg_w0 * self.wo)
            self.w -= self.lr * w_grad + (2 * self.reg_w * self.w)
            self._factor_step(loss)

    def _factor_step(self, loss):
        for ix, x in enumerate(self.X):
            for i in range(self.n_features):
                v_grad = loss[ix] * (x.dot(self.v).dot(x[i])[0] - self.v[i] * x[i] ** 2)
                self.v[i] -= self.lr * v_grad + (2 * self.reg_v * self.v[i])

    def _predict(self, X=None):
        linear_output = np.dot(X, self.w)
        factors_output = np.sum(np.dot(X, self.v) ** 2 - np.dot(X ** 2, self.v ** 2), axis=1) / 2.0
        return self.wo + linear_output + factors_output


class FMRegressor(BaseFM):
    def fit(self, X, y=None):
        super(FMRegressor, self).fit(X, y)
        self.loss = mean_squared_error
        self.loss_grad = elementwise_grad(mean_squared_error)


class FMClassifier(BaseFM):
    def fit(self, X, y=None):
        super(FMClassifier, self).fit(X, y)
        self.loss = binary_crossentropy
        self.loss_grad = elementwise_grad(binary_crossentropy)

    def predict(self, X=None):
        predictions = self._predict(X)
        return np.sign(predictions)