자연어와 단어의 분산 표현

자연어 처리란

컴퓨터 프로그래밍 언어, 마크업 언어가 아닌 인간이 말하고 쓰는 언어를 자연어라고 하며, 자연어 처리라고 하면 컴퓨터가 자연어를 컴퓨터가 이해할 수 있게 하는 기술이나 기법이다.

예시로는 검색 엔진, 기계 번역, 질의 응답 시스템 등이 이미 있다.

인간의 말은 문자로 구성되며, 말의 의미는 단어로 구성된다. (책에서 설명한 내용으로, 언어학 지식도 필요해 보인다.) 즉, 단어는 의미의 최소 단위로 봐도 되겠다.

그래서 자연어를 컴퓨터에게 이해시킬 때 단어의 의미를 이해시키는게 중요하다.

책에서는 이를 위한 기법 3가지를 소개한다.

  • 시소러스를 활용한 기법
  • 통계 기반 기법
  • 추론 기반 기법(word2vec) -> 다음 장에서 소개

시소러스 (p.79)

자연어 처리 초기 방식은 단어를 뜻과 대응시켜 컴퓨터에게 이해시키려고 하는 방법이 자연스러울 것이다. 하지만 인간의 사전처럼 이해시키는 것이 아닌 시소러스 기법을 사용하는 것이다.

시소러스란 유의어 사전으로, 뜻이 같은 단어(동의어)나 뜻이 비슷한 단어(유의어)가 한 그룹으로 분류되어 있다.

또한 자연어 처리에 이용되는 시소러스에서는 단어 사이의 상위와 하위 혹은 전체와 부분 등, 더 세세한 관계까지 정의해둔 경우가 있다. -> p.80을 참고하면 단어의 네트워크가 생성된 것을 볼 수 있다.

WordNet

  • 프린스턴 대학교에서 1985년부터 구축하기 시작한 전통 있는 시소러스
  • WordNet을 사용하면 유의어를 얻거나 '단어 네트워크'를 이용할 수 있다.
  • 단어 네트워크를 사용해 단어 사이의 유사도를 구할 수도 있다.
  • 부록에 WordNet 구현 부분이 있다. (WordNet 맛보기!)

시소러스의 문제점

  • 시대 변화에 대응하기 어렵다 : ex) Back to The Future; 언어의 뜻은 계속 변화되고, 신조어들은 계속해서 생겨나니 수작업으로 계속해서 갱신해야 된다.
  • 사람을 쓰는 비용이 크다 : 현존하는 영어 단어 수가 1,000만 개가 넘고 WordNet에 등록된 단어는 20만 개 이상이다.
  • 단어의 미묘한 차이를 표현할 수 없다 : 뜻은 같으나 뉘앙스가 다른 몇몇의 단어들을 표현할 쉬운 방법이 없고, 이를 구현하려고 수작업한다면 이 또한 문제가 될 것이다.

통계 기반 기법 (p.82)

  • 말뭉치(corpus, 대량의 텍스트 데이터; 자연어 처리 연구나 애플리케이션을 염두에 두고 수집된 텍스트 데이터)
  • 말이란 것 자체가 이미 사람이 쓴 글이다. 말뭉치에는 자연어에 대한 사람의 지식이 이미 담겨 있다고 할 수 있다. -> 문장을 쓰는 방법, 단어를 선택하는 방법, 단어의 의미 등
  • 이를 통계 기반으로 말뭉치에서 자동으로, 그리고 효율적으로 그 핵심을 추출하는 것이다.

파이썬으로 말뭉치 전처리 구현을 해본다. (p.83)

  • 정규표현식을 사용하면 전처리가 더 쉽다..?(p.84)

단어의 분산 표현 : 많은 분야에서 벡터화해 물질을 표현하는 것이 더 직관적이고, 정량화 하기 쉽다. (책에서는 RGB색을 예를 든다.) 이처럼 단어도 벡터로 표현하는 것을 자연어 처리 분야에선는 단어의 분산 표현(distributional representation)이라고 한다.

  • 단어의 분산 표현은 단어를 고정 길이의 밀집벡터(dense vector)로 표현한다.

분포 가설(distributional hypothesis) : 단어 자체에는 의미가 없고, 그 단어가 사용된 '맥락'(context)이 의미를 형성한다는 것.

  • 단어를 벡터로 표현한는 연구는 수없이 이뤄져 왔고, 그 연구들의 중요한 기법들이 대부분 이 분포 가설이라는 아이디어를 기초에 둔다.
  • '단어의 의미는 주변 단어에 의해 형성된다'라고도 말할수 있다.
  • 맥락의 크기를 윈도우 크기(window size)라고 하고 단어 주변 단어의 수를 가르킨다.

통계 기반(statistical based) : 어떤 단어에 주목했을 때, 그 주변에 어떤 단어가 몇 번이나 등장하는지를 세어 집계하는 방법 -> 이 방법을 이 책에서는 통계 기반이라고 한다.

