pytorch geometric을 사용한 link prediction 모델

안녕하세요,
Pytorch geometric 을 사용하여 link prediction 모델을 개발하고자 합니다.

input data는 사용자, 제품, 제품의 특성 등의 노드로 구성된 heterogeneous 한 graph이고, pytorch에 제공된 예시 코드를 참고하며 모델을 만들어 training, validation, test 과정을 거쳤습니다.

그런데 실제로 해당 모델을 사용하여 user 1번에게 추천된 item 리스트를 뽑아보고자하는 부분에서 막히어 질문을 올립니다.

예시를 참고하여 구축한 모델은 다음과 같습니다.

class GNNEncoder (torch.nn.Module):

def __init__(self, hidden_channels, out_channels):
    super().__init__()
    self.conv1 = GATConv((-1, -1), hidden_channels, add_self_loops=False)
    self.conv2 = GATConv((-1, -1), out_channels, add_self_loops=False)

def forward(self, x, edge_index):
    x = self.conv1(x, edge_index).relu()
    x = self.conv2(x, edge_index)
    return x

class EdgeDecoder(torch.nn.Module):

def __init__(self, hidden_channels):
    super().__init__()
    self.lin1 = Linear(2 * hidden_channels, hidden_channels)
    self.lin2 = Linear(hidden_channels, 1)

def forward(self, z_dict, edge_label_index):
    z = torch.cat([z_dict['user'][edge_label_index[0]], z_dict['item'][edge_label_index[1]]], dim=-1)
    z = self.lin1(z).relu()
    z = self.lin2(z)
    return z.view(-1)

class Model(torch.nn.Module):

def __init__(self, data, hidden_channels):
    super().__init__()
    self.encoder = GNNEncoder(hidden_channels, hidden_channels)
    self.encoder = to_hetero(self.encoder, data.metadata(), aggr='sum')
    self.decoder = EdgeDecoder(hidden_channels)

def forward(self, x_dict, edge_index_dict, edge_label_index):
    z_dict = self.encoder(x_dict, edge_index_dict)
    return self.decoder(z_dict, edge_label_index,)

그리고 heterogeneous한 그래프 데이터 형태는 다음과 같습니다.

  • 노드: 사용자 271907명, 아이템 339606개, 아이템 특성 1362개, 아이템 특성의 특성 748개
  • 엣지: (사용자, rate, 아이템) 772190개, (아이템, has, 특성) 25913031개, (특성, has, 특성의 특성) 169710개

해당 모델과 데이터를 가지고 어떻게 사용자 1번이 rate를 할 만한 새로운 아이템 리스트를 뽑을 수 있나요?

  • 아래 답변은 Anthropic의 Claude-2 모델로 자동 생성한 것으로, 정확하지 않을 수 있습니다.
  • 잘못된 답변을 보셨다면 댓글로 첨삭 지도 부탁드립니다. :sweat_smile:

안녕하세요. heterogeneous graph 기반의 link prediction 문제에 대한 질문 감사합니다.

질문 내용을 정리해보면, Pytorch Geometric을 사용하여 사용자-아이템 간의 rating 예측 모델을 구축하였습니다. 이 모델을 사용하여 특정 사용자에게 새로운 아이템을 추천하는 것이 목표이신 것 같습니다.

이 경우 inference 시에 특정 사용자 노드와 연결된 새로운 아이템 노드를 생성해봐야 할 것 같습니다.

다음과 같이 해보시는 것이 좋을 것 같습니다:

1.Inference를 위한 dataset을 생성합니다.

  • user node: 추천 대상이 되는 특정 사용자
  • item nodes: 추천 후보가 되는 모든 아이템
  • edge: user node와 모든 item node 간의 dummy edge
  1. 이 dummy edge에 대해 모델을 돌려서 예측 score를 얻습니다.

  2. score가 높은 순으로 item을 정렬하여 추천 아이템 리스트를 생성합니다.

예시 코드는 다음과 같습니다:

user_id = 1 # 추천 대상 사용자
item_ids = [1,2,3,4,5] # 추천 후보 아이템

# dataset 생성
user_nodes = [user_id] 
item_nodes = item_ids
edge_index = [[user_id]*len(item_ids), item_ids]

# 모델 예측
scores = model(x_dict, edge_index, edge_label_index) 

