본문 바로가기

Natural Language Processing

[NLP with D.L] 1) Word Representation, Word Sense and Word2Vec with Gensim

본 포스트는 Christopher Manning 교수님의 CS224N 강의를 바탕으로 hyyoka가 필요하다고 생각한 내용을 추가한 것임을 밝힙니다.

Contents

  1. Human langauge and word meaning
  2. Word2vec
  3. Gensim library를 이용한 Word2vec

1. Human langauge and word meaning

When we study human language, we are approaching what some might call the “human essence,” the distinctive qualities of mind that are, so far as we know, unique to man.
@ NOAM CHOMSKY, Language and Mind, 1968

1.1 What is Language?

인간과 동물의 가장 큰 차이가 무엇이냐고 물으면 일반적으로 사람들은 "언어"라는 답을 합니다. 언어 (human language)는 언어학에서 다음과 같이 정의됩니다.

  • A conventional set of arbitrary signs & a grammar with rules manipulating these signs and constraints on their distribution
  • A systematic means of communicating by the use of sounds or conventional symbols

즉, 언어란 기호들과 규칙으로 이루어진 것으로, 의사소통을 하기 위한 체계라고 할 수 있습니다. 언어는 인간을 invincible하게 만들었습니다. 언어체계로 인해 인간은 다른 동물과 큰 차이를 두며 우위를 점하였죠.

 

언어로 인해 사람들은 지식을 공유하고 기억할 수 있게 되었습니다. 말로 하는 의사소통부터 시작해서 '문자'를 발명함으로써 공간적인 제약도 줄어들게 되었죠. Writing system은 사실 5000년의 짧은 역사를 가졌지만, 현재 우리가 현대 문물을 누리게 한 결정적인 역할을 했습니다.

 

WhatisLanguage

 

위의 짧은 만화는 인간 언어의 큰 특징을 드러냅니다; 불확정성

 

언어는 의사소통의 도구입니다. 그리고 의사소통에는 비언어적 요소부터 언어적인 요소, 맥락 정보 등 많은 것들이 관여합니다. 언어는 formal system이 아닙니다. 꼭 교과서의 문법이 정답은 아니며, 같은 말이라고 해도 어조에 따라 다른 의미가 되기도 합니다. 모든 것은 청자의 해석에 의존합니다.

 

의사소통은 사실 굉장히 청자 의존적인 행위입니다. 우리는 다른 사람이 우리가 사용하는 단어를 알고, 문법을 알고, 이에 따른 배경지식이 있으며, 현재의 상황 맥락을 이해할 것이라는 전제 하에 발화를 합니다. 그리고, 해당 발화는 맥락 정보 없이는 모호성 (Ambiguity)을 가지고 있는 경우가 많습니다. 발화자의 역할은 청자가 올바른 해석을 할 수 있도록 상황 맥락 정보를 잘 전달하는 것이죠.

 

1.2 How do we represent the meaning of a word?

1.2.1 Word meaning

'의미'는 한 사람이 단어, 기호 등을 이용해서 표현하고 싶어 하는 개념을 의미합니다.

Meaning : the idea that a person wants to express by using words, signs, etc...

그렇다면 단어의 의미 (Word meaning)는 어떻게 정의할 수 있을까요?
의미론(Semantics)에서는 단어의 의미를 다음의 층위로 설명합니다.

 

wordmeaningSemantics

 

여기서 concept 은 다른 entity와 자신을 구별 짓는 것으로 그것이 될 수 있는 잠재적인 referent(상황 맥락에 적절한 object)에 대한 mental description입니다. 이것은 시각적인 것이 아니라 정말 순전히 개념적인 것입니다.

 

말이 너무 어렵네요. 이렇게 간단하게 이야기해봅시다. 단어를 구성하는 요소에는 그것의 기호, 그것이 담고 있는 의미가 있어요. 이를 기표와 기의 (Signifier and Signified)라고 하며 쉬운 단어로는 symbol과 idea라고 할 수 있습니다. 이 기의는 순전히 정신적인 개념이며 실제로 존재라는 대상을 describe 합니다. 혹은 category를 결정할 수도 있겠죠.

 