동시발생 행렬(co-occurrence matrix) : 모든 단어에 대해 동시발생하는 단어를 표에 정리한 것 -> 표가 행렬 형태를 띤다는 뜻에서 동시발생 행렬이라고 한다.
(p.86)부터 동시발생 행렬 구현 시작(자동화 가능! (p.91)) -> 동시발생 행렬로 단어를 정리한다면 단어를 벡터로 나타낼 수 있다.

벡터 간 유사도

  • 벡터 사이의 유사도를 측정하는 방법은 다양하다. ex) 벡터의 내적, 유클리드 거리

  • 단어 벡터의 유사도를 나타날 때는 코사인 유사도(cosine similarity)를 자주 이용한다.
    similarity(x,y)(x,y) = x×yxyx \times y \over \vert\vert x \vert\vert y\vert\vert = x1y1+...+xnynx12+...+xn2y12+...+yn2x_1y_1 + ... + x_ny_n \over \sqrt {x_1^2 + ... + x_n^2} \sqrt {y_1^2 + ... + y_n^2}

    • 분자에는 벡터의 내적이, 분모에는 각 베터의 노름(norm)이 등장한다.
    • 노름은 벡터의 크기를 나타낸 것으로 위에 노름은 L2 노름'을 계산한다.
    • 이 식의 핵심은 벡터를 정규화하고 내적을 구하는 것이다. -> 직관적으로 풀어보면 '두 벡터가 가리키는 방향이 얼마나 비슷한가' 이다.
    • 코사인 유사도 구현 (p.93) -> 0으로 나누지 않도록 1e-8을 넣어야 한다. (eps, epsilon)
    • 코사인 유사도를 사용해 단어 벡터의 유사도 구하기 구현 (p.93)
  • 유사 단어의 랭킹을 보기 쉽게 표시하는 구현 (p.94)

통계 기반 기법 개선하기 (p.97)

동시방생 행렬을 개선해 보는 작업을 해본다. -> 진짜 진짜 단어의 분산 표현을 해본다.

앞 절에서는 단어의 빈도수를 기준으로 특징을 잡아 유사도를 알아봤다. 하지만 빈도수만으로 유사도를 확인하기에는 문제점이 많다.
(직관적인 예시로는 'the'와 'car'이 'car'와 'drive'보다 빈도수가 높다.)

그래서 새로운 판단 척도로 PMI를 사용한다.

점별 상호정보량(Pointwise Mutual Information, PMI)

: PMI는 확률 변수 x와 y에 대해 다음 식으로 정의된다.

(확률은 다음 챕터에서 설명한다.)

PMI(x,y)(x,y) = log2\log_2 P(x,y)P(x)P(y)P(x,y) \over P(x)P(y)

  • P(x)는 x가 일어날 확률 , P(y)는 y가 일어날 확률 , P(x,y)는 x와 y가 동시에 일어날 확률이다.
  • PMI 값이 높을수록 관련성이 높다.
  • 예시로 P(x)는 단어 x가 말뭉치에 등장할 확률을 가르킨다.

예시로 통 10,000개의 말뭉치에서 'the'가 100번 등장한다면 PP('the') = 10010000100 \over 10000 = 0.010.01이 된다.

또, 단어 동시발생 확률로 'the'와 'car'를 대입해보고 값이 10번이 나온다면 PP('the', 'car') = 101000010 \over 10000 = 0.0010.001이 되는 것이다.

동시발생 행렬(각 원소는 동시발생한 단어의 횟수)을 사용하여 위에 식을 다시 써본다면,

PMI(x,y)(x,y) = log2\log_2 P(x,y)P(x)P(y)P(x,y) \over P(x)P(y) = log2\log_2 C(x,y)NC(x)NC(y)N{C(x,y) \over N} \over {C(x) \over N}{C(y) \over N} = log2\log_2 C(x,y)×NC(x)C(y)C(x,y) \times N \over C(x)C(y) 이 된다.
위에 CC는 동시발생 행렬, C(x,y)C (x,y)는 단어 x와 y가 동시발생하는 횟수... 이다.
NN는 말뭉치에 포함된 단어 수이다.

  • 위에 식을 사용하면 동시발생 행렬로부터 PMI를 구할 수 있다.
  • 각 단어의 동시발생 행렬 PMI 계산 결과 값은 (p.98)에 있다. -> 'the'와'car' 보다 'car'와 'drive'의 PMI 점수가 더 높게 나온다!
    • 이 결과의 이뉴는 단어가 단독으로 출연하는 횟수가 고려되었기 때문이다. (다음 챕터에서 설명한다고 했는데 자세히 안 나오면 따로 찾아봐야 겠다.)
    • 이 예에서는 'the'가 자주 출현했으므로 PMI 점수가 낮아진 것이다.
  • PMI의 한 가지 문제점은 두 단어의 동시발생 횟수가 0이면 log20=\log_20 = -\infin 가 된다는 것이다.
  • 이를 위한 해결법으로 양의 상호정보량(Positive PMI, PPMI)를 사용한다.

