摘要:电商行业中,对于用户的商品推荐一直是一个非常热门而且重要的话题,有很多比较成熟的方法,但是也各有利弊。
电商行业中,对于用户的商品推荐一直是一个非常热门而且重要的话题,有很多比较成熟的方法,但是也各有利弊,大致如下:
现在苏宁的商品有约4亿个,商品的类目有10000多组,大的品类也有近40个,如果通过传统的协同推荐,实时计算的话,服务器成本,计算能力都是非常大的局限,之前已经有过几篇应用介绍:基于推荐的交叉销售、基于用户行为的推荐预估。会员研发部门因为不是主要推荐的应用部门,所以在选择上,我们期望的是更加高效高速且相对准确的简约版模型方式,所以我们这边基于了word2vec的原始算法,仿造了itemNvec的方式。
首先,让我们对itemNvec进行理论拆分:
目标商品的前后商品对目标商品的影响程度
这是两个用户userA,userB在易购上面的消费time line,灰色方框内为我们观察对象,试问一下,如果换一下灰色方框内的userA、userB的购买物品,直观的可能性有多大?
直观的体验告诉我们,这是不可能出现,或者绝对不是常出现的,所以,我们就有一个初始的假设,对于某些用户在特定的类目下,用户的消费行为是连续影响的,换句话说,就是我买了什么东西是依赖我之前买过什么东西。如何通过算法语言解释上面说的这件事呢?
大家回想一下,naive bayes做垃圾邮件分类的时候是怎么做的?
假设“我公司可以提供发票、军火出售、航母维修”这句话是不是垃圾邮件?
P1(“垃圾邮件”|“我公司可以提供发票、军火出售、航母维修”)
=p(“垃圾邮件”)p(“我公司可以提供发票、军火出售、航母维修”/“垃圾邮件”)/p(“我公司可以提供发票、军火出售、航母维修”)
=p(“垃圾邮件”)p(“发票”,“军火”,“航母”/“垃圾邮件”)/p(“发票”,“军火”,“航母”)
同理
P2(“正常邮件”|“我公司可以提供发票、军火出售、航母维修”)
=p(“正常邮件”)p(“发票”,“军火”,“航母”/“正常邮件”)/p(“发票”,“军火”,“航母”)
我们只需要比较p1和p2的大小即可,在条件独立的情况下可以直接写成:
P1(“垃圾邮件”|“我公司可以提供发票、军火出售、航母维修”)
=p(“垃圾邮件”)p(“发票”/“垃圾邮件”)p(“军火”/“垃圾邮件”)p(“航母”/“垃圾邮件”)
P2(“正常邮件”|“我公司可以提供发票、军火出售、航母维修”)
=p(“正常邮件”)p(“发票”/“正常邮件”)p(“军火”/“正常邮件”)p(“航母”/“正常邮件”)
但是,我们看到,无论“我公司可以提供发票、军火出售、航母维修”词语的顺序怎么变化,不影响它最后的结果判定,但是我们这边的需求里面前面买的东西对后项的影响会更大。
冰箱=>洗衣机=>衣柜=>电视=>汽水,这样的下单流程合理
冰箱=>洗衣机=>汽水=>电视=>衣柜,这样的下单流程相对来讲可能性会更低
但是对于naive bayes,它们是一致的。
所以,我们这边考虑顺序,还是上面那个垃圾邮件的问题。
P1(“垃圾邮件”|“我公司可以提供发票、军火出售、航母维修”)
=p(“垃圾邮件”)p(“发票”)p(“军火”/“发票”)p(“军火”/“航母”)
P1(“正常邮件”|“我公司可以提供发票、军火出售、航母维修”)
=p(“正常邮件”)p(“发票”)p(“军火”/“发票”)p(“军火”/“航母”)
这边我们每个词只依赖前一个词,理论上讲依赖1-3个词通常都是可接受的。以上的考虑顺序的bayes就是基于著名的马尔科夫假设(Markov Assumption):下一个词的出现仅依赖于它前面的一个或几个词下的联合概率问题,相关详细的理论数学公式就不给出了,这边这涉及一个思想。
更大的数据存储形式
我们常用的user到item的映射是通过one hot encoding的形式去实现的,这有一个非常大的弊端就是数据存储系数且维度灾难可能性极大。
回到最初的那组数据:现在苏宁的商品有约4亿个,商品的类目有10000多组,大的品类也有近40个,同时现在会员数目达到3亿,要是需要建造一个用户商品对应的购买关系矩阵做基于用户的协同推荐的话,我们需要做一个4亿X6亿的1/0矩阵,这个是几乎不可能的,Huffman采取了一个近似二叉树的形式进行存储:
我们以易购商品购买量为例,讲解一下如何以二叉树的形式替换one hot encoding存储方式:
假设,818苏宁大促期间,经过统计,有冰箱=>洗衣机=>烘干机=>电视=>衣柜=>钻石的用户下单链条(及购买物品顺序如上),其中冰箱总售出15万台,洗衣机总售出8万台,烘干机总售出6万台,电视总售出5万台,衣柜总售出3万台,钻石总售出1万颗
Huffman树构造过程
1.给定{15,8,6,5,3,1}为二叉树的节点,每个树仅有一个节点,那就存在6颗单独的树
2.选择节点权重值最小的两颗树进行合并也就是{3}、{1},合并后计算新权重3+1=4
3.将{3},{1}树从节点列表删除,将3+1=4的新组合树放回原节点列表
4.重新进行2-3,直到只剩一棵树为止
针对每层每次分支过程,我们可以将所有权重大的节点看做是1,权重小的节点看做是0,相反亦可。现在,我们比如需要知道钻石的code,就是1000,也就是灰色方框的位置,洗衣机的code就是111;这样的存储利用了0/1的存储方式,也同时考虑了组合位置的排列长度,节省了数据的存储空间。
最大化当前数据出现可能的概率密度函数
对于钻石的位置而言,它的Huffman code是1000,那就意味着在每一次二叉选择的时候,它需要一次被分到1,三次被分到0,而且每次分的过程中,只有1/0可以选择,这是不是和logistic regression里面的0/1分类相似,所以这边我们也直接使用了lr里面的交叉熵来作为loss function。
其实对于很多机器学习的算法而言,都是按照先假定一个模型,再构造一个损失函数,通过数据来训练损失函数求argmin(损失函数)的参数,放回到原模型。
让我们详细的看这个钻石这个例子:
第一步
p(1|No.1层未知参数)=sigmoid(No.1层未知参数)
第二步
p(0|No.2层未知参数)=sigmoid(No.2层未知参数)
同理,第三第四层:
p(0|No.3层未知参数)=sigmoid(No.3层未知参数)
p(0|No.4层未知参数)=sigmoid(No.4层未知参数)
然后求p(1|No.1层未知参数)xp(0|No.2层未知参数)xp(0|No.3层未知参数)xp(0|No.4层未知参数)最大下对应的每层的未知参数即可,求解方式与logistic求解方式近似,未知参数分布偏导,后续采用梯度下降的方式(极大、批量、牛顿按需使用)
商品的相似度
刚才在part three里面有个p(1|No.1层未知参数)这个逻辑,这个NO.1层未知参数里面有一个就是商品向量。
举个例子:
存在1000万个用户有过:“啤酒=>西瓜=>剃须刀=>百事可乐”的商品购买顺序
10万个用户有过:“啤酒=>苹果=>剃须刀=>百事可乐”的商品购买顺序,如果按照传统的概率模型比如navie bayes 或者n-gram来看,P(啤酒=>西瓜=>剃须刀=>百事可乐)>>p(啤酒=>苹果=>剃须刀=>百事可乐),但是实际上这两者的人群应该是同一波人,他们的属性特征一定会是一样的才对。
我们这边通过了随机初始化每个商品的特征向量,然后通过part three的概率模型去训练,最后确定了词向量的大小。除此之外,还可以通过神经网络算法去做这样的事情。
Bengio 等人在 2001 年发表在 NIPS 上的文章《A Neural Probabilistic Language Model》介绍了详细的方法。
我们这边需要知道的就是,对于最小维度商品,我们以商品向量(0.8213,0.8232,0.6613,0.1234,...)的形式替代了0-1点(0,0,0,0,0,1,0,0,0,0...),单个的商品向量无意义,但是成对的商品向量我们就可以比较他们间的余弦相似度,就可以比较类目的相似度,甚至品类的相似度。
# -*- coding:utf-8 -*-
import pandas as pd
import numpy as np
import matplotlib as mt
from gensim.models import word2vec
from sklearn.model_selection import train_test_split
order_data = pd.read_table('C:/Users/17031877/Desktop/SuNing/cross_sell_data_tmp1.txt')
dealed_data = order_data.drop('member_id', axis=1)
dealed_data = pd.DataFrame(dealed_data).fillna(value='')
# 数据合并
dealed_data = dealed_data['top10'] + [" "] + dealed_data['top9'] + [" "] + dealed_data['top8'] + [" "] + \
dealed_data['top7'] + [" "] + dealed_data['top6'] + [" "] + dealed_data['top5'] + [" "] + dealed_data[
'top4'] + [" "] + dealed_data['top3'] + [" "] + dealed_data['top2'] + [" "] + dealed_data['top1']
# 数据分列
dealed_data = [s.encode('utf-8').split() for s in dealed_data]
# 数据拆分
train_data, test_data = train_test_split(dealed_data, test_size=0.3, random_state=42)
# 原始数据训练
# sg=1,skipgram;sg=0,SBOW
# hs=1:hierarchical softmax,huffmantree
# nagative = 0 非负采样
model = word2vec.Word2Vec(train_data, sg=1, min_count=10, window=2, hs=1, negative=0)
接下来就是用model来训练得到我们的推荐商品,这边有三个思路,可以根据具体的业务需求和实际数据量来选择:
# 最后一次浏览商品最相似的商品组top3
x = 1000
result = []
result = pd.DataFrame(result)
for i in range(x):
test_data_split = [s.encode('utf-8').split() for s in test_data[i]]
k = len(test_data_split)
last_one = test_data_split[k - 1]
last_one_recommended = model.most_similar(last_one, topn=3)
tmp = last_one_recommended[0] + last_one_recommended[1] + last_one_recommended[2]
last_one_recommended = pd.concat([pd.DataFrame(last_one), pd.DataFrame(np.array(tmp))], axis=0)
last_one_recommended = last_one_recommended.T
result = pd.concat([pd.DataFrame(last_one_recommended), result], axis=0)
考虑用户最后一次操作的关注物品x,干掉那些已经被用户购买的商品,剩下的商品表示用户依旧有兴趣但是因为没找到合适的或者便宜的商品,通过商品向量之间的相似度,可以直接计算出,与其高度相似的商品推荐给用户。
根据历史上用户依旧购买的商品顺序,判断根据当前这个目标用户近期买的商品,接下来他最有可能买什么?
比如历史数据告诉我们,购买了手机+电脑的用户,后一周内最大可能会购买背包,那我们就针对那些近期购买了电脑+手机的用户去推送电脑包的商品给他,刺激他的潜在规律需求。
# 向量库
rbind_data = pd.concat(
[order_data['top1'], order_data['top2'], order_data['top3'], order_data['top4'], order_data['top5'],
order_data['top6'], order_data['top7'], order_data['top8'], order_data['top9'], order_data['top10']], axis=0)
x = 50
start = []
output = []
score_final = []
for i in range(x):
score = np.array(-100000000000000)
name = np.array(-100000000000000)
newscore = np.array(-100000000000000)
tmp = test_data[i]
k = len(tmp)
last_one = tmp[k - 2]
tmp = tmp[0:(k - 1)]
for j in range(number):
tmp1 = tmp[:]
target = rbind_data_level[j]
tmp1.append(target)
test_data_split = [tmp1]
newscore = model.score(test_data_split)
if newscore > score:
score = newscore
name = tmp1[len(tmp1) - 1]
else:
pass
start.append(last_one)
output.append(name)
score_final.append(score)
在3.2中,我们根据了这个用户近期购买行为,从历史已购用户的购买行为数据发现规律,提供推荐的商品。还有一个近似的逻辑,就是通过目标用户最近一次的购买商品进行推测,参考的是历史用户的单次购买附近的数据,详细如下:
这个实现也非常的简单,这边代码我自己也没有写,就不贴了,采用的还是word2vec里面的predict_output_word(context_words_list, topn=10),Report the probability distribution of the center word given the context words as input to the trained model
其实,这边详细做起来还是比较复杂的,我这边也是简单的贴了一些思路,如果有不明白的可以私信我,就这样,最后,谢谢阅读。
——本文由作者 沙韬伟 投稿至数据分析网,并经编辑发布,版权归作者所有。
作者:沙韬伟,苏宁易购高级算法工程师。曾任职于Hewlett-Packard、滴滴出行。主要研究方向包括自然语言分析、机器学习和风控深度学习。目前专注于基于深度学习及集成模型下的用户行为模式的识别。
Original url: Access
Created at: 2018-09-29 19:50:31
Category: default
Tags: none
未标明原创文章均为采集,版权归作者所有,转载无需和我联系,请注明原出处,南摩阿彌陀佛,知识,不只知道,要得到
最新评论