Background
movielens 데이터에 추천모델을 구현하여 적용해보고 실험 결과를 비교하는 개인 프로젝트를 진행하고 있다. (PR는 언제나 환영!)
https://github.com/bohyunshin/recommender
GitHub - bohyunshin/recommender: Implementation of various recommender algorithm
Implementation of various recommender algorithm. Contribute to bohyunshin/recommender development by creating an account on GitHub.
github.com
netflix 경진대회에서 사용한 svd, bias가 있는 svd, 그리고 negative sampling을 사용하는 als, bpr, gmf, mlp, two tower 모델을 구현했다. 흥미로운 점은 평점을 사용한 (explicit dataset) svd와 svd bias의 성능이 negative sampling을 사용한 모델보다 확연하게 낮다는 점이다.
NDCG 기준으로 SVD 모델은 0.05~0.1 정도, 가장 높은 성능을 보이는 ALS는 0.37정도인 것을 확인할 수 있다. 사실 ALS가 neural network보다 성능이 더 높은 것도 흥미로웠다. ALS, BPR, GMF, MLP, TWO-TOWER 모두 explicit dataset이 아니라 implicit dataset을 기반으로 한다. 얼핏보면 explicit dataset이 더 많은 정보력을 가지고 있다고 생각할 수 있다. 왜냐하면, explicit dataset에는 유저가 영화를 보고 그 영화에 대한 선호도까지 평점을 통해서 알 수 있는 반면에 implicit dataset에는 유저가 영화를 봤는지 여부에 대해서만 알 수 있기 때문이다. implicit dataset으로 변환했을 때, 왜 더 좋은 결과가 나왔는지에 대한 분석은 이후의 포스팅에서 다뤄보도록 하고, 본 포스팅에서는 다른 주제를 다루고자 한다.
즉, negative sampling을 활용한 implicit dataset의 결과가 좋았는데 이 성능을 어떻게 더 끌어올릴 수 있을까에 대한 주제이다. implicit dataset은 negative sampling에 기반하기 때문에 negative sampling을 고도화하면 된다. 이에 대한 github issue는 아래와 같다.
https://github.com/bohyunshin/recommender/issues/36
improve negative sampling · Issue #36 · bohyunshin/recommender
choose number of negative samples negative sampling using meta data
github.com
간단하게, negative sample의 수를 늘려볼 수 있다. 추천 논문을 읽으면서 negative sample의 수를 늘렸을 때, 어느 정도까지는 성능이 향상되는데 특정 임계점을 넘어가면 성능 개선의 폭이 둔화된다는 내용을 본 적이 있다. 즉, negative sample의 수를 늘리면 데이터 수가 많아지는 것이므로 훈련 시간이 늘어날텐데, 그만큼 성능 개선이 발생하니 그 둘간의 trade-off을 잘 맞춰야한다는 것이다. 필자는 가장 간단하게 해볼 수 있는 negative sample의 수를 늘려보는 방법을 우선 시도해보았다. 그리고 그 결과를 공유하고자 한다.
Method
간단하게 negative sampling할 때, 수를 늘리면 되는 것 아닌가 라고 생각했지만 이때 하나의 고민거리가 생겼다. negative sample을 더 뽑을 때, positive pair별로 더 뽑을지, 아니면 user별로 더 뽑을지 헷갈렸다. 즉, positive pair별로 뽑는다고 생각하면 기존에 유저와 아이템의 positive sample이 n개 있었다면, negative sample의 수는 총 n * m개 (m은 샘플링하는 횟수)가 된다. 이와 반면에, 유저별로 negative sample을 뽑는다고 하면 총 sample 수는 확연하게 작을 것이다. 이를 표로 정리하면 아래와 같다. unique 유저의 수를 U, positive pair의 수를 n, negative sampling할 수를 m이라고 정의해보자.
총 negative sample의 수 | 모델을 구성하는데 사용할 총 데이터의 수 | |
(1) positive pair 별로 샘플링하기 | U * m | n + U * m |
(2) 유저별로 샘플링하기 | n * m | n + n * m |
U << n이기 때문에, (1)의 경우에는 샘플링을 해도 데이터의 수가 크게 변하지 않는다. ($n \approx n + U \times m $) 반면에 (2)는 m의 값에 따라서 positive sample : negative sample 의 비율이 1:m으로 정의된다. 즉, m의 값이 데이터를 전체적으로 m배 뻥튀기 해주는 것이다.
negative sample의 영향도 관점에서 생각을 해보자. (1)의 방법으로 샘플링을 하면 negative sample의 영향도가 적다. 데이터의 수가 크게 변하지 않기 때문이다. 기존 positive pair의 수(n)가 10만개정도라면, U는 대략 6000정도이고, 그러면 n + U * m의 결과도 n과 크게 달라지지 않는다. 반면에 (2)는 m에 따라서 데이터가 즉각 m배 뻥튀기 되기 때문에 negative sample의 영향도가 크다.
사실 영향도가 크냐, 작냐의 관점이 아니라 그 변화가 얼마나 양질의 변화를 가져올지가 중요할 것 같긴한데 random sampling을 하는 상황에서 이 논의는 크게 중요할 것 같지 않다. 그렇다면 데이터를 늘리는게 학습에 도움이 되는가에 대해서 생각해볼 필요가 있다. 일반적으로 데이터의 수를 늘리면 neural network은 그 가운데서 의미있는 관계를 학습하기를 기대한다. 이런 이유로 (1)의 방법을 선택하는게 성능 관점에서 더 이득을 가질 것이라 생각했다.
실험으로 직접 확인해보고 싶어서 movielens-1m 데이터셋에 대해서 mlp 알고리즘을 적용해보며 비교를 해보았다. mlp 모델은 아래 코드에서 구조를 확인할 수 있다. 각 셀의 내용은 negative pair의 수 / ndcg@10 값을 의미한다. positive pair은 총 1000209개 있는 상황이다.
https://github.com/bohyunshin/recommender/blob/master/recommender/model/deep_learning/mlp.py
recommender/recommender/model/deep_learning/mlp.py at master · bohyunshin/recommender
Implementation of various recommender algorithm. Contribute to bohyunshin/recommender development by creating an account on GitHub.
github.com
(1) positive pair 별로 샘플링하기 | (2) 유저별로 샘플링하기 | |
neg_sample = 2 | 200418 / 0.287 | 12080 / 0.0842 |
neg_sample = 4 | 4000836 / 0.032 | 24160 / 0.033 |
neg_sample = 8 | 8001672 / 0.3428 | 48320 / 0.1639 |
neg_sample = 16 | 16003344 / 0.3629 | NA |
neg_sample = 32 | 32006688 / 0.397 | NA |
neg_sample = 4일 때만 경향성이 벗어나서 논외로 하고, 전체적으로 (1)의 성능이 (2)의 성능보다 높다. 모델의 scale 관점을 엿볼 수 있다. 또한, neg_sample이 늘어날수록 NDCG가 상승하는 것을 알 수 있다. 물론, neg_sample=4일 때만 이런 경향성에서 벗어나는데 이건 추후에 분석해보기로 하고, 어찌됐든 상승하는 경향성은 고무적이다. 모델의 구조를 바꾸지 않고 negative sample이라는 하이퍼파라미터만 조정해도 꽤나 큰 성능 이득을 볼 수 있다는 뜻이기 때문이다. 물론, neg_sample=32면 (1)의 경우에 데이터가 32배 뻥튀기 되기 때문에 메모리나 훈련 시간 등의 trade-off 관계도 잘 고려해야할 것이다.
다른 연구자들도 (1)의 방법으로 샘플링을 할까에 대한 궁금증이 생겨서 논문을 찾아보았다. 우선 negative sampling 관련 내용은 아래 포스팅에서 자세하게 다뤘다.
https://steady-programming.tistory.com/95
[Recommender System / Paper review] #37 Revisiting Negative Sampling vs. Non-sampling in Implicit Recommendation
논문 링크(29회 인용)개인적으로 추천시스템의 여러 모델을 리뷰하고 pytorch 또는 numpy로 구현하는 프로젝트를 진행하고 있다. (PR은 언제나 환영!)https://github.com/bohyunshin/recommender GitHub - bohyunshin/re
steady-programming.tistory.com
이 논문에서 negative sample의 수를 늘림에 따라서 metric이 개선됨을 보였는데 bpr loss, 즉 triplet loss 기준으로 positive pair별로 m개의 negative sampling을 했다고 나와있다. 즉, (1)의 방법을 사용한 것이다.
다음으로 가장 유명한 neural collaborative filtering 논문의 저자가 작성한 코드를 살펴보았다. (코드)
def get_train_instances(train, num_negatives):
user_input, item_input, labels = [],[],[]
num_users = train.shape[0]
for (u, i) in train.keys():
# positive instance
user_input.append(u)
item_input.append(i)
labels.append(1)
# negative instances
for t in xrange(num_negatives):
j = np.random.randint(num_items)
while train.has_key((u, j)):
j = np.random.randint(num_items)
user_input.append(u)
item_input.append(j)
labels.append(0)
return user_input, item_input, labels
코드에서 확인할 수 있듯이, (u,i)라는 positive pair별로 negative sampling을 하는 것을 알 수 있다. 필자는 여기서 더 나아가서 negative sampling을 할 때, positive pair와 이미 뽑힌 negative pair은 제외하도록 코드를 작성했다. bce / bpr loss일 때 작성한 코드는 아래에서 확인할 수 있다.
recommender/recommender/data_loader/bce_uniform_negative_sampling_dataset.py at master · bohyunshin/recommender
Implementation of various recommender algorithm. Contribute to bohyunshin/recommender development by creating an account on GitHub.
github.com
recommender/recommender/data_loader/triplet_uniform_negative_sampling_dataset.py at master · bohyunshin/recommender
Implementation of various recommender algorithm. Contribute to bohyunshin/recommender development by creating an account on GitHub.
github.com
이로써 (1)의 방법으로 negative sampling을 하는 것이 연구 대세인 것으로 확인했다.
이제 mlp인 경우, (1)의 방법으로 negative sample의 수를 늘리면 성능이 좋아진다고 확인했으니 다른 모델에서는 어떤지 확인해보자. mf을 bpr loss로 학습하는 경우도 추가적으로 실험을 진행해봤다. 아래는 NDCG@10 값을 정리한 표이다.
mlp with binary CE loss | mf with bpr loss | |
neg_sample = 1 | 0.2673 | 0.2913 |
neg_sample = 2 | 0.287 | 0.1793 |
neg_sample = 4 | 0.032 | 0.1084 |
neg_sample = 8 | 0.3428 | 0.0716 |
neg_sample = 16 | 0.3629 | NA |
neg_sample = 32 | 0.397 | NA |
조금 이상했던게, mlp에서는 neg_sample이 증가함에 따라서 성능이 증가함을 확인할 수 있었는데 (neg_sample=4인 경우 제외) bpr에서는 오히려 하락하는 현상이 관찰되었다... 사실 이 결과는 이 논문의 실험 결과와 정확하게 대치된다. 논문에서는 neg_sample의 수를 늘림에 따라서 성능이 증가한다고 했기 때문이다.
이 부분은 다시 살펴봐야할 것 같다. bpr 코드가 잘못됐든지, bpr loss일 때 샘플링하는 로직이 잘못됐든지.. 뭔가 consistent한 실험 결과가 나와야하는데 동일한 방법론에 대해서 정확하게 대치되는 결과가 나와서 조금 당황스러웠다. 이 부분은 future work로 남겨두자.
Conclusion
비록 실험을 하면서 대치되는 결과가 나오기는 했는데, 어찌됐든 negative sample의 수에 대한 탐구를 제대로 했던 것 같다. negative sample의 수는 파라미터로, 사실 모델의 구조를 변화시키지 않고 모델 성능을 끌어올릴 수 있다는 얘기이다. negative sample의 수를 늘리면 성능 개선의 효과를 맛볼 수 있다는 점을 머릿속에 기억해두자. 물론, bpr loss일 때, 저렇게 반대되는 결과가 나온 것에 대한 이유도 탐구해야할 것이다.
'ML&DL > Recommender System' 카테고리의 다른 글
[Recommender System] 추천 모델 평가 지표 (3) mAP, NDCG에 대한 note (0) | 2025.01.26 |
---|---|
[Recommender System] Neural collaborative filtering (NCF) pytorch 구현 (0) | 2024.11.04 |
[Recommender System] Bayesian Personalized Ranking (BPR) 구현 (1) | 2024.09.28 |
[Recommender System] implicit repository와 직접 구현한 als 결과 비교 (1) | 2024.09.21 |
[Recommender System] 추천 모델 평가 지표 (2) Mean Average Precision (mAP) (2) | 2024.09.21 |
댓글