자연어(NLP) 처리 기초 정리

본 포스팅은 개인적인 학습을 목적으로 작성되었습니다. 수정해야할 부분이 있으시면 언제든지 댓글 남겨주세요 :)


자연어를 이해하는 방법

  • 한 개의 단어가 어떤 의미를 가지고 있는지를 알기 위해서는 문장의 컨텍스트(Context)를 이해해야 한다.
  • 사람은 ‘Apple’이 사과인지, 회사인지 문맥을 통해 바로 구분할 수 있지만 기계를 할 수 없다.
  • 그리고 ‘메시’를 겁색할 때 ‘‘축구’와 ‘호나우도’처럼 단어간의 의미적 관계성도 바로 인식할 수 있지만 기계는 어렵다.
  • 그렇기 때문에 단어를 수치적으로 표현하여 기계가 이해할 수 있도록 해야 한다.
  • 이를 Word Embedding이라고 표현하는데, 간단히 말해서 텍스트를 숫자로 표현하는 것인데 같은 문자라고 하더라도 다른 수치로 표현할 수 있다. 구체적으로 설명하면 단어를 사전을 사용하여 매핑하고 이를 벡터로 만드는 것이다.
    • ex) “the cat sat on the mat”
    • [the, cat, sat, on, mat]의 단어별로 분류하고 이를 One hot encoding을 통해 0 또는 1로 나타낸다.
    • 예를 들어 ‘cat’는 [0, 1, 0, 0, 0, 0 ]이며, ‘the’의 경우 [1, 0, 0, 0, 0]으로 나타낸다.
  • 이렇게 벡터화를 시켜 숫자로 표현하면 분류나 회귀 등의 다양한 분석이 가능해진다.

자연어 관련 용어

  • Document(문서)
  • Corpus(말뭉치): 텍스트(문서)의 집합
  • Token(토큰): 단어처럼 의미를 가지는 요소
  • Morphemes(형태소): 의미를 가지는 언어에서 최소 단위
  • POS(품사): ex) Nouns, Verbs
  • Stopword(불용어): I, my, me, 조사, 접미사와 같이 자주 나타나지만 실제 의미에 큰 기여를 하지 못하는 단어들
  • Stemming(어간 추출): 어간만 추출하는 것을 의미(running, runs, run -> run)
  • Lemmatization(음소표기법): 앞뒤 문맥을 보고 단어를 식별하는 것

자연어 처리 Python 라이브러리

1. NLTK

  • 대표적인 자연어 처리 라이브러리
  • 기능
    • 말뭉치(corpora) 다운로드
    • Word POS, NER Classification
    • Document Classification

2. KoNLPy

  • 우리나라 한글에 특화된 자연어 처리 라이브러리
  • 기능
    • 단어 품사 별 분류
      • Hannanum
      • Kkma
      • Komoran
      • Twitter

3. Gensim

  • 문서 사이의 유사도를 계산과 텍스트 분석을 돕는 라이브러리(Word2Vec을 제공)
  • 기능
    • Topic Modeling
      • LDA(Latent Dirichlet Allocation)
      • LSI(Latent Semantic Indexing)
      • HDP(Hierarchical Dirichlet Process)
    • Word Embedding
      • word2Vec

4. Scikit-learn

  • 대표적인 Python 머신러닝 라이브러리에서는 문서 전처리용 클래스를 제공한다.
  • 기능
    • DicVectorizer : 단어의 수를 세어놓은 사전에서 BOW 벡터를 만든다.
    • CountVectorizer: 문서 집합으로부터 단어의 수를 세어 BOW 벡터를 만든다.
    • Tfidfvectorizer: 문서 집합으로부터 단어의 수를 세고 TF-IDF 방식으로 단어의 가중치를 조정한 BOW 벡터를 만든다.(CounterVectorizer의 서브클래스로 CountVectorizer를 이용해 BOW를 만들고 TfidTransformer를 사용해 tf-idf로 변환)
    • HashingVectorizer: hashing trick을 사용하여 빠르게 BOW 벡터를 만든다.

