論文調(diào)研--NAIS -TKDE2018

論文地址

01-NAIS (item sim)

NAIS Neural Attentive Item Similarity Model for Recommendation-TKDE2018
論文鏈接:https://arxiv.org/pdf/1809.07053.pdf
論文代碼地址:https://github.com/AaronHeee/Neural-Attentive-Item-Similarity-Model

這篇論文運用attention機制的地方在計算項目相似度译秦,作者認為用戶的歷史商品對于推薦的目標(biāo)商品所產(chǎn)生的影響是不同的,所以在計算用戶對商品的預(yù)測評分時淮菠,根據(jù)用戶的歷史商品得到的用戶特征應(yīng)該根據(jù)影響因子作權(quán)重加和闭树。在使用傳統(tǒng)的softmax時發(fā)現(xiàn)效果不好蚌斩,因為要計算影響因子的數(shù)量太大霸奕,最后得出的概率分布并不好吭敢,所以在softmax的分母添加了指數(shù)衰減因子

Attention model: 源自人類的視覺注意力機制胚膊,在觀察一幅圖像時對信息量高的高質(zhì)量區(qū)域投入更多的注意力眷蜓。通俗地講就是通過對信息進行篩選分瘾,為質(zhì)量高的信息分配更多的注意力也就是權(quán)重。
user based CF:基于物品的協(xié)同過濾吁系,推薦系統(tǒng)的一種傳統(tǒng)算法芹敌,通過用戶行為計算物品相似度,為用戶推薦和用戶歷史商品相似的物品垮抗。

介紹

傳統(tǒng)的基于物品的協(xié)同過濾都是通過一些通用的相似度算法計算物品相似度氏捞,如cosine等。本文提出了一種定制的注意力框架來學(xué)習(xí)物品之間的相似度冒版。對于目標(biāo)商品液茎,根據(jù)用戶的歷史商品來預(yù)測用戶對目標(biāo)商品的評分時,用戶的歷史商品所貢獻的信息量是不同的,在傳統(tǒng)算法時捆等,預(yù)測公式采用相似度權(quán)重以及歷史商品評分的加權(quán)和滞造。本文對傳統(tǒng)的相似度權(quán)重進行創(chuàng)新,設(shè)計了一種利用注意力機制的網(wǎng)絡(luò)結(jié)構(gòu)栋烤,通過實驗證明可以更好的學(xué)習(xí)商品之間的相似度谒养,提高推薦準(zhǔn)確率。

1. 模型設(shè)計

預(yù)測模型


本文設(shè)計了兩種注意力模型明郭,第一種买窟,將歷史商品特征和目標(biāo)商品特征堆疊;第二種薯定,求歷史商品特征和目標(biāo)商品特征的點積始绍。注意力網(wǎng)絡(luò)采用MLP結(jié)構(gòu)。

損失函數(shù):

結(jié)構(gòu)圖
NAIS結(jié)構(gòu)圖

2. 實驗設(shè)計

參數(shù)設(shè)置大多數(shù)基本和代碼中的默認設(shè)置差不多话侄,實現(xiàn)過程中亏推,作者發(fā)現(xiàn)tensoflow在輸入數(shù)據(jù)的時候,用戶數(shù)據(jù)的歷史商品長度應(yīng)該是相同的年堆,所以作者設(shè)計了一種根據(jù)用戶來分組的mini-batch方法吞杭,也就是一個用戶的所有正樣本和負樣本構(gòu)成一個mini-batch,這樣用戶矩陣(b变丧,n)的維度n即為用戶的歷史商品數(shù)量芽狗,這樣設(shè)計有兩個好處,一個是不需要填充數(shù)據(jù)锄贷,二是免去了batch-size參數(shù)的考慮译蒂。

另一個需要注意的是曼月,作者采用的數(shù)據(jù)是另一篇論文已經(jīng)預(yù)處理好的數(shù)據(jù)谊却,具體論文地址,這些數(shù)據(jù)的測試集哑芹,每一個目標(biāo)用戶商品對炎辨,有99個負樣本。

數(shù)據(jù)集
實驗結(jié)果
參數(shù)分析
  • embedding size


