This section describes the Duine Demo application. It is a simple java client application that uses a recommender to calculate predictions about a user's interest in movies, and to process feedback by a user about movies. For your convenience we have provided 2 distributions of this demo application:
The duine-movielens-* distribution contains all demo sources and binaries, including all jar dependencies. You can run it from the command line. All runtime files are in the classes and lib directories. The sources are for reference only.
The duine-movielens-*-maven distribution contains a ready to use Maven project, including the demo sources. Building this project will collect all required dependencies from the global Maven repository. After the build you can run it within your favourite IDE.
The Duine Framework can work with all relational databases that are supported by Hibernate . The demo application is configured to work out-of-the-box with the HSQLDB in-memory database.
Note that this demo application does not require an initial dataset, it starts with empty databases. We also have a validation demo that runs the Duine recommender on the MovieLens data set.
Unpack the duine-movielens-* distribution of your choice.
No further installation is required, you can continue to the "Run" section below, unless you want to run against another database instead of HSQLDB-in-memory.
If you want to run against MySQL you will have to update the configuration file spring-datasources.properties. The location of this file depends on the distribution you are using: For the duine-movielens-* distribution it is in the classes directory, for the duine-movielens-*-maven distribution it is in the src/main/resources directory.
For MySQL you will also have to create the following databases manually: duine_profile, duine_movielens_content, duine_movielens_log, duine_movielens_validationresult. Make sure that the access credentials match the values specified in spring-datasources.properties .
You can try another database product as well (we didn't yet) by modifying spring-datasources.properties accordingly.
For the movielens distribution: Make the bin directory your current directory. If you are on Linux execute run , if you are on Windows execute run.bat . This will run the java application MovieLensRecommenderClient. The program will automatically (re)create the database tables and execute 2 small prediction/feedback scenarios.
For the movielens maven distribution: We assume that you know how to build a Maven project, either from the command line or from within your favourite IDE. Build the project (this will retrieve all dependencies, the Duine libraries are available in the global Maven repository) and execute the program MovieLensRecommenderClient.java to run the demo. Some 3d party libraries are not available in the central Maven repositories due to licensing restrictions. In case of build failures please follow the instructions described here .
Obviously, this is a simple example, the Duine version of "Hello world". It shows how easy it is to create a recommender, ask for predictions, and let it process feedback. There is really not much more to do from the toolkit client perspective. The complex part is the configuration of the toolkit with predictors (algorithms), feedback processors, and profiles. We will skip that for now, and just explain what happened by executing the demo program.
The code snippet below, copied from MovieLensRecommenderClient.java, shows how to create a recommender. The code instantiates a new recommender that is configured with the xml files that are provided as arguments. We use the Spring Framework to load and wire the configuration. The SpringRecommenderFactory is the only class of the Duine Framework core that has a dependency with Spring.
/** * This is where the configuration files are loaded. Together the loaded files represent one global application context. */ private void init() throws DuineException { IRecommenderFactory factory = new SpringRecommenderFactory(); recommender = factory.createRecommender("recommender", new String[] { "spring-recommender.xml", "spring-predictors.xml", "spring-profiles.xml", "spring-services.xml", "spring-datasources.xml" }); }
In this scenario we create a new user (user1) and a new movie (movie1), and ask the recommender to give a prediction for the interest of the user in this movie. Initially the recommender has no knowledge about the user.
public void runScenario1() { log.info("********* SCENARIO 1 **********"); log.info("Create a new movie with genre 'sports'"); RatableItemId item1 = new RatableItemId("1"); Movie movie1 = new Movie(item1); ArrayList<String> genres = new ArrayList<String>(); genres.add("sports"); movie1.setGenres(genres); movie1.setTitle("movie1"); log.info("Create a user id 'user1'"); UserId user1 = new UserId("user1"); log.info("Calculate a prediction for the interest of this user in this movie"); Prediction prediction = recommender.predict(user1, movie1); log.info("Prediction result: " + prediction); ...... }
Output:
INFO MovieLensRecommenderClient:73 - ********* SCENARIO 1 ********** INFO MovieLensRecommenderClient:74 - Create a new movie with genre 'sports' INFO MovieLensRecommenderClient:81 - Create a user id 'user1' INFO MovieLensRecommenderClient:83 - Calculate a prediction for the interest of this user in this movie INFO MovieLensRecommenderClient:85 - Prediction result: Prediction(item=1, pred=0, cert=0, invalid)
Explanation:
The prediction result is pretty obvious: a neutral value of 0 with a certainty of 0. The result is flagged as invalid: none of the algorithms in the Duine configuration were able to calculate a prediction, due to lack of data in the database. There is only 1 user in the database and nothing is known about him.
In this scenario we assume that the user has seen the movie and liked it a lot, and therefore gives it a high rating. After this the recommender is asked for a prediction again, for the same movie.
public void runScenario1() { ...... log.info("Give a rating to the movie (value=0.9, certainty=0.8)"); IRatableItemFeedback feedback = new RatableItemFeedback(movie1, 0.9, 0.8); recommender.enterFeedback(user1, feedback); log.info("Calculate a prediction for the interest of this user in this movie"); Prediction prediction2 = recommender.predict(user1, movie1); log.info("Prediction result: " + prediction2); ...... }
Output:
INFO MovieLensRecommenderClient:88 - Give a rating to the movie (value=0.9, certainty=0.8) INFO MovieLensRecommenderClient:91 - Calculate a prediction for the interest of this user in this movie INFO MovieLensRecommenderClient:93 - Prediction result: Prediction(item=1, pred=0.9, cert=0.8)
Explanation:
Again the prediction result is pretty obvious (don't forget this is the Duine "hello world"): The user rated the movie with value=0.9, certainty=0.8, and the prediction result has the same values.
In many application scenarios we simply want to display the ratings that users have given already. This scenario shows how to retrieve a rating for a specific movie by querying the rating model.
public void runScenario1() { ...... log.info("Retrieve the rating that the user gave"); IReadOnlyRatingModel ratingModel = (IReadOnlyRatingModel)recommender.getProfileModel("org.duineframework.recommender.profile.rating.ratingModel"); Rating rating = ratingModel.getRating(user1, item1); log.info("User " + user1 + " gave the following rating to item " + item1 + ": " + rating); }
Output:
INFO MovieLensRecommenderClient:96 - Retrieve the rating that the user gave INFO MovieLensRecommenderClient:99 - User user1 gave the following rating to item 1: Rating(user=user1, item=1, value=0.9, certainty=0.8, m=2009-02-04 10:56:28.639)
In scenario 1 we saw that the user provided a rating of a movie as feedback to the recommender. The recommender can also process another type of feedback, so called TermFeedback. The TermFeedback concept can be used to express the user's interest in "things".
Scenario 2 shows how a user expresses his interest in music and feeds it to the recommender. Furthermore it shows how the prediction result is influenced now this knowledge is available in the recommender.
public void runScenario2() { log.info("********* SCENARIO 2 **********"); log.info("Create a new movie with genre 'music'"); Movie movie2 = new Movie(new RatableItemId("2")); ArrayList<String> genres = new ArrayList<String>(); genres.add("music"); movie2.setGenres(genres); movie2.setTitle("movie2"); log.info("Create a user id 'user2'"); UserId user2 = new UserId("user2"); log.info("Calculate a prediction for the interest of this user in this movie"); Prediction prediction = recommender.predict(user2, movie2); log.info("Prediction result: " + prediction); log.info("Enter term feedback for user2: term='music', value=0.4, certainty='0.8'"); ITermFeedback termFeedback = new TermFeedback("music", 0.4, 0.8); recommender.enterFeedback(user2, termFeedback); log.info("Calculate a prediction for the interest of this user in this movie"); Prediction prediction2 = recommender.predict(user2, movie2); log.info("Prediction result: " + prediction2); log.info(prediction2.getExplanation()); }
Output:
INFO MovieLensRecommenderClient:109 - ********* SCENARIO 2 ********** INFO MovieLensRecommenderClient:110 - Create a new movie with genre 'music' INFO MovieLensRecommenderClient:116 - Create a user id 'user2' INFO MovieLensRecommenderClient:118 - Calculate a prediction for the interest of this user in this movie INFO MovieLensRecommenderClient:120 - Prediction result: Prediction(item=2, pred=0, cert=0, invalid) INFO MovieLensRecommenderClient:122 - Enter term feedback for user2: term='music', value=0.4, certainty='0.8' INFO MovieLensRecommenderClient:125 - Calculate a prediction for the interest of this user in this movie INFO MovieLensRecommenderClient:127 - Prediction result: Prediction(item=2, pred=0.4, cert=0.4) INFO MovieLensRecommenderClient:128 - Explanation by mainStrategy: [alreadyKnown: Prediction(item=2, pred=0, cert=0, invalid) rationalStrategy: Prediction(item=2, pred=0.4, cert=0.4) Explanation by rationalStrategy: [cbr: Prediction(item=2, pred=0, cert=0) firstTimeUser: Prediction(item=2, pred=0.4, cert=0.4) Explanation by firstTimeUser: [interestStrategy: Prediction(item=2, pred=0.4, cert=0.4) Explanation by interestStrategy: [mainGenreLMS: Prediction(item=2, pred=0.4, cert=0.4) subGenreLMS: Prediction(item=2, pred=0, cert=0) *interestStrategy] *firstTimeUser] *rationalStrategy] *mainStrategy]
Explanation:
The first 12 lines of code are similar to scenario 1 part 1. User 2 requests a prediction for movie2 with genre music. The recommender returns a value of 0 and a certainty of 0. In the next lines the user adds a TermFeedback object. This object expresses the interest of the user in music, with a value of 0.4, and a certainty of 0.8. After entering the feedback a new prediction is calculated for the same movie2. Now the prediction result has value 0.4 with certainty 0.4. The recommender has used the knowledge about the interest of the user to calculate the new prediction.
Scenario 2 illustrates some important features of the Duine recommender:
Explanation API : Duine can explain how the prediction result was determined. This explanation API can be used to create end-user friendly explanations in applications (e.g. "The predicted value is 5 stars, because you have already rated a similar movie in the past"). In the output of the scenario you see the toString() output of the Explanation object that is returned as a part of a Prediction.
Duine is a hybrid recommender : A hybrid recommender combines multiple prediction techniques (algorithms) instead of relying on one specific technique. The decision rules that determine which techniques are applied are called prediction strategies. The output above shows how the recommender "navigated" through a tree of prediction strategies and techniques. Ultimately it found a a technique that was capable to provide a prediction. The printed explanation shows that the values (0.4, 0.4) have been calculated by the mainGenreLMS prediction technique. A graphical representation of the Duine configuration and the technique selection behaviour is available in Getting started (2) .