2.1 자연어 처리란
2.1.1 단어의 의미
- 말의 의미는 '단어'로 구성
- 컴퓨터에게 '단어의 의미'를 이해시키자
세 가지 기법
- 시소러스를 활용한 기법 (2장)
- 통계 기반 기법 (2장)
- 추론 기반 기법(word2vec) (3장)
2.2 시소러스
시소러스
- 유의어 사전
- '뜻이 같은 단어(동의어)'나 '뜻이 비슷한 단어(유의어)'가 한 그룹으로 분류
- 단어 사이의 '상위와 하위' 혹은 '전체와 부분' 등 더 세세한 관계까지 정의해둔 경우가 있음
- 모든 단어에 대한 유의어 집합을 만든 다음, 단어들의 관계를 그래프로 표현하여 단어 사이의 연결을 정의
- '단어 네트워크'를 이용하여 컴퓨터에게 단어 사이의 관계를 가르칠 수 있음
- 시소러스의 사용 : 검색 엔진에서 유의어 검색 결과를 포함시켜주면 좋을 것
2.2.1 WordNet
- 자연어 처리 분야에서 가장 유명한 시소러스
- 프린스턴 대학교에서 1985년부터 구축하기 시작한 전통 있는 시소러스
- 많은 연구와 다양한 자연어 처리 애플리케이션에 활용
- 유의어를 얻거나 '단어 네트워크' 이용할 수 있음
- 단어 사이의 유사도 구할 수 있음
2.2.2 시소러스의 문제점
- 시대 변화에 대응하기 어렵다
- 새로운 단어 생겨남
- 시대에 따라 언어 의미 변함 - 사람을 쓰는 비용은 크다
- 엄청난 인적 비용을 들여, 방대한 단어들 모두에 대한 단어 사이의 관계 정의해야 함 - 단어의 미묘한 차이를 표현할 수 없다
- 시소러스에서는 뜻이 비슷한 단어들을 묶음
- 그러나 비슷한 단어에도 미묘한 차이가 있음 (ex. 빈티지 vs 레트로)
=> '통계 기반 기법'과 신경망을 사용한 '추론 기반 기법'
이를 통해 대량의 텍스트 데이터로부터 '단어의 의미'를 자동으로 추출
2.3 통계 기반 기법
말뭉치(corpus)
- 대량의 텍스트 데이터
- 맹목적으로 수집된 텍스트 데이터가 아닌 자연어 처리 연구나 애플리케이션을 염두에 두고 수집된 텍스트 데이터
통계 기반 기법의 목표
- 사람의 지식으로 가득한 말뭉치에서 자동으로, 효율적으로 그 핵심을 추출하는 것
2.3.1 파이썬으로 말뭉치 전처리하기
텍스트 데이터 전처리
- 데이터를 단어로 분할하고 그 분할된 단어들을 단어 ID 목록으로 변환하는 일
- 모든 문자 소문자로 변환, 공백 기준으로 분할
- 단어를 텍스트 그대로 조작하면 여러 면에서 불편하므로, 단어에 ID를 부여하고 ID의 리스트로 이용할 수 있도록 손질
- 파이썬 딕셔너리를 이용하여 단어 ID와 단어를 짝지어주는 대응표 작성
→ 단어를 가지고 단어 ID 검색, 단어 ID 가지고 단어 검색
text = 'You say goodbye and I say hello.'
# id_to_word
{0: 'you', 1: 'say', 2: 'goodbye', 3: 'and', 4: 'y', 5: 'hello', 6: '.'}
# word_to_id
{'you': 0. 'say': 1, 'goodbye': 2, 'and': 3, 'i': 4, 'hello': 5, '.': 6}
2.3.2 단어의 분산 표현
비유
- '색'을 RGB와 같은 벡터 표현으로 더 정확하게 명시
- '단어'도 벡터로 표현할 수 있을까?
분산 표현(distributional representation)
- '단어의 의미'를 정확하게 파악할 수 있는 벡터 표현
2.3.3 분포 가설
분포가설
- '단어의 의미는 주변 단어에 의해 형성된다'
- 자연어 처리 역사에서 중요 아이디어
- 단어 자체에는 의미가 없고, 그 단어가 사용된 '맥락(context)'이 의미를 형성
- ex) 'I drink beer' & 'We drink wine' → drink 주변에는 음료 등장하기 쉬움
'I guzzle beer' & 'We guzzle wine' → guzzle과 drink는 가까운 의미의 단어
맥락
- 특정 단어를 중심에 둔 그 주변 단어
- 맥락의 크기(주변 단어를 몇 개 포함할지) = '윈도우 크기'
2.3.4 동시발생 행렬
통계 기반(statistical based) 기법
- 분포 가설에 기초해 단어를 벡터로 나타내는 방법
- 주변 단어를 '세어 보는' 방법
- 어떤 단어에 주목했을 때, 그 주변에 어떤 단어가 몇 번이나 등장하는지를 세어 집계하는 방법
동시발생 행렬
- 모든 단어에 대해 동시발생하는 단어를 표에 정리한 것
- 행렬의 형태를 띤다는 뜻
- 동시발생 행렬을 활용하면 단어를 벡터로 나타낼 수 있음
- 위의 과정을 코드로 자동화하여 사용
2.3.5 벡터 간 유사도
벡터 사이의 유사도 측정 방법
- ex) 벡터의 내적, 유클리드 거리
- 단어 벡터의 유사도를 나타낼 때는 코사인 유사도(cosine similarity)를 자주 이용
코사인 유사도(cosine similarity)
- 분자 : 벡터의 내적
- 분모 : 각 벡터의 노름(norm)
* 노름 : 벡터의 크기, 여기서는 L2 노름 계산(각 원소 제곱 합의 제곱근) - 직관적으로 '두 벡터가 가리키는 방향이 얼마나 비슷한가'를 의미
- 벡터의 방향이 완전히 같다면 코사인 유사도 1, 완전히 반대라면 -1
- 'You say goodbye and I say hello.'
이를 코드로 구현하여 you와 i의 코사인 유사도 0.70...이 나옴 - 코사인 유사도 값은 -1 ~ 1 → 0.7은 값이 비교적 높다(유사성이 크다)
2.3.6 유사 단어의 랭킹 표시
어떤 단어가 검색어로 주어지면, 그 검색어와 비슷한 단어를 유사도 순으로 출력하는 함수
다음을 순서로 동작하는 코드 구현
- 검색어의 단어 벡터를 꺼낸다.
- 검색어의 단어 벡터와 다른 모든 단어 벡터와의 코사인 유사도를 각각 구한다.
- 계산한 코사인 유사도 결과를 기준으로 값이 높은 순서대로 출력한다.
2.4 통계 기반 기법 개선하기
2.4.1 상호정보량
동시발생 행렬의 원소
- = 두 단어가 동시에 발생한 횟수
- '발생' 횟수는 그리 좋은 특징이 아님
- ex) the와 car의 동시 발생 횟수는 아주 많을 것 → 관련성이 높아서가 아닌, the가 고빈도 단어이기 때문
이를 해결하기 위한 방법
점별 상호정보량(Pointwise Muual Information, PMI)
- P(x) : x가 일어날 확률
- P(y) : y가 일어날 확률
- P(x, y) : x와 y가 동시에 일어날 확률
- PMI 값이 높을수록 관련성이 높음
동시발생 행렬(각 원소는 동시발생한 단어의 횟수)를 사용하여 다시 쓴 [식 2.2]
- C : 동시발생 행렬(각 원소는 동시발생한 단어의 횟수)
- C(x, y) : 단어 x와 y가 동시발생하는 횟수
- C(x), C(y) : 각각 단어 x와 y의 등장 횟수
- N : 말뭉치에 포함된 단어 수
PMI를 활용한 예시
- PMI를 이용하면 car는 the 보다 drive와의 관련성이 강해짐
그러나, PMI의 문제
- PMI는 두 단어의 동시발생 횟수가 0이면 log0 = -∞ 가 됨
- 이를 해결하기 위해 PPMI
양의 상호정보량(Positive PMI, PPMI)
PMI가 음수일 때는 0으로 취급
그러나, PPMI 행렬의 문제
- 말뭉치의 어휘 수가 증가함에 따라 각 단어 벡터의 차원 수도 증가한다는 문제
- 행렬 내부 원소 대부분이 0, 각 원소의 '중요도'가 낮음, 노이즈에 약하고 견고하지 못하다는 단점
이를 해결하기 위해 벡터의 차원 감소
2.4.2 차원 감소
차원 감소(dimensionality reduction)
- 벡터의 차원을 줄이는 방법
- '중요한 정보'는 최대한 유지하면서 줄이는 것이 핵심
- 데이터의 분포를 고려하여 중요한 '축'을 찾는 일 수행
- 왼쪽은 2차원 좌표에 표시한 데이터점, 오른쪽은 새로운 축을 도입하여 똑같은 데이터를 좌표축 하나만으로 표시
- 1차원 값만으로도 데이터의 본질적인 차이 구별할 수 있어야 함
- 원소 대부분이 0인 희소행렬(희소벡터)에서 중요한 축을 찾아내 '밀집벡터'로 변환한 것 → 원하는 단어의 분산 표현
특잇값분해(Singular Value Decomposition, SVD)
- 차원을 감소시키는 방법
- 임의의 행렬 X를 U, S, V라는 세 행렬의 곱으로 분해
- U와 V는 직교행렬(orthogonal matrix), 그 열벡터는 서로 직교
- S는 대각행렬(diagonal matrix)(대각 성분 외에는 모두 0인 행렬)
- 행렬 U : 직교행렬, 어떠한 공간의 축9기저)을 형성, 맥락상 '단어 공간'으로 취급할 수 있음
- 행렬 S : 대각행렬, 대각성분에는 '특잇값(singular value)'이 큰 순서로 나열 (*특잇값 : '해당 축'의 중요도)
- [그림 2-10]과 같이 중요도가 낮은 원소(특잇값 작은 원소) 깎아내는 방법
- 행렬 S에서 특잇값이 작다면 중요도 낮음
→ 행렬 U에서 여분의 열벡터를 깎아내어 원래의 행렬을 근사
2.4.3 SVD에 의한 차원 감소
동시발생 행렬을 만들어, PPMI 행렬로 변환한 다음, SVD 적용
# coding: utf-8
import sys
sys.path.append('..')
import numpy as np
import matplotlib.pyplot as plt
from common.util import preprocess, create_co_matrix, ppmi
text = 'You say goodbye and I say hello.'
corpus, word_to_id, id_to_word = preprocess(text)
vocab_size = len(id_to_word)
C = create_co_matrix(corpus, vocab_size, window_size=1)
W = ppmi(C)
# SVD
U, S, V = np.linalg.svd(W)
np.set_printoptions(precision=3) # 유효 자릿수를 세 자리로 표시
print(C[0]) # 동시발생 행렬
# [0 1 0 0 0 0 0]
print(W[0]) # ppmi 행렬
# [0. 1.807 0. 0. 0. 0. 0. ]
print(U[0]) # SVD
# [ 3.409e-01 -1.110e-16 -1.205e-01 -4.441e-16 0.000e+00 -9.323e-01 2.226e-16]
- 희소벡터 W[0]가 SVD에 의해 밀집벡터 U[0]으로 변함
- 밀집벡터의 차원을 감소시키려면, 예를 들어 2차원 벡터로 줄이려면 처음의 두 원소 꺼내면 됨
print(U[0, :2]) # SVD
# [ 3.409e-01 -1.110e-16]
- 각 단어를 2차원 벡터로 표현한 후 그래프로 그림
- 직관과 비슷하게 비슷한 의미끼리 가까움(말뭉치가 작은 한계도 보임)
- 행렬 크기가 N이면 SVD는 O(N**3)이 걸림 → Truncated SVD 같은 빠른 기법 이용
2.4.4 PTB 데이터셋
2.4.5 PTB 데이터셋 평가
더 큰 규모의 데이터인 PTB 데이터셋에 통계 기반 기법 적용
- 단어의 의미 혹은 문법적인 관점에서 비슷한 단어들이 가까운 벡터로 나타남 → 직관과 비슷한 결과
- 단어의 등장 횟수를 센 후 PPMI 행렬로 변환
- SVD를 이용해 차원 감소하여 더 좋은 단어 벡터 얻음
- = 단어의 분산 표현, 각 단어는 고정 길이의 밀집벡터로 표현
2.5 정리
- WordNet 등의 시소러스를 이용하면 유의어를 얻거나 단어 사이의 유사도를 측정하는 등 유용한 작업을 할 수 있다.
- 시소러스 기반 기법은 시소러스를 작성하는 데 엄청한 인적 자원이 든다거나 새로운 단어에 대응하기 어렵다는 문제가 있다.
- 현재는 말뭉치를 이용해 단어를 벡터화하는 방식이 주로 쓰인다.
- 최근의 단어 벡터화 기법들은 대부분 '단어의 의미는 주변 단어에 의해 형성된다'는 분포 가설에 기초한다.
- 통계 기반 기법은 말뭉치 안의 각 단어에 대해서 그 단어의 주변 단어의 빈도를 집계한다(동시발생 행렬).
- 동시발생 행렬을 PPMI 행렬로 변환하고 다시 차원을 감소시킴으로써, 거대한 '희소벡터'를 작은 '밀집벡터'로 변환할 수 있다.
- 단어의 벡터 공간에서는 의미가 가까운 단어는 그 거리도 가까울 것으로 기대된다.
'AI > NLP 기초' 카테고리의 다른 글
[NLP 기초] 6. 게이트가 추가된 RNN (0) | 2024.02.20 |
---|---|
[NLP 기초] 5. 순환 신경망(RNN) (0) | 2024.02.19 |
[NLP 기초] 4. word2vec 속도 개선 (1) | 2024.02.14 |
[NLP 기초] 3. word2vec (1) | 2024.02.13 |
[NLP 기초] 1. 신경망 복습 (1) | 2024.02.13 |