# 추천 아이템 리스트 생성
_, indices = torch.sort(scores, descending=True)
recommendation = [item_ids[i] for i in indices]

이 방법으로 특정 사용자에 대한 개인화된 아이템 추천이 가능합니다.

제가 설명한 내용이 모호하거나 궁금한 점이 더 있으시다면 언제든 댓글 남겨주세요. 최선을 다해 도와드리겠습니다.


  • 답변이 어떠셨나요? 괜찮았다고 생각하신다면 아래 :heart:를 한 번 눌러주시길 부탁드려요!
  • 아래 답변은 OpenAI의 GPT-3.5(ChatGPT) 모델로 자동 생성한 것으로, 정확하지 않을 수 있습니다.
  • 잘못된 답변을 보셨다면 댓글로 첨삭 지도 부탁드립니다. :sweat_smile:

hjh0711님, 안녕하세요!

PyTorch Geometric을 사용하여 link prediction 모델을 개발하려고 하시는군요. 그리고 이미 PyTorch 예시 코드를 참고하여 모델을 구축하고 training, validation, test를 진행하셨다고 합니다. 그런데 사용자 1번에게 추천된 아이템 리스트를 뽑는 부분에서 막혀서 질문을 올리셨군요.

제공해주신 코드를 살펴보니, GNNEncoder, EdgeDecoder, Model 클래스로 구성된 모델이 있습니다. GNNEncoder는 GATConv를 사용하여 그래프의 노드를 인코딩하는 역할을 하고, EdgeDecoder는 인코딩된 노드를 사용하여 엣지를 디코딩하는 역할을 합니다. Model 클래스는 GNNEncoder와 EdgeDecoder를 조합하여 전체 모델을 구성합니다.

사용자 1번에게 추천된 아이템 리스트를 뽑기 위해서는 다음과 같은 절차를 따를 수 있습니다.

  1. 모델을 통해 사용자 1번의 인코딩된 벡터를 얻습니다.
  2. 인코딩된 벡터와 모든 아이템의 인코딩된 벡터를 사용하여 유사도를 계산합니다.
  3. 유사도가 가장 높은 상위 N개의 아이템을 추천 리스트로 반환합니다.

아래는 예시 코드입니다.

import torch
from torch_geometric.data import Data
from torch_geometric.nn import GATConv
from torch.nn import Linear
from torch_geometric.utils import to_hetero

class GNNEncoder(torch.nn.Module):
    def __init__(self, hidden_channels, out_channels):
        super().__init__()
        self.conv1 = GATConv((-1, -1), hidden_channels, add_self_loops=False)
        self.conv2 = GATConv((-1, -1), out_channels, add_self_loops=False)

    def forward(self, x, edge_index):
        x = self.conv1(x, edge_index).relu()
        x = self.conv2(x, edge_index)
        return x

class EdgeDecoder(torch.nn.Module):
    def __init__(self, hidden_channels):
        super().__init__()
        self.lin1 = Linear(2 * hidden_channels, hidden_channels)
        self.lin2 = Linear(hidden_channels, 1)

    def forward(self, z_dict, edge_label_index):
        z = torch.cat([z_dict['user'][edge_label_index[0]], z_dict['item'][edge_label_index[1]]], dim=-1)
        z = self.lin1(z).relu()
        z = self.lin2(z)
        return z.view(-1)

class Model(torch.nn.Module):
    def __init__(self, data, hidden_channels):
        super().__init__()
        self.encoder = GNNEncoder(hidden_channels, hidden_channels)
        self.encoder = to_hetero(self.encoder, data.metadata(), aggr='sum')
        self.decoder = EdgeDecoder(hidden_channels)

    def forward(self, x_dict, edge_index_dict, edge_label_index):
        z_dict = self.encoder(x_dict, edge_index_dict)
        return self.decoder(z_dict, edge_label_index)

# 모델 초기화
data = Data(...)  # 데이터 초기화
hidden_channels = 64
model = Model(data, hidden_channels)

# 모델 학습
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
criterion = torch.nn.MSELoss()

for epoch in range(100):
    optimizer.zero_grad()
    output = model(data.x_dict, data.edge_index_dict, data.edge_label_index)
    loss = criterion(output, data.y)
    loss.backward()
    optimizer.step()