作者解釋了在嵌入維度為8時聪姿,提出的算法效果要弱于基線算法的原因:
在嵌入維度過小時碴萧,非線性模型要比線性模型又更強的表示性。這個我不太理解末购?

  • weight_size(attention 網(wǎng)絡(luò)的隱藏層維度)


證明了在attention網(wǎng)絡(luò)嵌入層維度小的時候基于點乘的算法效果要好于基于concat的算法破喻,用
p_i\odot q_j
來學(xué)習(xí)
p_i^Tq_j
的效果要更好一些,隨著維度逐漸增大盟榴,基于concat的算法對
p_i
q_j
的特征表示的豐富逐漸補償了結(jié)構(gòu)上的不足曹质。

  • 平滑因子\beta

可以看到作者提出這個參數(shù)的原因,當(dāng)參數(shù)設(shè)置為1時,加入attention網(wǎng)絡(luò)的算法羽德,效果相當(dāng)糟糕几莽。作者認為是用戶歷史商品長度變化太大的原因,當(dāng)attention網(wǎng)絡(luò)用于文本或圖像時宅静,注意力因子長度十分固定章蚣,在計算softmax時不會出現(xiàn)這樣的問題。

3. 討論

  • 實時個性化

本文設(shè)計的算法在支持實時更新用戶特征上有著出色的表現(xiàn)姨夹。對于實時個性化纤垂,需要實時監(jiān)控用戶行為,用戶在對某個商品交互后匀伏,實施推薦系統(tǒng)同時更新用戶的推薦列表洒忧。因為重新訓(xùn)練整個模型不現(xiàn)實,一般都選擇更新模型參數(shù)够颠,然而因為用戶行為可能并行發(fā)生熙侍,更新模型的固有參數(shù)會發(fā)生沖突,雖然可以通過分布式結(jié)構(gòu)來解決但是分布式往往需要更多的消耗履磨。本文的算法在實時問題上有更好的解決方式蛉抓,首先用戶的特征可以直接通過加法更新,時間消耗基本是常數(shù)級的剃诅。比如系統(tǒng)檢測到用戶u消耗了商品t巷送,這時用戶u對于商品i的預(yù)測為原預(yù)測值\hat{y_{ui}}的基礎(chǔ)上加上a_{ij}p_i^Tq_t,根本不需要重新計算模型矛辕。

  • 兩種注意力模型結(jié)構(gòu)的選擇

從公式中可以看出笑跛,本文設(shè)計了兩種不同的注意力模型結(jié)構(gòu),一種是直接將p_iq_j直接連接在一起聊品,組成不同shape的特征矩陣飞蹂,另一種則是計算p_iq_j的點乘。前者保留了商品特征的原始結(jié)構(gòu)翻屈,但是因為矩陣的結(jié)構(gòu)發(fā)生變化可能導(dǎo)致網(wǎng)絡(luò)難以收斂陈哑。后者的矩陣結(jié)構(gòu)滿足學(xué)習(xí)的目標(biāo),但是丟失了學(xué)習(xí)的商品特征伸眶。兩種結(jié)構(gòu)各有利弊惊窖,也是作者設(shè)計兩種模型的原因。

4. 結(jié)論

本文出發(fā)點為用戶的歷史行為商品對目標(biāo)商品預(yù)測做出的貢獻是不同的厘贼,本文接下來的工作就是將NAIS結(jié)合更加先進的深度模型提高推薦準(zhǔn)確率界酒,以及考慮推薦系統(tǒng)的可解釋性。

相關(guān)工作

這部分作者介紹的很迷嘴秸,花了幾大段寫推薦系統(tǒng)的任務(wù)從顯示評分到隱式評分毁欣,分別采用不同的度量方式售担。介紹了一個最先進的排序方法,是對抗個性化排序署辉。然后介紹了深度學(xué)習(xí)在推薦系統(tǒng)的應(yīng)用族铆,分了兩個部分,一個是學(xué)習(xí)特征表示哭尝,另一個是學(xué)習(xí)scoring function哥攘。關(guān)于第二種介紹了三篇比較先進的論文。最后討論了另一個采用attention的論文材鹦,這篇論文是基于用戶的逝淹,并且強調(diào)了自己論文的亮點,想出了一個解決softmax計算大規(guī)模概率分布的方法桶唐。

