Author: srowen
Date: Mon Jun  8 17:26:17 2009
New Revision: 782709

URL: http://svn.apache.org/viewvc?rev=782709&view=rev
Log:
Incorporated Andre's fix, and shuffling enhancement. Also added a new Iterator 
I am interested in using later.

Added:
    
lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/common/PermutingIterator.java
Modified:
    
lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/svd/SVDRecommender.java

Added: 
lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/common/PermutingIterator.java
URL: 
http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/common/PermutingIterator.java?rev=782709&view=auto
==============================================================================
--- 
lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/common/PermutingIterator.java
 (added)
+++ 
lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/common/PermutingIterator.java
 Mon Jun  8 17:26:17 2009
@@ -0,0 +1,78 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.cf.taste.impl.common;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Random;
+
+/**
+ * An {...@link Iterator} that iterates in a random order over a given sequence
+ * of elements. It is non-destructive.
+ */
+public final class PermutingIterator<T> implements Iterator<T> {
+
+  private final T[] elements;
+  private final int[] permutation;
+  private int offset;
+
+  public PermutingIterator(T[] elements) {
+    this.elements = elements;
+    this.permutation = new int[elements.length];
+    offset = 0;
+    buildPermutation();
+  }
+
+  private void buildPermutation() {
+    int length = permutation.length;
+    for (int i = 0; i < length; i++) {
+      permutation[i] = i;
+    }
+    Random r = RandomUtils.getRandom();
+    for (int i = 0; i < length - 1; i++) {
+      int swapWith = i + r.nextInt(length - i);
+      if (i != swapWith) {
+        int temp = permutation[i];
+        permutation[i] = permutation[swapWith];
+        permutation[swapWith] = temp;
+      }
+    }
+  }
+
+  @Override
+  public boolean hasNext() {
+    return offset < elements.length;
+  }
+
+  @Override
+  public T next() {
+    if (offset >= elements.length) {
+      throw new NoSuchElementException();
+    }
+    return elements[permutation[offset++]];
+  }
+
+  /**
+   * @throws UnsupportedOperationException always
+   */
+  @Override
+  public void remove() {
+    throw new UnsupportedOperationException();
+  }
+
+}

Modified: 
lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/svd/SVDRecommender.java
URL: 
http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/svd/SVDRecommender.java?rev=782709&r1=782708&r2=782709&view=diff
==============================================================================
--- 
lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/svd/SVDRecommender.java
 (original)
+++ 
lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/svd/SVDRecommender.java
 Mon Jun  8 17:26:17 2009
@@ -17,12 +17,22 @@
 
 package org.apache.mahout.cf.taste.impl.recommender.svd;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
 import org.apache.mahout.cf.taste.common.NoSuchItemException;
 import org.apache.mahout.cf.taste.common.NoSuchUserException;
 import org.apache.mahout.cf.taste.common.Refreshable;
 import org.apache.mahout.cf.taste.common.TasteException;
 import org.apache.mahout.cf.taste.impl.common.FastMap;
 import org.apache.mahout.cf.taste.impl.common.FullRunningAverage;
+import org.apache.mahout.cf.taste.impl.common.RandomUtils;
 import org.apache.mahout.cf.taste.impl.common.RefreshHelper;
 import org.apache.mahout.cf.taste.impl.common.RunningAverage;
 import org.apache.mahout.cf.taste.impl.recommender.AbstractRecommender;
@@ -38,12 +48,6 @@
 import org.slf4j.LoggerFactory;
 import org.uncommons.maths.statistics.DataSet;
 
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Callable;
-
 /**
  * <p>A {...@link Recommender} which uses Single Value Decomposition to
  * find the main features of the {...@link DataSet}.
@@ -52,6 +56,7 @@
 public final class SVDRecommender extends AbstractRecommender {
 
   private static final Logger log = 
LoggerFactory.getLogger(SVDRecommender.class);
+  private static final Random random = RandomUtils.getRandom();
 
   private final RefreshHelper refreshHelper;
 
@@ -61,6 +66,7 @@
   private final Map<Object, Integer> userMap;
   private Map<Object, Integer> itemMap;
   private ExpectationMaximizationSVD emSvd;
+  private final List<Preference> cachedPreferences;
 
   /**
    * @param dataModel
@@ -98,19 +104,31 @@
     }
 
     double average = getAveragePreference();
-    double defaultValue = Math.sqrt((average - 1.0) / numFeatures);
+    double defaultValue = Math.sqrt((average - 1.0) / (double) numFeatures);
 
     emSvd = new ExpectationMaximizationSVD(numUsers, numItems, numFeatures, 
defaultValue);
-
+    cachedPreferences = new ArrayList<Preference>(numUsers);
+    recachePreferences();
 
     refreshHelper = new RefreshHelper(new Callable<Object>() {
       @Override
-      public Object call() {
+      public Object call() throws TasteException {
+        recachePreferences();
         //TODO: train again
         return null;
       }
     });
     refreshHelper.addDependency(dataModel);
+    
+  }
+
+  private void recachePreferences() throws TasteException {
+    cachedPreferences.clear();
+    for (User user : getDataModel().getUsers()) {
+      for (Preference pref : user.getPreferences()) {
+        cachedPreferences.add(pref);
+      }
+    }
   }
 
   private double getAveragePreference() throws TasteException {
@@ -123,20 +141,19 @@
     return average.getAverage();
   }
 
-  public void train(int steps) throws TasteException {
+  public void train(int steps) {
     for (int i = 0; i < steps; i++) {
       nextTrainStep();
     }
   }
 
-  private void nextTrainStep() throws TasteException {
+  private void nextTrainStep() {
+    Collections.shuffle(cachedPreferences, random);
     for (int i = 0; i < numFeatures; i++) {
-      for (User user : getDataModel().getUsers()) {
-        int useridx = userMap.get(user.getID());
-        for (Preference pref : user.getPreferencesAsArray()) {
-          int itemidx = itemMap.get(pref.getItem().getID());
-          emSvd.train(useridx, itemidx, i, pref.getValue());
-        }
+      for (Preference pref : cachedPreferences) {
+        int useridx = userMap.get(pref.getUser().getID());
+        int itemidx = itemMap.get(pref.getItem().getID());
+        emSvd.train(useridx, itemidx, i, pref.getValue());
       }
     }
   }
@@ -203,7 +220,7 @@
 
     @Override
     public double estimate(Item item) throws TasteException {
-      return estimatePreference(theUser, item.getID());
+      return estimatePreference(theUser.getID(), item.getID());
     }
   }
 


Reply via email to