ATRank - lshhhhh/deep-learning-study GitHub Wiki
논문: https://arxiv.org/pdf/1711.06632.pdf
코드: https://github.com/jinze1994/ATRank
- 이 논문은 "user - object 간의 binary 관계"와 관련하여 설명될 수 있는 일반적인 user behaviors에 초점을 맞추고 있다.
- User behavior는 다음과 같이 tuple로 나타낸다.
- {a=action(behavior type), o=object, t=timestamp}
- a, t는 atomic feature, o는 object에 해당하는 features
- Behavior type:
item,coupon,query(우리의 경우에는clk,conv로 둘 수 있다.)
- Amazon
- http://jmcauley.ucsd.edu/data/amazon/
- 각 user당 behavior sequence (b1, b2, ..., bk, ..., bn)
- Input: Sequence b1 ~ bk
- Label: bk+1 (k+1을 예측하기)
- Train: k = 1, 2, ..., n-2
- Test: k = n-1
- Model input 만드는 코드는
multi/input.py참조
- User U = {(aj, oj, tj) | j = 1, 2, ..., m}
나누기
-
U를 behavior type에 따라 partition (bg: behavior groups)
- G = {bg1, bg2, ... , bgn}
- U = Ui=1n bgi
Embedding하기
-
같은 bgi에 속한 tuple (aj, oj, tj)들은 같은 embedding building block(fi)으로 embed
- uij = fi(aj, oj, tj)
i: bg의 index
j: bgi에 속한 tuple index (j번째 tuple)
- uij = fi(aj, oj, tj)
-
fi
- oj: raw features를 embedding 후, concat
-
aj, tj: Temporal(시간) & Behavior Type(행동) Encoding
- Temporal Encoding (tj)
- RNN, CNN을 사용하지 않고도 sequence 시간 정보를 보존하기 위해서는 tj를 잘 encoding해야한다.
- 연속적인 time feature를 바로 encoding하는 것은 어려우므로,
- -> Bucketize
Continuous -> Categorial
ex) [0, 1), [1, 2), ..., [2k, 2k+1), .. -> 0, 1, ..., k, ... - -> Categorial feature lookups (one-hot encoding)
- -> Bucketize
- Behavior Type Encoding (aj)
- encoding lookup (embedding)
- Temporal Encoding (tj)
- uij = embio(oj) + lookupit(bucketizei(tj)) + lookupia(aj)
= shape(feature embedding dim)
-
이 layer의 최종 output: B = {ubg1, ubg2, ... , ubgn}
(이때, ubgi = concatj(uij) = shape(group i의 원소(behavior) 수,group i의 feature embedding dim)) -
코드 (
multi/model.py)hist_i_emb = tf.concat([ # a tf.nn.embedding_lookup(item_emb_w, self.hist_i_id), # o tf.nn.embedding_lookup(shop_emb_w, tf.gather(item_feat_shop_list, self.hist_i_id)), tf.nn.embedding_lookup(cate_emb_w, tf.gather(item_feat_cate_list, self.hist_i_id)), tf.nn.embedding_lookup(brand_emb_w, tf.gather(item_feat_brand_list, self.hist_i_id)), tf.nn.embedding_lookup(action_emb_w, self.hist_i_act), # t tf.one_hot(self.hist_i_time, 12, dtype=tf.float32), ], 2) hist_q_emb = tf.concat([ # a tf.nn.embedding_lookup(query_emb_w, self.hist_q_id), # t tf.one_hot(self.hist_q_time, 12, dtype=tf.float32), ], 2) hist_c_emb = tf.concat([ # a tf.nn.embedding_lookup(coupon_emb_w, self.hist_c_id), # o tf.nn.embedding_lookup(shop_emb_w, tf.gather(coupon_feat_shop_list, self.hist_c_id)), tf.nn.embedding_lookup(cate_emb_w, tf.gather(coupon_feat_cate_list, self.hist_c_id)), # t tf.one_hot(self.hist_c_time, 12, dtype=tf.float32), ], 2) -
Embedding Sharing
- Behavior group 간에 embedding sharing을 하는 경우도 있다.
- 예를 들어, item, coupon behavior의 objects에서 같은 의미를 가지는 것들의 embedding은 sharing이 가능하다.
- 샵 정보, 카테고리 정보:
shop_emb_w,cate_emb_w
- 샵 정보, 카테고리 정보:
- 하지만 temporal encoding lookup과 같은 경우에는 sharing하지 않는다. Behavior마다 time이 의미가 다를 수 있기 때문.
-
B는 ubgi가 i마다 길이가 다르므로, 모두 같은 길이 vector로 projection한다.
-
S = concat(0)(FM1(ubg1), FM2(ubg2), ..., FMn(ubgn)) (FMi: group i의 projection 함수)
-
코드 (
multi/model.py)i_layer = Dense(self.config['hidden_units'], name='i_layer') q_layer = Dense(self.config['hidden_units'], name='q_layer') c_layer = Dense(self.config['hidden_units'], name='c_layer') hist_i_emb = i_layer(hist_i_emb) hist_q_emb = q_layer(hist_q_emb) hist_c_emb = c_layer(hist_c_emb) hist_emb = tf.concat([hist_i_emb, hist_q_emb, hist_c_emb], 1) -
Sk = FPk(S) (FPk: k번째 semantic space를 만드는 projection 함수)
- 코드 (
multi/model.py#L421)num_blocks = k
with tf.variable_scope("all", reuse=reuse): with tf.variable_scope("user_hist_group"): for i in range(num_blocks): with tf.variable_scope("num_blocks_{}".format(i)):
- 코드 (
-
결론적으로,
- ubgi = shape(
group i의 원소(behavior) 수,group i의 feature embedding dim) -
Sk = shape(
모든 일어난 behavior 수,k번째 semantic space dim)
- ubgi = shape(
각 semantic space 안에서의 behavior 관계 capture
- Self-Attention
- Key = Query = Value:
user_history_emb(=hist_emb)
- Key = Query = Value:
- 코드 (
multi/model.py#L423) - 참고
- Attention?
- A Brief Overview of Attention Mechanism
- 논문 Transformer: Attention is all you need 번역
- Query, Key, Value?
Query가 어떤 key와 관련되어 있는지 찾기 위해서 모든 key들과 연산한다. 실제 연산을 보면 query와 key를 dot-product한 뒤 softmax를 취하는데, 의미하는 것은 하나의 query가 모든 key들과 연관성을 계산한뒤 그 값들을 확률 값으로 만들어 주는 것이다. 따라서 query가 어떤 key와 높은 확률로 연관성을 가지는지 알게 되는 것이다. 이제 구한 확률값을 value에 곱해서 value에 대해 scaling한다고 생각하면 된다.
- Attention?
Embedding된 user behavior model을 neural network에 적용
-
Vanilla Attention
- Key = Value:
user_history_emb(=hist_emb) - Query:
behavior_type_j_emb(=i_emb) - 코드 (
multi/model.py#L444) - Output:
u_emb
- Key = Value:
-
최종
logits: 해당 user에 대한 behavior type j의 종류(element)에 대한 score예를 들어, user A에 대한 각각의 item id에 대한 click할 score
self.logits = i_b + tf.reduce_sum(tf.multiply(u_emb, i_emb), 1) -
lossself.loss = tf.reduce_mean( tf.nn.sigmoid_cross_entropy_with_logits( logits=self.logits, labels=self.y) ) + self.config['regulation_rate'] * l2_norm
Self-Attention, Vanilla-Attention 모두 multi-head attention을 사용