All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.librec.recommender.context.rating.TrustSVDRecommender Maven / Gradle / Ivy

/**
 * Copyright (C) 2016 LibRec
 * 

* This file is part of LibRec. * LibRec 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. *

* LibRec 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 LibRec. If not, see . */ package net.librec.recommender.context.rating; import com.google.common.cache.LoadingCache; import net.librec.annotation.ModelData; import net.librec.common.LibrecException; import net.librec.math.structure.DenseMatrix; import net.librec.math.structure.DenseVector; import net.librec.math.structure.MatrixEntry; import net.librec.recommender.SocialRecommender; import java.util.List; import java.util.concurrent.ExecutionException; /** * Guo et al., TrustSVD: Collaborative Filtering with Both the Explicit and Implicit Influence of User Trust and * of Item Ratings, AAAI 2015. * * @author guoguibing and Keqiang Wang */ @ModelData({"isRating", "trustsvd", "userFactors", "itemFactors", "impItemFactors", "userBiases", "itemBiases", "socialMatrix", "trainMatrix"}) public class TrustSVDRecommender extends SocialRecommender { /** * impItemFactors denotes the implicit influence of items rated by user u in the past on the ratings of unknown items in the future. */ private DenseMatrix impItemFactors; /** * the user-specific latent appender vector of users (trustees)trusted by user u */ private DenseMatrix trusteeFactors; /** * weights of users(trustees) trusted by user u */ private DenseVector trusteeWeights; /** * weights of users(trusters) who trust user u */ private DenseVector trusterWeights; /** * weights of items rated by user u */ private DenseVector impItemWeights; /** * user biases and item biases */ private DenseVector userBiases, itemBiases; /** * bias regularization */ protected double regBias; /** * user-items cache, user-trustee cache */ protected LoadingCache> userItemsCache, userTrusteeCache; /** * Guava cache configuration */ protected static String cacheSpec; /** * initial the model * * @throws LibrecException if error occurs */ @Override public void setup() throws LibrecException { super.setup(); // userFactors.init(1.0); // itemFactors.init(1.0); regBias = conf.getDouble("rec.bias.regularization", 0.01); cacheSpec = conf.get("guava.cache.spec", "maximumSize=200,expireAfterAccess=2m"); //initialize userBiases and itemBiases userBiases = new DenseVector(numUsers); itemBiases = new DenseVector(numItems); userBiases.init(initMean, initStd); itemBiases.init(initMean, initStd); //initialize trusteeFactors and impItemFactors trusteeFactors = new DenseMatrix(numUsers, numFactors); impItemFactors = new DenseMatrix(numItems, numFactors); trusteeFactors.init(initMean, initStd); impItemFactors.init(initMean, initStd); //initialize trusteeWeights, trusterWeights, impItemWeights trusteeWeights = new DenseVector(numUsers); trusterWeights = new DenseVector(numUsers); impItemWeights = new DenseVector(numItems); for (int userIdx = 0; userIdx < numUsers; userIdx++) { int userFriendCount = socialMatrix.columnSize(userIdx); trusteeWeights.set(userIdx, userFriendCount > 0 ? 1.0 / Math.sqrt(userFriendCount) : 1.0); userFriendCount = socialMatrix.rowSize(userIdx); trusterWeights.set(userIdx, userFriendCount > 0 ? 1.0 / Math.sqrt(userFriendCount) : 1.0); } for (int itemIdx = 0; itemIdx < numItems; itemIdx++) { int itemUsersCount = trainMatrix.columnSize(itemIdx); impItemWeights.set(itemIdx, itemUsersCount > 0 ? 1.0 / Math.sqrt(itemUsersCount) : 1.0); } //initialize user-items cache, user-trustee cache userItemsCache = trainMatrix.rowColumnsCache(cacheSpec); userTrusteeCache = socialMatrix.rowColumnsCache(cacheSpec); } /** * train model process * * @throws LibrecException if error occurs */ @Override protected void trainModel() throws LibrecException { for (int iter = 1; iter <= numIterations; iter++) { loss = 0.0d; // temp user Factors and trustee factors DenseMatrix tempUserFactors = new DenseMatrix(numUsers, numFactors); DenseMatrix trusteeTempFactors = new DenseMatrix(numUsers, numFactors); for (MatrixEntry matrixEntry : trainMatrix) { int userIdx = matrixEntry.row(); // user userIdx int itemIdx = matrixEntry.column(); // item itemIdx double realRating = matrixEntry.get(); // real rating on item itemIdx rated by user userIdx // To speed up, directly access the prediction instead of invoking "predictRating = predict(userIdx,itemIdx)" double userBiasValue = userBiases.get(userIdx); double itemBiasValue = itemBiases.get(itemIdx); double predictRating = globalMean + userBiasValue + itemBiasValue + DenseMatrix.rowMult(userFactors, userIdx, itemFactors, itemIdx); // get the implicit influence predict rating using items rated by user userIdx List impItemsList = null; try { impItemsList = userItemsCache.get(userIdx); } catch (ExecutionException e) { e.printStackTrace(); } if (impItemsList.size() > 0) { double sum = 0; for (int impItemIdx : impItemsList) sum += DenseMatrix.rowMult(impItemFactors, impItemIdx, itemFactors, itemIdx); predictRating += sum / Math.sqrt(impItemsList.size()); } // the user-specific influence of users (trustees)trusted by user userIdx List trusteesList = null; try { trusteesList = userTrusteeCache.get(userIdx); } catch (ExecutionException e) { e.printStackTrace(); } if (trusteesList.size() > 0) { double sum = 0.0; for (int trusteeIdx : trusteesList) sum += DenseMatrix.rowMult(trusteeFactors, trusteeIdx, itemFactors, itemIdx); predictRating += sum / Math.sqrt(trusteesList.size()); } double error = predictRating - realRating; loss += error * error; double userWeightDenom = Math.sqrt(impItemsList.size()); double trusteeWeightDenom = Math.sqrt(trusteesList.size()); double userWeight = 1.0 / userWeightDenom; double itemWeight = impItemWeights.get(itemIdx); // update factors // stochastic gradient descent sgd double sgd = error + regBias * userWeight * userBiasValue; userBiases.add(userIdx, -learnRate * sgd); sgd = error + regBias * itemWeight * itemBiasValue; itemBiases.add(itemIdx, -learnRate * sgd); loss += regBias * userWeight * userBiasValue * userBiasValue + regBias * itemWeight * itemBiasValue * itemBiasValue; double[] sumImpItemsFactors = new double[numFactors]; for (int factorIdx = 0; factorIdx < numFactors; factorIdx++) { double sum = 0; for (int impItemIdx : impItemsList) sum += impItemFactors.get(impItemIdx, factorIdx); sumImpItemsFactors[factorIdx] = userWeightDenom > 0 ? sum / userWeightDenom : sum; } double[] sumTrusteesFactors = new double[numFactors]; for (int factorIdx = 0; factorIdx < numFactors; factorIdx++) { double sum = 0; for (int trusteeIdx : trusteesList) sum += trusteeFactors.get(trusteeIdx, factorIdx); sumTrusteesFactors[factorIdx] = trusteeWeightDenom > 0 ? sum / trusteeWeightDenom : sum; } for (int factorIdx = 0; factorIdx < numFactors; factorIdx++) { double userFactorValue = userFactors.get(userIdx, factorIdx); double itemFactorValue = itemFactors.get(itemIdx, factorIdx); double deltaUser = error * itemFactorValue + regUser * userWeight * userFactorValue; double deltaItem = error * (userFactorValue + sumImpItemsFactors[factorIdx] + sumTrusteesFactors[factorIdx]) + regItem * itemWeight * itemFactorValue; tempUserFactors.add(userIdx, factorIdx, deltaUser); itemFactors.add(itemIdx, factorIdx, -learnRate * deltaItem); loss += regUser * userWeight * userFactorValue * userFactorValue + regItem * itemWeight * itemFactorValue * itemFactorValue; for (int impItemIdx : impItemsList) { double impItemFactorValue = impItemFactors.get(impItemIdx, factorIdx); double impItemWeightValue = impItemWeights.get(impItemIdx); double deltaImpItem = error * itemFactorValue / userWeightDenom + regItem * impItemWeightValue * impItemFactorValue; impItemFactors.add(impItemIdx, factorIdx, -learnRate * deltaImpItem); loss += regItem * impItemWeightValue * impItemFactorValue * impItemFactorValue; } // update trusteeTempFactors for (int trusteeIdx : trusteesList) { double trusteeFactorValue = trusteeFactors.get(trusteeIdx, factorIdx); double trusteeWeightValue = trusteeWeights.get(trusteeIdx); double deltaTrustee = error * itemFactorValue / trusteeWeightDenom + regUser * trusteeWeightValue * trusteeFactorValue; trusteeTempFactors.add(trusteeIdx, factorIdx, deltaTrustee); loss += regUser * trusteeWeightValue * trusteeFactorValue * trusteeFactorValue; } } } for (MatrixEntry socialMatrixEntry : socialMatrix) { int userIdx = socialMatrixEntry.row(); int trusteeIdx = socialMatrixEntry.column(); double socialValue = socialMatrixEntry.get(); if (socialValue == 0) continue; double predtictSocialValue = DenseMatrix.rowMult(userFactors, userIdx, trusteeFactors, trusteeIdx); double socialError = predtictSocialValue - socialValue; loss += regSocial * socialError * socialError; double deriValue = regSocial * socialError; double trusterWeightValue = trusterWeights.get(userIdx); for (int factorIdx = 0; factorIdx < numFactors; factorIdx++) { double userFactorValue = userFactors.get(userIdx, factorIdx); double trusteeFactorValue = trusteeFactors.get(trusteeIdx, factorIdx); tempUserFactors.add(userIdx, factorIdx, deriValue * trusteeFactorValue + regSocial * trusterWeightValue * userFactorValue); trusteeTempFactors.add(trusteeIdx, factorIdx, deriValue * userFactorValue); loss += regSocial * trusterWeightValue * userFactorValue * userFactorValue; } } userFactors.addEqual(tempUserFactors.scale(-learnRate)); trusteeFactors.addEqual(trusteeTempFactors.scale(-learnRate)); loss *= 0.5d; if (isConverged(iter) && earlyStop) { break; } updateLRate(iter); }// end of training } /** * predict a specific rating for user userIdx on item itemIdx. * * @param userIdx user index * @param itemIdx item index * @return predictive rating for user userIdx on item itemIdx * @throws LibrecException if error occurs */ protected double predict(int userIdx, int itemIdx) throws LibrecException { double predictRating = globalMean + userBiases.get(userIdx) + itemBiases.get(itemIdx) + DenseMatrix.rowMult(userFactors, userIdx, itemFactors, itemIdx); //the implicit influence of items rated by user in the past on the ratings of unknown items in the future. List userItemsList = null; try { userItemsList = userItemsCache.get(userIdx); } catch (ExecutionException e) { e.printStackTrace(); } if (userItemsList.size() > 0) { double sum = 0; for (int userItemIdx : userItemsList) sum += DenseMatrix.rowMult(impItemFactors, userItemIdx, itemFactors, itemIdx); predictRating += sum / Math.sqrt(userItemsList.size()); } // the user-specific influence of users (trustees)trusted by user u List trusteeList = null; try { trusteeList = userTrusteeCache.get(userIdx); } catch (ExecutionException e) { e.printStackTrace(); } if (trusteeList.size() > 0) { double sum = 0.0; for (int trusteeIdx : trusteeList) sum += DenseMatrix.rowMult(trusteeFactors, trusteeIdx, itemFactors, itemIdx); predictRating += sum / Math.sqrt(trusteeList.size()); } return predictRating; } @Override protected double predict(int userIdx, int itemIdx, boolean bounded) throws LibrecException { double predictRating = predict(userIdx, itemIdx); return predictRating; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy