分享

Mahout案例实战--Dating Recommender 系统(hadoop2.6)

本帖最后由 nettman 于 2015-3-14 14:30 编辑
问题导读
1.Mahout案例实战中 数据库部分做了哪些工作?
2.云平台算法提交并监控的思路是什么?





软件版本:
Hadoop:2.6.0; Mahout:1.0(自行编译,只使用了两个jar文件);Spring:4.0.2;Struts:2.3;Hibernate:4.3;jQuery EasyUI :1.3.6;MySql:5.6;浏览器:chrome;MyEclipse:10.0;
Hadoop平台配置:
node1:  NameNode/ResourceManger/DataNode/NodeManager    Memory:2G
node2: NodeManager/DataNode/SecondaryNameNode/JobHistoryServer Memory:1.5G
node3: NodeManager/DataNode  Memory:1.5G

代码下载:http://download.csdn.net/detail/fansy1990/8490129

网盘下载:链接:http://pan.baidu.com/s/1c0Iq3ss 密码:7iak

系统运行:
(其中ratings.dat和gender.dat文件需要从 http://www.occamslab.com/petricek/data/下载)

1. 数据库部分:
   1)修改configuration/db.properties 文件,改为用户自己的用户名、密码以及数据库;
   2)启动tomcat,让Hibernate自动生成相关表。启动后可以看到数据库中有下面的表:

1.png

拷贝sql/treenode.sql中的第49行语句到命令行执行,然后浏览器访问tomcat发布的项目,比如这里的http://localhost:80/rec/ (或者http://localhost/rec/basic.jsp)即可访问项目主页,界面如下:

2.jpg

   3) 下载相关数据文件,下载解压后文件如下:

3.png


修改configuration/util.properties文件中相应文件的路径,修改rating.proportion,该比例为数据ratings.dat分隔的比例,由于云平台运行协同过滤算法比较耗时,而且消耗内存较大,如果使用全部数据(245M)那么运行CF算法时会比较慢,这里采取上传一部分数据的方法,即先对ratings.dat进行分割,然后上传较小比例到云平台进行计算。如果使用的云平台计算资源较充分,可以设置该比例为1,同时上传较大的文件;


   4)初始化用户基本信息表(即gender.dat数据入库)
     在浏览器导航列表中选择“用户表初始化”或者在右上角系统设置中点击“初始化用户表”进行用户表数据导入;插入完成后,浏览器会有提示:
4.png
tomcat会有日志提示有插入耗时以及数据量,在数据库中查询t_user表中的记录数和此数据量相同;

   5) 插入用户评分数据(即ratings.dat数据入库)
   由于此数据量较大,所以不使用Hibernate插入,而直接使用命令行的方式插入。拷贝sql/import_ratings.sql中的第4行命令(需修改ratings.dat文件位置)到mysql编辑器执行(或者dos命令窗口执行也行),一般耗时300s左右;插入完成后,可以查询到表t_user_item_pref有 17359346条记录。
  由于后面会根据userid进行查询,所以这里加上索引,运行第16行命令,一般耗时:4mins。

2. 云平台数据部分

   1) 分割ratings.dat文件
     点击浏览器右上角系统设置的“分割Rating数据” ,即可分割数据,分割完成后会给出下面的提示:

5.png

,后台也会提示数据分割完成!

    分割后的数据在原始的ratings.dat所在的文件夹中,分为两部分,一个big为较大部分,一个为small为较小部分;

   2)云平台验证:
       点击导航中的“云平台验证”,在Tab页中相应的位置输入NameNode、Resourcemanager的ip以及端口(由于这里本机做了ip和机器名的映射,所以使用机器名亦可),验证成功给出提示:

6.png

   3) 上传ratings.dat文件到云平台
   点击导航中的“数据上传”,在出现的Tab中选择刚才分割产生的small较小部分点击上传,即可上传数据到云平台;

7.png


8.png


3. 云平台算法部分

  0) 拷贝WebRoot/WEB-INF/lib下面的三个包到集群所有节点的目录:

9.png

  1) Top算法:计算所有评分中平均分最高的Top个,在页面设置,比如:

10.png

