본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성하였습니다.

https://abit.ly/lisbva

 

공부 시작

강의 종료

강의장

학습 인증샷

학습후기

 

 

DeepWalk

그래프를 임베딩하는 기법

랜덤워크 -> 시퀀스 생성 -> Word2Vec 학습 -> 노드 임베딩 완성

 

U1 ─ I1 ─ U2 ─ I2 ─ U3

이런 그래프가 있다고 한다면

 

여기서 랜덤하게 이동하면 

“U1 → I1 → U2 → I2” 같은 경로가 생긴다.

이걸 Word2Vec에 넣고 중심 단어와 주변 단어를 예측하면 각 노드에 의미있는 임베딩이 생기는 구조


그래프라면 이미 연결 관계가 정의되어 있는데 왜 랜덤하게 이동하는거지?

 

이는 단순히 연결 정보만 보는 게 아니라

걸음수를 통해 연결의 흐름과 간접적인 관계까지 문맥으로 보는 점에서 차별성이 있다.


 

GCN(Grapg Convolutional Network)

그래프 구조 위에서 작동하는 딥러닝 모델

 

CNN이 이미지에서 주변 픽셀을 보고 특징을 추출하듯,

GCN은 그래프에서 이웃 노드의 임베딩을 평균내고 노드의 새로운 표현을 만든다.

 


 

Word2Vec 복습

 

컴퓨터는 단어를 이해하지 못해 이 단어들을 수치화해서 모델이 계산 가능하게 만드는 과정이 필요하다.

CBOW(Continuous Bag od Words) : 주변 단어들을 보고 중심 단어 예측

skip-Gram : 중심 단어를 보고 주변 단어들을 예측

 

핵심은 같은 문맥에서 자주 등장하는 단어는 비슷한 벡터를 가진다!

각 단어는 랜덤한 임베딩 벡터를 가지고 시작

문장 속 문맥을 기준으로 예측이 잘 되게 weight를 업데이트한다.

따라서 같은 문맥에서 자주 등장하는 단어들의 벡터가 비슷해진다.

 

ex) king - man + woman ≈ queen

 

Word2Vec은 단어를 수치화하는 기술이지만, 단순한 숫자 나열이 아니라

문맥 안에서 의미가 비슷한 단어들을 비슷한 벡터로 만든다.

 

아래는 Word2Vec skip-gram을 pytorch로 구현하는 튜토리얼이다.

 

word2vec은 자연어 처리 공부할 때 공부했었는데 실제로 내가 데이터룰 구해서 학습할 때 기대한 성능이 안나오는 경우가 많았다.

 

import torch
import torch.nn as nn
import torch.optim as optim
from collections import Counter
import random
import pandas as pd

# 샘플 문장
corpus = "나는 학교에 간다 그리고 나는 도서관에도 간다 학교는 공부하는 곳이다 도서관도 조용하다"

# 전처리: 단어 토큰화 및 인덱스 매핑
tokens = corpus.split()
vocab = list(set(tokens))
word_to_ix = {w: i for i, w in enumerate(vocab)}
ix_to_word = {i: w for w, i in word_to_ix.items()}
vocab_size = len(vocab)

# 하이퍼파라미터
embedding_dim = 10
context_size = 2  # 좌우 2단어

# Skip-Gram 학습용 데이터 생성
def generate_skipgram_data(tokens, context_size):
    data = []
    for i in range(context_size, len(tokens) - context_size):
        center = tokens[i]
        context = tokens[i - context_size:i] + tokens[i + 1:i + context_size + 1]
        for ctx in context:
            data.append((center, ctx))
    return data

training_data = generate_skipgram_data(tokens, context_size)

# 모델 정의
class SkipGramModel(nn.Module):
    def __init__(self, vocab_size, embedding_dim):
        super().__init__()
        self.embeddings = nn.Embedding(vocab_size, embedding_dim)
        self.output = nn.Linear(embedding_dim, vocab_size)

    def forward(self, center_word_idx):
        embed = self.embeddings(center_word_idx)
        out = self.output(embed)
        log_probs = torch.log_softmax(out, dim=1)
        return log_probs

# 모델 초기화
model = SkipGramModel(vocab_size, embedding_dim)
loss_function = nn.NLLLoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)

# 학습
losses = []
for epoch in range(100):
    total_loss = 0
    for center, context in training_data:
        center_var = torch.tensor([word_to_ix[center]], dtype=torch.long)
        context_var = torch.tensor([word_to_ix[context]], dtype=torch.long)

        model.zero_grad()
        log_probs = model(center_var)
        loss = loss_function(log_probs, context_var)
        loss.backward()
        optimizer.step()
        total_loss += loss.item()
    losses.append(total_loss)

# 학습된 임베딩 추출
embedding_weights = model.embeddings.weight.data
embedding_df = pd.DataFrame(embedding_weights.numpy(), index=vocab)