딥러닝(Deep Learning) 기반 개인화 추천시스템

2023. 11. 15.Tech

 

안녕하세요. 케이뱅크에서 데이터사이언티스트로 일하고 있는 권혁민입니다.


지난 포스팅에선 고객의 선호에 따라 상품 컨텐츠를 추천하는 방법론을 제시 했었습니다(https://kbank-recruit.tistory.com/35).

금번에 다루어 볼 주제는 딥러닝 기반 Neural Collaborative Filtering 방식으로 비활성화되어 앱 내 행동패턴이 없는 고객을 대상으로 한 개인화 추천시스템 개발사례를 공유하고자 합니다.

 

 


 개  요

 

딥러닝을 기반으로 추천시스템을 개발한다고 하면 많이들 생소하실 겁니다.

보통 딥러닝은 Text, Image 처리 등이 주된 적용 사례일 텐데요, 기존에도 추천시스템에 적용은 했었지만 모델의 핵심 부분이 아닌 모델의 주요 Feature에 적용하는 정도로 한정적이었습니다. 본 모델은 user와 item의 관계를 liner 방식으로 학습하는 Matrix Factorization(행렬분해)의 한계를 Neural Net 기반의 architecture인 Neural Collaborative Filtering으로 고도화하여 적용하는 사례를 공유해 드리고자 합니다.

 

기존의 통계형 추천이나 기계 학습 알고리즘은 충분한 로그 정보를 가진 Active 고객의 추천인 경우 예측 정확도도 준수하고, 빠르고 안정적 서빙이 가능하다 보니 딥러닝까지 적용할 필요가 없었던 것이 사실입니다. 본 모델의 경우 기존의 방식으로 커버되지 못하는 비활성화 고객(1개월내 미로그인)에게 보다 유의한 추천을 제공하고자 개발이 되었습니다. 비활성 고객의 경우 가입 시 수집되는 선호정보 및 Demo 정보와 과거 계약 히스토리 정보를 활용하여 딥러닝을 적용하면 더 효과적인 추천 결과를 얻을 수 있고 기존의 타 알고리즘 추천 결과와 많이 중복되지 않는 장점을 가지고 있어 Neural CF를 적용하게 되었습니다.

 
*본 포스팅의 기본적인 개념과 방법론은 딥러닝 기반의 추천시스템 방법론을 제시한 Neural Collaborative Filtering,  International World Wide Web Conference(2017)을 참고하였습니다.

1708.05031.pdf (arxiv.org)


 협업 필터링(Collaborative Filtering)

 

협업 기반 추천시스템의 구현 방법을 크게 Memory based 와 Model based 방식 2가지로 정리 할 수 있습니다. (짧게 설명하자면 Memory 방식은 유사도 기반, Model 방식은 모델을 Training 해서 예측하는 방식)

구분 추천방식 알고리즘 내용
Memory기반 비슷한 성향을 지닌 고객들을 그룹화하여, 그룹이 선호하는 상품을 해당 그룹에 속한 사용자에게 추천 각각의 사용자 간 유사도를 벡터 간의 유사도로 계산(Cosine Similarity)
Model기반 기계 학습을 통해 고객 또는 아이템의 숨겨진 특성 값을 계산하여 고객의 선호도를 예측 행렬분해를 통해 잠재요인을 이용한 Latent Factor 를 도출

 

케이뱅크의 Active고객별 가입상품/서비스 Data를 보면 User X Item Matrix가 매우 Sparse한 형태를 보입니다.(고객의 수가 늘어나면 늘어날수록 점점 더 Sparsity 가 높아짐.) 당행고객의 경우 교차 가입보단 특정상품 Only 가입 고객이 다수로, 주요  상품 및 서비스를 2개 이하로 가입한 고객이 약 70%로 Sparse합니다.

Memory방식의 경우 Sparse 한 데이터에서 성능이 매우 떨어지는 모습을 보이기 때문에 Model based 방식으로 구현하였습니다. 그밖에도 Model방식은 Sparse data외에 Cold Start문제에 잘 대처 할 수 있고, 초기 모델만 잘 구축해 놓으면 Inference도 쉽게 가능하기 때문에 보다 효율적인 방식으로 선택했습니다. (단 초기 모델의 Training 연산이 오래 걸리는 단점 존재) 



Model 방식의 협업 필터링 방법론중  대중적으로 많이 사용된 방법론은 Matrix Factorization 입니다.

https://lazyprogrammer.me/tutorial-on-collaborative-filtering-and-matrix-factorization-in-python/

 

Tutorial on Collaborative Filtering and Matrix Factorization

Collaborative filtering and matrix factorization tutorial in Python. Machine learning and data science method for Netflix challenge, Amazon ratings, +more.

lazyprogrammer.me

 

'Netflix Prize'를 통하여 유명해진 Matrix Factorization은 latent space를 공유하는 User vector와 Item vector의 내적을 통해 Interation을 모델링하는 방법입니다. 아래 그림에서와 같이 Feature P(User latent matrix), Feature Q(Item latent matrix) 행렬분해하여 unknown sample을 추정하는 방식입니다. 추정시 내적의 값이 높을 수록 user i에게 item i가 더 좋은 추천이라고 판단할 수 있습니다.

 

Matrix Factorization 예시 (user=4, item=4, k=2 일경우)

 

 

 


Matrix Factorization 의 한계

 

User와 Item 간의 관계를 학습함에 있어 기존의 liner 방식에 기반한 MF 방식에는 한계가 있습니다.
MF가 Interaction을 모델링하기 위해 사용하는 내적(inner-product)은  Latent Feature를 선형적으로 곱하는 단순한 방법으로 매우 효율적 이지만, 이는 동시에 User와 Item 사이의 복잡한 비 선형적 상호관계를 모델링 하기에는 불충분할 수 있습니다. 

해결안1. 더 높은 차원의 latent space를 도입(하나의 축을 추가) 더 복잡한 표현이 가능
            ㄴ latent space의 차원을 증가 > 모델의 일반화 성능(generalization)을 저해 > X

 

해결안2. 선형적, 비선형적 표현의 관점에서 문제를 생각한다면, 비 선형적인 관계를 모델링할 수 있는 심층 신경망(Deep Neural Network) 을 활용하면 더 복잡한 차원의 상호관계를 분석가능하게 됩니다.  > O

 


 Neural CF 적용 

 

 

■ 데이터 준비
기존 포스팅에서 소개한 것처럼, 고객의 Feedback을 반영할 수 있는 당행 데이터는 크게 2가지가 있습니다. 최대한 많은 양의 데이터를 통해 신경망 구조의 유효성을 갖추기 위해 두 가지 항목을 모두 사용하였습니다.

 

1) Explicit Data : 고객이 상품에 대해 직접적인 평가를 포함하는 데이터 입니다. 고객이 직접 밝힌 선호도란 측면에서 양질의 데이터로 볼 수 있습니다. 고객의 선호 카테고리 선택 시 여러개를 동시에 선택가능하며, 모든 값은 0 또는 1의 값을 갖습니다.

