728x90
반응형

상호 정보량

  • 동시발생 행렬의 원소는 두 단어가 동시에 발생한 횟수를 나타낸다.
  • '발생'횟수는 좋은 특징이 아니다.
    • 고빈도로 눈을 돌려보면 이유를 알 수 있다. 
    • 예) "the", "car"동시발생 "drive"보다 "car"는 "the"와 강한 관련성을 가진다고 나온다. - "the"고빈도 단어라서
  • 이러한 문제 해결 - 접려 상호 정보량(Pointwise Mutual Information)(PMI)이라는 척도 이용
  • PMI는 확률 변수 x와 y에 대해 정의

pmi 수식

[그림1] PMI 수식

  • P(x)는 x가 일어날 확률, P(y)는 y가 일어날 확률, P(x, y)는 x와 y가 동시에 일어날 확률을 뜻한다.
  • PMI값이 높을수록 관련성이 높다는 의미다.
    • 예) 10,000개의 단어로 이루어진 말뭉치에서 "the"가 100번 등장한다면 P("the")=100/10000=0.01이 된다.
    • P(x, y)는 "the"와 "car"가 10번 동시 발생했다면 P("the", "car") = 10/10000 = 0.001이 되는 것이다.
  • C는 동시발생 행렬, C(x, y)는 x와 y가 동시 발생하는 횟수, C(x)와 C(y)는 각각 단어 x와 y의 등장 횟수다.

 

동시발생 행렬 포함 PMI수식

[그림2] 동시발생 행렬 포함 PMI수식

 

말뭉치(N) = 10,000, "the"와 "car'와 "drive"가 각 1,000번 20번 10번 등장했다고 가정

"the"와 "car"의 동시발생 횟수는 10회, "car"와 "drive"의 동시발생 횟수는 5회 가정

 

PMI관점에서 계산 결과

 

"the"와 "car"

[그림3] "the","car"연관성

"car"와 "drive"

[그림4] "car"와 "drive" 연관성

 

  • "car"와 "drive"의 연관성이 더 높다고 나온 이유는 단어가 단독으로 출현하는 횟수가 고려되었기 때문이다.
  • 두 단어의 동시발생 횟수가 0이면 log20 = -무한
  • 때문에 실제 구현 시에는 양의 상호 정보량(Positive PMI)(PPMI)을 사용한다.

 

PPMI수식

[그림5] PPMI수식

  • PMI가 음수일 때는 0으로 취급한다.
  • 단어 사이의 관련성을 0 이상의 실수로 나타낼 수 있다.
def ppmi(C, verbose=False, eps=1e-8):
    M = np.zeros_like(C, dtype=np.float32)
    N = np.sum(C)
    S = np.sum(C, axis=0)
    total = C.shape[0] * C.shape[1]
    cnt = 0
    
    for i in range(C.shape[0]):
        for j in range(C.shape[1]):
            pmi = np.log2(C[i,j] * N / (S[j]*S[i]) + eps)
            M[i, j] = max(0, pmi)
            
            if verbose:
                cnt += 1
                if cnt % (total/100) == 0:
                    print('% 1f% 완료 ' %(100*cnt/total))
                    
    return M
  • 인수 C는 동시발생 행렬, verbose는 진행상황 출력 여부 결정하는 플래그
  • 큰 말뭉치 다룰 시 verbose=True로 설정 시 중간중간 진행 상황을 알려준다.

 

 

import sys
sys.path.append('..')

text = 'You say goodbye and I say hello.'
corpus, word_to_id, id_to_word = preprocess(text)
vocab_size = len(word_to_id)
C = create_co_matrix(corpus, vocab_size)
W = ppmi(C)

np.set_printoptions(precision=3) # 유효 자릿수를 세 자리로 표시
print('동시발생 행렬 : ', C)
print('-' * 50)
print('PPMI : ', W)

[그림6] 동시발생 행렬 PPMI 행렬로 변환 결과

 

 

차원 감소(dimensionality reduction)

  • 벡터의 차원을 줄이는 방법
  • 단순히 줄이는 것이 아니라, '중요한 정보'는 최대한 유지하면서 줄이는 것이 핵심이다.

 

차원 감소 예 : 2차원 데이터를 1차원 표현하기 위해 중요한 축을 찾는다.

[그림7] 차원 감소 예

 

