一种基于共现的推荐算法
参考原文《亚马逊推荐系统20年》
现有所有用户的购物列表:
user1: G A B
user2: X C Y
user3: X E F
user4: X M Y Q N J H
user5: W Y
user6: L E Z W
现在要计算商品X的相似商品。
思路:要计算X和Y的相似度直接计算p(Y|X)即可。根据极大似然估计
\begin{equation} p(Y|X)=\frac{count(X,Y)}{count(X)}=\frac{2}{3} \label{orig} \end{equation}
公式(\ref{orig})有2个问题:
- 如果Y是一个很热门的商品,则它倾向于和任意商品共现。也就是说如果$count(X,Y)$比较大,并不能直接说明X和Y的相关度就大,也可能是因为Y商品非常地热门。
- 对于热衷于购物的用户,她会买很多东西,这些东西之间不一定有相关度。也就是说如果一个用户购物比较少,我们倾向于认为这些商品之间的相关度比较高;如果一个用户购物比较多,我们倾向于认为这些商品之间的相关度比较低。
符号约定:
$N_{XY}$:同时购买了商品X和Y的用户数
$p(Y)$:总体而言,商品Y被购买的概率。$p(Y)=\frac{Y被购买的次数}{卖出去的商品总数}$
$C_X$:购买了商品X的用户集合
$c$:$C_X$中的一个用户
$|c|$:用户$c$购物的总数减去用户$c$购买X的次数
先假设商品X和Y相互独立,则用户$c$除了买X外还买了$|c|$件其他商品,每次购买都看成是一次伯努力试验:是否购买商品Y。则用户$c$没有购买Y的概率为:
$$(1-p(Y))^{|c|}$$
用户$c$购买Y的期望为:
$$1-(1-p(Y))^{|c|}$$
用户集合$C_X$购买Y的期望次数为:
\begin{equation}E_{XY}=\sum_{c \in C_X}\left[1-(1-p(Y))^{|c|}\right] \label{E} \end{equation}
实际上X和Y可能不是相互独立的,它们之间的相关度为:
\begin{equation} relevance(X,Y)=\frac{N_{XY}-E_{XY}}{\sqrt{E_{XY}}} \label{R} \end{equation}
联合(\ref{E})式和(\ref{R})式可知:$p(Y)越大(即Y越热门),X和Y的相关度越低;$$|c|$越大(即用户的购物列表越长),X和Y的相关度越低。正好解决(或者说缓和)了本文一开头提出的那2个问题。
# coding=utf-8
__author__ = "orisun"
import math
from collections import defaultdict
target_position = set(
[3218357, 3222919, 3050675, 3097961, 3202165, 3122457, 2747795]) # 要为这些职位生成推荐
deliver_list_dict = defaultdict(list) # 包含target_position的投递列表
deliver_prob_dict = {}
def deliverCount(deliverFile):
"""统计每个职位被投递的概率
"""
global target_position
global deliver_list_dict
global deliver_prob_dict
total_deliver = 0 # 总投递次数
deliver_count_dict = defaultdict(int) # 每个职位被投递的次数
with open(deliverFile, 'r') as f_in:
for line in f_in:
arr = line.strip().split()
if len(arr) > 1:
deliver_set = set()
for ele in arr[1:]:
brr = ele.split(",")
if len(brr) == 3:
if "1" == brr[0]:
pid = int(brr[1])
total_deliver += 1
deliver_count_dict[pid] += 1
deliver_set.add(pid)
for target in target_position:
if target in deliver_set:
deliver_list_dict[target].append(list(deliver_set))
for pid, cnt in deliver_count_dict.items():
deliver_prob_dict[pid] = 1.0 * cnt / total_deliver
def recForPosition(pid):
global deliver_list_dict
global deliver_prob_dict
if pid in deliver_list_dict:
deliver_list_list = deliver_list_dict[pid]
cooccur_count_dict = defaultdict(int)
expect_count_dict = defaultdict(float)
for deliver_list in deliver_list_list:
for deliver in deliver_list:
if deliver != pid:
cooccur_count_dict[deliver] += 1
expect_count_dict[deliver] += 1 - \
math.pow(
1 - deliver_prob_dict[deliver], len(deliver_list) - 1)
relevant_dict = {}
for deliver, cnt in cooccur_count_dict.items():
relevant_dict[deliver] = 1.0 * (cnt - len(deliver_list_list) * expect_count_dict[
deliver]) / math.sqrt(expect_count_dict[deliver])
# 只取相关度最高的前50个作为推荐
return sorted(relevant_dict.items(), cmp=lambda x, y: cmp(y[1], x[1]))[:50]
return None
if __name__ == '__main__':
deliverCount("/data/orisun/dnn_for_rec/behavior/allBehavior.txt")
for target in target_position:
print "neighbors of position", target
print recForPosition(target)
本文来自博客园,作者:张朝阳讲go语言,转载请注明原文链接:https://www.cnblogs.com/zhangchaoyang/articles/7117031.html

浙公网安备 33010602011771号