代碼分析

定義參數(shù)

  • path : 數(shù)據(jù)路徑
  • dataset: 選擇的數(shù)據(jù)集栅葡,pinterest 還是movielens
  • pretrain: 0: No pretrain, 1: Pretrain with updating FISM variables, 2:Pretrain with fixed FISM variables.
  • verbose: Interval of evaluation
  • batch_choice: user: generate batches by user, fixed:batch_size: generate batches by batch size
  • epochs: 周期數(shù)
  • weight_size: weight size
  • embed_size: Embedding size
  • data_alpha: Index of coefficient of embedding vector
  • regs: Regularization for user and item embeddings.
  • alpha: Index of coefficient of embedding vector
  • train_loss: Caculate training loss or nor
  • beta: Index of coefficient of sum of exp(A)
  • num_neg: Number of negative instances to pair with a positive instance.
  • lr: learning rate 學(xué)習(xí)速率
  • activation: Activation for ReLU, sigmoid, tanh. 激活函數(shù)
  • algorithm: 0 for NAIS_prod, 1 for NAIS_concat ,attention 網(wǎng)絡(luò)算法選擇

定義輸入接口

 def _create_placeholders(self):
        with tf.name_scope("input_data"):
            self.user_input = tf.placeholder(tf.int32, shape=[None, None])  #the index of users
            self.num_idx = tf.placeholder(tf.float32, shape=[None, 1])  #the number of items rated by users
            self.item_input = tf.placeholder(tf.int32, shape=[None, 1])   #the index of items
            self.labels = tf.placeholder(tf.float32, shape=[None,1])    #the ground truth
  • user_input: 用戶的index尤泽, shape=[None, None]
  • num_idx: 每個用戶評分的物品數(shù)量欣簇,shape=[None, 1],用來控制衰減參數(shù)\beta
  • item_input: 所有物品的index坯约, shape=[None, 1]
  • labels: 標(biāo)簽熊咽, shape=[None,1]

創(chuàng)建變量

為了便于理解,簡化了參數(shù):
batch_size:b
embedding size: e
weight size: w attention 網(wǎng)絡(luò)嵌入
Q_:[N+1, e] 用來訓(xùn)練歷史商品的嵌入
Q:[N,e] 用來訓(xùn)練目標(biāo)商品的嵌入

attention 網(wǎng)絡(luò)的權(quán)重變量

b: [1, w]
h:[w,1]
dot product algo: W: [e,w]
concat product algo: W: [2e, w]

def _create_variables(self):
        with tf.name_scope("embedding"):  # The embedding initialization is unknown now
            trainable_flag = (self.pretrain!=2)
            self.c1 = tf.Variable(tf.truncated_normal(shape=[self.num_items, self.embedding_size], \
                mean=0.0, stddev=0.01), \
                name='c1', dtype=tf.float32, trainable=trainable_flag)
            self.c2 = tf.constant(0.0, tf.float32, [1, self.embedding_size], name='c2')
            self.embedding_Q_ = tf.concat([self.c1, self.c2], 0, name='embedding_Q_')
            self.embedding_Q = tf.Variable(tf.truncated_normal(shape=[self.num_items, self.embedding_size], mean=0.0, stddev=0.01), \
                name='embedding_Q', dtype=tf.float32,trainable=trainable_flag)
            self.bias = tf.Variable(tf.zeros(self.num_items),name='bias',trainable=trainable_flag)

            # Variables for attention
            if self.algorithm == 0:
                self.W = tf.Variable(tf.truncated_normal(shape=[self.embedding_size, self.weight_size], mean=0.0, \
                    stddev=tf.sqrt(tf.div(2.0, self.weight_size + self.embedding_size))),name='Weights_for_MLP', dtype=tf.float32, trainable=True)
            else:    
                self.W = tf.Variable(tf.truncated_normal(shape=[2*self.embedding_size, self.weight_size], mean=0.0, \
                    stddev=tf.sqrt(tf.div(2.0, self.weight_size + (2*self.embedding_size)))),name='Weights_for_MLP', dtype=tf.float32, trainable=True)
            self.b = tf.Variable(tf.truncated_normal(shape=[1, self.weight_size], mean=0.0, \
                stddev=tf.sqrt(tf.div(2.0, self.weight_size + self.embedding_size))),name='Bias_for_MLP', dtype=tf.float32, trainable=True)
            self.h = tf.Variable(tf.ones([self.weight_size, 1]), name='H_for_MLP', dtype=tf.float32)