표1. Zero Party Data 예시


2) Implicit Data : 간접적으로 고객의 선호를 추정할 수 있는 데이터로 클릭, 계약, 체류시간 등의 데이터로 고객의 선호를 추정할 수 있는 데이터입니다. 본 모델에선 최근 비활성화 고객이 대상이기 때문에 과거 계약정보가 주된 활용 정보입니다. Explicit Data처럼 고객별로 당행의 주요상품별 계약정보를 0과 1인 binary로 표현하였습니다. 0과 1값은 특별한 선호도를 나타내지 않습니다.

표2. 계약정보 Data 예시

 

 

 

따라서 본 모델에서 Neural CF가 학습하는 문제는 User와 Item 사이의 Interaction 유무를 예측하는 Binary Classification으로 볼 수 있습니다.

 




Pytorch기반 Neural CF 개발
Neural CF의 전체 Framework와 상세설명은 아래와 같습니다.

Neural CF Framework

 

 

Step1) Data set Class준비  - Input Layer 생성

Input Layer는 각각 user, item을 나타내는 feature vector 로 구성하였고, 고객의 상품계약, 상품/서비스 선호 정보들로 구성되어 있는 기본 데이터를 one-hot encoding으로 변환하였습니다.(변환과정은 생략) Pytorch 환경에서 사용할 수 있는 데이터 형태로 변환하기 위해 아래와 같이 데이터 세트 클래스를 정의하였습니다.
# DataLoader - custom Dataset
class InteractionDataset(Dataset):
    #1._init__매서드 : 객체생성시 실행되는 생성자 
    def __init__(self, user_ids, item_ids, labels): 
        self.user_ids = user_ids 
        self.item_ids = item_ids
        self.labels = labels

    #3. 학습에 사용할 데이터 총개수 return
    def __len__(self): 
        return len(self.labels)
    
    #2. Indexing기능구현 : (index를 input으로 받아서 index출력)
    def __getitem__(self, index): 
        return self.user_ids[index], self.item_ids[index], self.labels[index]

