package org.grouplens.mooc.cbf; import org.grouplens.lenskit.basic.AbstractItemScorer; import org.grouplens.lenskit.data.dao.UserEventDAO; import org.grouplens.lenskit.data.event.Rating; import org.grouplens.lenskit.data.pref.Preference; import org.grouplens.lenskit.vectors.MutableSparseVector; import org.grouplens.lenskit.vectors.SparseVector; import org.grouplens.lenskit.vectors.VectorEntry; import javax.annotation.Nonnull; import javax.inject.Inject; import java.util.List; /** * @author <a href="http://www.grouplens.org">GroupLens Research</a> */ public class TFIDFItemScorer extends AbstractItemScorer { private final UserEventDAO dao; private final TFIDFModel model; /** * Construct a new item scorer. LensKit's dependency injector will call this constructor and * provide the appropriate parameters. * * @param dao The user-event DAO, so we can fetch a user's ratings when scoring items for them. * @param m The precomputed model containing the item tag vectors. */ @Inject public TFIDFItemScorer(UserEventDAO dao, TFIDFModel m) { this.dao = dao; model = m; } /** * Generate item scores personalized for a particular user. For the TFIDF scorer, this will * prepare a user profile and compare it to item tag vectors to produce the score. * * @param user The user to score for. * @param output The output vector. The contract of this method is that the caller creates a * vector whose possible keys are all items that should be scored; this method * fills in the scores. */ @Override public void score(long user, @Nonnull MutableSparseVector output) { // Get the user's profile, which is a vector with their 'like' for each tag SparseVector userVector = makeUserVector(user); // Loop over each item requested and score it. // The *domain* of the output vector is the items that we are to score. for (VectorEntry e: output.fast(VectorEntry.State.EITHER)) { // Score the item represented by 'e'. // Get the item vector for this item SparseVector iv = model.getItemVector(e.getKey()); double similarity = iv.dot(userVector)/(iv.norm()*userVector.norm()); output.set(e.getKey(), similarity); // DA FARE Compute the cosine of this item and the user's profile, store it in the output vector } } private SparseVector makeUserVector(long user) { // Get the user's ratings List<Rating> userRatings = dao.getEventsForUser(user, Rating.class); if (userRatings == null) { // the user doesn't exist return SparseVector.empty(); } // Create a new vector over tags to accumulate the user profile MutableSparseVector profile = model.newTagVector(); // Fill it with 0's initially - they don't like anything profile.fill(0); // Iterate over the user's ratings to build their profile for (Rating r: userRatings) { // In LensKit, ratings are expressions of preference Preference p = r.getPreference(); // We'll never have a null preference. But in LensKit, ratings can have null // preferences to express the user unrating an item if (p != null && p.getValue() >= 3.5) { SparseVector sparseVectorForItem = model.getItemVector(p.getItemId()); profile.add(sparseVectorForItem); } } // The profile is accumulated, return it. // It is good practice to return a frozen vector. return profile.freeze(); } }