[Paper review] Matrix Factorization Techniques for Recommender Systems

chrisjune
6 min readAug 2, 2023

--

본 페이퍼를 간략하게 읽고 기억나는 부분을 한 줄씩 정리하고, Pytorch로 구현해보도록 합니다.

Recommender system strategies

Contents based filtering은 유저와 상품이 가진 본연의 특성으로 프로필을 만듭니다.

Collaborative filtering은 유저와 상품간의 관계를 사용합니다. 장점은 해당 도메인에 대하여 알지 못해도 된다는 것 (Domain-free)이고 단점은 Cold-start 문제입니다.

모델은 크게 Neighborhood와 Latent Factor로 나누어집니다.

유저기반 Neighborhood 방법은 내가 좋아하는 상품을 좋아하는 사람이 좋아하는 상품을 추천하는 것입니다. (나 → 상품 ← 다른 사람 → (상품)을 추천)

Latent Factor 모델은 유저와 상품의 관계에서 잠재적인 패턴을 발견해내려고 합니다. 방법중 하나인 Matrix Factorization은 확장에 유연하고 정확도 또한 높은 편입니다.

Matrix Factorization의 기본 모델

User-item interaction matrix는 두개의 latent factor matrix의 내적으로 구할 수 있습니다.

SVD는 Matrix를 분해하는 한 방법이지만, Sparse matrix에 취약하고, 값이 존재하는 데이터로만 학습할 경우 과적합이 발생하기 쉽습니다. 따라서 요즘은 존재하는 데이터를 학습시키되 Regularzation을 추가하여 과적합을 방지합니다. 아래는 정규항을 추가한 MF모델의 학습 목적함수입니다.

min (r - qTp)^2 + l(||q||^2 + ||p||^2)

알고리즘

SGD

이 방법은 학습세트의 에러를 모두 더하고, gradient의 반대방향으로 다시 q와 p에 더하여 초기 q와 p값을 개선시켜줍니다. 구현이 쉬우며 빠르지만, 특정 상황에서는 ALS가 나을 때가 있습니다.

ALS

목적함수가 단일 변수의 이차함수가 아니기 때문에 P와 Q를 번갈아가며 변수취급을 하고 미분을 하여 기울기를 구해줍니다. 마치 편미분을 하는것과 비슷합니다. SGD 대비 장점으로는 학습을 병렬로 할 수 있습니다.

기본모델의 변형

1. Bias 추가

유저마다 평점이 후하거나 박한 경향이 있고, 상품중에서는 다른 상품보다 평점이 더 좋은 경향이 있습니다. 이를 조정하기 위하여 유저와 상품에 대한 Bias를 추가해줍니다.

r^ = u + bi + bu + qTp (u는 평균평점)
min (r -u -qTp -bu -bi)^2 + l(||q||^2 + ||p||^2 + bu^2 + bi^2)

2. 메타정보 추가

사용자와 상품의 메타정보를 학습데이터에 추가하여 정확도를 높일 수 있습니다.

3. 시간 정보 추가

추천 데이터는 계절성을 고려해야하며, 유저의 평점또한 시간에 따라 변화하기 때문에 시간정보를 고려해야합니다.

r^ = u + bi(t) + bu(t) + qTp(t)

또는 학습데이터의 기준시간 이전데이터를 사용하지 않거나, 시간에 따른 score에 decay를 줄 수 있습니다.

4) 신뢰수준을 추가

어뷰징으로 인한 추천시스템의 영향도를 방어하기 위하여 신뢰수준을 추가할 수 있습니다.

min c(r -u -bu -bi -qTp)^2 + l(||p||^2 + ||q||^2 + bu^2 + bi^2)

Torch를 활용한 기본모델 구현

import torch
import torch.nn as nn
import torch.nn.functional as F


class MF(nn.Module):
def __init__(self, num_users, num_items, emb_size=100):
super().__init__()
self.user_emb = nn.Embedding(num_users, emb_size)
self.item_emb = nn.Embedding(num_items, emb_size)
self.user_emb.weight.data.uniform(0, 0.05)
self.item_emb.weight.data.uniform(0, 0.05)

def forward(self, user, item):
u = self.user_emb(user)
i = self.item_emb(item)
return (u * i).sum(1)


def train(model, df, epoch=10, lr=0.01, wd=0.0):
optimizer = torch.optim.Adam(model.parameters().lr = lr, weight_decay = wd)
model.train()
for i in range(epoch):
users = torch.LongTensor(df.userId.values)
items = torch.LongTensor(df.itemId.values)
ratings = torch.FloatTensor(df.score.values)

y_hat = model(users, items)
loss = F.mse_loss(y_hat, ratings)

optimizer.zero_grad()
loss.backward()
optimizer.step()
print(loss.items())


# df_train, df_val
num_users = len(df_train.userId.unique())
num_items = len(df_train.itemId.unique())

model = MF(num_users, num_items)
train(model, df_train)

지금까지 페이퍼 내용을 단락별로 간단히 요약하고 기본모델을 torch로 구현해보았습니다. 조금이나마 도움이 되었으면 좋겠습니다.

--

--