Multi-Objective Recommender System
The goal of this competition is to predict e-commerce clicks, cart additions, and orders. You’ll build a multi-objective recommender system based on previous events in a user session.
Recall@20 기반이며, 점수는 3가지 타입을 모두 고려한다.
OTTO 데이터는 session 기반의 데이터로 구성이 되어 있다.
session
- the unique session idevents
- the time ordered sequence of events in the sessionaid
- the article id (product code) of the associated eventts
- the Unix timestamp of the eventtype
- the event type, i.e., whether a product was clicked, added to the user’s cart, or ordered during the sessionFeatures of training datasets:
기존 Pandas를 사용하여 데이터 처리를 하였으나, 속도와 메모리의 한계로 Polars로 변경하였다.
Polars의 장점은 Rust 언어로 구성 되어 있어 가볍고 다중 스레드 지원이 되어 빠르다는 장점이 있었다.
Ultra Simplification of Graph Convolutional Networks for Recommendation
Session 기반 task임으로 session 기반 모델인 SR-GNN을 이용하려고 하였으나 학습 속도가 느려 더 가벼운 모델을 찾게 되었다. (SR-GNN은 session을 RNN을 통해 순차적으로 방문하여 예측한다. RNN으로 학습 속도에 제한을 받게 된다.)
GCN 계열 중에서 가벼운 모델을 찾아 진행하였다. 최근 모델인 UltraGCN을 사용하였다.
GCN
LightGCN
GCN에서 Linear Layer()가 제거되고 CF 목적으로 사용된다.
UltraGCN
위의 LightGCN Layer를 무수히 쌓았다고 가정하고, 아래와 같이 근사한다.
(위의 Layer 반복 역시 Self-loop와 비슷한 역할을 한다고 하여 제거한다.)
이를 통해 Layer를 반복하는 과정을 제거함으로 학습 효율을 높였다고 한다. 단순히 Layer 반복을 제거하는 것 외에 Graph structure 데이터를 사전에 계산하여 활용하였다.
하지만, 학습은 Ultra한 것 같지만 과정은 그렇게 Ultra하지 못하였다.
모델 내부 연산과정을 무한번 반복하였다 가정하고, 외부로 가져오기 때문이다.
이 과정에서 Item 전체 혹은 User 전체에 대한 를 구해야한다. 여기서 Item 혹은 User의 수가 매우 많다면, 메모리 문제로 제대로된 를 구하기 어려워진다. 단일 row or column 으로 계산을 해야 하는데, 이 역시 매우 많은 User 혹은 Item 수로 시간이 매우 많이 걸리게 된다.
특히나 Kaggle과 같은 제한된 Cloud에서는 사용이 어려운 점이 있었다.
메모리 문제로 해당 데이터 처리를 위해 특정 세션 수만큼 나누어 분할하여 처리를 해야만 했다. (학습도 동일)
User-Item matrix는 분할 처리가 가능하였으나, Item-Item matrix를 만드는 과정 자체가 처리가 불가능한 부분이 있어, Item-Item matrix는 사용하지 못하였다.
아차차.. 위의 내용을 보면 User-Item matrix
를 구한다. 하지만, OTTO는 Session-based task
이다. 세션이 가지는 성격은 해당 세션이 유지되는 동안 어떤 아이템을 선호하고 장바구니에 담거나 구매버튼을 누르거나 얼마나 해당 페이지에 머무는지 등이 있다. 이 문제를 해결하기 위해 간단한 접근으로 세션이 선호하는 아이템 세트를 하나의 특성
으로 보고 학습을 진행하였다.
세션마다 선호하는 아이템 총 개수도 다르고 중복된 것도 있다.
처리 방법은 아래와 같다.
접근 수
를 계산한다. (클릭, 장바구니 담기, 구매 모두 포함)10개
의 Item Id
를 담은 리스트
를 생성한다.Item Id
들에 0
을 채워 10개의 리스트를 만들었다.empty Item Id
를 만들기 위해 기존의 Item Id들에 1을 더했다
.위의 방법에서 구매 혹은 장바구니 담기에 Weight를 주어 아이템 접근 수를 전략적으로 할 수 있었지만 시간 여건상 할 수 없었다.
여기서 Item Id를 Session vector로 embedding 하는 과정에서 Item Id를 그대로 사용하는 것 보다는 Item ebeddings를 활용하여 session vector를 생성하도록 하였다.
Session
을 특정 유형
으로 분석하길 원하여 일반적인 Linear Layer들 보다는 VAE를 활용
하여 비슷한 유형들은 비슷한 latent space에 mapping 되길 원하였다.
아래는 VAE 방식을 활용한 session embedding 코드이다. rasbt의 코드를 참조하여 진행했다.
def user_embeds_as_vae(self, user_favors):
# https://github.com/rasbt/deeplearning-models/blob/master/pytorch_ipynb/autoencoder/ae-var.ipynb
x = self.item_embeds(user_favors) # Session의 선호 Item 10개가 포함되어 있다.
x = self.user_linear(x.flatten(start_dim=1)) # Embeddings를 Liear Layer을 활용하여 변환
x = self.batch_norm(x) # Dataset 마다 경향이 달라 필수적으로 요구 되었다.
x = F.dropout(x, p=0.3)
x = F.leaky_relu(x, negative_slope=0.0001)
x = self.hidden_1(x)
x = F.leaky_relu(x, negative_slope=0.0001)
z_mean = self.z_mean(x)
z_log_var = self.z_log_var(x)
# Sample epsilon from standard normal distribution
eps = torch.randn(z_mean.size(0), z_mean.size(1)).to(z_mean)
z = z_mean + eps * torch.exp(z_log_var/2.)
return z
성능이 부족하다면 일종의 Candidator
역할, 충분하다면 그 자체로 이용하려 하였다.
학습 과정 중 데이터세트가 나누어져 있기 때문에 셔플을 사용하더라도 전체에 대하여 적용되는 것이 아니기 때문에 데이터세트간 경향이 달라 데이터세트가 변경되면 학습이 불안정해지는 경우가 존재하여, batch_norm layer
를 추가하여 진행했다.
하나의 세션이 여러개의 아이템을 선호하도록 하기 때문에 Negative sample과 Positive sample간 중복을 고려하여, Negative sample에 큰 coefficient를 주지 않았다.
마지막 날에야 정상적으로 학습이 되면서 어느정도의 Recall@20 (약 0.5)을 보여주었지만, GPU 사용시간 초과로 제출은 하지 못하였다.
The equation of User embeddings of UltraGCN