推荐引擎利用特殊的信息过滤(IF,Information Filtering)技术,将不一样的内容(例如电影、音乐、书籍、新闻、图片、网页等)推荐给可能感兴趣的用户。一般状况下,推荐引擎的实现是经过将用户 的我的喜爱与特定的参考特征进行比较,并试图预测用户对一些未评分项目的喜爱程度。参考特征的选取多是从项目自己的信息中提取的,或是基于用户所在的社 会或社团环境。html
根据如何抽取参考特征,咱们能够将推荐引擎分为如下四大类:java
随 着互联网上数据和内容的不断增加,人们愈来愈重视推荐引擎在互联网应用中的做用。可想而知,因为互联网上的数据过多,用户很难找到本身想要的信息,经过提 供搜索功能来解决这个问题是远远不够的。推荐引擎能够经过分析用户的行为来预测用户的喜爱,使用户能更容易找到他们潜在须要的信息。这里以电子商务应用中 的推荐引擎为例来讲明推荐引擎在互联网应用中的重要性。mysql
电子商务推荐系统 (E-Commence Recommendation System) 向客户提供商品信息和购买建议,模拟销售人员帮助客户完成购买过程。智能推荐系统的做用能够归纳为:将电子商务网站的浏览者转变为购买者 ,提升电子商务网站的交叉销售能力,提升客户对电子商务网站的忠诚度。web
电子商务推荐系统的界面表现形式有如下几种:算法
回页首sql
Apache Mahout 是 Apache Software Foundation(ASF) 旗下的一个开源项目,提供一些可扩展的机器学习领域经典算法的实现,旨在帮助开发人员更加方便快捷地建立智能应用程序。经典算法包括聚类、分类、协同过 滤、进化编程等等,而且,在 Mahout 的最近版本中还加入了对 Apache Hadoop 的支持,使这些算法能够更高效的运行在云计算环境中。数据库
回页首apache
Taste 是 Apache Mahout 提供的一个协同过滤算法的高效实现,它是一个基于 Java 实现的可扩展的,高效的推荐引擎。Taste 既实现了最基本的基于用户的和基于内容的推荐算法,同时也提供了扩展接口,使用户能够方便的定义和实现本身的推荐算法。同时,Taste 不只仅只适用于 Java 应用程序,它能够做为内部服务器的一个组件以 HTTP 和 Web Service 的形式向外界提供推荐的逻辑。Taste 的设计使它能知足企业对推荐引擎在性能、灵活性和可扩展性等方面的要求。编程
Taste 由如下五个主要的组件组成:json
安装 Taste 的软件需求:
安装 Taste 并运行 Demo:
http://localhost:8080/[your_app]/RecommenderService.jws
WSDL 文件:http://localhost:8080/[your_app]/RecommenderService.jws?wsdl
也能够经过简单的 HTTP 请求调用这个 Web 服务:
http://localhost:8080/[your_app]/RecommenderService.jws?
method=recommend&userID=1&howMany=10
根据上面的步骤,咱们能够获得一个简单的推荐引擎 demo 环境,下面介绍如何使用 Taste 方便地构建自定义的推荐引擎。
直接使用 Mahout 的项目环境进行编码,须要使用 Ant 或者 Maven 进行编译,整个过程比较复杂,这里咱们将构建推荐引擎所须要的工具包从 Mahout 工程中抽取出来,从而方便的构建自定义的推荐引擎。
在 Eclipse 中建立 Web 应用的工程 MovieSite,将 demo 时生成的推荐引擎 Web 应用的 war 包解压缩,将 lib 下的 jar 文件拷贝到 MovieSite 的 lib 目录下。这样咱们就能够方便的编写本身的推荐引擎。
这里咱们想要编写一个电影推荐引擎,第一步须要对数据进行建模,分析应用中涉及的主要实体以及实体间的关系,从而设计数据库存储,程序中的类,以及推荐引擎的 DataModel。
数据模型中存在如下实体:
下面咱们就基于这个数据模型设计数据库的存储以及推荐引擎的 DataModel。
1 .建立 MySQL 数据库存储电影和用户的信息,用户的喜爱信息以及电影的类似度。
CREATE DATABASE movie; USE movie; CREATE TABLE movies ( // 保存电影相关的信息。 id INTEGER NOT NULL AUTO_INCREMENT, name varchar(100) NOT NULL, published_year varchar(4) default NULL, type varchar(100) default NULL, -- ...more movie information... PRIMARY KEY (id) ); CREATE TABLE users ( // 保存用户信息 id INTEGER NOT NULL AUTO_INCREMENT, name varchar(50) NOT NULL, email varchar(100) default NULL, -- ...more user information... PRIMARY KEY (id) ); CREATE TABLE movie_preferences ( // 保存用户对电影的评分,即喜爱程度 userID INTEGER NOT NULL, movieID INTEGER NOT NULL, preference INTEGER NOT NULL DEFAULT 0, timestamp INTEGER not null default 0, FOREIGN KEY (userID) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY (movieID) REFERENCES movies(id) ON DELETE CASCADE ); CREATE TABLE movie_similarity ( // 保存电影和电影的类似程度 movieID1 INTEGER NOT NULL, movieID2 INTEGER NOT NULL, similarity DOUBLE NOT NULL DEFAULT 0, FOREIGN KEY (movieID1) REFERENCES movies(id) ON DELETE CASCADE, FOREIGN KEY (movieID2) REFERENCES movies(id) ON DELETE CASCADE ); CREATE INDEX movie_preferences_index1 ON movie_preferences ( userID , movieID ); CREATE INDEX movie_preferences_index2 ON movie_preferences ( userID ); CREATE INDEX movie_preferences_index3 ON movie_preferences ( movieID );
在实际应用中,咱们须要将应用中的实例数据写入到数据库中。做为例子,这里将从 GroupLen 下载的数据源写入数据库。
因为上面采用数据库存储用户的喜爱信息,这里须要基于数据库的推荐引擎实现。这里扩展 MySQLJDBCDataModel 实现电影推荐引擎的 DataModel 实例。
public class MovieDataModel extends MySQLJDBCDataModel { // 保存用户对电影的评分的数据库表名 public final static String PERFERENCETABLE = "movie_preferences"; public final static String USERID_COLUMN = "userID"; // 表中用户标识的列名 public final static String ITEMID_COLUMN = "movieID"; // 表中电影标识的列名 public final static String PERFERENCE_COLUMN = "preference"; // 表中评分的列名 public MovieDataModel(String dataSourceName) throws TasteException { super(lookupDataSource(dataSourceName), PERFERENCETABLE, USERID_COLUMN, ITEMID_COLUMN, PERFERENCE_COLUMN); } public MovieDataModel() { //DBUtil.getDataSource() 将返回应用的数据源 // 此应用是 J2EE 应用,因此这里会采用 JDNI 的方式建立数据库连接。 super(DBUtil.getDataSource(), PERFERENCETABLE, USERID_COLUMN, ITEMID_COLUMN, PERFERENCE_COLUMN); } }
前面介绍了数据建模和 DataModel 的实现,下面来详细介绍推荐引擎的实现。如前面介绍的,Taste 既实现了最基本的基于用户的和基于内容的推荐算法,同时也提供了扩展接口,使用户能够方便的定义和实现本身的推荐算法。下面详细介绍如何扩展 Taste 的推荐引擎接口,实现基于用户类似度的推荐引擎,基于内容类似度的推荐引擎,以及 Slope One 的推荐引擎。Slope One 是一种很是快速简单的基于项目的推荐方法,须要使用用户的评分信息。
public class UserBasedRecommender implements Recommender { private final Recommender recommender; public UserBasedRecommender() throws IOException, TasteException { this(new MovieDataModel()); } public UserBasedRecommender(DataModel model) throws TasteException { UserSimilarity userSimilarity = new PearsonCorrelationSimilarity(model); userSimilarity.setPreferenceInferrer(new AveragingPreferenceInferrer(model)); UserNeighborhood neighborhood = new NearestNUserNeighborhood(3, userSimilarity, model); recommender = new CachingRecommender( new GenericUserBasedRecommender(model, neighborhood, userSimilarity)); } // 对外提供的推荐的接口,参数为用户标识和推荐项的个数 public List<RecommendedItem> recommend(long userID, int howMany) throws TasteException { return recommender.recommend(userID, howMany); } public List<RecommendedItem> recommend(long userID, int howMany, Rescorer<Long> rescorer) throws TasteException { return recommender.recommend(userID, howMany, rescorer); } // 如下方法都是实现 Recommender 的接口 public float estimatePreference(long userID, long itemID) throws TasteException { return recommender.estimatePreference(userID, itemID); } public void setPreference(long userID, long itemID, float value) throws TasteException { recommender.setPreference(userID, itemID, value); } public void removePreference(long userID, long itemID) throws TasteException { recommender.removePreference(userID, itemID); } public DataModel getDataModel() { return recommender.getDataModel(); } public void refresh(Collection<Refreshable> alreadyRefreshed) { recommender.refresh(alreadyRefreshed); } public String toString() { return "UserBasedRecommender[recommender:" + recommender + ']'; } }
从上面的代码示例清单 3 能够看出,实现一个推荐引擎须要实现 Recommender 接口,它通常是对于某种 Taste 提供的推荐引擎的扩展,这是对 GenericUserBasedRecommender 进行的扩展,其中最重要的方法就是实例化推荐引擎的构造方法,通常其中涉及如下步骤:
public class ItemBasedRecommender implements Recommender { private final Recommender recommender; public ItemBasedRecommender() throws IOException, TasteException { this(new MovieDataModel()); } public ItemBasedRecommender(DataModel dataModel) throws TasteException { Collection<GenericItemSimilarity.ItemItemSimilarity> correlations = MovieSimilarityTable.getAllMovieSimilarities(); ItemSimilarity itemSimilarity = new GenericItemSimilarity(correlations); recommender = new CachingRecommender(new EmbededItemBasedRecommender( new GenericItemBasedRecommender(dataModel, itemSimilarity))); } public List<RecommendedItem> recommend(long userID, int howMany) throws TasteException { return recommender.recommend(userID, howMany); } ......... //EmbededItemBasedRecommender 类的定义 private static final class EmbededItemBasedRecommender implements Recommender { // 包含一个 GenericItemBasedRecommender 实例; private final GenericItemBasedRecommender recommender; private EmbededItemBasedRecommender(GenericItemBasedRecommender recommender) { this.recommender = recommender; } public List<RecommendedItem> recommend(long userID, int howMany, Rescorer<Long> rescorer) throws TasteException { FastIDSet itemIDs = recommender.getDataModel().getItemIDsFromUser(userID); return recommender.mostSimilarItems(itemIDs.toArray(), howMany, null); } ........ }
从上面的代码示例清单 4 能够看出,与上一个实现相似它是对 GenericItemBasedRecommender 的扩展,它的构造方法涉及如下步骤:
public final class MovieRecommender implements Recommender { private final Recommender recommender; public MovieRecommender() throws IOException, TasteException { this(new MovieDataModel()); } public MovieRecommender(DataModel dataModel) throws TasteException { // 建立一个 SlopeOneRecommender 的实例 recommender = new CachingRecommender(new SlopeOneRecommender(dataModel)); } // 对外提供的推荐的接口,参数为用户标识和推荐项的个数 public List<RecommendedItem> recommend(long userID, int howMany) throws TasteException { return recommender.recommend(userID, howMany); } ........ }
Slope One 是一种很是快速简单的基于项目的推荐方法,它只须要使用用户的评分信息。具体的实现,只须要在咱们的推荐引擎中包含一个 SlopeOneRecommender 的实例。
完 成了推荐引擎的设计与实现,下面咱们须要设计一些 REST API,向外暴露推荐功能。为了提升推荐引擎的处理效率,这里采用 Singleton 模式实现一个推荐引擎的单例 MovieRecommenderSingleton。在 Servlet 启动的时候初始化推荐引擎的单例,之后每次调用推荐方法。
public class MovieRecommenderServlet extends HttpServlet { private static final int NUM_TOP_PREFERENCES = 20; private static final int DEFAULT_HOW_MANY = 20; private Recommender recommender; @Override public void init(ServletConfig config) throws ServletException { super.init(config); // 从 web.xml 中读取须要建立的推荐引擎类名 /* * <servlet> * <servlet-name>movie-recommender</servlet-name> * <display-name>Movie Recommender</display-name> * <description>Movie recommender servlet</description> * <servlet-class> * com.ibm.taste.example.movie.servlet.MovieRecommenderServlet * </servlet-class> * <init-param> * <param-name>recommender-class</param-name> * <param-value> * com.ibm.taste.example.movie.recommender.UserBasedRecommender * </param-value> * </init-param> * <load-on-startup>1</load-on-startup> * </servlet> */ String recommenderClassName = config.getInitParameter("recommender-class"); if (recommenderClassName == null) { throw new ServletException( "Servlet init-param \"recommender-class\" is not defined"); } try { MovieRecommenderSingleton.initializeIfNeeded(recommenderClassName); } catch (TasteException te) { throw new ServletException(te); } recommender = MovieRecommenderSingleton.getInstance().getRecommender(); } @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException { //Parameters.USER_ID = "userID" String userIDString = request.getParameter(Parameters.USER_ID); if (userIDString == null) { throw new ServletException("userID was not specified"); } long userID = Long.parseLong(userIDString); String howManyString = request.getParameter(Parameters.COUNT); //Parameters.COUNT = "count" int howMany = howManyString == null ? DEFAULT_HOW_MANY : Integer.parseInt(howManyString); String format = request.getParameter(Parameters.FORMAT); //Parameters.FORMAT = "format" if (format == null) { format = "json"; } try { // 为指定用户计算推荐的电影 List<RecommendedItem> items = recommender.recommend(userID, howMany); // 加载电影的相关信息,RecommendMovieList 是保存了一组推荐电影的相关信息和 // 引擎计算获得的他们的 ranking RecommendMovieList movieList = new RecommendMovieList(items); if ("text".equals(format)) { writePlainText(response, movieList); } else if ("json".equals(format)) { writeJSON(response, movieList); } else { throw new ServletException("Bad format parameter: " + format); } } catch (TasteException te) { throw new ServletException(te); } catch (IOException ioe) { throw new ServletException(ioe); } } //details please refer to the src code }
以上完成了电影推荐引擎服务器端的编程,下面咱们使用 FireFox 的插件 Poster 测试一下 HTTP 请求,查看推荐引擎的返回结果。对任意一个用户,推荐引擎应该基于必定的规则计算获得一组电影以及预计的评分,为了有更好的用户体验,引擎在拿到推荐电影 序号的列表后,从电影信息数据库中查询获得电影的相关信息,包括电影的名称,发表时间以及类型等信息。这里咱们采用 JSON 做为推荐引擎的响应格式。
实现一个推荐引擎的最后一步就是编写客户端代码,为电影推荐引擎提供一个友好的用户界面。下面展现一下咱们为电影推荐引擎写的一个简单的用户界面:右边红色框中的是该用户已经打分的电影列表,左边蓝色框中是推荐引擎为用户推荐的电影列表。
首先,展现一下基于用户的推荐引擎的推荐结果,推荐引擎会根据用户已打分的电影找到用户的“邻居”,将“邻居”们比较喜欢的电影推荐给当前用户。
其次,图 6 展现了基于内容的推荐引擎的推荐结果,推荐引擎会根据用户已打分的电影找到类似的电影,推荐给当前用户。
最后,展现 SlopeOne 推荐引擎的推荐结果,这种推荐引擎计算速度较快,效果很好,是一种很是快速简单的基于项目的推荐方法。
目 前几乎全部大型的电子商务系统,都不一样程度地使用了各类形式的推荐引擎。推荐技术的使用,不只大大的提升了用户购物的体验,增长了用户的粘着度,并且电子 商务公司也因为推荐系统的应用而大大的提升了交叉销售的可能,从而大大的提升了营业额。今天,你有本身的商品推荐系统么?
借鉴于电子商务的 成功经验,咱们能够把推荐技术应用到其余的领域。像咱们在文章中所演示的那样,你能够建立一个电影的推荐引擎。若是你是一个 blogger,那么你能够建立一个博客的推荐引擎,若是你是一个新闻提供商,你可使用推荐技术为不一样的用户推荐它可能关心的新闻,等等。
今天,你推荐了么?
描述 | 名字 | 大小 |
---|---|---|
本文示例代码 | src.zip | 14 MB |
原文连接:http://www.ibm.com/developerworks/cn/java/j-lo-mahout/