#論文を読む# [シーケンス推薦] グラフニューラルネットワークによるセッションベースの推薦

#論文題名: [Sequence Recommendation] SR-GNN: Session-based Recommendation with Graph Neural Networks (SR-GNN: Session-based Graph Neural Network Recommendation) #論文アドレス: https://arxiv.org/abs/1811.00855
#
論文ソース コード オープン ソース アドレス: https://github.com/CRIPAC-DIG/SR-GNN
#論文の会議: AAAI 2019
#論文の単位: 中国科学院
ここに画像の説明を挿入

1.はじめに

SR-GNN は、中国科学院によって提案されたセッション シーケンス モデリングに基づくレコメンデーション システムです. ここでいうセッションとは、ユーザーのやり取りを指します (各セッションはユーザーの行動と対応するサービスを表すため、各ユーザー レコードは次のように構築されます)。図)、ここで言及されているセッション シーケンスは、過去のある期間のユーザーのインタラクション シーケンスを具体的に表す必要があります。セッションベースの推奨は、一般的に使用される推奨方法であり、一般的に使用されるセッションの推奨には、再帰型ニューラル ネットワークとマルコフ連鎖が含まれます。ただし、これらの一般的に使用されるセッションの推奨方法には、次の 2 つの欠点があります。

  • セッション内のユーザー アクションの数が非常に限られている場合 (つまり、比較的少ない場合)、このメソッドでユーザーの動作表現をキャプチャすることは困難です。たとえば、会話型レコメンデーション モデリングに RNN ニューラル ネットワークを使用する場合、前のシーケンスのアクション アイテムが少ない場合、最後の出力がレコメンデーション アイテムを生成するときに、レコメンデーションの結果はあまり正確ではありません。
  • 以前の作業によると、以前のアイテムの転送モードはセッションの推奨において非常に重要な機能ですが、RNN とマルコフ プロセスは、セッション内の他のアイテムを無視しながら、2 つの隣接するアイテムの単一のアイテム転送ベクトルのみをモデル化します。[RNN 法は全体像を欠き、単一項目の伝達ベクトルしか構築せず、情報を表現する能力が十分でないことを意味します]

まず, 論文を解釈するとき, シーケンスリコールの入力と出力が何であるかを理解する必要があります. 一般的に言えば, シーケンスリコールの入力はユーザーの行動シーケンス (ユーザーが操作したアイテム ID のリスト) です.予測する必要があるのは、ユーザーの次の瞬間です. クリックされた上位k項目. 実際の操作プロセスでは、通常、ユーザーの行動シーケンスをユーザーの特性ベクトルに抽出し、次にいくつかの ANN メソッドを使用してアイテムのベクトルをすばやく取得し、ユーザーの特性ベクトルに最も類似したトップを除外します -kアイテム。以下に紹介する SR-GNN は、上記の 2 つのプロセスを完了します。

1.1 モデル構造

ここでは、次のように、入力ユーザーの行動シーケンスからユーザーのベクトル表現を抽出していることがわかります。

  • 1. ユーザーの一連の行動をセッション グラフに構築する
  • 2. GNN を使用して、取得したセッション グラフの特徴を抽出し、各アイテムのベクトル表現を取得します。
  • 3. GNN がセッション グラフを抽出した後、ユーザーのベクトル表現を取得するために、すべてのアイテムのベクトル表現を融合する必要があります. ユーザーのベクトル表現を取得した後、次の考え方に従ってモデルをトレーニングできます
    。シーケンスリコール / モデルが検証されたので、これらの 3 つのポイントがどのように展開するかについて説明しましょう

1.2 セッション グラフの作成

ここではまず利用者の行動系列に従ってグラフを構成する必要があります. ここでは各利用者の行動系列ごとにグラフを構成する必要があります. 構成方法も非常に簡単です. ここでは有向グラフとみなします. v 2 の場合v_2v2そしてv1 v_1v1ユーザーの行動シーケンスで隣接し、v 2 v_2v2v 1 v_1v1その後、v 2 v_2からラインを接続します。v2v1 v_1v1このルールに従って、グラフを作成できます。(以下は論文に掲載されているサンプル画像です)

ここで各ユーザーの行動がグラフにパッケージ化されている理由は、ユーザーと製品のやり取りをすべてグラフに配置すると、ユーザー固有の関心を捉える際に混乱を招くため、個別に構築して構築する必要があるためです。この方法の利点は、その後のトレーニング中に、サンプルの各行をグラフに構築できることです。これは非常に便利です。

構成が完了したら、変数を使用してこの画像を保存する必要があります。ここでは、A s A_sを使用します。s合成の結果を表すために、この行列は a ( d , 2 d ) (d,2d)( d 2 d )、これは a( d , d ) (d,d)( d d )外出行列と a( d , d ) (d,d)( d d ) Outing 行列の Incoming 行列については、ノードから外側に伸びるエッジの数を直接カウントします.ノードから外側に伸びるノードの数が 1 より大きい場合は、正規化操作を実行します (たとえば、ノード v 2v_2v2拡張 2 ノードv 3 、 v 4 v_3、v_4v3v4、次にノードv 2 v_2v2ノードへv 3 、 v 4 v_3、v_4v3v4値はすべて 0.5 です)。入ってくる行列は同じ