예를 들어보면, the horse 라는 표현이 있을 때, 이것의 denotation은

  • all particular equine; the one referred to on a partivular occasion of utterance입니다.

다른 예를 들어보면, Horse 라는 common noun이 있을 때, 이것의 denotation은

  • a set of animals, all of them equines

Ruin 이라는 동사가 있을 때, 이것의 denotation은

  • all events of ruining이 됩니다.

concept는 구체적인 상황, 맥락에 맞는 referent를 만나 합쳐지게 됩니다. 그 과정은 다음 섹션에서 설명합니다.

1.2.2 Word Sense and Process of Composition

인간은 기본적으로 언어를 사용할 때 Economic principle 하에 행동합니다. 효율성을 극대화하는 과정에서 정보량이 적은 정보는 생략되죠.

 

인간은 이렇게 생략된 문장이 주어져도 주변 맥락 정보를 종합적으로 활용해 함축, 내포 등 생략된 정보를 찾아냅니다.

 

  • 의미 구축 과정 (Process of Composition)
    1) Expressions meaning = Lexical meaning + Grammatical meaning + Syntactic meaning
    2) Enriched with Contextual info
    3) Disambiguation from a context
    4) Meaning shifts to fit Context of Utterance

어휘, 문법, 통사적인 의미를 합쳐서 Expressions meaning이라고 합니다. 이 세 가지는 함께 고려되며 1개 혹은 여러 개의 의미들을 산출합니다. 중의적인 표현이 대표적이죠.

 

그러나, Expressions meaning은 상확 맥락 정보와 합쳐지면 오로지 한 개의 의미만을 산출합니다. 이를 CoU (Context of Utterance)라고 하는데, 이와 합쳐지면서 2,3,4의 변화를 겪습니다.

해당 과정이 꼭 필요한 이유는, 언어의 불확정성; 모호성(Ambiguity) 때문입니다.

[Ambiguity] Single Expression : Multiple meanings

동형이의어, 동음이의어가 대표적입니다.

위의 과정을 통해 인간은 모호성을 해소합니다. 그러나, 컴퓨터는 이러한 것을 해결하지 못하고 있죠.

1.3 How do we have usable meaning in a computer?

그렇다면, 컴퓨터로는 어떻게 단어를 표현할 수 있을까요?

1.3.1 시소러스 기반 : 어휘 분류 사전

대표적인 방법으로는 워드넷 (WordNet)이 있습니다. 워드넷은 1985년 심리학 교수 "George Miller"가 구축한 어휘 분류 사전으로, 단어들 간의 관계를 트리 구조로 저장해놓은 것이죠.

 

  • 단어의 관계
    1) Synonym (동의어) : 음성학적으로는 다르지만 매우 연관된 뜻을 가진 단어들
    2) Oppositions (반의어) : 다 같지만 핵심적인 부분에서 다른 단어들
    3) Hypernyms & Hyponyms (상, 하위어)

워드넷을 통해서는 위의 세 가지 관계를 모두 유추할 수 있습니다. 다음은 코드입니다:

# 동의어

from nltk.corpus import wordnet as wn

for synset in wn.synsets('good'):
    print(synset.lemmas())

 

# 상위어

from nltk.corpus import wordnet as wn

panda = wn.synset("panda.n.01")
hyper = lambda s:s.hypernyms()
list(panda.clouser(hyper))

 

시소러스의 문제

Manning 교수는 시소러스 기반의 접근이 다음의 문제를 가진다고 말합니다:

  • 자원으로 사용하기에는 아주 훌륭하지만 뉘앙스를 놓친다.
  • 한 단어의 새로운 의미들을 포함하지 않는다.
  • 주관적이다.
  • 생성하고 사용하는데 노동력이 많이 필요하다.
  • 단어 유사성을 정확하게 계산하지 못한다.

이러한 문제들은 단어를 표현하기 위한 또 다른 방법을 찾게 합니다.

 

1.3.2 Representing words as discrete symbols