計算inference


第一步:在計算輸出之前需要先定義兩個嵌入矩陣embedding_q_embedding_q分別是從嵌入變量矩陣embedding_Q_embedding_Q中查找出來的闹丐,分別對應(yīng)著用戶的歷史商品嵌入和目標(biāo)商品嵌入横殴。

with tf.name_scope("inference"):
            self.embedding_q_ = tf.nn.embedding_lookup(self.embedding_Q_, self.user_input) # (b, n, e)
            self.embedding_q = tf.nn.embedding_lookup(self.embedding_Q, self.item_input) # (b, 1, e)

第二步:將這兩個嵌入矩陣輸入到attention網(wǎng)絡(luò)中,求歷史商品的加權(quán)和卿拴。

if self.algorithm == 0:
                self.embedding_p = self._attention_MLP(self.embedding_q_ * self.embedding_q) #(b,e)
            else:
                n = tf.shape(self.user_input)[1]
                self.embedding_p = self._attention_MLP(tf.concat([self.embedding_q_, \
tf.tile(self.embedding_q, tf.stack([1,n,1]))],2))
attention network



作者定義了一個attention函數(shù)衫仑,輸入是矩陣q_和q的concat或者點積。輸出矩陣每行結(jié)果為為
\sum a_{ij}q_j

代碼中第一部分求
f(p_i,q_j)
, 第二部分求添加指數(shù)衰減的的softmax

def _attention_MLP(self, q_):
       with tf.name_scope("attention_MLP"):
            b = tf.shape(q_)[0]
            n = tf.shape(q_)[1]
            r = (self.algorithm + 1)*self.embedding_size

            MLP_output = tf.matmul(tf.reshape(q_,[-1,r]), self.W) + self.b #(b*n, e or 2*e) * (e or 2*e, w) + (1, w)
            if self.activation == 0:
                MLP_output = tf.nn.relu( MLP_output )
            elif self.activation == 1:
                MLP_output = tf.nn.sigmoid( MLP_output )
            elif self.activation == 2:
                MLP_output = tf.nn.tanh( MLP_output )

            A_ = tf.reshape(tf.matmul(MLP_output, self.h),[b,n]) #(b*n, w) * (w, 1) => (None, 1) => (b, n)

            # softmax for not mask features
            exp_A_ = tf.exp(A_)
            num_idx = tf.reduce_sum(self.num_idx, 1)
            mask_mat = tf.sequence_mask(num_idx, maxlen = n, dtype = tf.float32) # (b, n)
            exp_A_ = mask_mat * exp_A_
            exp_sum = tf.reduce_sum(exp_A_, 1, keep_dims=True)  # (b, 1)
            exp_sum = tf.pow(exp_sum, tf.constant(self.beta, tf.float32, [1]))

            A = tf.expand_dims(tf.div(exp_A_, exp_sum),2) # (b, n, 1)

            return tf.reduce_sum(A * self.embedding_q_, 1)      

其中在計算注意力softmax函數(shù)的分母的代碼有些復(fù)雜堕花,其中有個mask的運用
tf.sequence_mask( lengths, maxlen=None, dtype=tf.bool, name=None )

tf.sequence_mask([1, 3, 2], 5)  # [[True, False, False, False, False],
                                #  [True, True, True, False, False],
                                #  [True, True, False, False, False]]

tf.sequence_mask([[1, 3],[2,0]])  # [[[True, False, False],
                                  #   [True, True, True]],
                                  #  [[True, True, False],
                                  #   [False, False, False]]]

第三步:求output

