밑바닥 DL

[밑바닥 DL] 3.PPMI의 한계와 차원 감소(feat.SVD)

독립성이 강한 ISFP 2024. 4. 5. 16:11
728x90
반응형

1.PPMI의 한계

PPMI 에는 몇 가지 제약이 존재합니다. 가장 중요한 문제 중 하나는 말뭉치 내 어휘의 수가 증가함에 따라, 단어 벡터의 차원 또한 비례하여 증가한다는 것입니다. 예를 들어, 어휘 수가 10만 개에 이른다면, 각 단어 벡터는 10만 차원의 공간에 배치됩니다. 이렇게 고차원의 벡터는 계산적으로 부담스럽고, 현실적인 데이터 처리에 있어 심각한 제약을 의미합니다.

동시 발생 행렬과 PPMI

더 나아가, 해당 이미지의 PPMI 행렬을 자세히 살펴보면, 대부분의 원소가 0이라는 점을 알 수 있습니다. 이는 벡터의 대부분의 원소가 중요하지 않음을 시사하며, 각 원소의 '중요도'가 낮다는 것을 의미합니다. 이는 데이터의 희소성을 나타내며, 해당 벡터가 노이즈에 취약하고 견고하지 않다는 문제점을 드러냅니다. 이러한 고차원이고 희소한 벡터를 처리하는 과정에서 벡터의 차원을 축소하는 것이 필수적이며, 이를 통해 데이터의 핵심 구조를 보존하면서 계산 효율성을 높일 수 있습니다.

 

 

2.차원 축소

특잇값 분해(Singular Value Decomposition, SVD)는 매우 중요한 선형 대수 기법 중 하나입니다. 이 방법은 어떠한 행렬도 세 가지 특별한 형태의 행렬로 분해할 수 있다는 점에서 큰 의미가 있습니다.

3. 특잇값 분해 (SVD)

식으로 표현하면, SVD는 임의의 행렬 $X$를 $U$, $S$, $V^T$ ($V$의 전치행렬)라는 세 행렬의 곱으로 분해합니다.

$$X = U \cdot S \cdot V^T$$

여기서 각 행렬은 다음과 같은 특징을 가집니다.

SVD에 의한 행렬의 변환(S의 흰색 부분은 0임)

  • U (직교행렬, Orthogonal Matrix): $U$의 열 벡터들은 서로 직교하며, 이는 $U$의 열 벡터들이 서로 독립적이라는 것을 의미합니다. 직교행렬은 어떠한 공간의 축(기저)을 형성하며, 이 경우 '단어 공간'으로 생각할 수 있습니다. 즉, 우리가 다루는 데이터(예: 단어 벡터)를 새로운 축으로 재배치하는 데 사용됩니다.

  • S (대각행렬, Diagonal Matrix): $S$는 대각선상에 특잇값(singular values)을 가지며, 이외의 모든 성분은 0입니다. 이 특잇값들은 데이터에서 얼마나 중요한지를 나타내는 지표로, 큰 순서로 정렬됩니다. 데이터의 중요도변동성을 측정하는 역할을 하며, 각 축의 중요도를 나타냅니다.

  • V^T (직교행렬의 전치, Transpose of an Orthogonal Matrix): $V$ 역시 직교행렬이며, 데이터 공간에 대한 또 다른 기저를 형성합니다. $V^T$는 보통 데이터의 패턴을 포착하는데 사용됩니다.

이 분해 과정에서, 우리는 차원을 축소하기 위해 가장 중요한 몇 개의 특잇값(큰 값)과 관련된 벡터들만을 유지하고, 나머지는 제거(깎아냄)하는 방법을 사용할 수 있습니다. 예를 들어, $S$ 행렬에서 작은 특잇값을 가지는 성분을 제거함으로써 차원을 축소하고, $U$와 $V$ 행렬에서 해당 성분에 대응하는 열을 제거함으로써, 더 낮은 차원으로의 데이터 근사를 얻을 수 있습니다.

이 방법을 사용하면, 원본 데이터의 중요한 특성과 구조는 유지하면서, 데이터를 더 적은 수의 차원으로 표현할 수 있습니다. 이는 데이터를 시각화하거나, 계산을 더 효율적으로 만들거나, 노이즈를 제거하는 데 유용합니다. 

 

