/** * Construct a new user bias model. * @param global The global bias. * @param users The user biases. * @param items The item biases. */ public UserItemBiasModel(double global, Long2DoubleMap users, Long2DoubleMap items) { intercept = global; userBiases = Long2DoubleSortedArrayMap.create(users); itemBiases = Long2DoubleSortedArrayMap.create(items); }
@Override public Long2DoubleMap unapply(Long2DoubleMap vector) { return Long2DoubleSortedArrayMap.create(vector); } };
@Override public Long2DoubleMap apply(Long2DoubleMap vector) { return Long2DoubleSortedArrayMap.create(vector); }
/** * Score all items into an accumulator. * @param user The user. * @param items The items to score. * @param accum The accumulator. */ private void scoreItems(long user, @Nonnull Collection<Long> items, ItemItemScoreAccumulator accum) { Long2DoubleMap ratings = Long2DoubleSortedArrayMap.create(rvDAO.userRatingVector(user)); logger.trace("user has {} ratings", ratings.size()); InvertibleFunction<Long2DoubleMap, Long2DoubleMap> transform = normalizer.makeTransformation(user, ratings); Long2DoubleMap itemScores = transform.apply(ratings); LongIterator iter = LongIterators.asLongIterator(items.iterator()); while (iter.hasNext()) { final long item = iter.nextLong(); scoreItem(itemScores, item, accum); } accum.applyReversedTransform(transform); }
protected void scoreItem(Long2DoubleMap userData, long item, ItemItemScoreAccumulator accum) { // find the usable neighbors Long2DoubleSortedArrayMap allNeighbors = Long2DoubleSortedArrayMap.create(model.getNeighbors(item)); Long2DoubleMap neighborhood = allNeighbors.subMap(userData.keySet()); if (neighborhoodSize > 0) { if (logger.isTraceEnabled()) { logger.trace("truncating {} neighbors to {}", neighborhood.size(), neighborhoodSize); } Long2DoubleAccumulator acc = new TopNLong2DoubleAccumulator(neighborhoodSize); for (Long2DoubleMap.Entry e: neighborhood.long2DoubleEntrySet()) { acc.put(e.getLongKey(), e.getDoubleValue()); } neighborhood = acc.finishMap(); } assert neighborhoodSize <= 0 || neighborhood.size() <= neighborhoodSize; if (neighborhood.size() < minNeighbors) { return; } if (logger.isTraceEnabled()) { logger.trace("scoring item {} with {} of {} neighbors", item, neighborhood.size(), allNeighbors.size()); } scorer.score(item, neighborhood, userData, accum); } }
/** * Construct a new user bias model. * @param global The global bias. * @param users The user biases. * @param items The item biases. */ public UserItemBiasModel(double global, Long2DoubleMap users, Long2DoubleMap items) { intercept = global; userBiases = Long2DoubleSortedArrayMap.create(users); itemBiases = Long2DoubleSortedArrayMap.create(items); }
@Override public Long2DoubleMap unapply(Long2DoubleMap vector) { return Long2DoubleSortedArrayMap.create(vector); } };
@Override public Long2DoubleMap apply(Long2DoubleMap vector) { return Long2DoubleSortedArrayMap.create(vector); }
@Test public void testSubMapIndexes() { Long2DoubleMap map = new Long2DoubleOpenHashMap(); map.put(1, 1.0); map.put(2, 2.0); map.put(3, 3.0); map.put(4, 4.0); Long2DoubleSortedArrayMap sam = Long2DoubleSortedArrayMap.create(map); Long2DoubleSortedArrayMap s2 = sam.subMap(2, 4); assertThat(s2.keySet(), contains(2L, 3L)); assertThat(s2.getKeyByIndex(0), equalTo(2L)); assertThat(s2.getKeyByIndex(1), equalTo(3L)); assertThat(s2.getValueByIndex(0), equalTo(2.0)); assertThat(s2.getValueByIndex(1), equalTo(3.0)); } }
@Test public void testRandomMaps() { for (Map<Long,Double> map: someMaps(longs(), doubles())) { Long2DoubleSortedArrayMap vec = Long2DoubleSortedArrayMap.create(map); Set<Long> picked = sets(map.keySet()).next(); Set<Long> extra = sets(longs()).next(); LongSortedSet wanted = LongUtils.setUnion(LongUtils.asLongSet(picked), LongUtils.asLongSet(extra)); Long2DoubleSortedMap sv = vec.subMap(wanted); assertThat(sv.keySet(), everyItem(isIn(wanted))); assertThat(sv.keySet(), containsInAnyOrder(picked.toArray())); assertThat(sv.entrySet(), everyItem(isIn(map.entrySet()))); } }
@Test public void testCombineSorted() { Long2DoubleMap x = new Long2DoubleOpenHashMap(); x.put(4L, 3.5); x.put(5L, 4.0); Long2DoubleMap y = Long2DoubleMaps.singleton(4L, 0.5); Long2DoubleMap res = Vectors.combine(Long2DoubleSortedArrayMap.create(x), y, 2, 1); assertThat(res.get(4L), closeTo(5.5, 1e-6)); assertThat(res.get(5L), closeTo(5.0, 1e-6)); assertThat(res.get(1L), equalTo(0.0)); }
@Test public void testManyItems() { Generator<Double> globals = doubles(); for (Map<Long,Double> map: someMaps(positiveLongs(), doubles())) { double bias = globals.next(); Long2DoubleMap itemBiases = Long2DoubleSortedArrayMap.create(map); BiasModel model = new UserItemBiasModel(bias, Long2DoubleMaps.EMPTY_MAP, itemBiases); assertThat(model.getIntercept(), equalTo(bias)); assertThat(model.getItemBiases(itemBiases.keySet()), equalTo(itemBiases)); for (Set<Long> users : someSets(positiveLongs())) { Long2DoubleMap biases = model.getItemBiases(LongUtils.packedSet(users)); for (long user: users) { if (itemBiases.containsKey(user)) { assertThat(biases.get(user), equalTo(itemBiases.get(user))); } else { assertThat(biases.get(user), equalTo(0.0)); } } } } }
@Test public void testManyUsers() { Generator<Double> globals = doubles(); for (Map<Long,Double> map: someMaps(positiveLongs(), doubles())) { double bias = globals.next(); Long2DoubleMap userBiases = Long2DoubleSortedArrayMap.create(map); BiasModel model = new UserItemBiasModel(bias, userBiases, Long2DoubleMaps.EMPTY_MAP); assertThat(model.getIntercept(), equalTo(bias)); assertThat(model.getUserBiases(userBiases.keySet()), equalTo(userBiases)); for (Set<Long> users : someSets(positiveLongs())) { Long2DoubleMap biases = model.getUserBiases(LongUtils.packedSet(users)); for (long user: users) { if (userBiases.containsKey(user)) { assertThat(biases.get(user), equalTo(userBiases.get(user))); } else { assertThat(biases.get(user), equalTo(0.0)); } } } } }
@Test public void testAddScalarSorted() { for (Map<Long,Double> map: someMaps(longs(), doubles(-1000, 1000))) { double scalar = doubles(-250, 250).next(); Long2DoubleMap m = Long2DoubleSortedArrayMap.create(map); Long2DoubleMap result = Vectors.addScalar(m, scalar); assertThat(Vectors.sum(result), closeTo(Vectors.sum(m) + m.size() * scalar, 1.0e-6)); for (long key: result.keySet()) { assertThat(result.get(key), closeTo(map.get(key) + scalar, 1.0e-6)); } } }
/** * Score all items into an accumulator. * @param user The user. * @param items The items to score. * @param accum The accumulator. */ private void scoreItems(long user, @Nonnull Collection<Long> items, ItemItemScoreAccumulator accum) { Long2DoubleMap ratings = Long2DoubleSortedArrayMap.create(rvDAO.userRatingVector(user)); logger.trace("user has {} ratings", ratings.size()); InvertibleFunction<Long2DoubleMap, Long2DoubleMap> transform = normalizer.makeTransformation(user, ratings); Long2DoubleMap itemScores = transform.apply(ratings); LongIterator iter = LongIterators.asLongIterator(items.iterator()); while (iter.hasNext()) { final long item = iter.nextLong(); scoreItem(itemScores, item, accum); } accum.applyReversedTransform(transform); }
protected void scoreItem(Long2DoubleMap userData, long item, ItemItemScoreAccumulator accum) { // find the usable neighbors Long2DoubleSortedArrayMap allNeighbors = Long2DoubleSortedArrayMap.create(model.getNeighbors(item)); Long2DoubleMap neighborhood = allNeighbors.subMap(userData.keySet()); if (neighborhoodSize > 0) { if (logger.isTraceEnabled()) { logger.trace("truncating {} neighbors to {}", neighborhood.size(), neighborhoodSize); } Long2DoubleAccumulator acc = new TopNLong2DoubleAccumulator(neighborhoodSize); for (Long2DoubleMap.Entry e: neighborhood.long2DoubleEntrySet()) { acc.put(e.getLongKey(), e.getDoubleValue()); } neighborhood = acc.finishMap(); } assert neighborhoodSize <= 0 || neighborhood.size() <= neighborhoodSize; if (neighborhood.size() < minNeighbors) { return; } if (logger.isTraceEnabled()) { logger.trace("scoring item {} with {} of {} neighbors", item, neighborhood.size(), allNeighbors.size()); } scorer.score(item, neighborhood, userData, accum); } }