由于集群有三个节点,所以这里设置reducer的个数为3,最小评分人数是指评分的人数,如果某个item只有一个人评分,而且是10分(最高分),那么其平均分也是10分,这样明显不合适,所以设置这个值可以剔除一些不合理的值;

设置好参数后,点击提交,会给出正在提交任务到云平台的提示,任务提交完成,即可打开算法监控界面,如下:

11.png

任务运行完成,会进行Top数据解析入库:

12.png

入库成功后,在表t_rec_top中查询到300条记录;

  2)调用协同过滤算法

点击导航中的“协同过滤算法”,在弹出的Tab页面设置参数,点击提交,与Top算法类似,不过其监控界面不同,一般如下(此算法运行事件比较长):
运行过程1:

13.png

运行过程2:

14.png

运行过程3:

15.png

CF算法解析主要是更新用户的推荐数据。

3) 调用单用户推荐
单用户推荐就是把云平台上推荐的结果展示而已,比如下面的数据

16.png

在单用户推荐界面可以看到下图:

17.png

4) 新用户推荐界面:

注意在调用新用户推荐算法前,需要先调用CF算法,因为新用户推荐算法的输入数据是CF算法中间的一个步骤产生的数据;
新用户推荐先选择需要展示的top用户的性别:

18.png

然后确定后,会显示top用户,可以对其进行评分(这里只能是盲目的评分,因为没有其他信息)

19.png

评分默认是5,可以进行编辑,编辑后保存即可,最后点击推荐。点击推荐类似Top算法,会向云平台提交新用户推荐任务,然后是监控界面:

20.png

查询相似推荐后,会给出推荐的列表(暂时设置为5个):


21.png


4. 部分代码思路及实现

1) 云平台算法提交并监控
   采用多线程的方式提交任务。首先初始化JobID为空(null),接着新线程启动云平台任务后,会初始化JobID,然后间隔一定时间去获得该JobID,如果获得则说明任务提交成功,前台返回。前台接收到返回的正确信息后,打开监控界面,并开启timer,每隔一定时间向后台请求JobID任务运行情况,得到返回结果进行显示。任务运行完成,则关闭timer,并提示。
其代码如下:


  1. public void cf() throws IOException{
  2.             try{
  3.                     // 初始化JobID
  4.                     Utils.initialJobID();
  5.                     // 提交任务
  6.                     new Thread(new CFThread(numRecommenderations,similarityClassname,maxPerfPerUser,
  7.                                     minPerfPerUser,maxSimilaritiesPerItem,maxPrefsPerUserInItemSimilarity,
  8.                                     Integer.parseInt(reducerSize))).start();
  9.                     while(Utils.getJobID()==null){
  10.                             Thread.sleep(500); // 暂停500毫秒,初始化JObID
  11.                     }
  12.             }catch(Exception e){
  13.                     Utils.stringToWriter(Utils.FIALCHECK);
  14.                     return ;
  15.             }
  16.             Utils.stringToWriter(Utils.PASSCHECK);
  17.             // 任务提交成功,初始化jobIDList
  18.                 Utils.initialJobIDList(Utils.CFJOBNUM);
  19.             return ;
  20.     }
复制代码


这里需要注意的是,在JobSubmitter类中增添了获取JobID的方法,因为JobID是私有变量,所以添加了一个static方法,可以在外面直接获取;

