ATRank - lshhhhh/deep-learning-study GitHub Wiki

ATRank: An Attention-Based User Behavior Modeling Framework for Recommendation

논문: https://arxiv.org/pdf/1711.06632.pdf
코드: https://github.com/jinze1994/ATRank

Overview

  • 이 논문은 "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로 둘 수 있다.)

Data

  • 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 참조

Architecture

Process

  • User U = {(aj, oj, tj) | j = 1, 2, ..., m}

Raw Feature Spaces

나누기

  • U를 behavior type에 따라 partition (bg: behavior groups)
    • G = {bg1, bg2, ... , bgn}
    • U = Ui=1n bgi

Behavior Embedding Spaces

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)

  • 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)
      • Behavior Type Encoding (aj)
        • encoding lookup (embedding)
    • 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이 의미가 다를 수 있기 때문.

Latent Semantic Spaces

  • 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)

Self-Attention Layer

각 semantic space 안에서의 behavior 관계 capture

  • Self-Attention
    • Key = Query = Value: user_history_emb (= hist_emb)
  • 코드 (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한다고 생각하면 된다.

Downstream Application Network

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
  • 최종 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)
    
  • loss

    self.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을 사용

⚠️ **GitHub.com Fallback** ⚠️