PPMI(x,y)(x,y) = max(0,\max (0, PMI(x,y))(x,y))

  • 위 식은 PMI가 음수일 때는 0으로 취급한다.

동시발생 행렬을 PPMI 행렬로 변환하는 함수 구현 (p.99)
동시발생 행렬을 PPMI 행렬로 변환하기 (p.100)

차원 감소 (dimensionality reduction)

차원 감소 : 중요한 정보를 가진 벡터들은 최대한 보존하면서 벡터의 차원을 줄이는 방법

PPMI 행렬에도 여전히 문제가 있는데, 그것은 말뭉치의 어희 수가 증가함에 따라 각 단어 벡터의 차원 수도 선형적으로 증가한다는 문제다.

또한 대부분의 원소들은 0이다. -> 중요하지 않은 내용들이다. & 이런 벡터는 노이즈에 약하고 견고하지 못하다는 약점도 있다.

위에 문제들을 해결하고자 벡터의 차원 감소 기법을 사용한다.

  • 데이터의 분포를 고려해서 중요한 '축'을 찾는 일을 수행해야 한다. (p.102 그림 참고)
  • 각 데이터점의 값은 새로운 축으로 사영된 값으로 변한다. 그리고 이 축을 1차원 값으로 나타내고 비교했을 때 데이터의 본질적인 차이를 구별할 수 있어야 한다. (나도 직관적으로만 이해가 됐다. 찾아봐야 할 듯 하다.)

이 책에서는 차원을 감소시키는 방법으로 특이값분해를 이용한다.

특이값분해(Singular Value Decomposition, SVD) : 임의의 행렬을 세 행렬의 곱으로 분해한다. 수식은 아래와 같다.
X=USVTX = USV^T

  • 위 식과 같이 행렬 XXU,S,VU, S, V라는 세 행렬의 곱으로 분해한다.
  • UUVV는 직교행렬(orthogonal matrix)이고, 그 열벡터는 서로 직교한다.
  • SS는 대각행렬(diagonal matrix)(대각성분 외에는 모두 0인 행렬)이다.
  • (p.103 그림 참고)

UU는 직교행렬이다. -> 어떤한 공간의 축(기저)을 형성한다. -> 지금 맥락에서는 이 UU행렬을 '단어 공간'으로 취급할 수 있다.

SS는 대각행렬이다. -> 대각성분에는 '특잇값(singular value)'이 큰 순서로 나열되어 있다. -> 특잇값은 '해당 축'의 중요도라고 간주 할 수 있다. -> 그래서 중요도가 낮은 원소 (특이값이 작은 원소)를 깎아내는 방법을 생각할 수 있다.
(저도 정확히 이해되지 않았고 작가님도 직관적으로 설명했다고 하고 참고문헌을 남겼다. 따로 찾아봐야 될 듯하다.)

SVD를 사용해서 PPMI행렬에 적용하기 구현(p.104) 앞에 구현한 PPMI의 행렬의 문제점인 행렬을 깍아내는 구현이라고 봐도 되겠다.

  • 말뭉치 데이터가 적어서(I say hello and you say goodbye. 이니까) 구현 결과를 봐도... 그냥 결과다.

PTB 데이터셋 (Penn TreeBank)

  • 이 말뭉치 데이터셋은 주어진 기법의 품질을 측정하는 벤치마크로 자주 이용된다.
  • word2vec의 발명자인 토마스 미콜로프(Tomas Mikolov)의 웹페이지에서 받을 수 있다.
  • 텍스트 파일로 제공되며, 원래의 PTB 문장에 몇 가지 전처리가 돼 있다. (희소한 단어의 특수 문자로 치환, 구체적인 숫자 대체 등)
  • 책에서는 각 문장을 연결한 '하나의 큰 시계열 데이터'로 취급한다. (문장 단위로 구분, 처리 할 수 있지만, 간단하게 넘어간다고 한다.)

PTB 사용 예시 구현 (p.108)
PTB 데이터셋에 통계 기반 기법 적용(p.109) -> 위에서 구현한 내용 전부 -> SVD는 큰 행렬에서 적용해야 하므로 고속 SVD 사용 (sklearn 모듈)

요약

큰일났다...! 자연어 처리가 진짜 찐또배기다... 합성곱은 어려운 것도 아니었다... 대충 설명하고 넘어가는 수학 부분이 너무 많다. 아니 설명도 안 한다. 특히 선형대수학의 필요성을 뼈저리게 느꼈다.

다음 장인 추론 기반 기법에서는 얼마나 난리일지 감도 안온다...

이 챕터가 자연어 처리 부분에 근본이 되는 기술과 이론들을 모아놓은 부분일 것이다.

구현 : https://github.com/Jaejuna/DL_from_scratch

profile
Learning bunch, mostly computer and language

0개의 댓글