1.3 GNN による Item のベクトル表現の学習

この部分では主に、アイテムのベクトル表現をグラフから学習する方法に焦点を当てます。ここでは、vit v_{i}^{t}を設定します。vtt 回目の GNN 反復後のアイテム i のベクトル表現を表します。A s , i ∈ R 1 × 2 n A_{s,i} \in R^{1 \times 2n}s , iεR1 × 2 n はA s A_{s}を意味しますsマトリックスのii行i はiiを表しますアイテムiに関連する近隣情報次に、式 (1) を使用して、主に行列A s , i A_{s,i} をs , iおよびユーザーシーケンス[ v 1 t − 1 , . . , vnt − 1 ] T ∈ R n × d [v_{1}^{t-1},...,v_{n}^{t-1} ]^{T} \in R^{n \times d}[ v1t 1... ,vnt 1]TεRn × dの掛け算は集計されますが、ここでの式は厳密には書かれていないことに注意してください. 実際には、2 つのR 1 × 2 n と R n × d R^{1 \times 2n} と R^{ n \ 回 d}R1 × 2nR _n × d行列は直接乗算することはできません. コードの実装では、行列 A を in と out の 2 つの行列に分割し、ユーザーの行動シーケンスと乗算します。

as , it = As , i [ v 1 t − 1 , . . . , vnt − 1 ] TH + b (1) a_{s,i}^{t}=A_{s,i}[v_{1}^{t-1},...,v_{n}^{ t-1}]^{T}\textbf{H}+b \tag{1}as t=s , i[ v1t 1... ,vnt 1]T H+b( 1 )

'''
A : [batch,n,2n] 图的矩阵
hidden : [batch,n,d] 用户序列的emb
in矩阵:A[:, :, :A.size(1)]
out矩阵:A[:, :, A.size(1):2 * A.size(1)]
inputs : 就是公式1中的 a 
'''
input_in = paddle.matmul(A[:, :, :A.shape[1]], self.linear_edge_in(hidden)) + self.b_iah
input_out = paddle.matmul(A[:, :, A.shape[1]:], self.linear_edge_out(hidden)) + self.b_ioh
# [batch_size, max_session_len, embedding_size * 2]
inputs = paddle.concat([input_in, input_out], 2)

式 (1) の as を取得すると、a_{s,i}^{t}as tその後、2 つの中間変数zs 、 it 、 rs 、 it z_{s,i}^{t},r_{s,i}^{t} が式 (2)( 3) に従って計算されます。s trs tzs 、 it 、 rs 、 it z_{s,i}^{t},r_{s,i}^{t} と考えて、 LSTM と簡単に比較できます。s trs tそれらは忘却ゲートと更新ゲートです

zs , it = σ ( W zas , it + U zvit − 1 ) ∈ R d (2) z_{s,i}^{t}=\sigma(W_{z}a_{s,i}^{t} +U_{z}v_{i}^{t-1}) \in R^{d} \tag{2}s t=s ( Was t+vt 1)εRd( 2 )

rs , it = σ ( W ras , it + U rvit − 1 ) ∈ R d (3) r_{s,i}^{t}=\sigma(W_{r}a_{s,i}^{t} +U_{r}v_{i}^{t-1}) \in R^{d} \tag{3}rs t=s ( Wras t+rvt 1)εRd( 3 )

ここで、 zs 、 it 、 rs 、 it z_{s,i}^{t},r_{s,i}^{t} を計算していることに注意してください。s trs tロジックはまったく同じで、パラメータの重みが異なるだけで、
式 (2) (3) の中間変数を取得した後、次の更新ゲートの更新の特性を式 (4) によって計算します。そして式(5)に従って最終結果を得る

vit ∼ = tanh ( W oas , it + U o ( rs , it ⊙ vit − 1 ) ) ∈ R d (4) {v_{i}^{t}}^{\sim}=tanh(W_{o} a_{s,i}^{t}+U_{o}(r_{s,i}^{t} \odot v_{i}^{t-1})) \in R^{d}\tag{ 4}vt=英語( W _oas t+o( rs tvt 1)))εRd( 4 )

vit = ( 1 − zs , it ) ⊙ vit − 1 + zs , it ⊙ vit ∼ ∈ R d (5) v_{i}^{t}=(1-z_{s,i}^{t}) \ odot v_{i}^{t-1} + z_{s,i}^{t} \odot {v_{i}^{t}}^{\sim} \in R^{d} \tag{5 }vt=( 1s t)vt 1+s tvtεRd( 5 )