SVD에 의한 차원 감소

위 그림에서 설명된 바와 같이, 특잇값 분해(SVD)를 사용하여 차원을 축소하고 행렬의 근사를 얻는 과정은 다음과 같습니다.

  1. 원본 행렬 $X$: 이는 단어 ID와 단어 벡터를 포함하는 행렬입니다. 여기서 각 행은 특정 단어의 벡터 표현이며, 각 열은 해당 벡터의 차원을 나타냅니다. 예를 들어, 단어 ID에 해당하는 단어의 벡터가 저장되어 있는데, 이는 단어의 PPMI 값이 될 수 있습니다.
  2. 차원 축소: SVD를 사용하여 $X$를 세 개의 행렬 $U'$, $S'$, $V'^T$로 분해합니다. 여기서 $U'$는 축소된 차원의 공간에서 단어 벡터를 표현하는 행렬이며, $S'$는 특잇값들을 포함하는 대각 행렬입니다. $V'^T$는 원래 데이터 공간에서 새로운 축(기저)을 형성합니다.
  3. 중요도에 따른 축소: $S'$ 행렬의 특잇값 중 작은 값들은 중요도가 낮다고 간주되어 제거됩니다. 이로 인해 $U'$ 행렬의 여분의 열 벡터들이 깎여나가고, 축소된 차원에서 원본 행렬 $X$를 근사하는 데 사용되는 열만 남습니다.
  4. 단어 벡터의 차원 축소: 원래의 $X$ 행렬에서 각 행이 나타내는 단어 벡터는 이제 $U'$ 에 의해 축소된 차원에서 표현됩니다. 이는 각 단어에 대해 더 적은 수의 특성을 가진 벡터로 단어를 표현하는 것과 같습니다. 이런 방식으로 차원이 줄어든 단어 벡터는 계산을 더 효율적으로 만들고, 시각화하기 쉬우며, 노이즈를 줄일 수 있는 장점이 있습니다.

4.SVD를 단어의 PPMI 행렬에 적용

각 단어에 차원 축소를 적용할 수 있습니다. 이렇게 되면 원래의 고차원 PPMI 행렬에 대한 처리와 분석이 더 용이해집니다. 예를 들어, 원래의 PPMI 행렬이 단어 간의 관계를 표현하는 데 사용되는 큰 행렬이라면, SVD를 통해 차원이 줄어든 벡터는 더 작고 관리하기 쉬운 행렬로 변환되어, 복잡한 자연어 처리 작업에서 효율적인 연산을 가능하게 합니다.

동시발생 행렬에 SVD를 적용한 후, 각 단어를 2차원 벡터로 변환해 그린 그래프 (i와 goodbye가 겹쳐있다.)

위 이미지는 단어 벡터의 2차원 시각화를 나타냅니다. 이 시각화는 SVD를 통해 차원 축소된 단어 벡터를 2차원 평면에 플로팅한 것으로, 여기서 단어들은 그들의 축소된 차원 벡터에 따라 위치가 정해집니다. 이미지에서 확인할 수 있는 몇 가지 특징은 다음과 같습니다.

  • "goodbye"와 "you"는 서로 가까운 위치에 있습니다. 이는 두 단어가 유사한 문맥에서 사용될 가능성이 높다는 것을 암시할 수 있습니다.
  • "and"와 "say"는 그래프의 아래쪽에 위치하며, 이는 다른 단어들과는 다른 문맥이나 사용 패턴을 가질 수 있음을 시사합니다.


그림의 해석에서 고려해야 할 중요한 점은 사용된 말뭉치의 크기입니다. 작은 말뭉치에서 얻은 결과는 단어 간의 실제 관계를 완벽하게 반영하지 않을 수 있으며, 때문에 저자는 PTB(Penn Treebank) 데이터셋과 같은 더 큰 말뭉치를 사용하여 같은 실험을 수행해보는 것을 제안합니다. PTB 데이터셋은 보다 많은 문장과 다양한 문맥을 포함하고 있어, 단어 벡터의 차원 축소 및 시각화가 더 신뢰성 있고 유의미한 결과를 제공할 수 있습니다.

 

5.Penn Treebank (PTB) 데이터셋 설명