자연어 처리 기법을 이용한 머신러닝 방법

처음 자연어 처리를 접했을 때 흐름이 잘 이해가 되지 않았다. word2vec에서 벡터화 단계에서부터 신경망을 사용하고 단어간의 유사도 분석이 가능했다. 그래서 분류/예측 등을 위한 학습 단계로 넘어가는 것이 혼란스러웠다. 간단히 아래의 단계로 진행된다.

  1. Word Embedding: 벡터화
  2. 학습
  3. 예측

결국, 어떤 방식으로 Word Embedding을 하느냐가 가장 중요한 관건이다. 벡터화된 데이터가 자연어의 어떤 의미를 담고 있는지가 결정되기 때문이다. 이후 학습 단계에서 CNN, RNN 등의 기법을 활용하여 성능을 높이게 된다.

자연어 처리 Word Embedding 기초 이론

  • Word Embedding에는 크게 두 가지 방식으로 나눌 수 있다.
    1. Frequency based Embedding
    2. Prediction based Vector

1. Frequency based Embedding

BOW(Bag of words)

문서를 자동으로 분류하기 위한 방법이다. 주어진 말뭉치에서 사용된 각각의 단어에 인덱스를 매겨서 사전처럼 만든다. 이름처럼 단어 주머니를 만든다고 이해하면 쉽다.

그리고 입력된 문서의 각 단어가 해당 단어 사전(단어 주머니)에서 해당 인덱스에 얼마나 나타나는지 표시하는 방식으로 입력된 문장을 분석하는 방법이다. 아래는 간단한 예시이다.

  • 학습 말뭉치 : “It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness”

  • Bag of words 만들기

Word Index
:-:
It 1
was 2
the 3
best 4
of 5
times 6
age 7
wisdom 8
foolishness 9
  • 입력 문장을 인덱스에 세기
    "it was the worst of times" = [1, 1, 1, 0, 1, 1, 1, 0, 0, 0]
    

Count Vector

이름 처럼 벡터의 갯수를 세는 방법이다. 말뭉치(Corpus)에 여러개의 문서들(Documents)가 있다고 했을 때 각 문서마다 고유의 토큰(Token)을 세어 이를 행렬(Matrix)로 나타내는 방법이다.

그래서 행(Row)는 각각의 문서를 나타내고 열은 각각의 고유의 토큰(Token)을 나타낸다. 아래에는 간단한 예시이다.

  • D1: He is a lazy boy. She is also lazy.
  • D2: Neeraj is a lazy person.
  • Number of Tokens : [‘He’,’She’,’lazy’,’boy’,’Neeraj’,’person’]

아래처럼 2 X 6의 행렬로 나타낼수 있다.

  He She lazy boy Neeraj person
D1 1 1 2 1 0 0
D2 0 0 1 0 1 1

이렇게 각 문서의 단어를 수치화하여 벡터로 표현할 수 있게 되었으며 각 문서의 특성을 확인할 수 있게 되었다. 하지만 이런 방법은 각각의 문서와 단어를 세기 때문에 엄청나게 큰 매트릭스를 만들어 비효율적인 연산을 할 수 밖에 없다.

TF-IDF Vector

다른 방법은 많이 알려진 TF-IDF입니다. 단어를 세는 것은 같지만 각각의 문서에서 단어의 갯수를 세는 것뿐만이 아니라 전체 말뭉치(Corpus)에서 단어의 갯수도 함께 세는 방법이다.

이 방법의 의미는 이름인 TF-IDF(Term Frequency - Inverse Document Frequency)를 보면 잘 알 수 있습니다. 다시 말해서 특정 단어가 문서 내에 얼마나 자주 등장하는 지를 나타내는 TF(단어 빈도)와 어떤 단어가 문서 전체 집합에서 얼마나 많이 나오는지를 나타내는 IDF(역문서 빈도)로 이 둘의 곱으로 TF-IDF를 값을 구할 수 있다.

