用意: 網絡上有不少關於使用mahout搭建推薦系統的文章,可是尚未一個從創建推薦系統原型至部署到簡單服務器的完整教程. 雖然部分朋友對推薦系統很感興趣, 可是因hadoop的複雜而卻步. 同時對於那些沒有任何Web開發經驗的朋友來講, 一個完整的小型推薦系統能夠很大的激發學習的興趣和動手的衝動. 我以爲動手的衝動比看書的衝動要重要的多. html
原型分爲兩個系列 : JAVA原型和Python原型. java
這篇博客主要是介紹JAVA推薦系統原型: 主要參考[1] web
使用MyEclipse和Mahout開發一個REST風格[3]的簡單推薦系統 算法
Library見下圖 core server client json. apache
<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> "; } }
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 便可在瀏覽器中訪問.
返回:
[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