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);
   }
+  
 }


Reply via email to