Mapreduce之基于内容的电影推荐(一)
背景
你是不是很想知道腾讯或爱奇艺是如何为用户创建推荐电影?或者淘宝京东如何为用户推荐图书?肯定有某种魔法算法生成的这些推荐系统,那么有那些推荐系统呢?
这里介绍基于内容的推荐系统,基于内容的推荐系统会检查项目(如电影)的属性来为用户作出推荐,例如一个用户如果看了很多动作片,那么系统就会为他推荐这一类电影
原理
在基于内容的推荐系统中,我们得到的内容信息(如邻域和元数据)越多,算法就会变得越复杂,不过推荐也会变得更准确,更合理,如,要实现电影推荐,系统应当有一些元数据,如演员,导演和制片人,在这个例子中,算法仅限于对电影评分。接下来使用关联度算法,对Mapreduce处理好的数据来找出每个电影之间的关联,进而进行推荐。
MapReduce解决方案如下:
- 阶段1:找出各个电影的评分总人数
- 阶段2:对每个电影对A和B,找出同时对A和B评分的人
- 阶段3:找出每两个相关的电影之间的关联,在这个阶段使用三个不同的关联度算法(Pearson,Cosine和Jaccard)一般的,要根据具体数据需求来选择关联度算法,不过这个阶段可以使用任何想要的算法作实验
样例输入
输入数据包括用户ID,电影ID,评分。
即< UserID > < MovieID > < rating >
样例生成程序如下:
package com.deng.MovieRecommend;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class create {
public static void main(String[] args) throws IOException, IOException {
String path="input/movieRecommend.txt";
File file=new File(path);
if(!file.exists()){
file.getParentFile().mkdirs();
}
file.createNewFile();
FileWriter fw=new FileWriter(file,true);
BufferedWriter bw=new BufferedWriter(fw);
for(int i=0;i<5000;i++){
int id=(int)(Math.random()*100)+1;
int movieId=(int)(Math.random()*300);
while(id==movieId) movieId=(int)(Math.random()*300);
bw.write("UserId=User"+id+" Movie=Movie"+movieId+" rating="+(int)(Math.random()*5)+"\n");
}
bw.flush();
bw.close();
fw.close();
}
}
程序创建5000行用户对电影评分的信息
MapReduce阶段1
这个阶段的目标是读取输入数据 < UserID > < MovieID > < rating >, ,通过简单的MapReduce获取每个电影的评价人数
如下:
UserID | MovieID | rating |
---|---|---|
User1 | Movie1 | 1 |
User1 | Movie2 | 2 |
User1 | Movie2 | 3 |
User2 | Movie1 | 1 |
User2 | Movie2 | 2 |
User2 | Movie3 | 3 |
生成如下格式
其中,numberOfPairs表示评价该电影的人数
UserID | MovieID | rating | numberOfPairs |
---|---|---|---|
User1 | Movie1 | 1 | 45 |
User1 | Movie2 | 2 | 56 |
mapper阶段任务
通过处理输入记录 < UserID> ,< MovieID> < rating > 生成 < < MoiveID >,<Tuple2( UserID , rating) >> 的键值对。
mappre阶段编码
package com.deng.MovieRecommend;
import com.deng.MRDPUtil;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
import java.util.Map;
public class findNumberOfRatersMapper extends Mapper<LongWritable,Text,Text, Tuple2> {
private Text K;
private Tuple2<String,Integer> V;
public void map(LongWritable key,Text value,Context context){
String line=value.toString();
// 解析数据
Map<String,String> parsed= MRDPUtil.transInformation(line);
K=new Text(parsed.get("Movie"));
String user=parsed.get("UserId");
Integer rating=Integer.parseInt(parsed.get("rating"));
V=new Tuple2(user,rating);
// 使用try -catch,这样如果发生一场就不会结束整个程序的运行
try {
context.write(K,V);
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
其中Tuple2的设计模式如下:
package com.deng.MovieRecommend;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.io.WritableComparable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class Tuple2<T1,T2> implements Writable, WritableComparable<Tuple2<T1,T2>>{
private T1 _1;
private T2 _2;
public Tuple2(){
}
public Tuple2(T1 _1, T2 _2){
set(_1,_2);
}
private void set(T1 s1, T2 s2) {
_1=s1;
_2=s2;
}
public T1 first() {
return _1;
}
public void setFirst(T1 _1) {
this._1 = _1;
}
public T2 second() {
return _2;
}
public void setSecond(T2 _2) {
this._2 = _2;
}
@Override
public int compareTo(Tuple2 o) {
return 0;
}
@Override
public void write(DataOutput dataOutput) throws IOException {
Text.writeString(dataOutput, String.valueOf(_1));
Text.writeString(dataOutput, String.valueOf(_2));
}
@Override
public void readFields(DataInput dataInput) throws IOException {
_1= (T1) Text.readString(dataInput);
_2= (T2) Text.readString(dataInput);
}
public String toString(){
StringBuffer sb=new StringBuffer("Tuple2[");
sb.append(_1).append(",").append(_2);
return sb.append("]").toString();
}
}
reduce阶段任务
对mapper发送过来的数据进行处理,生成 < < UserID> <Tuple3( MovieID,rating,numberOfPairs) > >的键值对
package com.deng.MovieRecommend;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class findNumberOfRatersReducer extends Reducer<Text,Tuple2,Text,Tuple3> {
public Integer numberOfRaters; //通过获取键值对的个数来判断电影被评价的次数
public Text K;// 用户名称
public Tuple3<String,Integer,Integer> V;
public void reduce(Text key,Iterable<Tuple2> values,Context context){
List<Tuple2> tuple2List=new ArrayList<Tuple2>();
for(Tuple2 t:values) {
tuple2List.add(new Tuple2(t.first(),t.second()));
};
numberOfRaters=tuple2List.size();
Map<Tuple2,Integer> mp=new HashMap<Tuple2, Integer>();
for(Tuple2 t2:tuple2List){
if(mp.get(t2)==null){
mp.put(t2,1);
K=new Text((String) t2.first());
V=new Tuple3(key.toString(),t2.second(),numberOfRaters);
try {
context.write(K,V);
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
continue;
}
}
}
public Integer totalValues(Iterable<Tuple2> V){
Integer total=0;
for(Tuple2 t:V) total++;
return total;
}
}
其中Tuple3设计模式如下:
package com.deng.MovieRecommend;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.io.WritableComparable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class Tuple3<T1,T2,T3> implements Writable, WritableComparable<Tuple3<T1,T2,T3>> {
private T1 _1;
private T2 _2;
private T3 _3;
public Tuple3(){
}
public Tuple3(T1 _1,T2 _2,T3 _3){
set(_1,_2,_3);
}
private void set(T1 first,T2 second,T3 third){
_1=first;
_2=second;
_3=third;
}
public T1 first(){
return _1;
}
public void setFirst(T1 _1){
this._1=_1;
}
public T2 second(){
return _2;
}
public void setSecond(T2 _2){
this._2=_2;
}
public T3 third(){
return _3;
}
public void setThird(T3 _3){
this._3=_3;
}
public boolean equal(Tuple3 o){
if(_1==o._1&&_2==o._2&&_3==o._3) return true;
return false;
}
@Override
public int compareTo(Tuple3 o) {
String num=_1.toString().substring(5);
String numO=o._1.toString().substring(5);
if(Integer.parseInt(num)<Integer.parseInt(numO)){
return 1;
}else return -1;
}
@Override
public void write(DataOutput dataOutput) throws IOException {
Text.writeString(dataOutput, String.valueOf(_1));
Text.writeString(dataOutput, String.valueOf(_2));
Text.writeString(dataOutput, String.valueOf(_3));
}
@Override
public void readFields(DataInput dataInput) throws IOException {
_1= (T1) Text.readString(dataInput);
_2= (T2) Text.readString(dataInput);
_3= (T3) Text.readString(dataInput);
}
public String toString() {
StringBuilder sb = new StringBuilder("Tuple3[");
sb.append(_1).append(",").append(_2).append(",").append(_3);
return sb.append("]").toString();
}
}