지금까지는 단순한 문장을 활용해서 실습을 진행했었는데요, 이번에는 실제적인 자연어 처리 연구에 자주 사용되는 중간 크기의 말뭉치를 활용해 보겠습니다. 그 말뭉치는 바로 Penn Treebank, 약칭 'PTB' 데이터셋입니다. 이 말뭉치는 워드 임베딩의 발명가인 토마스 미콜로프(Tomas Mikolov)의 웹페이지에서 다운로드할 수 있으며, 주로 연구 목적으로 사용됩니다.

PBT 말뭉치의 예시

PTB 데이터셋은 원문에 여러 전처리가 적용된 텍스트 파일로 제공됩니다. 이러한 전처리에는 희소한 단어를 '〈unk〉'(unknown의 줄임말)로 대체하거나 구체적인 숫자를 'N'으로 바꾸는 작업 등이 포함됩니다. 위 그림에서는 PTB 데이터셋의 구조를 보여줍니다. 각 문장은 하나의 줄로 저장되며, 문장의 끝에는 '〈eos〉'(end of sentence의 줄임말)라는 특수 문자가 추가되어 있습니다.

 

6.Penn Treebank (PTB) 데이터셋을 활용한 실습 코드

6.1.Penn Treebank (PTB) 데이터셋 불러오기

import sys
sys.path.append('..')
from dataset import ptb

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

print('말뭉치 크기:', len(corpus))
print('corpus[:30]:', corpus[:30])
print()
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'])

--------------------------------------------------------------
말뭉치 크기: 929589
corpus[:30]: [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 24 25 26 27 28 29]

id_to_word[0]: aer
id_to_word[1]: banknote
id_to_word[2]: berlitz

word_to_id['car']: 3856
word_to_id['happy']: 4428
word_to_id['lexus']: 7426

 

말뭉치를 다루는 방법은 지금까지 우리가 배운 내용과 동일합니다. 
`corpus` 변수에는 각 단어에 할당된 고유 ID의 목록이 저장되어 있습니다. 이 ID들은 말뭉치 전체의 단어 순서를 나타냅니다. 
`id_to_word`는 단어 ID에서 해당 단어로 변환하는 딕셔너리로, 단어의 식별자를 실제 텍스트로 매핑합니다. 
반대로 `word_to_id`는 각 단어를 그에 해당하는 고유한 ID로 변환하는 딕셔너리입니다.


앞서 보여드린 코드에서 `ptb.load_data()` 함수는 PTB 데이터셋을 읽어들이는 역할을 합니다. 이 함수에 전달할 수 있는 인수로는 `'train'`, `'test'`, `'valid'`가 있으며, 각각 훈련용 데이터, 테스트용 데이터, 검증용 데이터를 불러옵니다. 

 

6.2.동시발생 행렬, PPMI, SVD 구하기

통계 기반의 자연어 처리 기법을 Penn Treebank(PTB) 데이터셋에 적용하는 과정을 나타냅니다. 먼저, 주변 단어의 발생 빈도를 나타내는 동시발생 행렬을 계산합니다. 그런 다음, 이 동시발생 행렬을 바탕으로 긍정적 상호정보량(PPMI)을 계산하여, 단어 간의 연관성을 더 잘 파악할 수 있는 가중치를 부여합니다.

계산된 PPMI 행렬은 상당히 크고 희소할 가능성이 높기 때문에, 여기에 특잇값 분해(SVD)를 적용하여 단어 벡터의 차원을 축소합니다. 이때, 효율성을 위해 고속 SVD인 `randomized_svd` 함수를 사용하는 것이 좋습니다. 이 함수는 SVD의 변형인 Truncated SVD를 사용하여, 모든 특잇값 대신 상위 몇 개(여기서는 5개)의 중요한 특잇값만을 계산해 속도를 개선합니다.

그 후, 차원이 축소된 단어 벡터를 사용하여 특정 단어들과 가장 유사한 단어들을 찾는 작업을 수행합니다. 여기서 '유사하다'는 말은 벡터 공간 상에서 가까운 위치에 있다는 것을 의미합니다.
결과는 실행할 때마다 다를 수 있는데, 이는 `randomized_svd`가 무작위성을 기반으로 하기 때문입니다.

