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