// Copyright (C) 2011 Zeno Gantner, Chris Newell // // This file is part of MyMediaLite. // // MyMediaLite is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // MyMediaLite is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with MyMediaLite. If not, see <http://www.gnu.org/licenses/>. package org.mymedialite.itemrec; import org.mymedialite.datatype.MatrixExtensions; /** * Matrix Factorization model for item prediction optimized for a soft margin (hinge) ranking loss, * using stochastic gradient descent (as in BPR-MF). * * Literature: * * Steffen Rendle: * Context-Aware Ranking with Factorization Models. * Studies in Computational Intelligence. Springer 2011. * http://www.springer.com/engineering/computational+intelligence+and+complexity/book/978-3-642-16897-0 * * Markus Weimer, Alexandros Karatzoglou, Alex Smola: * Improving Maximum Margin Matrix Factorization. * Machine Learning Journal 2008. * * Steffen Rendle, Christoph Freudenthaler, Zeno Gantner, Lars Schmidt-Thieme: * BPR: Bayesian Personalized Ranking from Implicit Feedback. * UAI 2009. * http://www.ismll.uni-hildesheim.de/pub/pdfs/Rendle_et_al2009-Bayesian_Personalized_Ranking.pdf * * This recommender supports incremental updates. * @version 2.03 */ public class SoftMarginRankingMF extends BPRMF { public SoftMarginRankingMF() { learnRate = 0.1; } /** * Update latent factors according to the stochastic gradient descent update rule. * @param u the user ID * @param i the ID of the first item * @param j the ID of the second item * @param update_u if true, update the user latent factors * @param update_i if true, update the latent factors of the first item * @param update_j if true, update the latent factors of the second item */ protected void updateFactors(int u, int i, int j, boolean update_u, boolean update_i, boolean update_j) { double x_uij = itemBias[i] - itemBias[j] + MatrixExtensions.rowScalarProductWithRowDifference(userFactors, u, itemFactors, i, itemFactors, j); double common_part = x_uij < 0 ? 1 : 0; // Adjust bias terms if (update_i) { double biasUpdate = common_part - biasReg * itemBias[i]; itemBias[i] += learnRate * biasUpdate; } if (update_j) { double biasUpdate = -common_part - biasReg * itemBias[j]; itemBias[j] += learnRate * biasUpdate; } // Adjust factors for (int f = 0; f < numFactors; f++) { double w_uf = userFactors.get(u, f); double h_if = itemFactors.get(i, f); double h_jf = itemFactors.get(j, f); if (update_u) { double uf_update = (h_if - h_jf) * common_part - regU * w_uf; userFactors.set(u, f, w_uf + learnRate * uf_update); } if (update_i) { double if_update = w_uf * common_part - regI * h_if; itemFactors.set(i, f, h_if + learnRate * if_update); } if (update_j) { double jf_update = -w_uf * common_part - regJ * h_jf; itemFactors.set(j, f, h_jf + learnRate * jf_update); } } } /** * Compute approximate loss. * @return the approximate loss */ public double computeLoss() { throw new UnsupportedOperationException(); } /** * */ public String toString() { return this.getClass().getName() + " numFactors=" + numFactors + " biasReg=" + biasReg + " regU=" + regU + " regI=" + regI + " regJ=" + regJ + " numIter=" + numIter + " learnRate=" + learnRate + " boldDriver=" + boldDriver + " fastSamplingMemoryLimit=" + fastSamplingMemoryLimit + " initMean=" + initMean + " initStdev=" + initStDev ; } }