나/이슈

NLP 노트

Lou Park 2021. 3. 3. 00:06

단어의 표현 방법 Word Representation


국소 표현 Local Representation:

단어를 기호로 취급, 정수 등 특정값을 매핑하여 단어를 표현, Discrete Representation

- Bag of Words(BoW), N-gram, One-hot Vector

 

분산 표현 Distributed Representation:

단어의 의미와 뉘앙스 표현가능. 주변 단어를 참고하여 단어를 표현, Continuous Representation

- Word2Vec, LSA, GloVe

 

카운트 기반의 단어 표현


# Bag of Words

빈도 수 기반의 표현방법. 단어의 등장 순서를 전혀 고려하지 않음 ("Bag").

scikit learn의 CountVectorizer를 이용하면 쉽게 만들 수 있다.

- (1) 각 단어에 고유한 Index 부여

- (2) 각 Index에 해당 단어의 빈도 수 (frequency)를 기록한 벡터 생성

 

문서 단어 행렬 Document-Term Matrix (DTM)

표현 방법은 BoW와 같음, 공통의 Vocabulary에 대한 여러 문서의 BoW 행렬이라는 점이 다르다.

One-hot Vector와 마찬가지로 (단어 집합의 크기) x (문서의 수) 만큼의 행렬이 생성되고 , 대부분의 값이 0인 Sparse Vector가 될 수 있기 때문에 전처리를 통해 Vocabulary 자체를 의미있게 줄이는 것이 중요하다.

 

TF-IDF, Term Frequency-Inverse Document Frequency

TF-IDF를 이용하면 문서에서 각 단어의 중요도에 대한 가중치를 구할 수 있다.

TF-IDF는 TF와 IDF의 곱이다.

 

- TF: 특정 문서에서 특정단어의 등장 횟수

- DF: 특정단어가 등장한 문서의 수

- IDF: DF에 반비례하는 수. 하지만 역수를 취하지는 않고 분모 + 1에 log를 씌워 전체 문서에 해당 단어가 하나도 없을때 Divide by zero 문제와 희귀 단어들에 엄청난 가중치가 부여되는 상황을 막는다.

식으로 표현하면 IDF = ln ( TotalDocumentCount / (1 + DF) ) 가된다. TF-IDF에서는 자연로그(ln)을 사용한다.

예를들어 4개의 문서에서 단어 t가 2개의 문서에서 등장 했다면 lDF, 즉 역 문서빈도는 ln(4 / (1 + 2)) = 0.28768이된다.

 

TF-IDF는 DTM의 행렬에서 (단어의 빈도 수 * 가중치)로 표현할 수 있다. 각 문서에서 중요도가 높은 단어가 많이 사용되면 값이 높아진다.

 

문서 유사도


코사인 유사도 Cosine Similarity

코사인 유사도는 두 벡터 간의 코사인 각도를 이용해 구할 수 있다. 

-1 ~ 1 사이의 값을 가지는데, 서로 역방향이면 -1, 같은 방향일 경우 1, 90도 각도로 전혀 다른 방향일경우 0이다.

따라서 코사인 유사도가 1에 가까울 수록 두 문서가 유사하다고 볼 수 있다.

 

TF-IDF와 코사인 유사도를 사용하여 추천 시스템을 만들 수도 있다.

 

 

자카드 유사도 Jaccard Similarity

자카드 유사도는 0~1의 값을 가지며, 두 집합이 동일하면 1, 공통 원소가 없다면 0의 값을 갖는다. 

두 문서의 총 단어 집합에서 공통적으로 등장한 단어의 비율이다.

- (1) 두 문서에서 토큰화를 수행하여 두 문서 토큰의 합집합 u를 구한다.

- (2) 두 문서토큰의 교집합 i 를 구한다.

- (3) 자카드 유사도를 파이썬으로 표기하면 jaccard_sim = len(i) / len(u)가 된다. 

 

토픽 모델링


토픽 모델링이란 머신 러닝, NLP 분야에서 문서 집합의 추상적인 주제 (Topic)를 발견하기 위한 통계적 모델 중 하나로, 텍스트 본문의 숨겨진 의미 구조를 발견하기 위해 사용되는 텍스트 마이닝 기법이다.

 

# 잠재 의미 분석 Latent Semantic Analysis (LSA)

BoW 같은 단어의 빈도 수를 이용한 수치화 방법은 단어의 의미(토픽)를 고려하지 못한다는 단점이있다. 이에 대한 대안이 DTM의 잠재된 의미를 이끌어내는 방법인 LSA다. 

 

특이값 분해 Singular Value Decomposition (SVD)

LSA를 이해하기 위해서 선형대수학의 SVD를 이해해야한다.  

SVD란,  A = (m x n 행렬)일때 3개의 다른 행렬의 곱으로 분해되는 것을 말한다.

