오답노트

[RS] Neural Collaborative Filtering 정리 본문

Python/DL

[RS] Neural Collaborative Filtering 정리

권멋져 2023. 3. 5. 18:46

https://arxiv.org/pdf/1708.05031.pdf

서론

LG U+에서 주최한 AI Ground 경진대회에서 알게된 딥러닝 기반 추천시스템 알고리즘이다.

경진대회를 참가 했을 때는 추천 시스템에 대한 개념이 아예 없어서 제대로 해보지도 못하고 대회를 마무리 했는데,

어떤 계기로 인해 추천 시스템을 계속 공부하고 있다.

 

본론

기존 Matrix Factorization

sij 를 유저 i와 유저j 의 관계로 봤을 때, s23(0.66) > s12(0.5) > s13(0.4)의 관계를 가지고 있다.

이를 기하학적으로 봤을 때, (b)처럼 p2와 p3이 가장 가깝고 p1과 p3이 가장 멀다.

선형적인 공간에서 u4과 같은 새로운 유저가 등장하면 문제가 발생한다.

 

유저 4와 다른 유저들과의 관계는 s41(0.6) > s43(0.4) > s42(0.2)로 표현할 수 있다. 하지만 이를 기하학적으로 표현할 수 없다. 이것이 기존 Matrix Factorization이 가진 한계다.

 

선형성으로인해 새로운 데이터가 등장하면 그 관계를 유연하게 표현하지 못하는 것이다.

General Framework

위에서 설명한 선형성으로 인해 새로운 데이터가 등장하면 그 관계를 유연하게 표현하지 못하는 것을 극복하기 위해 고완된 DNN으로 형성한 모델이다.

  • Input Layer (Sparse) : One Hot Encoding된 데이터를 입력으로 받는다.
  • Embedding Layer : Sparese Vector를 Dense Vector로 임베딩한다.
  • Neural CF Layers : User Latent Vector와 Item Latent Vector를 Concatenate 하여 DNN 통과 시킨다.
  • Output Layer : User와 Item을 0과 1사이의 값으로 얼마나 연관이 있는지 나타낸다.

Generalized Matrix Factorization (GMF)

MF는 NCF의 특별한 경우라고 생각할 수 있다.

NCF 레이어에서 사용자 벡터와 아이템 벡터를 합쳐지는 것을 내적으로 표현할 수 있다.

aout을 identical function(항등함수)로, h를 [1,1,1,....] 형태의 uniform vector로 가정한다면, Matrix Factorization과 같은 형태가 될 수 있다. 따라서 NCF는 MF의 일반적인 형태로 볼 수 있다. 그래서 이를 Generalized Matrix Factorization 명명했다.

 

Multi-Layer Perceptron(MLP)

MLP 또한 NCF framework의 특별한 형태로 표현할 수 있다.

 

MLP는 GMF에 비해 여러 층의 신경망을 사용하여 더 비선형적이며 복잡한 관계를 학습할 수 있다.

 

NeuMF

NeuMF는 GMF와 MLP를 합친 것이다.

여기서 임베딩 벡터는 GMF와 MLP를 위한 두 개가 존재한다. 

그 이유는 GMF와 MLP가 같은 임베딩 레이어를 사용한다면 성능이 제한될 수 있다.

그리고 각각의 임베딩 레이어는 서로 다른 차원을 가질 수 있다는 장점도 있다.

 

NeuMF는 기존 MF의 선형성과 DNN으로 표현 가능한 비선형성을 모두 가지는 것이 특징이다.

 

Pre-Training

대회에서는 NeuMF를 기반으로 side-information을 활용한 모델을 사용한 경험이 있는데, 학습률은 좋았지만 실제 결과는 별로 안좋았다.

 

논문에서는 GMF와 MLP의 Pre-Train 모델을 사용해야한다는 언급이 있다.

 

즉 NeuMF에서 좋은 점수를 얻기 위해서는 가중치를 load해야 했을 것으로 생각된다.

 

Model Code

아래 모델 코드는 다른 코드를 참고하면서 구현했다. 성능은 위에서 설명한 대로 학습은 잘되지만 실제 예측은 잘 못한다.

참고만 하도록 하자.

 

from tensorflow import keras

keras.backend.clear_session()

user_input_layer = keras.layers.Input(shape=(1,))
item_input_layer = keras.layers.Input(shape=(1,))

user_vector_layer_gmf = keras.layers.Embedding(input_dim= cfg['embedd_input_user'], output_dim=cfg['embedd_output_user'])(user_input_layer)
user_vector_layer_gmf = keras.layers.Flatten()(user_vector_layer_gmf)

item_vector_layer_gmf = keras.layers.Embedding(input_dim= cfg['embedd_input_item'], output_dim=cfg['embedd_output_item'])(item_input_layer)
item_vector_layer_gmf = keras.layers.Flatten()(item_vector_layer_gmf)

user_vector_layer_mlp = keras.layers.Embedding(input_dim= cfg['embedd_input_user'], output_dim=cfg['embedd_output_user'])(user_input_layer)
user_vector_layer_mlp = keras.layers.Flatten()(user_vector_layer_mlp)

item_vector_layer_mlp = keras.layers.Embedding(input_dim= cfg['embedd_input_item'], output_dim=cfg['embedd_output_item'])(item_input_layer)
item_vector_layer_mlp = keras.layers.Flatten()(item_vector_layer_mlp)

# Layer that multiplies (element-wise) a list of inputs.
gmf_layer = keras.layers.Multiply()([user_vector_layer_gmf,item_vector_layer_gmf])

mlp_layer_1 = keras.layers.Concatenate(name='mlp_layer_1')([user_vector_layer_mlp,item_vector_layer_mlp])

mlp_layer_2 = keras.layers.Dense(64, activation='relu')(mlp_layer_1)
drop_out_2 = keras.layers.Dropout(0.4)(mlp_layer_2)
batch_2 = keras.layers.BatchNormalization()(drop_out_2)

mlp_layer_3 = keras.layers.Dense(32, activation='relu')(mlp_layer_2)
drop_out_3 = keras.layers.Dropout(0.4)(mlp_layer_3)
batch_3 = keras.layers.BatchNormalization()(drop_out_3)

mlp_layer_4 = keras.layers.Dense(16, activation='relu')(mlp_layer_3)
drop_out_4 = keras.layers.Dropout(0.4)(mlp_layer_4)
batch_4 = keras.layers.BatchNormalization()(drop_out_4)

neumf_layer = keras.layers.Concatenate(name='neumf_layer')([gmf_layer,mlp_layer_4])

output_layer = keras.layers.Dense(cfg['output'],activation='sigmoid')(neumf_layer)

model = keras.models.Model([user_input_layer,item_input_layer],output_layer)
model.compile(loss= 'binary_crossentropy', optimizer = 'adam')

 

나중에 파이토치 코드도 추가하자.

'Python > DL' 카테고리의 다른 글

[DL] Embedding  (0) 2023.03.04
[NLP/RS] Negative Sampling  (0) 2023.03.03
[밑딥] 합성곱 신경망 (CNN)  (0) 2023.02.27
[밑딥] 학습 관련 기술들  (0) 2023.02.27
[밑딥] 오차역전파법  (0) 2023.02.27