kakao / buffalo

TOROS Buffalo: A fast and scalable production-ready open source project for recommender systems
Apache License 2.0
576 stars 106 forks source link

Add eALS #77

Closed dkkim1005 closed 8 months ago

dkkim1005 commented 10 months ago

eALS

Element-wise ALS algorithm, dubbed as eALS, is added to the buffalo framework. (Detailed algorithm flow is presented in "Fast Matrix Factorization for Online Recommendation with Implicit Feedback")

Because eALS is an extended version of ALS, it shares the same interfaces as ALS. One could deploy a code just substituting "ALS" into "EALS". But eALS needs the additional hyperparameters to construct a loss function for training, such as "c0" and "exponent" where "c0" is the strength of the negative feedback and "exponent" is the control parameter of how item popularity affects the user preferences for unseen items(No user-item interactions). User should give hyperparameters "c0" and "exponent" to eALS when an instance is instantiated.

linking BLAS

A BLAS subroutine SSYRK will be called during the training process. Using OpenBLAS is highly recommended so that it is employed as a default setting in setup.py. One can switch from OpenBLAS to any compatible BLAS library that supports multithreading operations.

usecase

eALS supports the same interfaces as ALS. The example code is presented as follows,

import os
from os.path import join as pjoin
from typing import Any, Union

import buffalo
from buffalo.misc import aux

class EALS:
    def __init__(
        self,
        matrix: Union[str, os.PathLike],
        uid_fname: str,
        iid_fname: str,
        D: int = 20,
        **kwargs: Any,
    ):
        data_opt = buffalo.MatrixMarketOptions().get_default_option()
        data_opt.input.main = matrix
        data_opt.input.uid = uid_fname
        data_opt.input.iid = iid_fname
        data_opt.data.value_prepro = aux.Option({'name': 'OneBased'})

        self.data = buffalo.data.load(data_opt)
        self.data.create()
        # Set model option
        self.opt = buffalo.EALSOption().get_default_option()
        self.opt.d = D
        self.opt.num_iters = kwargs.get('iter', 10)
        self.opt.alpha = kwargs.get('alpha', 32.0)
        self.opt.reg_u = kwargs.get('reg_u', 1.0)
        self.opt.reg_i = kwargs.get('reg_i', 1.0)
        self.opt.c0 = kwargs.get('c0', 64)
        self.opt.save_factors = True
        self.opt.exponent = kwargs.get('exponent', 0.5)
        self.opt.compute_loss_on_training = kwargs.get('check_rmse', True)
        self.opt.num_workers = kwargs.get('num_proc', 4)
        self.model = buffalo.EALS(self.opt, data=self.data)
        self.model.initialize()

    def train(self) -> float:
        metrics = self.model.train()
        self.trained = True
        return metrics.get('train_loss', 0.0)

if __name__ == '__main__':
    prefix_for_path='blabla'
    D = 20
    iter = 10
    alpha = 32.0
    reg_u = 1.0
    reg_i = 1.0
    num_proc = 4
    c0 = 64.0
    exponent = 0.5
    model = EALS(
        matrix=pjoin(prefix_for_path, 'test.mm'),
        uid_fname=pjoin(prefix_for_path, 'uid'),
        iid_fname=pjoin(prefix_for_path, 'iid'),
        D=D,
        iter=iter,
        alpha=alpha,
        reg_u=reg_u,
        reg_i=reg_i,
        num_proc=num_proc,
        c0=c0,
        exponent=exponent,
    )

    model.train()
CLAassistant commented 10 months ago

CLA assistant check
All committers have signed the CLA.

ita9naiwa commented 8 months ago

테스트 좀 추가해주실 수 있나요?

dkkim1005 commented 8 months ago

@ita9naiwa 테스트 코드 추가하였습니다.