hadoop2.5.2学习14--MR之协同过滤天猫推荐算法实现02

2.3 第三个mapreduce, 计算物品的同现矩阵

根据第二个mapreduce的输出:每个用户的购买情况

u26 i276:1,i201:1,i348:1,i321:1,i136:1,

分析i276:1,i201:1,i348:1,i321:1,i136:1, 获取同现矩阵
对每行数据, 及每个用户的数据, 遍历他的购物篮,得出同现矩阵
通过两层循环获取每个用户的两两商品的次数

            for (int i = 0; i < vs.length; i++) {
                for (int j = i+1; j < vs.length; j++) {
                    String itemI = vs[i].split(":")[0];
                    String itemJ = vs[j].split(":")[0];
                    String K = itemI+":"+itemJ;
                    context.write(new Text(K), new IntWritable(1));
                }
            }

在reduce中,默认分组,按照reduce的输入key是否相同,进行分组,

所以只要对同一组的数据进行累加,就是同现次数

第三个mapreduce的输出结果

i84:i530    1
i84:i532    1
i84:i533    2
i84:i534    3
i84:i535    4
i84:i536    3
i84:i537    1
i84:i538    2

第三个mapreduce的代码

package com.chb.catTest;

import java.io.IOException;
import java.util.Map;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

/**
 * 求取同现矩阵
i100:i100   3
i100:i105   1
i100:i106   1
i100:i109   1
i100:i114   1
i100:i124   1
 */
public class Step3 {
    public static boolean run(Configuration conf, Map<String, String> paths) throws Exception {
        FileSystem fs = FileSystem.get(conf);
        Job job = Job.getInstance();
        job.setJar("C:\\Users\\12285\\Desktop\\cr.jar");
        job.setJarByClass(Step3.class);
        job.setJobName("Step3");

        job.setMapperClass(Step3Mapper.class);
        job.setReducerClass(Step3Reducer.class);

        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(IntWritable.class);

        Path in = new Path(paths.get("step3Input"));
        FileInputFormat.addInputPath(job, in);
        Path out = new Path(paths.get("step3Output"));
        if (fs.exists(out)) {
            fs.delete(out, true);
        }
        FileOutputFormat.setOutputPath(job, out);
        boolean f = job.waitForCompletion(true);
        return f;
    }
    static class Step3Mapper extends Mapper<LongWritable, Text, Text, IntWritable> {
        //u26   i276:1,i201:1,i348:1,i321:1,i136:1,
        //只需要统计物品两两同现的次数
        @Override
        protected void map(LongWritable key, Text value, Context context)
                throws IOException, InterruptedException {
            String[] vs = value.toString().split("\t")[1].split(",");
            for (int i = 0; i < vs.length; i++) {
                for (int j = i+1; j < vs.length; j++) {
                    String itemI = vs[i].split(":")[0];
                    String itemJ = vs[j].split(":")[0];
                    String K = itemI+":"+itemJ;
                    context.write(new Text(K), new IntWritable(1));
                }
            }
        }
    }
    static class Step3Reducer extends Reducer<Text, IntWritable, Text, IntWritable> {
        @Override
        protected void reduce(Text key, Iterable<IntWritable> values, Context context)
                throws IOException, InterruptedException {
            int sum = 0;
            for (IntWritable iw : values) {
                sum += iw.get();
            }
            context.write(key, new IntWritable(sum));
        }
    }
}


根据前三个mapreduce的计算,获取了用户的喜爱得分,和物品的同现矩阵
所以,在第四个mapreduce中对两个矩阵相乘得到推荐矩阵
由于是一大一小两个表,
所以将小表加载到内存中。

第四个mapreduce的代码实现

同现矩阵和喜爱评分矩阵相乘得到用户的推荐向量矩阵

将小表喜爱的评分表加载到内存中

        //将喜爱得分的表加载到内存中。
        job.addCacheFile(new Path(paths.get("step4InputSmall")).toUri());

在Mapper的setup方法中获取喜爱评分表
原始喜爱评分数据:

//读取信息:u26  i276:1,i201:1,i348:1,i321:1,i136:1,

因为在推荐向量是以用户为维度,根据推荐值推荐不同的商品
所以在写到HashMap中以userId为key, value为{itemId: 喜爱评分}

//{userID: {itemID:喜爱评分}}

Item的同现矩阵数据格式:

             i100:i184  2
             i100:i185  1
             i100:i187  1

我们在会看一下推荐向量的计算图表:
i100相当于103, 对应ItemB 相当于与之对应的101-107 ,与用户U3对101-107的喜爱评分相乘得到推荐向量矩阵:
这里写图片描述

在map()中对同现矩阵的每条记录

获取ItmeA与B的同现次数, 和ItemB的喜爱评分

                //同现矩阵,i100:i184    2
                String itemA = key.toString().split(":")[0];
                String itemB = key.toString().split(":")[1];
                int txNum = Integer.parseInt(value.toString());
                int  prefScore = 0;
               //获取用户对itemB的喜爱得分
                if (itemMap.get(itemB) != null) {//Error: java.lang.NullPointerException
                    prefScore = itemMap.get(itemB);
                }


对每个用户的每个ItemA的推荐向量有各个分量类型,
对每条数据可以计算一个推荐分量 ,
推荐向量的分量:itemB贡献 的为itemA和itemB同现次数*用户对ItemB的喜爱评分

               //推荐向量的分量:itemB贡献 的为itemA和itemB同现次数*用户对ItemB的喜爱评分
               int uItemB = txNum * prefScore;

Mapper的输出是ItemA的推荐分量,

        //输出
        context.write(new Text(userId+":"+itemA), new IntWritable(uItemB));

所以,需要在reduce中对每个分量进行合并

由于mapper的输出的key为userId:ItemAId, 所以合并是非常简单, 只需要累加

            int sum = 0;
            for (IntWritable iw : values) {
                sum += iw.get();
            }
            context.write(key, new IntWritable(sum));

来看看推荐向量的结果:

统计的是对一个用户的每个item的推荐值:

u13:i1  3
u13:i10 0
u13:i100    1
u13:i101    1
u13:i102    2
u13:i103    0
u13:i104    1
u13:i105    5
u13:i106    2
u13:i107    2
u13:i108    1
u13:i109    2
u13:i11 0

接下来最后一个mapreduce

就是对推荐向量分每个用户进行排序, 选取前10个商品,作为对用户的推荐
两点:

  • 推荐向量降序排序
  • 用户分组

    自定义分组和排序

可以参考: MR之统计每月最高三个温度

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值