전통적인 NLP에서 단어들은 이산적인 단위로서 여겨집니다. 그중, 해당 단어 자체만 보고 특정값을 표현하는 방법을 Local Representation이라고 합니다.

 

가장 대표적인 것은 원-핫(one-hot) 벡터로 표현하는 방법입니다.

  • One-hot encoding
    • 단어를 컴퓨터가 인지할 수 있는 수치로 바꾸는 가장 간단한 방법
    • 단 하나의 1과 수많은 0으로 표현된 인코딩 방식
    • 차원 = 어휘 집합 크기
    • 해당 단어가 나타난 위치에 1, 나머지 자리는 0으로 채운다.

그러나 이러한 방법은 크게 2가지의 문제가 있습니다.

  1. 차원이 너무 크다.
    차원이 높을수록 같은 정보를 표현하는데 불리하다 (차원의 저주). 0이 대다수인 sparse vec, 차원이 너무 크다!
  2. 단어 유사성 표현 불가*
    다른 말로는 단어의 의미를 표현할 수 없다.

따라서, 연구자들은 단어의 의미 정보를 담기 위해 워드 임베딩 기법을 탄생시켰습니다.

1.3.3 Word Embedding : Representing words by their context

워드 임베딩을 한마디로 이야기하면 단어의 맥락 정보를 고려하여 sparse vector를 dense vector로 바꾸는 과정입니다. 즉, 위에서 언급한 두 가지 문제를 해결하기 위한 것이죠.

 

embedding

 

좀 더 명확하게 그림으로 설명해보겠습니다.

 

임베딩이란 one-hot이라는 sparse vector를 가운데의 작은 사각형을 구하는 과정입니다. 가운데의 작은 사각형에 있는 가중치값들은 최종적으로 dense vector인 마지막 큰 사각형을 만들어냅니다. 이 큰 사각형의 벡터 값은 임베딩 공간에 한 위치를 차지하게 됩니다.

중간의 작은 사각형은 어떠한 맥락 정보를 이용하느냐에 따라 다른 값을 가지게 됩니다.


대표적으로는 3가지를 들 수 있습니다.

 

종류 정보 설명
Word2Vec 주변 단어 한 단어의 주변 단어를 통해 그 단어의 의미를 파악 : context=>target인 CBOW 모델 & target=>context인 skip-gram 모델
FastText 주변 단어 + n-gram Word2Vec과 유사한 방식으로 학습하나, 단어를 n-gram으로 나누어 학습하여 subword information을 고려
GloVe 주변 단어 + 전체 코퍼스의 분포 한 단어의 의미를 표현하는 데 주변 단어만 고려한다는 한계를 극복하기 위해 전체 코퍼스의 통계 정보를 고려

 

이 포스트에서는 Word2Vec에서 다룹니다.


2. Word2vec 

[Distributional Semantics] A word's meaning is given by the words that frequently appear close-by.

 

Word2Vec은 분포 가설을 따라, 한 단어의 주변 단어를 통해 해당 단어의 의미를 파악합니다. 주변 단어가 비슷하니 의미도 비슷할 것이란 가정하에 학습하는 것이죠. 예를 들어봅시다:

a. ㅁㄴㅇ에 물을 주니 쑥쑥 자랐다.
b. ㅂㅈㄷ에 물을 주니 쑥쑥 자랐다.

우리는 위의 문장에 의미를 알 수 없는 기호가 있다는 것을 알 수 있습니다. 하지만 만약에 저 기호들에 의미가 있다면, 해당 기호들은 비슷한 의미를 가질 것이라 유추할 수 있습니다. 모두 물을 주면 자라는 속성을 가진 것이기 때문이죠.

 

Word2Vec은 크게 CBOW(Continuous Bag of Words)와 Skip-Gram 두 가지 방식으로 나누어집니다. 전자는 주변 단어들(context word)을 가지고 중심 단어(center word)를 맞추는 방식이고, 후자는 중심 단어로 주변 단어를 예측하는 방법입니다

 

주변 단어는 맥락 정보로서 활용됩니다. 더 구체적으로 Word2Vec에서 맥락이란 다음을 의미합니다:

[ Context ] the set of words that appear nearby within a fixed-size window

2.1 Word2vec objective function

2.1.1 Optimization

최적화(optimization)란 어떤 목적함수(objective function)의 값을 최적화(최대화 또는 최소화)시키는 파라미터 조합을 찾는 것을 의미합니다.

 

이때 목적 함수의 값을 최대화시키는 것을 Maximization, 최소화시키는 것을 Minimization 문제라고 부릅니다. 그런데, 사실 최대화와 최소화는 같은 문제라고 볼 수 있습니다. $f(x)$를 최대화시키는 것은 $-f(x)$를 최소화하는 것과 동일하기 때문이죠.

 

모델의 훈련(Train)이란 해당 모델의 목적 함수 값을 최적화시키는 것을 의미합니다. 즉, 손실의 값을 최소화하거나 정답 레이블과 같은 값이 나오도록 최대화하는 것이죠. 조정되는 것은 파라미터들입니다.

 

- Minimizing Loss == maximizing predictive accuracy

2.1.2 Word2Vec objective function

$$
P(o|c) = {exp(u_o^Tv_c) \over \sum_{w \in W} exp(u_w^Tv_c)}
$$

 

해당 식의 의미를 살펴보도록 하겠습니다.

  • $o$는 주변 단어(context word), $c$는 중심 단어(center word)를 의미합니다.

  • $P(o|c)$는 중심 단어($c$)가 주어졌을 때 주변 단어($o$)가 등장할 조건부 확률입니다.

즉, Word2Vec의 목표는 $P(o|c)$를 최대화하는 것입니다. 중심 단어가 주어졌을 때, 올바른 주변 단어가 나올 확률 값을 최대화하는 것이죠.

이번에는 우변을 보겠습니다. 위 식의 우변을 최대화는 분모의 값을 줄이고 분자의 값을 키움으로써 이루어집니다.

 

  • $exp()$는 exponentiation의 약자로, 흔히 모든 것을 positive로 바꾸기 위해 사용된다.
  • $u_o^Tv_c$ 는 중심 단어($c$)와 주변 단어($o$)의 내적 값을 통해 유사도를 계산한다. 내적 값이 클수록 확률 값이 높다.
    $$
    u^Tv = u \cdot v = \sum_{i=1}^nu_iv_i
    $$
  • $\sum_{w \in W} exp(u_w^Tv_c)$은 전체 단어들을 Normalize 하는 것으로, 확률 분포를 보기 위함이다.

분모는 중심 단어($c$)와 모든 단어를 각각 내적 한 것의 총합이라고 할 수 있습니다. 즉, 분모를 줄이기 위해서는 내적 값들을 줄여야 합니다. 주변 단어와의 내적 값은 키우고, 주변 단어가 아닌 단어들과의 내적값은 줄이는 것이죠. 이렇게, 주변 단어가 등장할 확률은 높이고 주변 단어가 아닌 단어가 등장할 확률을 낮춥니다.

2.1.3 중심 단어 벡터의 gradient

이번 섹션에서는 Word2Vec skip-gram 모델에서 어떻게 미분을 통해 기울기를 구하고, 이를 벡터를 업데이트시키는지 수식으로 정리해보겠습니다.

 

Manning 교수는 Likelihood Maximization이 본질적으로 Negative Log Likelihood와 동일하다는 것을 언급하며, 이를 gradient를 설명하는 데 사용합니다.

 

$$
P(o|c) \Rightarrow -\log P(o|c)
= -\log{exp(u_o^Tv_c) \over \sum_{w \in W} exp(u_w^Tv_c)}
$$

 

이제 이 식을 미분해보도록 하겠습니다. 좀 더 명쾌하게 하기 위해 손으로 적었습니다.

word2vec_gradient

최종적으로 나온 식의 의미는 다음과 같습니다:

  • observed actual word - predicted word

3. Gensim을 이용한 word2vec

gensim은 Word2Vec, FastText 등, 여러 임베딩 기법들을 쉽게 사용할 수 있도록 합니다. 이를 통해 단어 사이의 유사도 등을 쉽게 계산할 수 있습니다. 자세한 내용은 다음의 사이트에서 확인할 수 있습니다. https://radimrehurek.com/gensim/apiref.html