self.embedding_q = tf.reduce_sum(self.embedding_q, 1) #(b,e)
self.bias_i = tf.nn.embedding_lookup(self.bias, self.item_input)
self.coeff = tf.pow(self.num_idx, tf.constant(self.alpha, tf.float32, [1]))
self.output = tf.sigmoid(self.coeff *tf.expand_dims(tf.reduce_sum(self.embedding_p*self.embedding_q, 1),1) + self.bias_i)

科普
tf.tile( input, multiples, name=None ):將input按照維度乘以multiples文狱,其實就是按照維度擴展
tf.math.pow( x, y, name=None ):指數(shù)冪
tf.stack( values, axis=0, name='stack' ):按axis維度疊加
代碼舉例:

a = [[[1,1,1]]] #(1,1,3)
b = [[[1,1,1],[1,1,1]]] #(1,2,3)
c = [[[2],[2]]] #(1,2,1)
ta = tf.placeholder(shape=(1,1,3),dtype=tf.float32)
tb = tf.placeholder(shape=(1,2,3),dtype=tf.float32)
tc = tf.placeholder(shape=(1,2,1),dtype=tf.float32)
with tf.Session() as sess:
    print(sess.run(tf.reduce_sum(tb,1), feed_dict={tb:b,tc:c}),'\n')
    print(sess.run(tf.tile(ta, tf.stack([1,2,1])),feed_dict={ta:a,tb:b,tc:c}),'\n')
    print(sess.run(tf.stack([1,2,1]), feed_dict={tb:b,tc:c}),'\n')
#results
[[2. 2. 2.]] 

[[[1. 1. 1.]
  [1. 1. 1.]]] 

[1 2 1] 

計算Loss

 def _create_loss(self):
        with tf.name_scope("loss"):
            self.loss = tf.losses.log_loss(self.labels, self.output) + \
                        self.lambda_bilinear * tf.reduce_sum(tf.square(self.embedding_Q)) + \
                        self.gamma_bilinear * tf.reduce_sum(tf.square(self.embedding_Q_)) + \
                        self.eta_bilinear * tf.reduce_sum(tf.square(self.W))

構(gòu)建優(yōu)化器

 def _create_optimizer(self):
        with tf.name_scope("optimizer"):
            self.optimizer = tf.train.AdagradOptimizer(learning_rate=self.learning_rate,  \
initial_accumulator_value=1e-8).minimize(self.loss)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市航徙,隨后出現(xiàn)的幾起案子如贷,更是在濱河造成了極大的恐慌陷虎,老刑警劉巖到踏,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異尚猿,居然都是意外死亡窝稿,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進店門凿掂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來伴榔,“玉大人纹蝴,你說我怎么就攤上這事∽偕伲” “怎么了塘安?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長援奢。 經(jīng)常有香客問我兼犯,道長,這世上最難降的妖魔是什么集漾? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任切黔,我火速辦了婚禮,結(jié)果婚禮上具篇,老公的妹妹穿的比我還像新娘纬霞。我一直安慰自己,他們只是感情好驱显,可當(dāng)我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布诗芜。 她就那樣靜靜地躺著,像睡著了一般埃疫。 火紅的嫁衣襯著肌膚如雪绢陌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天熔恢,我揣著相機與錄音脐湾,去河邊找鬼。 笑死叙淌,一個胖子當(dāng)著我的面吹牛秤掌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鹰霍,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼闻鉴,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了茂洒?” 一聲冷哼從身側(cè)響起孟岛,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎督勺,沒想到半個月后渠羞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡智哀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年次询,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瓷叫。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡屯吊,死狀恐怖送巡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情盒卸,我是刑警寧澤骗爆,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站蔽介,受9級特大地震影響淮腾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜屉佳,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望武花。 院中可真熱鬧圆凰,春花似錦、人聲如沸专钉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽浮还。三九已至,卻和暖如春叔营,著一層夾襖步出監(jiān)牢的瞬間十办,已是汗流浹背区赵。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工浪南, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留笼才,地道東北人。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓络凿,卻偏偏與公主長得像骡送,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子絮记,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內(nèi)容