A = U(m x m 직교행렬) x V(n x n 직교행렬) x Σ(m x n 직사각 대각행렬)가 된다.

 

행렬 설명 시간

* 주 대각선: 행렬을 가르는 백슬래시(\) 방향의 대각선. 원소의 행의 값과 열의 값이 같다. (0, 0) ... (n, n)

- 전치행렬 Transposed Matrix: 원래 행렬이 m x n 이었다면 n x m 으로 행과 열을 바꾼 행렬. [A에 작은 T를 붙여 표현]

- 단위행렬 Identity Matrix: 주 대각선의 원소가 모두 1이고 나머지 원소들은 0인 정사각 행렬.  [대문자 I]

- 역행렬 Inverses Matrix: 행렬 A와 어떤 행렬 X를 곱했을때, 결과가 단위행렬이 나온다면  X는 A의 역행렬이다. [A에 작은 -1]

- 직교행렬 Orthogonal Matrix: 정사각 행렬 A과 A의 전치행렬 T가 있을때, A x T = I && T x A = I 인 행렬 A를 직교 행렬이라고 한다.

- 대각행렬 Diagonal Matrix: 주 대각선을 제외한 곳의 원소가 모두 0인 행렬을 의미한다. [Σ]

 

절단된 SVD Truncated SVD

위에서 설명한 SVD를 Full SVD라고 한다. 하지만 LSA는 풀SVD에서 나온 3개의 행렬에서 일부 벡터들을 삭제시킨 절단된 SVD를 사용한다.

t = 우리가 찾고자 하는 토픽의 수를 반영한 HyperParameter 값일때, 절단된 SVD에서는 대각행렬 Σ의 대각원소 중에서 상위값 t개만 남게된다.나머지 직교행렬U,는 행렬의 t열까지만 남기고 자른다.

- 장점 1: 계산 비용이 낮아진다.

- 장점 2: 상대적으로 중요하지 않은 정보를 삭제하는 효과가 있다.

 

*Hyper Parameter: 사용자가 직접 값을 선택, 성능에 영향을 주는 매개변수. 예를들어 어떤 게임이 어드벤쳐, 공포, 퍼즐, 꾸미기 4가지의 카테고리에서 어떤게 가장 가까운가? 가 궁금할때는 t = 4로 줄 수 있다.

 

잠재 의미 분석

TF-IDF 행렬은 단어의 빈도수만 고려할뿐, 단어의 의미를 전혀 고려하지 못한다는 단점을 갖고있다. LSA는 DTM이나 TF-IDF 행렬에 절단된 SVD를 사용하여 차원을 축소시켜 단어들의 잠재적인 의미를 끌어낸다.

 

어떤 4x9의 DTM A가 있을때 np.linalg.svd(A, full_matrices=True)로 SVD를 구할 수 있다. 

U, s, VT = np.linalg.svd(A, full_matrices=True) 이다. 여기서 s는 직사각 대각행렬인데, Numpy의 linal.svd()가 대각행렬이 아니라 특이값의 리스트를 반환하기 때문에 이를 다시 대각행렬 S로 형태를 바꾸어 주어야 한다.

S = np.zeros((4, 9)) # 대각 행렬의 크기인 4x9임의 행렬 생성
S[:4, :4] = np.diag(s) # 특이값을 대각행렬에 삽입

풀 SVD를 구했으므로, U * S * VT = A가 되어야 한다.  Numpy의 allclose()는 2개의 행렬이 동일하면 True를 반환하는데, 이를 이용해 기존 행렬의 A와 동일한지 확인을 할 수 있다.

np.allclose(A, np.dot(np.dot(U, S), VT))

 

이제 절단된 SVD를 구해야한다. 찾고자하는 토픽의 수인 t를 먼저 정해야한다. t=2일때, 대각행렬 S내의 특이값 중, 상위 2개만 남기고 제거를 하면 이렇게 된다. (S에서 대각선으로 값이 내림차순이라 2x2만 끊어주면 된다.)

S = S[:2, :2]

 

직교행렬 U에도 2개의 만 남기고 제거한다.

U = U[:, :2] 

 

행렬 V의 전치행렬인 VT 역시 2개의 만남기고 제거한다.

VT = VT[:2, :]

 

이제 절단된 SVD를 곱하면 기존의 A와는 다른 값을 갖게 된다. 이제 절단된 U * S * VT 연산의 결과값을 A_prime이라고 할때, 기존의 행렬 A와 값을 비교하면,

# A
[[0 0 0 1 0 1 1 0 0]
 [0 0 0 1 1 0 1 0 0]
 [0 1 1 0 2 0 0 0 0]
 [1 0 0 0 0 0 0 1 1]]
 
