티스토리 뷰
"파랑"과 "핑크" 중 "빨강"에 가까운 것은?
단어의 형태는 카테고리 형태이지만, 실제로 우리가 생각할 때에는 계층적으로 의미 구조를 지닌다. 따라서 단어들 혹은 문장들 간 '유사한가, 반대인가' 등의 개념이 들어간다. 계층적이라는 말은 카테고리화 할 수 있다는 의미이다. 예를 들면 색(Color)은 Hypernym이라면, 빨강(Red)은 Hyponym으로 색의 하위 카테고리에 속한다.
위의 예시를 보면, 우리는 단순히 vocab의 순서나 인덱스로 단어를 찾는 것이 아니라, 유사도로 찾는 것에 가깝다. 따라서 one-hot encoding 보다는 dense vector로 표현하는 것이 유사성을 머신에게 이해시키기 좋은 표현이다. 샘플(데이터)를 잘 설명하는 특징을 모아서 하나의 vector로 표현한 것을 feature vector라고 한다. 텍스트 데이터에서 단어의 feature vector는 어떻게 표현할까?
WordNet
1985년부터 프린스턴 대학에서 만든 비순환 방향 그래프(DAG) 형식의 동의어, 상위어, 하위어에 대한 집합 사전이다.

WordNet을 활용하면 코퍼스 없이 계층적 구조, 동의어 집합을 파악하고, 유사도를 계산할 수 있다.
WordNet은 NLTK의 모듈로 임포트 할 수 있다.
import nltk
nltk.download('wordnet')
from nltk.corpus import wordnet as wn
wn.synsets("people")
#>> [Synset('people.n.01'),
#>> Synset('citizenry.n.01'),
#>> Synset('people.n.03'),
#>> Synset('multitude.n.03'),
#>> Synset('people.v.01'),
#>> Synset('people.v.02')]
hypernyms를 출력하는 함수를 통해서 두 단어 간의 거리를 구해보자.
#hypernyms 출력 함수
def hypernyms(word):
current_node = wn.synsets(word)[0]
yield current_node
while True:
try:
current_node = current_node.hypernyms()[0]
yield current_node
except IndexError:
break
for h in hypernyms('policeman'):
print(h)
#>> Synset('policeman.n.01')
#>> Synset('lawman.n.01')
#>> Synset('defender.n.01')
#>> Synset('preserver.n.03')
#>> Synset('person.n.01')
#>> Synset('causal_agent.n.01')
#>> Synset('physical_entity.n.01')
#>> Synset('entity.n.01')
두 단어의 hypernyms가 같을 때까지의 거리를 더하면 두 단어의 거리가 된다.
def distance(word1, word2):
word1_hypernyms = [h for h in hypernyms(word1)]
for i, word2_hypernym in enumerate(hypernyms(word2)):
try:
return i + word1_hypernyms.index(word2_hypernym)
except ValueError:
continue
distance('soccer', 'baseball')
#>> 11
distance에 -log를 취하면, similarity를 표현 할 수 있다.
import numpy as np
def similarity(word1, word2):
return -np.log(distance(word1, word2))
print(similarity('soccer', 'baseball'))
print(similarity('taekwondo', 'karate'))
#>> -2.3978952727983707
#>> -0.6931471805599453
WordNet처럼 잘 정리된 사전을 활용하면, 사전에 대한 의존도가 매우 높아진다. 사전에 틀린 내용이 있으면 틀린 방향으로 모델이 학습해버린다. 신조어나 특정 도메인에 대한 지식을 부각시켜서 학습하기도 어렵다. 이런 특정 목적이 있을 경우에는 Data-Driven method를 사용해야 한다.
TF-IDF(Term Frequency - Inverse Document Frequency)
어떤 단어(w)가 문서(d) 내에서 얼마나 중요한 지 나타내는 수치이다.

- TF : 단어가 문서 내에서 출현한 횟수, 클수록 문서 내에서 중요한 단어
- IDF : 그 단어가 출현한 문서의 숫자의 역수, 클수록 일반적으로 많이 쓰이는 단어
문서가 비슷한 단어들은 비슷한 의미를 지닐까? 각 문서에서 중요도를 feature 삼아서 vector를 만들 수 있다.
Cosine Similarity
벡터 간의 유사도는 cosine similarity로 구할 수 있다.