# 사용자 1번의 인코딩된 벡터 얻기
user_index = 1
user_encoding = model.encoder(data.x_dict['user'], data.edge_index_dict['user']).detach()[user_index]

# 모든 아이템의 인코딩된 벡터 얻기
item_encoding = model.encoder(data.x_dict['item'], data.edge_index_dict['item']).detach()

# 유사도 계산
similarity = torch.cosine_similarity(user_encoding.unsqueeze(0), item_encoding, dim=-1)

# 유사도가 가장 높은 상위 N개의 아이템 추천
top_n = 10
_, indices = torch.topk(similarity, top_n)
recommended_items = indices.tolist()

print(recommended_items)

위의 코드에서 data는 PyTorch Geometric의 Data 클래스로 초기화된 그래프 데이터입니다. data.x_dict는 그래프의 노드 특성을 담고 있는 딕셔너리이고, data.edge_index_dict는 그래프의 엣지 인덱스를 담고 있는 딕셔너리입니다. data.y는 예측할 값(여기서는 rate)을 담고 있는 텐서입니다.

이 코드를 사용하여 사용자 1번에게 추천된 아이템 리스트를 얻을 수 있습니다.

더 자세한 내용은 PyTorch Geometric 공식 문서를 참고하시기 바랍니다. 아래 링크에서 PyTorch Geometric의 튜토리얼과 예제 코드를 확인할 수 있습니다.

이 답변이 도움이 되었는지 확인 부탁드리며, 추가로 궁금한 사항이 있다면 언제든지 질문해주세요!


  • 답변이 어떠셨나요? 괜찮았다고 생각하신다면 아래 :heart:를 한 번 눌러주시길 부탁드려요!
  • 아래 답변은 OpenAI의 GPT-4 모델로 자동 생성한 것으로, 정확하지 않을 수 있습니다.
  • 잘못된 답변을 보셨다면 댓글로 첨삭 지도 부탁드립니다. :sweat_smile:

안녕하세요, hjh0711님.

PyTorch Geometric을 사용하여 link prediction 모델을 구현하고, 특정 사용자에게 추천할 아이템 리스트를 생성하고자 하는 문제를 이해했습니다.

모델의 학습 과정은 잘 진행된 것으로 보이며, 이제 특정 사용자에 대한 아이템 추천 리스트를 생성하려고 합니다. 이를 위해서는 모델이 학습한 임베딩을 사용하여 사용자와 아이템 사이의 가능한 모든 조합에 대한 점수를 계산해야 합니다. 이 점수는 사용자와 아이템 사이의 연결 가능성을 나타냅니다. 점수가 가장 높은 아이템들을 추천 리스트로 사용할 수 있습니다.

아래는 이를 구현하는 예시 코드입니다.

def recommend_items(model, user_id, item_ids):
    # 모델을 평가 모드로 설정
    model.eval()

    # 사용자와 아이템의 임베딩을 얻음
    user_embedding = model.encoder['user'](user_id)
    item_embeddings = model.encoder['item'](item_ids)

    # 사용자와 각 아이템 사이의 점수를 계산
    scores = model.decoder(user_embedding, item_embeddings)

    # 점수를 기준으로 아이템을 정렬하고 상위 N개를 선택
    recommended_item_ids = item_ids[scores.argsort(descending=True)[:N]]

    return recommended_item_ids

위의 코드에서 user_id는 추천을 받을 사용자의 ID이고, item_ids는 추천 대상이 될 모든 아이템의 ID 리스트입니다. N은 추천할 아이템의 수입니다.

이 코드는 간단한 예시이며, 실제 구현에서는 사용자와 아이템의 ID를 모델이 이해할 수 있는 형태로 변환하거나, 모델의 입력 형식에 맞게 데이터를 전처리하는 등의 추가 작업이 필요할 수 있습니다.

더 자세한 내용은 PyTorch Geometric의 공식 문서를 참조하시기 바랍니다. PyTorch Geometric Documentation

답변이 hjh0711님의 질문에 도움이 되었기를 바라며, 추가적인 질문이 있으시면 언제든지 물어보세요. 제가 제공한 답변이 틀릴 수도 있으니, 다른 자료나 의견도 참고하시는 것을 추천드립니다.


  • 답변이 어떠셨나요? 괜찮았다고 생각하신다면 아래 :heart:를 한 번 눌러주시길 부탁드려요!