# A_prime
[[ 0.   -0.17 -0.17  1.08  0.12  0.62  1.08 -0.   -0.  ]
 [ 0.    0.2   0.2   0.91  0.86  0.45  0.91  0.    0.  ]
 [ 0.    0.93  0.93  0.03  2.05 -0.17  0.03  0.    0.  ]
 [ 0.    0.    0.    0.    0.    0.    0.    0.    0.  ]]

기존에 0인 값들은 0에 가까운 값이, 1인 값들은 1에 가까운 값이 나온다. 

 

축소된 U는 4* 2 의 크기를 가지는데 이는 문서의 개수 * 토픽의 수(t)의 크기다. 원래 DTM의 크기는 4 * 9로 9개의 단어에 대한 것이었지만 4개의 문서 각각을 2개의 값으로 표현을 하게 되었다. U의 각 행은 잠재의미를 표현하기 위한 수치화된 각각의 문서 벡터라고 볼 수 있다.

 

축소된 VT는 2* 9로, 토픽의 수(t) * 단어의 개수다. VT의 각 열은 잠재의미를 표현하기 위해 수치화된 각각의 단어 벡터라고 볼 수 이싸.

 

이 문서 벡터들과, 단어 벡터들을 통해 다른 문서와의 유사도, 다른 단어의 유사도, 단어로부터 문서의 유사도를 구하는 것들이 가능하다.

하지만 SVD의 특성상 이미 계산된 LSA에 새로운 데이터를 추가하여 계산하려고 하면 처음부터 다시 계산을 해야하는 문제가 있다. SVD구하기부터....전부다시~ 즉, 새로운 정보에 대한 업데이트가 어렵다. 이는 최근 LSA 대신 Word2Vec 등 단어의 의미를 벡터화할 수 있는 또다른 방법론인 인공 신경망 기반의 방법론이 떠오르는 이유가 되기도 한다.

 

잠재 디리클레 할당 Latent Dirichlet Allocation (LDA)

잠재 디리클레 할당은 토픽 모델링의 대표적 알고리즘이다. LDA는 문서의 집합으로부터 어떤 토픽이 존재하는지 알아내기위해서 BoW의 행렬 DTM 또는 TF-IDF 행렬을 입력으로 한다. 즉 LDA는 단어의 순서는 신경쓰지 않는다.

 

LDA에서 문서는 다음과 같은 과정을 거쳐서 작성되었다고 가정하고있다.

 

1) 문서에 사용할 단어의 개수 N을 정한다.

2) 문서에 사용할 토픽의 혼합을 확률 분포에 기반하여 결정한다. 예를들면 토픽이 2개일때 A 토픽 60%, B 토픽 40%와 같이 선택하여 작성할 수 있다.

3) 토픽 분포에서 토픽 T를 확률적으로 고르고 (A 토픽은 60% 확률로 골라지는 것)

4) 선택한 토픽 T에서 단어의 출현 확률 분포에 기반해 문서에 사용할 단어를 고른다. 3,4를 반복하며 문서를 완성한다.

 

LDA는 위 과정에 Reverse Engneering을 수행하여 토픽을 뽑아낸다.

 

LDA의 수행 과정

1. 사용자는 알고리즘에 토픽의 개수 k를 알려준다.

LDA에서 토픽의 수는 Hyper Parameter에 해당한다. LDA는 토픽의 개수 k를 사용자로 부터 입력 받으면, k개의 토픽이 M개의 전체 문서에 걸쳐 분포되어 있다고 가정한다.

 

2. 모든 단어를 k개 중 하나의 토픽에 할당한다.

LDA는 모든 문서의 모든 단어에 대해서 k개중 하나의 토픽을 랜덤으로 할당한다. 

이 작업이 끝나면 각 문서는 토픽과, 토픽의 단어 분포를 가지게 된다. 

 

3. 모든 문서의 모든 단어에 대해 아래 사항을 반복 진행한다.

 

4. 어떤 문서 단어의 w는 자신은 잘못된 토픽에 할당 되어있지만, 다른 단어들은 전부 올바른 토픽에 할당되어져있는 상태라고 가정한다. 이에 따라 단어 w는 아래의 두 가지 기준에 따라서 토픽이 재할당된다.

- 문서 d의 단어들 중 토픽 t에 해당하는 단어들의 비율

- 각 토픽들 t에서 해당 단어 w의 분포

 

LSA와 LDA의 차이

LSA: DTM을 차원 축소하여 축소한 차원에서 근접 단어들을 토픽으로 묶는다.

LDA: 단어가 특정 토픽에 존재할 확률과 문서에 특정 토픽이 존재할 확률을 결합확률로 추정하여 토픽을 추출한다.

 

 

 

 

 

 

 

 

 

 

 

 

* 이 글은 wikidocs.net/24949 를 공부하면서 이해를 돕기 위해 옮겨 쓴 것입니다.