net.librec.recommender.cf.rating.RBMRecommender Maven / Gradle / Ivy
Show all versions of librec-core Show documentation
/**
* 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.cf.rating;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import net.librec.common.LibrecException;
import net.librec.math.algorithm.Randoms;
import net.librec.math.structure.SequentialSparseVector;
import net.librec.math.structure.Vector;
import net.librec.recommender.MatrixRecommender;
import net.librec.util.Lists;
import net.librec.util.ZeroSetter;
import java.util.Random;
/**
* This class implementing user-oriented Restricted Boltzmann Machines for
* Collaborative Filtering
*
* The origin paper:
*
* Salakhutdinov, R., Mnih, A. Hinton, G, Restricted BoltzmanMachines for
* Collaborative Filtering, To appear inProceedings of the 24thInternational
* Conference onMachine Learning 2007.
* http://www.cs.toronto.edu/~rsalakhu/papers/rbmcf.pdf
*
* @author bin wu(Email:[email protected])
*/
public class RBMRecommender extends MatrixRecommender {
int featureNumber;
int softmax;
int maxIter;
int tSteps;
double epsilonw;
double epsilonvb;
double epsilonhb;
double momentum;
double lamtaw;
double lamtab;
double[][][] weights;
double[][] visbiases;
double[] hidbiases;
double[][][] cDpos;
double[][][] cDneg;
double[][][] cDinc;
double[] poshidact;
double[] neghidact;
char[] poshidstates;
char[] neghidstates;
double[] hidbiasinc;
char[] curposhidstates;
double[][] posvisact;
double[][] negvisact;
double[][] visbiasinc;
double[][] negvisprobs;
int[] negvissoftmax;
int[] moviecount;
String predictionType;
BiMap ratingToIndex = HashBiMap.create();
BiMap indexToRating = HashBiMap.create();
public RBMRecommender() {
}
protected void setup() throws LibrecException {
super.setup();
softmax = ratingScale.size();
for (int i=0; i Randoms.random()) {
poshidstates[h] = 1;
poshidact[h] += 1.0;
} else {
poshidstates[h] = 0;
}
}
for (int h = 0; h < featureNumber; h++) {
curposhidstates[h] = poshidstates[h];
}
int stepT = 0;
do {
boolean finalTStep = (stepT + 1 >= tSteps);
for (Vector.VectorEntry ve : userVec) {
int m = ve.index();
for (int h = 0; h < featureNumber; h++) {
if (curposhidstates[h] == 1) {
for (int r = 0; r < softmax; r++)
negvisprobs[m][r] += weights[m][r][h];
}
}
for (int r = 0; r < softmax; r++)
negvisprobs[m][r] = 1. / (1 + Math.exp(-negvisprobs[m][r] - visbiases[m][r]));
double tsum = 0;
for (int r = 0; r < softmax; r++) {
tsum += negvisprobs[m][r];
}
if (tsum != 0) {
for (int r = 0; r < softmax; r++) {
negvisprobs[m][r] /= tsum;
}
}
double randval = Randoms.random();
for (int ratingIndex=0; ratingIndex Randoms.random()) {
neghidstates[h] = 1;
if (finalTStep)
neghidact[h] += 1.0;
} else {
neghidstates[h] = 0;
}
}
if (!finalTStep) {
for (int h = 0; h < featureNumber; h++)
curposhidstates[h] = neghidstates[h];
ZeroSetter.zero(negvisprobs, numItems, softmax);
}
} while (++stepT < tSteps);
for (Vector.VectorEntry ve : userVec) {
int m = ve.index();
int r = ratingToIndex.get(ve.get());
for (int h = 0; h < featureNumber; h++) {
if (poshidstates[h] == 1) {
cDpos[m][r][h] += 1.0;
}
cDneg[m][negvissoftmax[m]][h] += (double) neghidstates[h];
}
}
update(u, num);
}
}
}
private void update(int user, int num) {
int bSize = 100;
if (((user + 1) % bSize) == 0 || (user + 1) == numUsers) {
int numcases = user % bSize;
numcases++;
for (int m = 0; m < numItems; m++) {
if (moviecount[m] == 0)
continue;
for (int h = 0; h < featureNumber; h++) {
for (int r = 0; r < softmax; r++) {
double CDp = cDpos[m][r][h];
double CDn = cDneg[m][r][h];
if (CDp != 0.0 || CDn != 0.0) {
CDp /= ((double) moviecount[m]);
CDn /= ((double) moviecount[m]);
cDinc[m][r][h] = momentum * cDinc[m][r][h]
+ epsilonw * ((CDp - CDn) - lamtaw * weights[m][r][h]);
weights[m][r][h] += cDinc[m][r][h];
}
}
}
for (int r = 0; r < softmax; r++) {
if (posvisact[m][r] != 0.0 || negvisact[m][r] != 0.0) {
posvisact[m][r] /= ((double) moviecount[m]);
negvisact[m][r] /= ((double) moviecount[m]);
visbiasinc[m][r] = momentum * visbiasinc[m][r]
+ epsilonvb * (posvisact[m][r] - negvisact[m][r] - lamtab * visbiases[m][r]);
visbiases[m][r] += visbiasinc[m][r];
}
}
}
for (int h = 0; h < featureNumber; h++) {
if (poshidact[h] != 0.0 || neghidact[h] != 0.0) {
poshidact[h] /= ((double) (numcases));
neghidact[h] /= ((double) (numcases));
hidbiasinc[h] = momentum * hidbiasinc[h]
+ epsilonhb * (poshidact[h] - neghidact[h] - lamtab * hidbiases[h]);
hidbiases[h] += hidbiasinc[h];
}
}
Zero();
}
}
private void Zero() {
cDpos = new double[numItems][softmax][featureNumber];
cDneg = new double[numItems][softmax][featureNumber];
poshidact = new double[featureNumber];
neghidact = new double[featureNumber];
posvisact = new double[numItems][softmax];
negvisact = new double[numItems][softmax];
moviecount = new int[numItems];
}
protected double predict(int u, int m) throws LibrecException {
double[] scoreProbs = new double[softmax];
double[] factorProbs = new double[featureNumber];
SequentialSparseVector userVec = trainMatrix.row(u);
double[] sumW = new double[featureNumber];
for (Vector.VectorEntry ve : userVec) {
int item = ve.index();
int rateIdx = ratingToIndex.get(ve.get());
for (int h = 0; h < featureNumber; h++) {
sumW[h] += weights[item][rateIdx][h];
}
}
for (int h = 0; h < featureNumber; h++) {
factorProbs[h] = 1.0 / (1.0 + Math.exp(0 - sumW[h] - hidbiases[h]));
}
for (int h = 0; h < featureNumber; h++) {
for (int r = 0; r < softmax; r++) {
scoreProbs[r] += factorProbs[h] * weights[m][r][h];
}
}
double probSum = 0.0;
for (int r = 0; r < softmax; r++) {
scoreProbs[r] = 1.0 / (1.0 + Math.exp(0 - scoreProbs[r] - visbiases[m][r]));
probSum += scoreProbs[r];
}
for (int r=0; r max_value) {
max_index = r;
}
}
predict = indexToRating.get(max_index);
} else if (predictionType.equals("mean")) {
double mean = 0.0;
for (int r = 0; r < softmax; r++) {
mean += scoreProbs[r] * indexToRating.get(r);
}
predict = mean;
}
return predict;
}
}