import sys
sys.path.append('..')
import numpy as np
from common.util import most_similar, create_co_matrix, ppmi
from dataset import ptb

window_size = 2
wordvec_size = 100

corpus, word_to_id, id_to_word = ptb.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('calculating 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)
    
    
--------------------------------------------------------------------

[query] you
 i: 0.7290024161338806
 we: 0.617862343788147
 do: 0.5302550196647644
 else: 0.5208256840705872
 anybody: 0.49975281953811646

[query] year
 quarter: 0.6443896889686584
 month: 0.6135730743408203
 third: 0.5938683748245239
 next: 0.5851823091506958
 earlier: 0.579291582107544

[query] car
 auto: 0.6412657499313354
 luxury: 0.6398544907569885
 domestic: 0.5687968134880066
 truck: 0.5389368534088135
 cars: 0.5161221623420715

[query] toyota
 motor: 0.7532292008399963
 nissan: 0.6804566383361816
 lexus: 0.6171441674232483
 mazda: 0.5932931900024414
 motors: 0.5877329111099243

결과는 단어의 의미적 관계를 나타내는 벡터 표현의 효과를 보여줍니다. 'you'라는 단어에 대한 쿼리에서는 'i', 'we', 'do', 'else', 'anybody' 같은 인칭 대명사와 동작을 수행하는 주체를 나타내는 단어들이 가깝게 나타났어요. 이는 'you'가 종종 이러한 맥락에서 사용되기 때문이죠. 

'year'에 대해서는 시간을 나타내는 'quarter', 'month', 'next', 'earlier' 같은 단어들이 관련 단어로 나왔습니다. 시간의 흐름이나 기간을 나타내는 단어들과 'year'가 연관되어 있음을 알 수 있어요.

'car'의 경우에는 'auto', 'luxury', 'domestic', 'truck', 'cars' 같은 자동차와 밀접한 단어들이 유사하게 추출되었습니다. 이는 'car'가 자동차 관련 맥락에서 자주 언급되기 때문이며, 여기에는 'truck' 같은 특정 차종도 포함되어 있어요.

그리고 'toyota'라는 특정 브랜드 이름에 대해선 'motor', 'nissan', 'lexus', 'mazda', 'motors' 등 다른 자동차 브랜드나 관련 용어들이 나타났습니다. 이는 'toyota'가 자동차 제조업체의 하나로, 다른 유사한 업체들과 함께 묶이는 것을 보여줍니다.

이러한 결과들은 단어의 의미를 수치적으로 표현할 수 있음을 시사합니다. 원래의 말뭉치에서 단어의 발생 빈도를 수집한 후, PPMI로 가중치를 부여하고, SVD를 통해 벡터 차원을 축소함으로써 의미있는 단어 벡터를 생성했습니다. 각 단어는 이제 고정된 길이의 밀집 벡터로 표현되며, 이는 맥락상 유사한 단어들이 벡터 공간에서 서로 가깝게 위치하게 만들었습니다.

이것은 단어의 분산 표현이라 불리는데, 다른 많은 단어들에 대해서도 유사한 특성을 확인할 수 있으며, 더 큰 말뭉치를 사용하면 이러한 단어 벡터의 품질이 더 향상될 것입니다.

 

7.실습 코드 파일

 

ch02_실습코드.ipynb

Colab notebook

colab.research.google.com


이전 글

 

 

동시발생 행렬의 한계와 해결책(feat. PPMI)

동시발생행렬은 말뭉치(corpus) 내에서 일정한 맥락 안에서 각 단어 쌍이 함께 등장하는 횟수를 세는 표입니다. 언어학, 자연어 처리, 데이터 분석에서 관계와 패턴을 분석하는데 

resultofeffort.tistory.com

 

다음 글

 

word2vec와 추론 기반 기법 (feat.CBOW와 Skip-gram 모델로 단어 학습)

단어의 의미를 이해하고 처리하는 방법으로는 세 가지가 있다고 앞에서 설명했습니다.1. 시소러스를 활용한 기법2. 통계 기반 기법 시소러스와 통계 기반 기법(feat.동시 발생행렬, 코사인 유사

resultofeffort.tistory.com

 

728x90
반응형