Julie의 Tech 블로그

Neural Collaborative Filtering, NCF - 코드리뷰, python3.x 버전 본문

Tech/RecSys

Neural Collaborative Filtering, NCF - 코드리뷰, python3.x 버전

Julie's tech 2021. 7. 5. 00:03
728x90

본 편에서는 지난번 논문 리뷰를 통해 살펴본 NCF를 실제 데이터셋과 연동하여 학습하고, 결과를 내볼 것이다.

Github에 기재된 코드가 모두 python2.x버전일 뿐더러, keras 역시 굉장히 down grade된 버전이라, 현 버전에 맞추어 코드를 작성하였다.

https://github.com/myevertime/Neural-Collaborative-Filtering

 

우선 논문 github을 clone하여 코드를 다운받는다. 그 후 이미지를 빌드하여 생성한다.

$ git clone https://github.com/hexiangnan/neural_collaborative_filtering

$ cd neural_collaborative_filtering
$ docker build --no-cache=true -t ncf-keras-theano .

실제로 코드를 실행해보는 건 간단하다. 위 과정을 완료하였다면, 아래에 모델별로 parameter를 설정하여 돌려주는 것만 남았다.

// GMF
$ docker run --volume=$(pwd):/home ncf-keras-theano python GMF.py --dataset ml-1m --epochs 20 --batch_size 256 --num_factors 8 --regs [0,0] --num_neg 4 --lr 0.001 --learner adam --verbose 1 --out 1
zsh: no matches found: [0,0] // zsh 쉘을 사용하고 있을 경우 위와 같이 수행하게 되면 에러가 발생한다. 그 땐 아래와 같이 수행
// docker run --volume=$(pwd):/home ncf-keras-theano python GMF.py --dataset ml-1m --epochs 20 --batch_size 256 --num_factors 8 --regs '[0,0]' --num_neg 4 --lr 0.001 --learner adam --verbose 1 --out 1

본격적으로 코드리뷰를 하기 전에, 데이터셋부터 살펴보면 두 가지 정도로 구성되어 있다.

- MovieLens 1 Million (ml-1m)

- Pinterest (pinterest-20).

두 데이터 모두 정제되어 깃헙에 저장되어 있다.

모델은 총 세 개가 구현되어있는데, NeuMF, GMF, MLP이다.

논문에서 NCF는 GMF의 linearity와 MLP의 non-linearity 장점을 각각 합친 것이라 되어 있는데, 이 모델들이 각각 구현되어있다.

모든 모델은 argument의 도움말과 사용법을 설정하는 script부터 구성되어있고, 그 이후 모델을 정의하고 있다.

모델은 keras의 Sequential() 형이 아닌, 함수형 API로 구성되어있다.

* 함수형 API : 직접 텐서들의 입출력을 다룸, 함수처럼 층을 사용하여 텐서의 입출력을 받음

우선 GMF.py부터 살펴보면,

Input layer로는 아이템과 유저 벡터가 각각 담기고, latent dimension으로 벡터를 임베딩한다.

(input_dim = max sequence length, output_dim = embedding dimensionality)

Input layer는 <KerasTensor: shape=(None, 1) dtype=int32> 객체를 반환하게 된다.

Embedding Layer는 유저 수만큼 데이터를 받아 latent dimension만큼 임베딩한 벡터를 반환하게 된다.

(shape=(samples, input_length, latent_dim) : 출력층 크기)

해당 벡터를 2D 텐서로 펼치는 Flatten() 층도 추가한다. (shape=(samples, input_length * latent_dim))

 

// Input Layer
user_input = Input(shape=(1,), dtype='int32', name = 'user_input')
// <KerasTensor: shape=(None, 1) dtype=int32, (created by layer 'user_input')>
item_input = Input(shape=(1,), dtype='int32', name = 'item_input')


// Embedding Layer (keras.__version__ = 2.4.3)
MF_Embedding_User = Embedding(input_dim = num_users, output_dim = latent_dim, 
                   name = 'user_embedding', embeddings_initializer='uniform', 
                   embeddings_regularizer = l2(0), input_length = 1)
MF_Embedding_Item = Embedding(input_dim = num_items, output_dim = latent_dim,
                   name = 'item_embedding', mbeddings_initializer='uniform', 
                   embeddings_regularizer = l2(0), input_length = 1)

//<KerasTensor: shape=(None, latent_dim) dtype=float32 (created by layer 'flatten')>
user_latent = Flatten() (MF_Embedding_User(user_input))
item_latent = Flatten() (MF_Embedding_Item(item_input))

user와 item을 latent vector로 임베딩한 뒤, 두 값을 내적해주면 유저가 아이템을 선호할 정도가 산출된다.

그 후 그 값에 sigmoid 활성화 함수를 적용하여 최종 예측값을 산출한다.

모델은 굉장히 단순하게 생겼고, 유저와 아이템 벡터를 임베딩하고 - 내적한 뒤 - 예측값을 sigmoid함수 값으로 변환하여 보여준다.

MLP.py도 유사하게 유저와 아이템 input을 임베딩하여 latent vector로 표현 한 뒤,

0번 째 레이어에 벡터를 삽입하는데, 이 때는 multiply가 아니라 concatentation하게 된다. 그 뒤 레이어 수만큼 Dense 레이어를 생성하고, 예측한다.

논문에서 소개하는 모델인 NeuMF는 간단하다. 위 두 모델을 합치는 것이다. (예측 벡터를 concatenate)

합친 코드를 아래에 소개해보면,

MF_Embedding_User MF_Embedding_Item MLP_Embedding_User MLP_Embedding_Item

를 각각 생성하고, 각 유저x아이템 벡터를 MF는 내적, MLP는 concatenate한다.

MLP에서 여러개 Dense층을 쌓아 산출한 예측 벡터를 MF와 함께 concatenate한다.

 

predict_vector = merge([mf_vector, mlp_vector], mode='concat')

학습 데이터는 모두 userID\t itemID\t rating\t timestamp 이렇게 tab으로 3-4개 value가 한 라인을 구성하고 있다.

이 파일들을 한줄씩 읽어들여 tab으로 split한 뒤 각 값을 matrix로 담아 Input 벡터에 넣는다.

본 논문이 다소 오래되어 현재 github코드가 상용 라이브러리와 버전이 맞지 않는 부분이 많다.

keras함수도 파라미터나 받는 부분이 다소 맞지 않아 수정이 조금 필요할 것이다.

이에 따라 keras 2.4.3버전과 python 3.x버전으로 맞춘 구현 코드를 공유한다.

반응형