예를 들면, ‘is’, ‘the’와 같은 be동사나 관사는 영어에서 빈번하게 나타나는 단어이다. 하지만, ‘Messi’같은 단어는 특정 문서에서만 빈번하게 나타난다. 아래의 예를 통해 살펴보자.

  • document1
Term Count
This 1
is 1
about 2
Messi 4
  • document2
Term Count
This 1
is 2
about 1
TF-IDF 1
  • TF = 특정 문서에서 단어가 나타난 수 / 특정 문서에 있는 전체 단어의 갯수
    • Document1에서 This의 TF = 1/8
    • Document2에서 This의 TF = 1/5
    • Document1에서 Messi의 TF = 4/8
  • IDF = log( 말뭉치에서의 전체 문서의 수 / 말뭉치에서 해당 단어가 나타난 문서의 수)
    • This의 IDF = log(2/2) = 0
    • Messi의 IDF = log(2/1) = 0.301
  • Document1의 This의 TF-IDF = (1/8) X 0 = 0
  • Document1의 Messi의 TF-IDF = (4/8) X 0.301 = 0.15

따라서 전체 말뭉치의 컨텍스트로부터 ‘Messi’는 Document1에서 중요한 의미로 나타난다는 것을 알 수 있다.

정리하면, 특정 문서에서의 단어 빈도를 전체 말뭉치에서 단어 빈도와 비교해서 특정 문서에서 해당 단어가 얼마나 중요한지를 알 수 있도록 해준다.

2. Prediction based Vector

앞에서 설명한 단어의 빈도를 기반으로 한 자연어 처리 방법은 문서에 나타난 단어를 통해서 어떤 특징을 찾아 낼 수는 있지만 여러 단어 간의 유사성을 나타내기는 어렵다.

예를 들어, ‘Soviet’라는 단어는 ‘Union’이라는 단어와 함께 나올 확률이 높고, ‘United’는 ‘Nations’라는 단어와 나올 확률이 높다. 이처럼 단어간의 관계를 다차원 공간에서 벡터로 나타낸다면 유사도를 측정하고 수치적으로 연산이 가능하여 추론이 가능하다.

  • 다음 링크에서 벡터 연산으로 추론하는 것을 확인할 수 있다.

이러한 복잡한 벡터를 연산하고 학습하기 위해서는 신경망 모델을 사용해야 한다. 앞으로 소개할 모델은 신경망을 이용해서 학습하여 벡터값을 구하게 된다.

word2vec

word2vec은 앞으로 설명할 CBOW와 Skip-gram 방식의 Word Embedding을 C++ 라이브러리로 개발한 것이다. Python에서는 gensim이라는 패키지에 word2vec 클래스로 구현되어 있다.

word2vec은 Word Embedding을 통해 단어간의 유사성을 포함할 수 있다. 아래의 그림처럼 벡터화를 시켰을 때 두 단어의 거리의 유사성을 통해 ‘man’과 ‘woman’은 ‘king’과 ‘queen’을 거리와 유사하기 때문에 의미적인 관계를 갖는다고 볼 수 있다.

word2vec

word2vec에서는 word embedding의 방벙으로 CBOW와 skip-gram 두 가지 방법이 있는데 CBOW 보다는 skip-gram이 주로 사용된다. 그 이유는 CBOW는 주변의 단어의 평균 값을 가지고 예측을 하기 때문에 대체로 skip-gram이 더 성능이 좋다.

CBOW

CBOW 모델은 문장에서 한 단어에 앞뒤로 붙어 있는 단어들을 통해서 해당 단어를 유추하는 방법이다. 예를 들어, “너무 더워서 ___ 음료수를 사서 마셨다.” 라는 문장이 있다면 ‘더워서’랑 ‘음료수’를 통해 ‘시원한’이라는 단어를 유추하는 것이다.