3.1 gensim의 word2vec 사용해보기

  • similarity() : 두 단어의 유사도 계산
  • most_similar() : 가장 유사한 단어를 출력
  • wv.doesnt_match() : 가장 유사하지 않은 단어 출력
import gensim
from gensim.models import Word2Vec

 

path = '/content/wiki_small800.txt'

 

corpus = gensim.models.word2vec.Text8Corpus(path)

 

model = Word2Vec(corpus, min_count=5, size=100, window=2, iter=100, sg=1)

 

# 유사도 측정

model.similarity('사람', '인간')

 

[output]
0.4663391

 

# 가장 유사한 단어들 반환

w2v_model.most_similar(positive=["영화"], topn=10)

 

[output]

[('만화', 0.5728510618209839),
 ('작품', 0.5642774105072021),
 ('시상식', 0.5582197904586792),
 ('감독', 0.5553828477859497),
 ('작자', 0.5487478971481323),
 ('미술가', 0.5485845804214478),
 ('하야오', 0.5445706844329834),
 ('가쓰히로', 0.5442630052566528),
 ('문소리', 0.5339727401733398),
 ('워즈', 0.5329844951629639)]

most_similar 메서드는 positive 인수와 negative 인수를 사용하여 다음과 같은 단어 간 관계도 찾을 수 있습니다.

she + (actor - actress) = he

# 단어간 연산

w2v_model.most_similar(positive=['개그맨', '여자'], negative= ['남자'], topn=10)

 

[output]

[('개그우먼', 0.6489680409431458),
 ('리포터', 0.6390106678009033),
 ('미스코리아', 0.5832093358039856),
 ('다카하시', 0.5593729019165039),
 ('매드', 0.5586073994636536),
 ('남춘천', 0.5573046207427979),
 ('희극인', 0.5518606901168823),
 ('슈퍼주니어', 0.5491580963134766),
 ('경희대', 0.5474306344985962),
 ('MC', 0.5407893657684326)]

 

# 가장 관련없는 단어 반환

w2v_model.wv.doesnt_match('남자 여자 주방'.split())

 

[output]
'남자'

좀 더 의미 있는 연구도 진행할 수 있겠죠. 마치 위의 결과물같이 말입니다. 해당 코퍼스에 잠재된 의식을 파악할 수 있습니다.

3.2 Visualization

임베딩 시각화에서는 다음의 3가지를 중점적으로 확인하면 됩니다.

  1. 클러스터 만들어졌나?
  2. 각 단어들의 위치; 중심부? 우상향? 중심으로 갈수록 어디에서나 출현 가능하다는 것, 일반적으로 기능어이다.
  3. 단어들 간의 거리: 멀다면 배타적인 관계에 있다
import matplotlib.pyplot as plt
import seaborn as sns

import pandas as pd

 

# [참고] https://woolulu.tistory.com/133
from sklearn.manifold import TSNE
import matplotlib.font_manager as fm
import matplotlib as mpl

path_nanum = "/content/NanumBarunpenB.otf"
prop = fm.FontProperties(fname=path_nanum)

# 그래프에서 마이너스 폰트 깨지는 문제에 대한 대처
mpl.rcParams['axes.unicode_minus'] = False

model = Word2Vec.load('w2v_model')
vocab = list(model.wv.vocab)
X = model[vocab]
tsne = TSNE(n_components=2)

# 100개의 단어에 대해서만 시각화
X_tsne = tsne.fit_transform(X[:100,:])
df = pd.DataFrame(X_tsne, index=vocab[:100], columns=['x', 'y'])

 

%matplotlib inline

fig = plt.figure()
fig.set_size_inches(12, 8)
ax = fig.add_subplot(1, 1, 1)
ax.scatter(df["x"], df["y"])

for word, pos in list(df.iterrows()):
    ax.annotate(word, pos, fontsize=18, fontproperties=prop)

plt.show()

출처: https://privatedevelopnote.tistory.com/81 [개인노트]