특잇값 분해(SVD)(Singular Value Decomposition)

  • 차원을 감소시키는 방법
  • 임의의 행렬을 세 행렬의 곱으로 분해한다. (X = U, S, V)로 분해
  • U와 V는 직교 행렬(orthogonal matrix), 열 벡터는 서로 직교한다.
  • S는 대각 행렬(diagonal matrix)(대각 성분 외에는 모두 0인 행렬)이다.

 

SVD 수식

[그림8] SVD 수식

 

SVD에 의한 행렬 변환

[그림9] SVD에 의한 행렬 변환

 

  • 직교 행렬은 어떠한 공간의 축(기절)을 형성한다.
  • U는 '단어 공간'으로 취급 가능
  • S는 대각 행렬, 대각 성분에는 '특이값(singular value)이 큰 순서로 나열되어 있다.
    • 특이값 : '해당 축'의 중요도

 

SVD에 의한 차원 감소

[그림10] SVD에 의한 차원 감소

 

 

SVD에 의한 차원 감소

# SVD에 의한 차원 감소

import sys
sys.path.append('..')

text = 'You say goodbye and I say hello.'
corpus, word_to_id, id_to_word = preprocess(text)
vocab_size = len(word_to_id)
C = create_co_matrix(corpus, vocab_size)
W = ppmi(C)

# SVD
U, S, V = np.linalg.svd(W)

print("동시발생 행렬 : ",C[0]) # 동시발생 행렬

print("PPMI행렬 : ",W[0]) # PPMI 행렬

print("SVD : ",U[0]) # SVD

[그림11] SVD에 의한 차원 감소 결과

 

PTB 데이터셋

  • 적당한 말뭉치 사용 - 펜 트리 뱅크(Penn Treebank)(PTB)
  • word2의 발명자 토마스 미콜 로프(Tomas Mikolov)의 웹페이지에서 다운 가능

 

# PTB 데이터셋

import sys
sys.path.append('..')
import pbt

corpus, word_to_id, id_to_word = pbt.load_data('train')

print('말뭉치 크기 : ',len(corpus))
print('corpus[:30] : ', corpus[:30])
print('id_to_word[0] : ', id_to_word[0])
print('id_to_word[1] : ', id_to_word[1])
print('id_to_word[2] : ', id_to_word[2])
print()
print('word_to_id["car"] : ',word_to_id['car'])
print('word_to_id["happy"] : ',word_to_id['happy'])
print('word_to_id["lexus"] : ',word_to_id['lexus'])

[그림12] 실행 결과

  • corpus - 단어 ID목록
  • id_to_word - 단어 ID에서 단어로 변환하는 딕셔너리
  • word_to_id - 단어에서 단어 ID로 변환하는 딕셔너리
  • ptb.load_data()  - 데이터를 읽어 들인다. 

 

 

PTB 데이터셋 평가

  • 큰 행렬에 SVD 적용해야 하므로 고속 SVD이용
    • sklearn 모듈
# PTB 데이터셋 평가
# PTB 데이터셋

import sys
sys.path.append('..')
import pbt

window_size = 2
wordvec_size = 100

corpus, word_to_id, id_to_word = pbt.load_data('train')
vocab_size = len(word_to_id)
print('동시발생 수 계산 ... ')
C = create_co_matrix(corpus, vocab_size, window_size)
print('PPMI 계산 ...')
W = ppmi(C, verbose=True)

print('SVD 계산 ...')
try:
    # truncated SVD(빠르다.)
    from sklearn.utils.extmath import randomized_svd
    U, S, V = randomized_svd(W, n_components=wordvec_size, n_iter=5, random_state=None)
    
except ImportError:
    # SVD(느리다.)
    U, S, V = np.linalg.svd(W)
    
word_vecs = U[:, :wordvec_size]

querys = ['you','year','car','toyota']

for query in querys:
    most_similar(query, word_to_id, id_to_word, word_vecs, top=5)

[그림13] skearn이용한 결과

 

 

 

출처 : 밑바닥부터 시작하는 딥러닝2

https://www.hanbit.co.kr/store/books/look.php?p_code=B8950212853

 

밑바닥부터 시작하는 딥러닝 2

이 책은 『밑바닥부터 시작하는 딥러닝』에서 다루지 못했던 순환 신경망(RNN)을 자연어 처리와 시계열 데이터 처리에 사용하는 딥러닝 기술에 초점을 맞춰 살펴본다. 8장 구성으로 전체를 하나

www.hanbit.co.kr

 

728x90
반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기