Getting Started (2)

Introduction

In scenario 2 of Getting started (1) a user expressed his interest in music, and a new prediction was calculated for a music movie. We saw that the recommender had used the knowledge about the interest of the user to calculate this new prediction. The printed explanation of scenario 2 showed that the prediction result was calculated by the mainGenreLMS prediction technique.

This section zooms in on what's going on behind the scene.

We assume that by now you are familiar with the main concepts of the Duine Framework.

Why the mainGenreLMS prediction technique?

The figure below shows how the hybrid recommender "walked" the predictor tree and finally decided to apply the mainGenreLMS (Least Mean Square) prediction technique. At every node in the tree the associated prediction strategy decided which child should handle the calculation, until a leave was reached, in this case associated with the mainGenreLMS prediction technique. More information about mainGenreLMS can be found in the Duine scientific documentation , at page 91.

Duine demo predictor tree

How does mainGenreLMS get it's data?

In the main concepts section we saw that a prediction technique is reading from a user profile, and that a feedback processor updates the user profile. This is again shown in the figure below , but now for the more specific case of scenario 2: The mainGenreLMS prediction technique is reading from the mainGenreInterestModel profile model. The mainGenreInterestModel.termFeedbackProcessor updates the mainGenreInterestModel profile model.

Configuration of mainGenreLMS

Configuration of the predictor, profile model and feedback processor

At this point it is interesting to take a look at the actual XML configuration used in the demo application. The configuration snippet below shows how the mainGenreLMS prediction technique, the mainGenreInterestModel.termFeedbackProcessor and the mainGenreInterestModel are wired together. It might look a bit complex, but essentially it is just the XML representation of the figure above.

<!-- see spring-predictors.xml in demo application -->
<bean id="mainGenreLMS" class="org.duineframework.recommender.predictor.InterestLMS" >
        <description>Uses main genres for its predictions.</description>
        <property name="name" value="mainGenreLMS"/>
        <property name="interestModel" ref="mainGenreInterestModel"/>
        <property name="ratable2InterestAdapter" ref="mainGenreRatable2InterestAdapter"/>
</bean>

<!-- see spring-profiles.xml in demo application -->
<bean id="mainGenreInterestModel" class="org.duineframework.recommender.profile.interest.InterestModel" >
        <property name="modelId" value="org.duineframework.recommender.profile.interest.mainGenreInterestModel"/>
        <property name="defaultSubject" value="$maingenre.default"/>
        <property name="interestModelDAO" ref="interestModelDAO"/>
</bean>

<!-- see spring-profiles.xml in demo application -->
<bean id="mainGenreInterestModel.LMSfeedbackProcessor" class="org.duineframework.recommender.profile.interest.InterestLMSFeedbackProcessor">
        <description>Feedback processor for main genres that learns using the Least Mean Square method.</description>
        <property name="id" value="mainGenreInterestModel.LMSfeedbackProcessor"/>
        <property name="interestModel" ref="mainGenreInterestModel"/>
        <property name="learningModerator" value="0.14">
                <description>The step size with which the processor adapts its weight.</description>
        </property>
        <property name="certaintyModerator" value="0.20">
                <description>The step size with which the processor adapts its certainty. </description>
        </property>
        <property name="predictor" ref="mainGenreLMS">
                <description>Note that this feedback processor needs a predictor to process feedback.</description>
        </property>
        <property name="ratable2InterestAdapter" ref="mainGenreRatable2InterestAdapter"/>
</bean>

<bean id="mainGenreRatable2InterestAdapter" class="org.duineframework.movielens.MovieGenre2InterestAdapter">
</bean>

This configuration shows that the feedback processor has some parameters that can tweak it's learning behaviour. Furthermore, it refers to a ratable2InterestAdapter. This is explained below.

Adapting domain object interfaces to the predictor interface

In scenario 2 we created a movie with genre music . The method getGenres() is part of the Movie interface. The predictor has no knowledge of domain objects, it only "sees" the IRatableItem interface. Then how can the predictor get access to the genres of the Movie? Since the predictor is part of the framework it can not cast the IRatableItem type to the Movie type, see the code below for an illustration of this problem.

public interface IRatableItem {
        RatableItemId getId();
}

public class Movie implements IRatableItem {

        private RatableItemId id;
    private List<String> genres = new ArrayList<String>();

        .... 
                
        public List<String> getGenres() {
                return genres;
        }
                        
        public void setGenres(List<String> genres) {
                this.genres = genres;
        }

        ....
}

public void runScenario2() {
        .....
        Movie movie2 = new Movie(new RatableItemId("2"));
        ArrayList<String> genres = new ArrayList<String>();
        genres.add("music");
        movie2.setGenres(genres);
        ....
        Prediction prediction = recommender.predict(user2, movie2);
        ....    
}

The solution is based on the usage of adapters. This adapter pattern is applied on various locations in the framework where a predictor needs access to domain specific data. In these cases a predictor is designed to expect an adapter that implements a specific interface. The adapter implementation is provided by the framework client application and therefore allowed to have the knowledge of the interface of the domain object. Below you can see an example of an adapter implementation for the MovieGenre2InterestAdapter Because this code is part of the application logic (and not part of the Duine Framework) the IRatableItem can be casted to a Movie , and the genres can be returned as strings.

public class MovieGenre2InterestAdapter implements IRatable2InterestAdapter {

        /**
         * Returns the genres of the movie (the specified content item)
         */
        public List<String> getSubjects(IRatableItem item) {
                if (!(item instanceof Movie)) {
                        return new ArrayList<String>();
                }
                Movie movie = (Movie) item;     
                return movie.getGenres();
        }
}