이를 위해서는 Input Layer와 Hidden Layer, 그리고 타겟이 되는 Output Layer가 필요하다. Input Layer는 각 단어들의 One hot encoding 되어 있는 벡터로 구성되어 있다. Input 데이터는 앞의 단어와 뒤의 단어로 2개의 타겟을 가지고 있다. 위의 문장을 예로 들면 ‘시원한’이라는 Input Layer는 ‘더워서’와 ‘음료수’라는 Output Layer를 가질 수 있다. 그리고 그 사이에는 Hidden Layer가 있어서 타겟에 최적화된 가중치(w)를 찾는 것이 핵심이다. one_cbow <word2vec Parameter Learning Explained”, Xin Rong>

<예시>

  • ‘시원한’ -> (Hidden Layers) -> ‘더워서’
  • ‘시원한’ -> (Hidden Layers) -> ‘음료수’
  • ‘사서’ -> (Hidden Layers) -> ‘음료수’
  • ‘사서’ -> (Hidden Layers) ->’마셨다’

이런식으로 주어진 문장들의 단어를 벡터화하여 Hidden Layer를 통과시켜 적절한 가중치를 찾아내는 것이다.

one_cbow <”word2vec Parameter Learning Explained”, Xin Rong>

이는 MLP(Multi-Layers Perceptron)처럼 신경망 모델과 같은 방식으로 작동되는데 CBOW에서의 차이점은 손실함수는 MSE(Mean Square Error)가 아니라 -log(p(Wo|WI))라는 점이다.

CBOW Equation

  • Wo : output word
  • WI : context words

CBOW의 단점은 컨텍스트에서 단어의 평균을 사용한다는 점이다. 따라서, 주어진 말뭉치에서 ‘시원한’이 ‘음료수’와 ‘속이’라는 단어와 같이 쓰일 경우 문제가 될 수 있다.

Skip-gram

Skip-gram 은 앞서 설명한 CBOW와 매우 유사하다. 접근 방법이 반대라고 생각할 수 있다. 앞에서 예로 들은 “너무 더워서 시원한 음료수를 사서 마셨다.” 라는 문장에서 ‘시원한’이란 단어를 통해서 가장 유사한 단어인 ‘더워서’와 ‘음료수’를 찾는 방식이다.

<예시>

Input Output(Context1) Output(Context2)
시원한 더워서 음료수
사서 음료수 마셨다

방법은 앞에서 설명한 CBOW와 같으며, 손실 함수도 CBOW와 같으며 Layer의 구조만 바뀌게 된다.

skip gram

<”word2vec Parameter Learning Explained”, Xin Rong>

Skip-Gram의 장점은 한 단어와 연관된 두 가지의 의미론적 벡터를 찾을 수 있다는 점이며, CBOW 보다 결과가 더 좋다고 한다.

자연어 처리 모델(향후 업데이트 예정)

  • CNN
  • RNN
  • Recursive Neural Network

sequence to sequence

attention

최근에 각광 받고 있는 attention 모델에 대해서 간단히 살펴보고자 한다. 간단히 말하면 attention이라는 이름처럼 딥러닝 모델이 특정 벡터에 주목하게 만들어서 성능을 높이는 방법이다.

attention 모델이 도입된 동기는 s2s(sequence-to-sequence) 모델에서 처음 도입되었다. s2s에서는 입력값을 벡터로 만들어주는 인코더(encoder)와 출력된 벡터를 타켓랭귀지로 출력하는 디코더(decoder)로 구성되어 있다. 하지만 입력값이 길어질 수록 그 성능이 떨어지기 때문에 중요한 부분만 집중하게 만드는 attention 모델이 나오게 되었다.

자연어 처리 학습에 유용한 자료(국문)

  • ratsgo’s blog: 상세하고 방대한 자료 말이 필요 없다.
  • lucy Park’s Blog: 현재 네이버 연구원으로 계시는 박은정님의 자료도 추천한다. 자연어 처리를 공부하신만큼 검색하다보면 굉장히 실용적이면서 유용한 자료를 많이 얻을 수 있다. 최근에는 바쁘셔서 활동을 안하신다ㅜㅜ

References