728x90
반응형
딥러닝 기반 협업필터링¶
이번 포스팅에서는 신경망을 기반으로 한 협업필터링을 구현해본다.
신경망 기반 협업필터링은, 기존의 MF기반 협업필터링과는 조금 다르다.
MF기반은 벡터끼리의 내적곱을 활용하지만,
신경망 기반은 비선형 활성함수(relu)를 사용하게 된다.
In [43]:
# 패키지 import
from pathlib import Path
import pandas as pd
import numpy as np
In [44]:
data = pd.read_csv('./ml-latest-small/ratings.csv')
data.head()
Out[44]:
In [45]:
# 먼저 데이터를 Train과 Validation데이터로 나눈다
np.random.seed(3)
msk = np.random.rand(len(data)) < 0.8
train = data[msk].copy()
val = data[~msk].copy()
print(train.head())
print(val.head())
데이터 인코딩¶
지난번 포스팅과 같은 데이터 인코딩 기법을 사용한다.
In [23]:
# 다음은 Pandas의 컬럼을 범주형의 id로 인코드해주는 함수이다
def proc_col(col, train_col=None):
""" Encodes a pandas column with continous ids. """
# Unique한 row를 찾는다 즉 사용자 혹은 영화이다
if train_col is not None:
uniq = train_col.unique()
else:
uniq = col.unique()
# 사용자/영화를 인덱스와 매핑해준다
name2idx = {o:i for i,o in enumerate(uniq)}
# 그리고 그것을 포맷팅해서 리턴한다
return name2idx, np.array([name2idx.get(x, -1) for x in col]), len(uniq)
In [24]:
# 다음은 실제로 데이터를 인코딩으로 만들어주는 함수이다
# 위에서 정의해준 proc_col을 사용한다
def encode_data(df, train=None):
""" Encodes rating data with continous user and movie ids.
If train is provided, encodes df with the same encoding as train.
"""
df = df.copy()
for col_name in ["userId", "movieId"]:
train_col = None
if train is not None:
train_col = train[col_name]
_,col,_ = proc_col(df[col_name], train_col)
df[col_name] = col
df = df[df[col_name] >= 0]
return df
In [25]:
# encoding the train and validation data
df_train = encode_data(train)
df_val = encode_data(val, train)
In [26]:
num_users = len(df_train.userId.unique())
num_items = len(df_train.movieId.unique())
print(num_users, num_items)
학습과 검증 함수¶
학습과 검증 함수 역시 지난번 포스팅과 같은 함수들을 활용한다.
In [27]:
import torch
import torch.nn as nn
import torch.nn.functional as F
In [ ]:
def validation_loss(model, unsqueeze=False):
model.eval()
users = torch.LongTensor(df_val.userId.values)
items = torch.LongTensor(df_val.movieId.values)
ratings = torch.FloatTensor(df_val.rating.values)
if unsqueeze:
ratings = ratings.unsqueeze(1)
y_hat = model(users, items)
loss = F.mse_loss(y_hat, ratings)
print("validation loss {:.3f}".format(loss.item()))
In [36]:
def train_mf(model, epochs=10, lr=0.01, wd=0.0, unsqueeze=False):
optimizer = torch.optim.Adam(model.parameters(), lr=lr, weight_decay=wd)
model.train()
for i in range(epochs):
users = torch.LongTensor(df_train.userId.values)
items = torch.LongTensor(df_train.movieId.values)
ratings = torch.FloatTensor(df_train.rating.values)
if unsqueeze:
ratings = ratings.unsqueeze(1)
y_hat = model(users, items)
loss = F.mse_loss(y_hat, ratings)
optimizer.zero_grad()
loss.backward()
optimizer.step()
print(loss.item())
validation_loss(model, unsqueeze)
신경망 모델¶
아래는 신경망 기반의 협업필터링 모델이다.
기존 MF모델과 비슷하게 Embedding을 사용하기는 하지만,
히든레이어인 선형모델들 뒤에 비선형 활성함수인 relu들이 붙어있는것을 볼 수 있다.
In [46]:
class NNCollabFiltering(nn.Module):
def __init__(self, num_users, num_items, emb_size=100, n_hidden=10):
super(NNCollabFiltering, self).__init__()
self.user_emb = nn.Embedding(num_users, emb_size)
self.item_emb = nn.Embedding(num_items, emb_size)
self.lin1 = nn.Linear(emb_size*2, n_hidden)
self.lin2 = nn.Linear(n_hidden, 1)
self.drop1 = nn.Dropout(0.1)
def forward(self, u, v):
U = self.user_emb(u)
V = self.item_emb(v)
x = F.relu(torch.cat([U, V], dim=1))
x = self.drop1(x)
x = F.relu(self.lin1(x))
x = self.lin2(x)
return x
model = NNCollabFiltering(num_users, num_items, emb_size=100)
학습 결과¶
학습시킨 결과를 보면, 기존 MF기반의 모델과 성능이 비슷한 것을 볼 수 있다.
In [47]:
train_mf(model, epochs=15, lr=0.05, wd=1e-6, unsqueeze=True)
In [48]:
train_mf(model, epochs=10, lr=0.01, wd=1e-6, unsqueeze=True)
In [49]:
train_mf(model, epochs=10, lr=0.001, wd=1e-6, unsqueeze=True)
참고자료
아래의 원본 소스를 기반으로 번역 및 개인 의견을 덧붙인 코드입니다.
728x90
반응형
'데이터사이언스 > PyTorch' 카테고리의 다른 글
PyTorch 4 - 협업필터링 (0) | 2019.07.14 |
---|---|
PyTorch 3 - 데이터 로더, 신경망 (0) | 2019.07.13 |
PyTorch 2 - 로지스틱 회귀 (0) | 2019.07.11 |
PyTorch 1 - 텐서 (0) | 2019.07.09 |