In this post I will briefly describe three optimisations that GER (Good Enough Recommendations) uses to improve recommendations. These optimisations could be useful for other recommender systems as they have increased the quality of GER’s recommendations.

Separated Data

The two fundamental steps in recommender systems like GER are:

  1. find a neighbourhood of similar people
  2. recommend things that those people like

The similarity between people can vary widely from one set of items to another. For example, my friend and I have very similar tastes in action movies, but we don’t like the same drama movies. If all the data was combined, our similarity would be very weak. However, if we separated the data based on movie genre we would be very similar in the “action” genre, and dissimilar in the “drama” genre, now I can get some good “action movie” recommendations.

GER allows users to separate their data into namespaces, which are just buckets of isolated data. This way GER can find more similar people and provide more targeted recommendations, with the added bonus that it also improves performance. However, deciding where to split the data can be difficult and is domain specific, e.g. how would you split restaurant recommendations? By price, by location, by mean portion size?

Recent Data is More Relevant

If a user looked at SLR cameras last week, and is looking at TVs today, would they be more receptive to recommendations for TVs or cameras? Probably TVs, because what they are doing now is more important to them. This is an example of a broader fact, data becomes less relevant over time to generate recommendations from. The rate at which data becomes less relevant changes based on domain. For example, recommending to a user an iPhone they liked 5 years ago is not good idea, but recommending to a user a movie they liked 5 years ago is better because movies tastes change slower than technology does.

By changing the measure of similarity between people to take into account how old the data is, can make older data less relevant. This way the recommendations can adjust themselves to a users tastes and whims of the user over time.

To implement this in GER the weight of an action is calculated with respect to time that action occurred. This is done using the event_decay_rate configuration, where the final weight is calculated as initial_weight * event_decay_rate ^ (- days since event). So, if the rate is 1.05 an event that occurred 14 days ago will have half its effect than if it occurred today.

No Recommendations Can Be Better Than Bad Recommendations

Bad recommendations presented to the user can be worse than no recommendations. A user who sees bad recommendations might unsubscribe from an email, or not come back to the website. A recommender system that returns some kind of heuristic about how good it thinks the recommendations are, can help the client decide whether they should present the recommendations to the user. If the recommendations are bad, then the client might not send an email out, or just send a generic “top 10 items” instead, which may not cause the user to respond negatively.

GER uses a minimum_history_required field which stops any recommendations being generated unless there is a selected amount of minimal data about a user. And when generating recommendations GER returns a confidence heuristic calculated using the amount of data available about the user, how many similar users were found, and the weights of the recommendations calculated. This way the client can see the confidence and decide whether to present the calculated recommendations.

Conclusion

These are probably not novel optimisations, but they have been useful in improving the quality of GER’s recommendations. If you have any other optimisations, or comments about GER’s please comment below :)