同时,在CF算法中由于有多个MR任务,所以需要根据当前的JobID或MR的任务个数提前初始化将要运行的JobID,这样在运行那个JObID时就不需要再次获取了。
2) 页面监控前台后台:
前台,定时Ajax刷新,重新reload datagrid的数据

  1. function monitor_cf_refresh() {
  2.         // console.info("monitor_cf_refresh()函数"); // 不要打开,会重复多次
  3.         $.ajax({ // ajax提交
  4.                 url : 'cloud/cloud_cfmonitor.action',
  5.                 // data : "id=" + row,
  6.                 dataType : "json",
  7.                 success : function(data) {
  8.                         if (data.finished == 'error') {// 获取信息错误 ,返回数据设置为0,否则正常返回
  9.                         // $.messager.alert('提示', '获取任务信息错误,请查看后台日志!');
  10.                                 // 设置提示:
  11.                                 clearInterval(monitor_cf_interval);
  12.                                 $('#returnMsg_monitorcf').html('获取任务信息错误!');
  13.                                 // console.info(data);
  14.                         } else if(data.finished == 'true'){
  15.                                 
  16. //                                $('#returnMsg_monitorcf').html('任务运行中...');// 直接显示任务运行中即可
  17.                                 // 所有任务运行成功则停止timer
  18.                                 $('#cfMonitorId').datagrid('loadData', data.rows);// 设置多一遍
  19.                                 clearInterval(monitor_cf_interval);
  20.                                 $('#returnMsg_monitorcf').html('任务运行完成!');
  21.                                 // 这里向后台请求解析CF结果,并入库,直接调用cfresult2db()函数即可
  22.                                 cfresult2db();
  23.                         }else{
  24.                                 // 设置提示,并更改页面数据,多行显示job任务信息
  25.                                 // ------ 设置datagrid 的数据
  26.                                 $('#cfMonitorId').datagrid('loadData', data.rows);
  27.                         }
  28.                 }
  29.         });
复制代码

后台:后台获得任务信息后,在附加一个属性值finished,表明此次返回任务状态数据时所有任务是否都已经完成,其代码如下:


  1. /**
  2.      * 测试监控页面
  3.      * @throws IOException
  4.      */
  5.     public void cftmp() throws IOException{
  6.             Utils.stringToWriter(Utils.PASSCHECK);
  7.             return ;
  8.     }
  9.     /**
  10.      * CF监控
  11.      * @date 2015/2/27 23:56 算法监控完成,页面未完善
  12.      * @throws IOException
  13.      */
  14.     public void cfmonitor() throws IOException{
  15.             Map<String ,Object> jsonMap = new HashMap<String,Object>();
  16.             List<CurrentJobInfo> currJobList =null;
  17.             try{
  18. //                    log.info("cfmonitor...");
  19.                     currJobList = Utils.getJobList(Utils.CFJOBNUM);
  20.                     jsonMap.put("rows", currJobList);// 放入数据
  21.                     
  22.                     if (currJobList==null||currJobList.size()<=0){
  23.                             jsonMap.put("finished", "error");
  24.                             log.info("CF算法运行失败!");
  25.                     }else if(currJobList.size()==Utils.CFJOBNUM){
  26.                     // 当所有的任务都运行完成,需要发送完成消息
  27.                 // 完成消息后,需要进行数据解析入库
  28.                 // 入库使用前台发送请求到UserAction
  29.                         CurrentJobInfo lastJob = currJobList.get(Utils.CFJOBNUM-1);
  30.                         if(Utils.isJobFinished(lastJob)){
  31.                                 jsonMap.put("finished", "true");
  32.                                 log.info("CF算法运行完成!");
  33.                         }
  34.                 }else{
  35.                         jsonMap.put("finished", "false");
  36.                 }
  37. //                    currJobList = Utils.getTmpJobList(Utils.CFJOBNUM);// 测试代码
  38. //                    log.info(JSON.toJSONString(currJobList)); // 打印查看
  39.                     Utils.stringToWriter(JSON.toJSONString(jsonMap));// 使用JSON数据传输
  40.             }catch(Exception e){
  41.                     e.printStackTrace();
  42.                     Utils.stringToWriter(Utils.FIALCHECK);
  43.                     return ;
  44.             }
  45.             return ;
  46.     }
复制代码



以上,如有疑问,可发邮件到fansy1990@foxmail.com , 欢迎交流。。。




转载请注明blog地址:http://blog.csdn.net/fansy1990


加微信w3aboutyun,可拉入技术爱好者群

已有(4)人评论

跳转到指定楼层
hb1984 发表于 2015-4-26 18:41:49
谢谢楼主分享。        
回复

使用道具 举报

breaking 发表于 2015-7-27 10:39:51
你好 请问在插入数据表treenode时里面的第二个参数怎么是乱码呢  
回复

使用道具 举报

kanaka10 发表于 2015-9-1 10:15:35
谢谢楼主分享
回复

使用道具 举报

是饭饭 发表于 2016-9-19 15:51:59

学习是唯一的生产力,努力学习,努力挣钱!!

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条