Step2) Embedding Layer -  MatrixFactorization 클래스 정의

User X Item Latent Factor 학습하는 과정으로 model안에 __init__, forward함수를 만들고 nn.Module을 상속받아서 neural network를 만들게 됩니다. User와 Item의 Embedding을 학습하고, MLP를 통해 User, Item의 상호작용을 모델링하여 추천을 수행합니다. 추가로 모델의 순전파(forward propagation)을 구현하여 모델의 입력을 받아 순전파 연산을 수행하고 모델출력을 반환하는 형태로 구성하였습니다.
# NCF 모델 구성
class NeuralCollaborativeFiltering(nn.Module):
    def __init__(self, num_users, num_items, latent_dim, mlp_dim):# 네트워크 내부에서 사용할 구조
        super(NeuralCollaborativeFiltering, self).__init__()
        self.user_embedding = nn.Embedding(num_users, latent_dim)
        self.item_embedding = nn.Embedding(num_items, latent_dim)
        self.mlp_layers = nn.Sequential(
            nn.Linear(latent_dim * 2, mlp_dim),
            nn.ReLU(),
            nn.Linear(mlp_dim, 1)
        )

    def forward(self, user_ids, item_ids):
        user_embed = self.user_embedding(user_ids)
        item_embed = self.item_embedding(item_ids)
        concat_embed = torch.cat((user_embed, item_embed), dim=1)
        output = self.mlp_layers(concat_embed)
        return output.view(-1)

Step3) Neural CF Layers

Training 환경설정하는 과정부터 시작입니다. 모델연산 과정자체가 무겁기 때문에 GPU를 사용하지만, 사용량의 대한 과금문제 때문에 GPU를 사용할 수 있는 환경이면 'cuda' 로, 그렇지 않은 경우는 'cpu'를 디바이스로 설정하였습니다.
또한 MF 모델 훈련을 위해 미니배치로 나누어 모델을 훈련하였습니다. 먼저 Train을 하기위해 loss, optimizer연산이 필요한데, Score예측결과와 실Target과의 비교를 위해 MSE loss로 loss를 정의하여 학습이 진행되며, gradient descent 인 optimizer는 Adam방식으로 수행하였습니다. Latent Factor의 크기는 10, 은닉층 크기는 32, batch사이즈는 512로 설정하였습니다. epoch은 10으로 전체데이터를 10회 반복했습니다. 

 

# 3. Train
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
       
# NCF 모델 훈련
latent_dim = 10
mlp_dim = 32
batch_size = 512 #hyperparameter설정
dataset = InteractionDataset(user_ids_tensor, item_ids_tensor, labels_tensor)
dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)

## train을 위한 loss, optimizer연산
model = NeuralCollaborativeFiltering(num_users, num_items, latent_dim, mlp_dim)
# model = model.to(device) #gpu로 넘김
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# Train
epochs = 10
for epoch in range(epochs):
    for idx, (user_ids_batch, item_ids_batch, labels_batch) in enumerate(dataloader):
        optimizer.zero_grad()
        # user_ids_batch, item_ids_batch = user_ids_batch.to(device), item_ids_batch.to(device)
        outputs = model(user_ids_batch, item_ids_batch)
        loss = criterion(outputs, labels_batch)
        loss.backward()
        optimizer.step()
        if idx % 500 == 0:
            print('[Train_log] {0}: {1} / {2}'.format(epoch, idx, len(dataloader)) , end='\r')
            print('')
            
    save_path = f"{epoch}_weight.pth"
    torch.save(model.state_dict(), save_path)