ここで、式 (4) が実際に t 番目の GNN レイヤーの Update 部分、つまりvit ∼ {v_{i}^{t}}^{\sim} を計算していることがわかります。vt、式 (5) の忘却ゲート zs を渡すと、z_{s,i}^{t}s tt 番目の GNN 更新を制御するには、vit − 1 v_{i}^{t-1}vt 1${v_{i} {t}} {\sim} $の割合。これで GNN 部分のアイテムの表現学習は完了です

ここでコードを書くとき、式 (3)(4)(5) に注意を払う必要があります。注意深く観察します。 for as , it , vit − 1 a_{s,i}^{t},v_{i} ^{t-1}as tvt 1この 2 つの変数に関する限り、各変数は 3 つの行列で乗算されます. ここでの計算ロジックは同じであるため、W a , U v Wa,UvUvは計算単位と見なされます.式 (3)(4)(5) では、そのような操作が 1 つ含まれているため、これら 3 つの操作をまとめて、結果を 3 つの部分に分割できます.3 つの式を元に戻すと、関連するコードは次のとおりです

'''
inputs : 公式(1)中的a
hidden : 用户序列,也就是v^{t-1}
这里的gi就是Wa,gh就是Uv,但是要注意这里不该是gi还是gh都包含了公式3~5的三个部分
'''

# gi.size equals to gh.size, shape of [batch_size, max_session_len, embedding_size * 3]

gi = paddle.matmul(inputs, self.w_ih) + self.b_ih
gh = paddle.matmul(hidden, self.w_hh) + self.b_hh
# (batch_size, max_session_len, embedding_size)
i_r, i_i, i_n = gi.chunk(3, 2)   # 三个W*a
h_r, h_i, h_n = gh.chunk(3, 2)   # 三个U*v
reset_gate = F.sigmoid(i_r + h_r)  #公式(2)
input_gate = F.sigmoid(i_i + h_i)  #公式(3)
new_gate = paddle.tanh(i_n + reset_gate * h_n)  #公式(4)
hy = (1 - input_gate) * hidden + input_gate * new_gate  # 公式(5)

1.4 ユーザーベクトル表現の生成 (セッション埋め込みの生成)

GNN を介してアイテムの埋め込み表現を取得した後、作業の半分以上が完了し、残りは、ユーザー シーケンスの複数のアイテムの埋め込み表現を埋め込み表現のシーケンス全体に融合することです。

ここで、SR-GNN は最初に Attention メカニズムを使用して、シーケンスvn ( s 1 ) v_{n}(s_1)の最後のアイテムのシーケンス内の各アイテムを取得します。vn( s1)の注意力を計算し、加重して合計すると、具体的な計算プロセスは次のようになります。

ai = q T σ ( W 1 vn + W 2 vi + c ) ∈ R 1 sg = ∑ i = 1 naiv I ∈ R d (6) a_{i}=\textbf{q}^{T} \sigma( W_{1}v_{n}+W_{2}v_{i}+c) \in R^{1} \tag{6} \\ s_{g}= \sum_{i=1}^{n} a_{i}v_{I}\in R^{d}a=qT σ(W1vn+W2v+c )εR1sg=私は= 1nav私はεRd( 6 )

get sg s_gでsgその後、sg s_gを実行します。sgシーケンス内の最後のアイテム情報と組み合わせて、最終シーケンスの埋め込み表現を取得します

sh = W 3 [s 1 ; sg ] ∈ R d (7) s_h = W_{3}[ s_1 ;s_g] \in R^{d} \tag{7}s時間=W3[1;sg]εRd( 7 )

'''
seq_hidden : 序列中每一个item的emb
ht : 序列中最后一个item的emb,就是公式6~7中的v_n(s_1)
q1 : 公式(6)中的 W_1 v_n
q2 : 公式(6)中的 W_2 v_i 
alpha : 公式(6)中的alpha
a : 公式(6)中的s_g
'''
seq_hidden = paddle.take_along_axis(hidden,alias_inputs,1)
# fetch the last hidden state of last timestamp
item_seq_len = paddle.sum(mask,axis=1)
ht = self.gather_indexes(seq_hidden, item_seq_len - 1)
q1 = self.linear_one(ht).reshape([ht.shape[0], 1, ht.shape[1]])
q2 = self.linear_two(seq_hidden)

alpha = self.linear_three(F.sigmoid(q1 + q2))
a = paddle.sum(alpha * seq_hidden * mask.reshape([mask.shape[0], -1, 1]), 1)
user_emb = self.linear_transform(paddle.concat([a, ht], axis=1))

ここまでで、SR-GNN のユーザー ベクター生成が完了しました。あとは、従来の順次呼び出し方式に従って実行できます。

参考:https://aistudio.baidu.com/aistudio/projectdetail/5313491

おすすめ

転載: blog.csdn.net/CRW__DREAM/article/details/128609712
おすすめ