参考原文《亚马逊推荐系统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) |
本文来自博客园,作者:高性能golang,转载请注明原文链接:https://www.cnblogs.com/zhangchaoyang/articles/7117031.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 字符编码:从基础到乱码解决