Content Based recommender system 2 - UCM-GAIA/RecoLibry-Core GitHub Wiki

The complete example is implemented in the test2 of RecoLibry-Example repository.


In this section, we explain how to build a content-based recommender system with RecoLibry-Core. This example builds the recommender system using a configuration file. We will make a recommender system of movies. It will recommend movies with similar genres than a user profile. To build this recommender system, it is necessary to download the file movies.csv form Movielens 100K dataset and insert it in your project.

The schema of our project will be as follow:

Project Structure

Next, we add the RecoLibry-Core in our project adding the dependency in our pom.xml file. To remember that you can visit the Start section.

Configuration

First, we are going to configure the recommender system. In this example, we use a JSON file to configure the recommender system. This file must contain two elements: generateClass and configuration.

{
	"generateClass": {
    ...
  },
  "configure": [
    ...
  ]
}

Generate auxiliary class

In our example, we need to make an auxiliary class to save the movies information. For this reason, we must complete the generated method. This class will call MovieDescription and should contain the next attributes based on the movies.csv file:

  • id: Movie identity number. It is an Integer type.
  • name: Movie name. It is a String value.
  • genres: List with genres of the movie. The list type is String.

Then, we can add all this information to our configuration file. The two first attributes in generateClass are the package that will contain the new class (packageName) and the name of the class (className):

"generateClass": {
  "packageName": "com.recolibry.example.MoviesRecommender",
  "className": "MovieDescription",
  ...
}

Next, we define the attributes included in our new class MovieDescription. These attributes are separated into two groups. The first group (attributes) contains attributes with a simple data type (Integer, Double, Float, String or Boolean). The second group (attributesList) contains attributes with a list type. The list types must be also of these types. Then, we need to include each attribute in its corresponding list. For each attribute, we define the name and the type of this attribute:

"generateClass": {
  ...,
  "attributes": [
			{
				"name": "id",
				"type": "Integer"
			},
			{
				"name": "title",
				"type": "String"
			}
		],
		"attributeList": [
			{
				"name": "genres",
				"type": "String"
			}
		]
},

That is all the information that we need to make the auxiliary class. Next, we define the components used in our recommender system.

Define components

Next, we define which components are in our recommender system. To do that, we define what class or instance is used in our recommender system using the template explained in the configuration section.

Next, we configure the recommender algorithm. In this example, we will use the RecommenderJColibri algorithm. This algorithm needs extra components: a connector and similarity functions. We start to define the connector. The movie's description is in CSV file. For this reason, we are going to configure a CSVConnector. This connector needs 3 elements: a BeansFactory to convert the CSV data in MoviesDescriptions, the path of out file and a boolean data that set if the CSV file contains a title row. We add these bindings in the list of configure:

"configure": [
  {
		"type": "Instance",
		"bind": "java.lang.String",
		"annotated": "BeanClass",
		"to": "com.recolibry.example.MoviesRecommender.MovieDescription"
	},
  {
		"type": "Instance",
		"bind": "java.lang.String",
		"annotated": "fileName",
		"to": "./data/movies.csv"
	},
	{
		"type": "Instance",
		"bind": "java.lang.Boolean",
		"annotated": "existTitleRow",
		"to": true
	},
  ...
]

Now we have defined the components used by CSVConnector. Next, we define to RecommenderJColibry that we will use a CSVConnector component:

"configure": [
  ...,
  {
		"type": "Class",
		"bind": "es.ucm.fdi.gaia.jcolibri.cbrcore.Connector",
		"annotated": null,
		"to": "es.ucm.fdi.gaia.recolibry.implementations.jcolibri.CSVConnector"
	},
  ...
]

The next elements that our recommender system needs are similarity functions. In JColibry there are 2 types of similarity functions: global similarity functions and local similarity functions. Global similarity functions return the similarity between 2 objects. On the other hand, local similarity functions return the similarity between 2 attributes with the same name in 2 objects. In our example, we will have a local similarity function to compare the movies genres respect the genres in the query. This similarity function is not included in jColibri. For this reason, we need to create this in a class called GenresSimilarity. You can copy the next code in your new class:

public class GenresSimilarity implements LocalSimilarityFunction {

	@Override
	public double compute(Object caseObject, Object queryObject) throws NoApplicableSimilarityFunctionException {
		List<String> caseGenres = (List<String>) caseObject;
		List<String> queryGenres = (List<String>) queryObject;
		
		double equal = 0;
		double totalGenres = 0;
		
		for(int i=0; i<caseGenres.size(); i++)
			for(int j=0; j<queryGenres.size(); j++)
				if (caseGenres.get(i).equals(queryGenres.get(j)))
					equal++;
		
		totalGenres = caseGenres.size() + queryGenres.size() - equal;
		
		return equal / totalGenres;
	}

	public boolean isApplicable(Object caseObject, Object queryObject) {
		if (caseObject != null && queryObject != null && 
				caseObject instanceof List && queryObject instanceof List)
			return true;
		else
			return false;
	}

}

