ratsgo / embedding

한국어 임베딩 (Sentence Embeddings Using Korean Corpora)
https://ratsgo.github.io/embedding
MIT License
452 stars 129 forks source link

p.62, 표 2-3 오타 + TF를 세는 법에 대해서 #74

Closed stet-stet closed 4 years ago

stet-stet commented 4 years ago

안녕하세요,

Q1. 표 2-3(p.62) "를" - "운수 좋은 날"의 임베딩값이 잘못된 것이 아닌가요?

표 2-3. TF-IDF 행렬 구분 | 메밀꽃 필 무렵 | 운수 좋은 날 | 사랑 손님과 어머니 | 삼포 가는 길 담배 | 0.2603 | 0.2875 | 0.0364 | 0.2932 를 | 0 | 0.0034 | 0 | 0

TF-IDF 식은 p.61에서 다음과 같이 제시되었습니다.

TF-IDF(w) = TF(w) * log(N/DF(w))

이 값이 0이려면 TF(w) = 0이거나, DF(w) = N이어야 합니다. 그런데 후자라면 "를"의 가중치는 모두 0여야 합니다. 따라서 "메밀꽃 필 무렵" "사랑 손님과 어머니" "삼포 가는 길"의 TF("를")은 0이고, DF(w) = 1이며, log(N/DF(w)) = log(4)가 되는 것이라고 이해했습니다. 그런데 TF값은 정수이므로, 만약 "운수 좋은 날" - "를" 칸의 값이 0이 아니라면 IDF값인 log(4)보다 커야 합니다. 이 값은 로그의 밑이 10이나 2라면 0.0034보다는 무조건 큽니다. 따라서, 모순이 생깁니다.

Q2. 찾아 보니, TF도 따로 관계식을 만들어 사용하는 경우가 있는 것 같습니다. 예시, 6.5챕터를 봐 주세요 책에서 제시해 주신 TF 메트릭이 최근 들어서는 가장 일반적이게 쓰인다고 보아도 무방할까요?

ratsgo commented 4 years ago

@stet-stet 님 예리한 지적입니다. Q1과 관련해 말씀주신 모순이 맞습니다. 코멘트 주신 걸 기초로 제가 코드와 데이터를 확인해본 결과 제가 작성했던 코드에서 실수가 있었던 점을 확인했습니다. 더 나은 설명을 위해 예시 단어를 바꿔, 표2-3을 다음과 같이 고쳤습니다. 관련 설명 수정은 전자책과 정오표에 반영할 예정입니다.

구분 메밀꽃 필 무렵 운수 좋은 날 사랑 손님과 어머니 삼포 가는 길
어머니 0.066 0.0 0.595 0.0
0.2622 0.098 0.145 0.0848

위의 표를 만드는 데 사용한 데이터와 코드는 다음과 같습니다.

code

import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from preprocess.supervised_nlputils import get_tokenizer

doc1 = ' '.join([line.strip() for line in open("메밀꽃필무렵", 'r').readlines()])
doc2 = ' '.join([line.strip() for line in open("운수좋은날", 'r').readlines()])
doc3 = ' '.join([line.strip() for line in open("사랑방손님과어머니", 'r').readlines()])
doc5 = ' '.join([line.strip() for line in open("삼포가는길", 'r').readlines()])

tokenizer = get_tokenizer("mecab")
tokens_doc1 = ' '.join(tokenizer.nouns(doc1))
tokens_doc2 = ' '.join(tokenizer.nouns(doc2))
tokens_doc3 = ' '.join(tokenizer.nouns(doc3))
tokens_doc4 = ' '.join(tokenizer.nouns(doc5))
corpus = [tokens_doc1, tokens_doc2, tokens_doc3, tokens_doc4]

vectorizer = TfidfVectorizer(
        min_df=1,
        ngram_range=(1, 1),
        lowercase=True,
        tokenizer=lambda x: x.split())

tfidf = vectorizer.fit_transform(corpus)
tfidf_array = tfidf.toarray()

target_words = ["어머니", "것"]
results = np.zeros((len(target_words), len(corpus)))
for word_idx, word in enumerate(target_words):
    results[word_idx, :] = tfidf_array[:, vectorizer.vocabulary_[word]]

data : data.zip

ratsgo commented 4 years ago

Q2와 관련해서 TF-IDF에서 Term Frequency는 도서에 제시된대로 raw count를 주로 사용하는 것으로 알고 있습니다. 말씀주신 책을 보니 raw count 말고도 다음과 같이 여러 방식으로 TF를 줄 수 있을 것 같습니다.

스크린샷 2020-02-02 오후 9 08 59 스크린샷 2020-02-02 오후 9 09 13

해당 책 저자는 이와 관련해 다음과 같이 언급하고 있는데요. 저도 아래 intuition에 동의합니다. raw count보다는 위의 방식으로 TF를 계산하는 것이 좀 더 나은 품질의 임베딩을 추출할 수 있을 거라 생각합니다.

위키백과를 보면 TF 말고도 IDF에 대해서도 다양한 변종이 존재합니다. 다만 도서에서는 TF-IDF를 백오브워즈 기법을 이해하는 수단으로 소개하고 있어서 기본적인 뼈대 정도만 설명하는 현재 서술 그대로 두는 게 바람직하지 않나 생각합니다. 다시 한번 감사드립니다.