자연어와 단어의 분산 표현

deep learning
공개

2025년 8월 10일

자연어 처리(NLP)

  • 우리 말을 컴퓨터에게 이해시키기 위한 기술
    • 기계 번역, 검색, 등

시소러스

  • 사람이 직접 레이블링 한 유의어 사전
  • 대표적으로 WordNet이 있다.

한계

  • 시대 변화에 따라 단어의 의미가 바뀌는 경우가 있다.
  • 사람 쓰는 비용이 크다.
  • 단어의 미묘한 차이를 표현할 수 없다.

통계 기반 기법

  • 말뭉치: 언어의 실제 사용 예시를 모은 텍스트 데이터
  • 위키백과, 뉴스 기사, 블로그 글 등
import numpy as np

def preprocess(text):
    text = text.lower()
    text = text.replace(".", " .")
    words = text.split(' ')

    word_to_id = {}
    id_to_word = {}

    for word in words:
        if word not in word_to_id:
            new_id = len(word_to_id)
            word_to_id[word] = new_id
            id_to_word[new_id] = word
    corpus = np.array([word_to_id[w] for w in words])
    return corpus, word_to_id, id_to_word
text = "The quick brown fox jumps over the lazy dog."
corpus, word_to_id, id_to_word = preprocess(text)

단어의 분산 표현

  • 단어의 의미를 벡터로 표현하는 방법
  • 분포 가설: 단어의 의미는 그 단어의 주변 단어들(맥락)에 의해 결정된다.
def create_co_matrix(corpus, vocab_size, window_size=1):
    co_matrix = np.zeros((vocab_size, vocab_size), dtype=np.int32)
    corpus_size = len(corpus)

    for idx, word_id in enumerate(corpus):
        left = max(0, idx - window_size)
        right = min(corpus_size, idx + window_size + 1)
        for i in range(left, right):
            if i == idx:
                continue
            co_matrix[word_id, corpus[i]] += 1
    return co_matrix
def cos_similarity(x, y, eps=1e-8):
    nx = x / (np.sqrt(np.sum(x**2)) + eps)
    ny = y / (np.sqrt(np.sum(y**2)) + eps)
    return np.dot(nx, ny)
vocab_size = len(word_to_id)
C = create_co_matrix(corpus, vocab_size, window_size=1)

C0 = C[word_to_id['the']]
C1 = C[word_to_id['dog']]
similarity = cos_similarity(C0, C1)
similarity
0.4082482852200891

유사 단어 랭킹

def most_similar(query, word_to_id, id_to_word, C, top=5):
    if query not in word_to_id:
        print(f"{query}는 사전에 없습니다.")
        return None

    query_id = word_to_id[query]
    query_vec = C[query_id]

    vocab_size = C.shape[0]
    similarity = np.zeros(vocab_size)

    for i in range(vocab_size):
        similarity[i] = cos_similarity(query_vec, C[i])

    # 유사도 순으로 정렬
    count = 0
    for i in (-1 * similarity).argsort():
        if id_to_word[i] == query:
            continue
        print(f"{id_to_word[i]}: {similarity[i]:.4f}")
        count += 1
        if count >= top:
            return

통계 기반 기법 개선

이전 방법의 한계

  • 단어의 의미를 벡터로 표현하는 방법이 단순히 주변 단어의 빈도수에 의존한다.
    • 점별 상호정보량(PMI)
def ppmi(C, eps=1e-8):
    M = np.zeros_like(C, dtype=np.float32)
    N = np.sum(C)
    S = np.sum(C, axis=0)

    for i in range(C.shape[0]):
        for j in range(C.shape[1]):
            pmi = np.log2((C[i, j] * N) / (S[i] * S[j]) + eps)
            M[i, j] = max(0, pmi)
    return M
맨 위로