This class will be our local similarity function to compare genres. To add local similarities functions in our recommender system, In the configure section, you can copy the template to define a local similarity function. Now, we add the new local similarity function in our configuration:

"configure": [
  ...,
  {
		"type": "LocalSimilarity",
		"bind": "LocalSimilarityConfiguration",
		"annotated": null,
		"to": null,
		"similarities": [{
		"attribute": "genres",
		"class": "com.recolibry.example.MoviesRecommender.MovieDescription",
			"similarities": "com.recolibry.example.MoviesRecommender.GenresSimilarity"
		}]
	}
]

In addition, we must define which global similarity function we will use. In this case, we will use the class Average from jColibri:

"configure": [
  {
		"type": "Class",
		"bind": "es.ucm.fdi.gaia.jcolibri.method.retrieve.NNretrieval.similarity.GlobalSimilarityFunction",
		"annotated": null,
		"to": "es.ucm.fdi.gaia.jcolibri.method.retrieve.NNretrieval.similarity.global.Average"
	},
]

The last element that we need to include in our recommender algorithm is how many results we want to recover. To do that, we bind the number of results using the N-Results property. Our example will return 10 results:

"configure": [
	...,
  {
		"type": "Instance",
		"bind": "java.lang.Integer",
		"annotated": "N-Results",
		"to": 10
	},
  ...
]

At this moment, we have all the components for the recommender algorithm. The next step is to define which algorithm we will use in the recommender system. To do that, we set the RecommenderJColibry class as our recommender algorithm:

"configure": [
  ...,
  {
    "type": "Class",
		"bind": "es.ucm.fdi.gaia.recolibry.api.RecommenderAlgorithm",
		"annotated": null,
		"to": "es.ucm.fdi.gaia.recolibry.implementations.jcolibri.RecommenderJColibri"
  }
]

Finally, we need to define the Query for our recommender system. In our case, the query will be a QueryJColibri :

"configure": [
	...,
  {
    "type": "Class",
		"bind": "es.ucm.fdi.gaia.recolibry.api.Query",
		"annotated": null,
		"to": "es.ucm.fdi.gaia.recolibry.implementations.jcolibri.QueryJColibri"
  }
]

We complete all the configuration of our recommender system. If you need to check your configuration, in the RecoLibry-Examples repository you have the complete configuration. Next, we can create a new instance of our recommender system.

Build a recommender system

For the next steps, we will create a new class called Main. This class will contain a static method to get an instance of our recommender system and the main method to execute it:

public class Main {

	public static RecommenderSystem getRecommender() {
	
	}
	
	public static void main(String[] args) {
	
	}

}

In this section, we will complete the method to obtain an instance of the system. We create the recommender system using the RecommenderSystemFactory. To do that, we call the method makeRecommenderByJson() with the path of our configuration file. Finally, we return the system created:

public static RecommenderSystem getRecommenderSystem() {
  //Make new instance of recommender system
	RecommenderSystemFactory factory = new RecommenderSystemFactory();
	factory.makeRecommenderByJson(System.getProperty("user.dir") + "/configurations/configuration.json");
    
  // Return recommender system
  return factory.getRecommender();
}

Now, we can execute our recommender system.

Execute a recommender system

In this section, we explain how to use the recommender system created before. In this example, we will ask our system which movies recommend to a user who likes the genres: Adventure, Fantasy, and Children.

First, we call the method to obtain the recommender system that we will execute. Next, we call the method getQuery() in our recommender system. It returns the query object that can use in our system:

public static void main(String[] args) {

  //Get recommender system
  RecommenderSystem recSys = getRecommenderSystem();
	
  //Get query object
  Query query = recSys.getQuery();
  
  ...
}

Next, we need to prepare our query before to execute in our recommender system. To do that, we call the query method initialize(). After that, we add the genres of the user in the attribute genre of MovieDescription:

public static void main(String[] args) {
	...
  
  //Initialize query
  query.initialize();
  
  //Add genres in our query
  String[] genresArray = new String[]{"Adventure", "Children", "Fantasy"};
  List<String> genres = new ArrayList<>(Arrays.asList(genresArray));
  query.setAttributeValue("genres", genres);
  
  ...
}

At this moment, our query contains all information and we can send it to the recommender system. Before executing the recommender system, we must call the function initRecommender(). Next, we execute the method recommend() with the query object completed before. Finally, if we don't need to execute the recommender system again, we can finish its execution calling the method closeRecommender(). Our example has the next instructions:

public static void main(String[] args) {
	...
    
  //Initialize recommender system
  recSys.initRecommender();
  
  //Get recommendations from query
  List<RecommenderResult> results = recSys.recommend(query);

  // Close recommender system
  recSys.closeRecommender();
  
  ...
}

Finally, we print the result of our recommender system:

public static void main(String[] args) {
  ...
  
  // Print the result of recommender system
  for(RecommenderResult r : results)
    System.out.println(r);
}
⚠️ **GitHub.com Fallback** ⚠️