코사인 유사도는 직각이면 0이되고, 방향이 완벽하게 일치하면 1이 된다. 방향(각도)가 고려되기 때문에 벡터 간의 유사도로 적합하다.
전통적인 방식에서의 word Embedding 실습
모든 코드를 나열하는 것은 무의미하니, 절차만 간단하게 요약하면 아래와 같다.
- 단어의 빈도를 counting
- context windowing(w=2)을 실행하여 context counting
- 데이터프레임으로 각 row는 단어, 각 column은 단어의 feature가 되도록 matrix를 구한다.
: 이 matrix는 각 단어 별로 문장 내에 유사하게 나온 단어들을 묶어준다.
세 절차를 30만개의 리뷰를 가지고 하려니, 데이터프레임을 뽑는데에 생각보다 시간이 오래걸려서 10만개의 리뷰만 뽑아서 사용하였다. 또한 모든 vocab을 다 쓰는건 한 두 번 나온 단어들을 모두 쓰는 낭비를 범할 수 있으니, 빈도수 기준으로 1000개만 사용하기로 한다.
1000개의 데이터프레임을 뽑아보면 아래와 같다.

"반품"에 대해서 코사인 유사도 순으로 30개를 뽑아보면 아래와 같이 나온다.
def get_cosine_similarity(x1, x2):
return (x1 * x2).sum() / ((x1**2).sum()**.5 * (x2**2).sum()**.5 + 1e-10)
def get_nearest(query, dataframe, metric, top_k, ascending=True):
vector = torch.from_numpy(dataframe.loc[query].values).float()
distances = dataframe.apply(
lambda x: metric(vector, torch.from_numpy(x.values).float()),
axis=1,
)
top_distances = distances.sort_values(ascending=ascending)[:top_k]
print(', '.join([f'{k} ({v:.1f})' for k, v in top_distances.items()]))
print('\nCosine similarity:')
get_nearest('반품', df, get_cosine_similarity, 30, ascending=False)
#>> Cosine similarity:
#>> 반품 (1.0), 교환 (0.9), ▁반품 (0.9), ▁그냥 (0.9), ▁황당 (0.9), ▁교환 (0.9), ▁참 (0.9), ▁걍 (0.9), 사용 (0.9), ▁찝찝 (0.9), ▁그래서 (0.9), 그냥 (0.9), ▁. (0.8), ▁허접 (0.8),
#>> ㅠㅠ (0.8), 정말 (0.8), ▁뭐 (0.8), 너무 (0.8), 다 (0.8), ㅠ (0.8), 그리고 (0.8), ▁일단 (0.8), ▁불편 (0.8), ㅜ (0.8), 배송 (0.8), ▁후회 (0.8), ㅋ (0.8), 진짜 (0.8), ▁진짜 (0.8), ㅡㅡ (0.8)
이상 전통적인 방식(딥러닝 이전의)에서의 word embedding과 유사도를 계산해 보았다.
'Study > NLP' 카테고리의 다른 글
NLP with DeepLearning (8) - Text Classification (0) | 2021.12.06 |
---|---|
NLP with DeepLearning (7) - Word Embedding(Word2Vec) (0) | 2021.11.30 |
NLP with DeepLearning (5) - Minibatch (0) | 2021.11.24 |
NLP with DeepLearning (4) - Subword Segmentation (0) | 2021.11.23 |
NLP with DeepLearning (3) - Tokenization (0) | 2021.11.20 |
- Total
- Today
- Yesterday
- productowner
- 인공지능
- 전처리
- Oreilly
- 머신러닝파이프라인
- 자연어처리
- MLOps
- PO
- docker
- DDUX
- pmpo
- container
- productmanager
- Kubernetes
- PM
- Bert
- dl
- 딥러닝
- 쿠버네티스
- torch
- 스타트업
- deeplearning
- productresearch
- 도커
- mlpipeline
- ML
- 파이프라인
- Tennis
- 머신러닝
- nlp
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |