利用深度神经网络做推荐

技术宅 rainforest 3年前 (2017-03-29) 451次浏览 0个评论

最近谷歌发表的一篇“怎么利用深度学习做推荐?”的论文很火。文论链接请点这里,感兴趣的同学可以下载来看看。文章把推荐问题转化成一个多分类问题,先利用传统的方法对用户可能感兴趣的视频进行召回,然后计算召回的视频在当前User和Context的情况下的概率,选择概率最大的视频推荐给用户。

我们采用类似的思路,但把这个问题当成一个相关性问题,这样在物理意义上更加好解释。也是先利用传统的方法对用户可能感兴趣的item进行召回,然后计算每个item与当前User和Context的相关性,取相关性top的item推荐给用户。

对于User,我们可以用他历史的行为(搜索、点击、浏览)进行用户画像,最终用term或tag的不定长序列来表示。举个例子,用户搜索“北京鲜花购买”,然后浏览了“《剃刀边缘》编剧余飞:把剧本给文章放心又担心”的文章,那么可以把该用户用“<s><q>北京 鲜花 购买</q><v>剃刀 边缘 编剧 余飞 把 剧本 给 文章 放心 又 担心</v></s>”,id化之后为”0 1 2 3 4 5 6 7 8 9 … “。对于Context可以利用类似的机制进行序列化。对于Item,如果是句子直接可以进行切词来序列化,如果是视频可以用视频的tag来进行序列化,总之这样可以把User、Context、Item三者用同一体系id进行序列化。

将User、Context、Item三者序列化之后,我们的神经网络构建如下图所示:

image

 

  1. 首选对User、Context、Item采用统一的embeding层,把每个id embedding成固定维度的向量;
  2. 经过vsum层,将多个id对应的向量加和成一个向量;
  3. 上面再加一个隐藏层,User和Item分别使用不同的隐藏层,但对于Positive Item和Negative Item使用的是相同的隐藏层;
  4. 然后User分别与Positive Item和Negative Item计算Cosine相似度;
  5. 最终以Pairwise Hinge Loss为损失函数:对于任意两个正负例,计算(S+ – S-)的Hinge Loss,
    E(z)=max(0, 1-z)

以内部的一个神经网络框架为例,构建网络的代码如下(懂tensowflow的同学可以很方便用tensowflow实现):

#!/usr/bin/env python
#coding:utf-8
# 基于用户的用户画像、浏览点击行为建模

import sys
import os
from lego_config import LegoNetWrapper
from lego_config import LegoNetWrapper
from lego_config import Feed
from lego_config import Layer

# 隐藏层参数,输入128维度,输出也是128维
fc_param = {"num_in":128, "num_out":128}
fc_learn = {"lr_mult":1.0, "decay_mult":0.0}

# embedding层参数,num_voc:term词典的数量,num_emb:输出的向量的维度
emb_param = {"num_voc":1165992, "num_emb":128}
emb_learn = {"lr_mult":3.0, "decay_mult":0.0}

# 损失参数
loss_param = {"margin":0.1}

# 抽样
sampler = {"type":"ConstantSampler",
           "constant_param": {"sampling_rate":0.1}}

# 用户id化表示
user_slots ={"valtype":["uint"], "name":["user"],
         "allow_empty":[0], "max_seq_len":[1024]}

# 点击/展现词id化表示
query_slots = {"valtype":["uint"], "name":["query"],
          "allow_empty":[0], "max_seq_len":[1024]}

# 训练数据
# 里面定义的key,如qslots、tslots是固定的模式,不能改成别的名字,value可以自行定义
traindatafeed = Feed({"type":"AdvancedPairwise",
                 "name":"uq_basic_train_data",
                 "qslots":user_slots, "tslots":query_slots,
                 "sampler":sampler,
                 "top_dict":{"query":["u"],
                        "pos_title":["pq"],
                        "neg_title":["nq"],
                        "label":["label"]}})
# 预测数据
testdata = Feed({"type":"AdvancedPairwiseTest",
                 "name":"uq_basic_test_data",
                 "qslots":user_slots, "tslots":query_slots,
                 "top_dict":{"query":["u"],
                        "title":["pq"],
                        "label":["label"],
                        "qid":["qid"]}})

# embedding层,user, positive query and negative query一起embedding
# pred_index:总共有三个输入[user, pq, nq],预测时使用[user, pq]进行预测
emb_layer = Layer({"type":"Embedding", "param":emb_param, "learn":emb_learn,
                   "bottom":["u", "pq", "nq"], "top":["u_emb", "pq_emb", "nq_emb"],
                   "pred_index":[0, 1]})

# vsum层:按修了去和,有词的向量相加得到句子的向量,输入是embeding层的输出向量,输出是相加后的向量
# 每个Layer的输出呀都可以添加一次inplace Layer的计算,一般用来做非线性变换(激活函数),激活函数的输入和输出的维数是一样的
# Softsign:softsign(x) = x/(1+abs(x)),将数据的每一维映射到(-1,1)的区间
vsum_layer = Layer({"type":"VSum", "bottom":["u_emb", "pq_emb", "nq_emb"],
                    "top":["u_ss", "pq_ss", "nq_ss"],
                    "inplace":"Softsign", "pred_index":[0, 1]})
# 对user的隐藏层
ufc_layer = Layer({"type":"FC", "param":fc_param, "learn":fc_learn,
                   "bottom":["u_ss"], "top":["u_fc"], "pred_index":[0]})

# 对query的隐藏层
qfc_layer = Layer({"type":"FC", "param":fc_param, "learn":fc_learn,
                  "bottom":["pq_ss", "nq_ss"], "top":["pq_fc", "nq_fc"], "pred_index":[0]})

# 余弦相似度层
cos_layer = Layer({"type":"Cosine", "bottom":[["u_fc", "pq_fc"], ["u_fc", "nq_fc"]],
                   "top":["ps", "ns"], "pred_index":[0]})

# 损失层
# PairwiseHingeLoss:(ps-ns)的值于label(应该恒为1)之间的higeloss E(z) = max(0, 1-z)
# 大体上是最大化ps-ns的值
loss_layer = Layer({"type":"PairwiseHingeLoss", "param":loss_param,
                    "bottom":[["ps", "ns", "label"]], "top":["loss"]})

layers = [emb_layer, vsum_layer, ufc_layer, qfc_layer, cos_layer, loss_layer]
solver_config = {"thread":12, "init_model":"",
                 "base_lr":2e-2, "base_reg":0,
                 "batch":128, "filelist":"filelist.txt",
                 "datadir":"data", "updater":"MiniBatchSGDUpdater"}
net = LegoNetWrapper({"layers":layers, "train_data":traindatafeed,
                      "test_data":testdata, "name":"simnet_hidden1",
                      "solver":solver_config, "trainfile":sys.argv[1],
                      "testfile":sys.argv[2], "solverfile":sys.argv[3],
                      "workdir":sys.argv[4]})
net.build_lego_net()

乐趣公园 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:利用深度神经网络做推荐
喜欢 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址