마지막으로 고객별로 5개의 추천상품 리스트를 도출해봤고, 결과 예시는 아래와 같습니다. 5개 상품을 추천하는 함수 정의시 user_id를 입력받아 고객의 임베딩벡터를 얻고, item_embed와 내적합니다. user_embed와 item_embed 내적한 결과는 고객과 상품의 유사도를 나타냅니다. (유사도가 높은 상품일 수록 사용자가 선호할 가능성이 높음.) 최종적으로 유사도 score를 정렬하여 5개를 추천하였습니다.

 

# 사용자에게 상위 5개 상품 추천하기
def recommend(user_id, num_recommendations=5):
    user_embed = model.user_embedding(torch.LongTensor([user_id]))
    item_embed = model.item_embedding.weight
    scores = torch.matmul(user_embed, item_embed.t()).squeeze()
    top_items = torch.argsort(scores, descending=True)[:num_recommendations]
    recommended_items = [item_ids[item] for item in top_items]
    return recommended_items
# 결과예시
for user_id_to_recommend in range(5):
    recommendations = recommend(user_id_to_recommend)
    print(f"User {user_id_to_recommend}에게 추천하는 상품: {recommendations}")

User 0에게 추천하는 상품: ['FPMCRD040300', 'PBKOFR000001', 'FPMLON240003', 'FPMDPT100400', 'FPMDPT100000']
User 1에게 추천하는 상품: ['FPMLON100000', 'FPMDPT090000', 'ASTRES010000', 'FPMLON240003', 'PBKINQ170000']
User 2에게 추천하는 상품: ['FPMLON240003', 'FPMLON100000', 'FPMDPT090000', 'FPMCRD040200', 'PBKINQ170000']
User 3에게 추천하는 상품: ['FPMCRD040200', 'PBKINQ070000', 'PBKOFR000001', 'FPMLON230000', 'FPMLON250000']
User 4에게 추천하는 상품: ['FPMLON250000', 'FPMCRD030600', 'FPMCRD040200', 'FPMLON100000', 'PBKINQ220000']

 

 


 

 

마무리

 

지금까지 딥러닝 기반의 추천시스템을 만드는 과정을 세부적으로 기술해 봤습니다. 

 

고객속성 및 상품 데이터는 다양한 형태의 정보를 갖고 있습니다. 딥러닝 모델을 활용하면 텍스트, 숫자, 이미지와 같이 유형이 다른 데이터들을 한 모델 안에서 동시에 학습할 수 있기 때문에, 배너컨텐츠 및 이미지, 상품 카테고리, 가격 등 과 같은 서로 다른 형태의 데이터를 결합한 복합적인 학습이 가능해집니다. 아래 그림과 같이 고객이 연관된 압축된 인코딩형태 Output layer를 다시 CF모델의 Input layer로 넣어서 Text나 Image 네트워크의 시멘틱한 정보들을 주요 Feature로 사용할 수도 있습니다.

 

딥러닝 추천시스템 확장구조

 

또한 이러한 복합적 학습을 통해 추천에 필요한 요인들 간 가중치를 기계가 스스로 알아낼 수 있다 보니, 기존엔 고려하지 못한 멀티모달 데이터까지 포함된 입력 요인들을 조합해 새로운 요인을 발굴해 낼 수 있는 큰 기회가 될 수 있을 것 같습니다.  (기존에는 정형화된 데이터 간의 상관관계만을 가지고 비즈니스 수행함.)

 

지금까진 운영자관점에서 모델 변수 가중치나 로직에 많은 관여를 했지만, 금번 포스팅을 하면서 점차 딥러닝을 통해 자동화, 고도화 할 필요를 많이 느끼게 되는 계기가 되었습니다.

 

긴글 읽어주셔서 감사합니다.

 

케이뱅크와 함께 하고 싶다면 🚀