ItemCF_基於物品的協同過濾
1. 概念

2. 原理




如何給用戶推薦?
給用戶推薦他沒有買過的物品--103
3. java代碼實現思路
數據集:

第一步:構建物品的同現矩陣
第二步:構建用戶的得分矩陣
第三步:同現矩陣*評分矩陣
第四步:拿到最終結果,排序,得到給用戶的推薦列表
問題一:物品同現矩陣和用戶得分矩陣如何構建?
問題二:矩陣相乘如何來做?
六個MapReduce
step1_第一個MapReduce:
目的-->去重去除數據集中重復的數據
第一個MapReduce最終運行的結果:

Mapper端:
key:LongWritable(偏移量)
value:一行數據
步驟一:
context.write(value, NullWritable.get());
Reducer
端:key:一行數據
value:NullWritable
步驟一:
context.write(key, NullWritable.get());
step2_第二個MapReduce:
目的-->按用戶分組,計算所有物品出現的組合列表,得到用戶對物品的喜愛度得分矩陣

第二個MapReduce最終運行的結果:

Mapper端:
key:LongWritable(偏移量)
value:
i1,u2723,click,2014/9/14 9:31 ||
i1,u2723,pay,2014/9/14 9:31
步驟一:按照“,”切割,
得到item(i1), user(u2723), action(click)
步驟二:構建輸出的key和value(
key:user, value:item:物品的得分【根據用戶action得到】
)
步驟三:輸出key,value
Reducer
端:key:user(u2723)
value:{i1:1, i1:2, i3:2}
步驟一:遍歷
{i1:1, i1:2, i3:2},對於迭代器中的每一個Text(i1:1),按照“:”切割,分別得到item(i1)和action(1),對同一物品的action進行累加,將結果存儲到map對象中(map.put(item, action))
步驟二:構建StringBuffer,
key:user(u2723),value:{i1:3, i2:4, i3:5}, 並輸出
step3_第三個MapReduce:
目的-->對物品組合列表進行計數,建立物品的同現矩陣
第三個MapReduce最終運行的結果:

Mapper端:
key:LongWritable(偏移量)
value:
u26 i276:1,i201:1,i348:1,i321:1,i136:1,
步驟一:切割"\t",得到tokens[1]
步驟二:
雙重for循環,得到每一個物品和其他物品的同現的次數
步驟三:輸出,(key=itemA:itemB,value=1)
這里只是得到了單用戶的物品同現,在Reducer端得到的是所有用戶--同一物品對其他物品的同現次數
Reducer
端:第一種--key:itemA:itemB
value:{1,1,1}
步驟一:對Iterable<IntWritable>遍歷,統計sum,
得到itemA:itemB同現的次數
step4_第四個MapReduce:
目的-->把物品同現矩陣和用戶得分矩陣相乘
第四個MapReduce最終運行的結果:

Mapper端:
key:LongWritable(偏移量)
value:
u14 i25:1,i223:1 ||
i100:i105 1
步驟一:因為Mapper讀取了第二次輸出(用戶得分矩陣)和第三次輸出的結果(物品同現矩陣),
所以要對maptask所對應的split進行判斷,判讀所讀的數據集屬於哪一個,這里采用了重寫了setup(Context context)方法,定義了flag來進行標識
步驟二:
如果為同現矩陣(step3)
// 樣本: i100:i181 1
key:i100 value:A:i181,1 輸出
如果為得分矩陣(step2)
// 樣本: u24 i64:1,i218:1,i100:2,
遍歷得分矩陣--
key:i100 value:B:u24,2, 輸出
Reducer
端:
key:i100
value: {
A:i181,1, A:i180,1 A:i167,3}
||
{
B:u24,2,B:u25,3, B:u26,3}

步驟一:因為value中的A:B:標識同現矩陣,得分矩陣
val.startWith("A:")
某一個物品
i100,針對它和其他物品的同現次數,存在mapA-->
value: {
A:i181,1, A:i180,1 A:i167,3}
mapA.put(i181,1),map.put(i180,1)...
val.startWith("B:")
該物品(key中的itemID),
所有用戶的推薦權重分數
mapB--
{
B:u24,2,B:u25,3, B:u26,3}
mapB.put(u24,2),mapB.put(u25,3)...
步驟二:進行矩陣相乘運算,對於物品i100,它的同現商品以及對應的次數存放到了mapA,而物品對於i100,所有用戶的評分已經存放到了mapB,只需要遍歷mapA,將其中同現的每一個商品乘以對應的mapB中每一個用戶對i100的評分
步驟三:輸出,
key=userId, value=itemId,result (u24
i101,8.0
)
step5_第五個MapReduce:目的-->把相乘后的矩陣相加,獲得結果矩陣
第五個MapReduce最終運行的結果:

Mapper端:
key:LongWritable(偏移量)
value:u13 i9,5.0
步驟一:key=u13,value=i9,5.0 輸出
Reducer端:key:u13 value:{i101,2.0, i103,4.5, i101, 5.7}
步驟一:利用map對同一itemId矩陣求和
步驟二:輸出,key=userId, value=itemId,score(
樣本: u13 i9,5.0)
step6_第六個MapReduce:
目的-->按照推薦得分降序排序,取前十條(二次排序)
第六個MapReduce最終運行的結果:

Mapper端:
key:LongWritable(偏移量)
value:
u13 i9,5.0
步驟一:將用戶id,物品和得分封裝到一個對象
PairWritable
,
步驟二:輸出,key:(
PairWritable) value:(item:num)
Shuffle中Sort:
注意:

重寫compare()方法,先比較Uid,相等的話,再比較Num
Shuffle中Group:
注意:

重寫compare()方法,Uid相同的為一組
Reducer
端:
key:
PairWritable
value:Text{i160:58.0,i352:9.0,i192:8.0,i455:7.0...}
步驟一:取前十個,利用StringBuffer拼接
步驟二:輸出,key=uid,value=sb.toString()