Author: srowen
Date: Tue Apr 12 18:58:13 2011
New Revision: 1091540
URL: http://svn.apache.org/viewvc?rev=1091540&view=rev
Log:
MAHOUT-640 make factorizers / SVD recommender properly refreshable
Modified:
mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/svd/AbstractFactorizer.java
mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/svd/ExpectationMaximizationSVDFactorizer.java
mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/svd/Factorizer.java
mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/svd/SVDRecommender.java
Modified:
mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/svd/AbstractFactorizer.java
URL:
http://svn.apache.org/viewvc/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/svd/AbstractFactorizer.java?rev=1091540&r1=1091539&r2=1091540&view=diff
==============================================================================
---
mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/svd/AbstractFactorizer.java
(original)
+++
mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/svd/AbstractFactorizer.java
Tue Apr 12 18:58:13 2011
@@ -17,9 +17,14 @@
package org.apache.mahout.cf.taste.impl.recommender.svd;
+import java.util.Collection;
+import java.util.concurrent.Callable;
+
+import org.apache.mahout.cf.taste.common.Refreshable;
import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.impl.common.FastByIDMap;
import org.apache.mahout.cf.taste.impl.common.LongPrimitiveIterator;
+import org.apache.mahout.cf.taste.impl.common.RefreshHelper;
import org.apache.mahout.cf.taste.model.DataModel;
/**
@@ -27,10 +32,25 @@ import org.apache.mahout.cf.taste.model.
*/
public abstract class AbstractFactorizer implements Factorizer {
- private final FastByIDMap<Integer> userIDMapping;
- private final FastByIDMap<Integer> itemIDMapping;
+ private final DataModel dataModel;
+ private FastByIDMap<Integer> userIDMapping;
+ private FastByIDMap<Integer> itemIDMapping;
+ private final RefreshHelper refreshHelper;
protected AbstractFactorizer(DataModel dataModel) throws TasteException {
+ this.dataModel = dataModel;
+ buildMappings();
+ refreshHelper = new RefreshHelper(new Callable<Object>() {
+ @Override
+ public Object call() throws TasteException {
+ buildMappings();
+ return null;
+ }
+ });
+ refreshHelper.addDependency(dataModel);
+ }
+
+ private void buildMappings() throws TasteException {
userIDMapping = createIDMapping(dataModel.getNumUsers(),
dataModel.getUserIDs());
itemIDMapping = createIDMapping(dataModel.getNumItems(),
dataModel.getItemIDs());
}
@@ -40,11 +60,19 @@ public abstract class AbstractFactorizer
}
protected Integer userIndex(long userID) {
- return userIDMapping.get(userID);
+ Integer userIndex = userIDMapping.get(userID);
+ if(userIndex == null) {
+ userIndex = userIDMapping.put(userID, userIDMapping.size());
+ }
+ return userIndex;
}
protected Integer itemIndex(long itemID) {
- return itemIDMapping.get(itemID);
+ Integer itemIndex = itemIDMapping.get(itemID);
+ if(itemIndex == null) {
+ itemIndex = itemIDMapping.put(itemID, itemIDMapping.size());
+ }
+ return itemIndex;
}
private static FastByIDMap<Integer> createIDMapping(int size,
LongPrimitiveIterator idIterator) {
@@ -55,4 +83,10 @@ public abstract class AbstractFactorizer
}
return mapping;
}
+
+ @Override
+ public void refresh(Collection<Refreshable> alreadyRefreshed) {
+ refreshHelper.refresh(alreadyRefreshed);
+ }
+
}
Modified:
mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/svd/ExpectationMaximizationSVDFactorizer.java
URL:
http://svn.apache.org/viewvc/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/svd/ExpectationMaximizationSVDFactorizer.java?rev=1091540&r1=1091539&r2=1091540&view=diff
==============================================================================
---
mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/svd/ExpectationMaximizationSVDFactorizer.java
(original)
+++
mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/svd/ExpectationMaximizationSVDFactorizer.java
Tue Apr 12 18:58:13 2011
@@ -37,7 +37,6 @@ public final class ExpectationMaximizati
private static final Logger log =
LoggerFactory.getLogger(ExpectationMaximizationSVDFactorizer.class);
- private final Random random;
private final double learningRate;
/** Parameter used to prevent overfitting. 0.02 is a good value. */
private final double preventOverfitting;
@@ -45,14 +44,15 @@ public final class ExpectationMaximizati
private final int numFeatures;
/** number of iterations */
private final int numIterations;
+ private final double randomNoise;
/** user singular vectors */
- private final double[][] leftVectors;
+ private double[][] leftVectors;
/** item singular vectors */
- private final double[][] rightVectors;
+ private double[][] rightVectors;
private final DataModel dataModel;
- private final List<SVDPreference> cachedPreferences;
- private final double defaultValue;
- private final double interval;
+ private List<SVDPreference> cachedPreferences;
+ private double defaultValue;
+ private double interval;
public ExpectationMaximizationSVDFactorizer(DataModel dataModel,
int numFeatures,
@@ -68,14 +68,19 @@ public final class ExpectationMaximizati
double randomNoise,
int numIterations) throws
TasteException {
super(dataModel);
- random = RandomUtils.getRandom();
this.dataModel = dataModel;
this.numFeatures = numFeatures;
this.numIterations = numIterations;
this.learningRate = learningRate;
this.preventOverfitting = preventOverfitting;
+ this.randomNoise = randomNoise;
+ }
+
+ @Override
+ public Factorization factorize() throws TasteException {
+ Random random = RandomUtils.getRandom();
leftVectors = new double[dataModel.getNumUsers()][numFeatures];
rightVectors = new double[dataModel.getNumItems()][numFeatures];
@@ -94,10 +99,6 @@ public final class ExpectationMaximizati
}
}
cachedPreferences = new ArrayList<SVDPreference>(dataModel.getNumUsers());
- }
-
- @Override
- public Factorization factorize() throws TasteException {
cachePreferences();
double rmse = (dataModel.getMaxPreference() -
dataModel.getMinPreference());
for (int ii = 0; ii < numFeatures; ii++) {
Modified:
mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/svd/Factorizer.java
URL:
http://svn.apache.org/viewvc/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/svd/Factorizer.java?rev=1091540&r1=1091539&r2=1091540&view=diff
==============================================================================
---
mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/svd/Factorizer.java
(original)
+++
mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/svd/Factorizer.java
Tue Apr 12 18:58:13 2011
@@ -17,12 +17,13 @@
package org.apache.mahout.cf.taste.impl.recommender.svd;
+import org.apache.mahout.cf.taste.common.Refreshable;
import org.apache.mahout.cf.taste.common.TasteException;
/**
* Implementation must be able to create a factorization of a rating matrix
*/
-public interface Factorizer {
+public interface Factorizer extends Refreshable {
Factorization factorize() throws TasteException;
Modified:
mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/svd/SVDRecommender.java
URL:
http://svn.apache.org/viewvc/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/svd/SVDRecommender.java?rev=1091540&r1=1091539&r2=1091540&view=diff
==============================================================================
---
mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/svd/SVDRecommender.java
(original)
+++
mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/svd/SVDRecommender.java
Tue Apr 12 18:58:13 2011
@@ -43,6 +43,7 @@ import org.slf4j.LoggerFactory;
public final class SVDRecommender extends AbstractRecommender {
private Factorization factorization;
+ private final Factorizer factorizer;
private final RefreshHelper refreshHelper;
private static final Logger log =
LoggerFactory.getLogger(SVDRecommender.class);
@@ -54,17 +55,23 @@ public final class SVDRecommender extend
public SVDRecommender(DataModel dataModel, Factorizer factorizer,
CandidateItemsStrategy candidateItemsStrategy)
throws TasteException {
super(dataModel, candidateItemsStrategy);
- factorization = factorizer.factorize();
+ this.factorizer = factorizer;
+ train();
refreshHelper = new RefreshHelper(new Callable<Object>() {
@Override
- public Object call() {
- // TODO: train again
+ public Object call() throws TasteException {
+ train();
return null;
}
});
refreshHelper.addDependency(getDataModel());
+ refreshHelper.addDependency(factorizer);
}
+ private void train() throws TasteException {
+ factorization = factorizer.factorize();
+ }
+
@Override
public List<RecommendedItem> recommend(long userID, int howMany, IDRescorer
rescorer) throws TasteException {
Preconditions.checkArgument(howMany >= 1, "howMany must be at least 1");
@@ -108,8 +115,12 @@ public final class SVDRecommender extend
}
}
+ /**
+ * Refresh the data model and factorization.
+ */
@Override
public void refresh(Collection<Refreshable> alreadyRefreshed) {
refreshHelper.refresh(alreadyRefreshed);
}
+
}