使用Mahout搭建推薦系統之入門篇1-搭建REST風格簡單推薦系統

     用意: 網絡上有不少關於使用mahout搭建推薦系統的文章,可是尚未一個從創建推薦系統原型至部署到簡單服務器的完整教程. 雖然部分朋友對推薦系統很感興趣, 可是因hadoop的複雜而卻步.  同時對於那些沒有任何Web開發經驗的朋友來講, 一個完整的小型推薦系統能夠很大的激發學習的興趣和動手的衝動. 我以爲動手的衝動比看書的衝動要重要的多. html

     原型分爲兩個系列 : JAVA原型和Python原型. java

這篇博客主要是介紹JAVA推薦系統原型: 主要參考[1] web

使用MyEclipse和Mahout開發一個REST風格[3]的簡單推薦系統 算法

一. 搭建一個Hello world的REST服務器
1. 選擇創建MyEclise Web Service Project
    填寫Project Name, 填寫完畢後,注意Context root URL的內容.二者是一致的.固然你也可讓二者不同,本身選擇一個名字.如Project Name爲testHello.而URL爲/rs.(URL十分重要,是最終WEB訪問路徑的一部分)

     Library見下圖 core server client json. apache


2. 打開/WEB-INF下的web-xml能夠看到

<servlet-mapping>
<servlet-name>JAX-RS REST Servlet</servlet-name>
<url-pattern>/test/*</url-pattern>
</servlet-mapping> json

其中的url-pattern將是WEB路徑的一部分,具體見下文. api

2. 在/src目錄下加入HelloRS文件. 內容以下 瀏覽器


import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;


//設置路徑爲http://域名:端口/ConTextRootURL/url-pattern + /hello
//以個人機子爲例:http://localhost:8080/rs/rest/hello

@Path("/recommend")
public class HelloRS {

  // 這個方法將返回普通文本
  @GET
  @Produces(MediaType.TEXT_PLAIN)
  public String sayPlainTextHello() {
    return "Hello REST";
  }

  // 這個方法將返回XML文件
  @GET
  @Produces(MediaType.TEXT_XML)
  public String sayXMLHello() {
    return "<?xml version=\"1.0\"?>" + "<hello> Hello REST" + "</hello>";
  }

  // 這個方法將返回HTML文件
  @GET
  @Produces(MediaType.TEXT_HTML)
  public String sayHtmlHello() {
    return "<html> " + "<title>" + "Hello REST" + "</title>"
        + "<body><h1>" + "Hello REST" + "</body></h1>" + "</html> ";
  }

}


3. 選擇Project,使用Run as->MyEclipse Server Application便可.
使用 http://localhost:8080/rs/rest/hello 既能夠看到返回Hello REST了.

修改文件後Tomcat將會自動檢測並佈置.
注:有任何問題,看console中的日誌輸出,找到Exception或者error, Google一下.

二. 利用mahout搭建簡單的推薦系統,數據使用代碼生成

1. 在Java Build Path引入mahout相關庫文件, 下載地址: http://pan.baidu.com/s/1Ea8pI
mahout核心類: 提供推薦Model等核心類
    mahout-core-0.5.jar
    mahout-math-0.5.jar
輔助類: 提供Log和部分數學公式類.
    uncommons-maths-1.2.jar
    google-collections-1.0-rc2.jar
    guava-r0.3.jar
    slf4j-api-1.6.0.jar

2. 在src下新建一個RecommenderIntro.java文件,代碼以下: tomcat

import org.apache.mahout.cf.taste.impl.model.file.*; import org.apache.mahout.cf.taste.impl.neighborhood.*; import org.apache.mahout.cf.taste.impl.recommender.*; import org.apache.mahout.cf.taste.impl.similarity.*; import org.apache.mahout.cf.taste.model.*; import org.apache.mahout.cf.taste.neighborhood.*; import org.apache.mahout.cf.taste.recommender.*; import org.apache.mahout.cf.taste.similarity.*; import java.io.*; import java.util.*; class RecommenderIntro { private FileDataModel model; private PearsonCorrelationSimilarity similarity; private NearestNUserNeighborhood neighborhood; private GenericUserBasedRecommender recommender; // 從filename中讀取數據(用戶id, 物品id, 評分rate), 生成數據類model, 類似類similarity以及最相近的鄰居類(2個) public RecommenderIntro(String filename) throws Exception { model = new FileDataModel(new File(filename)); similarity = new PearsonCorrelationSimilarity(model); neighborhood = new NearestNUserNeighborhood(2, similarity, model); recommender = new GenericUserBasedRecommender( model, neighborhood, similarity); } // 對用戶userid推薦前num個物品. public List<RecommendedItem> SimpleRecommend(int userid, int num) throws Exception { List<RecommendedItem> recommendations = recommender.recommend(1, 1); return recommendations; } }

代碼介紹:  服務器

    本算法是最簡單的基於用戶的協同過濾. 現實解釋:你想別人給你推薦一個電影,你會從一堆人中找到與你最熟悉的幾我的推薦電影給你,而後找到被推薦次數最多的電影. Model類用來存儲數據, mahout爲了節約內存, 數據結構設計的很好,下次找個機會聊聊. Similarity計算兩我的之間的類似性, 而Neighborhood則是爲每一個人保存最類似的2我的.最後recommener結合model\neighborhood和similarity來爲某個user推薦N個好友. 

3. 修改helloRS文件修改以下所示:

    將intro.csv文件放到src/目錄下

import java.util.List; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import org.apache.mahout.cf.taste.recommender.RecommendedItem; //Sets the path to base URL + /hello @Path("/recommend") public class HelloRS { private RecommenderIntro recommender = null; private String filename = null; // This method is called if TEXT_PLAIN is request @GET @Produces(MediaType.TEXT_PLAIN) public String sayPlainTextHello( @DefaultValue("1") @QueryParam("id") String id, @DefaultValue("1") @QueryParam("num") String num) throws Exception { int userId = Integer.valueOf(id); int rankNum = Integer.valueOf(num); String resultStr = getRecommender(userId, rankNum); return resultStr; } // This method is called if XML is request @GET @Produces(MediaType.TEXT_XML) public String sayXMLHello(@DefaultValue("1") @QueryParam("id") String id, @DefaultValue("1") @QueryParam("num") String num) throws Exception { int userId = Integer.valueOf(id); int rankNum = Integer.valueOf(num); String resultStr = getRecommender(userId, rankNum); return "<?xml version=\"1.0\"?>" + "<hello> " + resultStr + "</hello>"; } // This method is called if HTML is request @GET @Produces(MediaType.TEXT_HTML) public String sayHtmlHello(@DefaultValue("1") @QueryParam("id") String id, @DefaultValue("1") @QueryParam("num") String num) throws Exception { System.out.println(id + " " + num); int userId = Integer.valueOf(id); int rankNum = Integer.valueOf(num); String resultStr = getRecommender(userId, rankNum); return "<html> " + "<title>" + "Hello REST" + "</title>" + "<body><h1>" + resultStr + "</body></h1>" + "</html> "; } private String getRecommender(int userId, int num) throws Exception { if (filename == null) { String classPath = this.getClass().getClassLoader() .getResource("/").getPath(); classPath = classPath.replace("%20", "\\ "); filename = classPath + "intro.csv"; System.out.println(filename); } if (recommender == null) recommender = new RecommenderIntro(filename); List<RecommendedItem> recommendedList = recommender.SimpleRecommend( userId, num); String resultStr = "Result=" + recommendedList.get(0).getItemID() + " " + recommendedList.get(0).getValue(); return resultStr; } }

代碼介紹: 代碼提供了XML\HTML和普通文本三個格式, 以瀏覽器默認的HTML格式爲例. 

若是瀏覽器 輸入 http://localhost:8080/rs/rest/recommend?id=1&num=1 

參數表@DefaultValue("1") @QueryParam("id") String id, @DefaultValue("1") @QueryParam("num") String num表示得到參數

id = "1", num = "1". 以後經過getRecommender來初始化Recommender並得到數據. QueryParam表示GET方法的數據.

注: 因爲intro.csv數據集比較少,全部部分id和num值沒法返回合適的結果.

注: 由與intro.csv最終會部署在tomcat上,因此須要得到tomcat中class的路徑.

注: recommender做爲成員函數,保證每個函數都引用同一份數據,保證一致性.

獲取路徑的方法以下:

String classPath = this.getClass().getClassLoader() .getResource("/").getPath();

4. 運行代碼,便可使用http://localhost:8080/rs/rest/recommend?id=1&num=1 便可在瀏覽器中訪問.

返回:

Result=104 4.257081


一些問題: 
注意: 碰到tomcat端口設置問題: 8080 被佔用.緣由是已啓動了tomcat了.使用/etc/init.d/tomcat6 stop讓程序中止便可.
注意: Myeclipse安裝路徑中不要出現空格,不然會出現問題.

參考資料:
[1] Sean Owen "Mahout in Action"  http://book.douban.com/subject/4893547/

[2] Lars Vogel  REST with Java (JAX-RS) using Jersey - Tutorial http://www.vogella.com/articles/REST/article.html[3] REST 參考豆瓣API http://developers.douban.com/wiki/?title=movie_v2#reviews

相關文章
相關標籤/搜索