NLP 3. 컴퓨터는 자연어를 어떻게 이해하는가

3. 컴퓨터는 자연어를 어떻게 이해하는가

자연어의 특성

토큰화를 통해 단어사전을 표현 -> 단어 하나를 어떻게 표현할까

효율성을 높이는 과정에서 정보량이 낮은 정보들이 생략됨

유사성 and 모호성

겉으로 보이는 형태 : 표제어

표제어 안에 많은 의미를 담음

하나의 표제어 안에 많은 의미가 담기는 중의성 문제는 매우 중요함

  1. 동형어 & 다의어
    동형어
    형태는 같으나 서로 다른 단어
    ex. 배 : 먹는 배, 타는 배, 인체의 일부
    다의어
    여러 의미를 가지지만 그 의미가 서로 연관이 있는 단어

  2. 동의어
    서로 다른 형태의 단어들이 동일한 의미를 가지는 단어
    동의어 집합 : 하나의 의미에 대한 여러개의 동의어의 집합
    워드넷의 ‘synset’을 통해 동의어 집합에 접근 가능

  3. 상위어 & 하위어
    개념 간에 계층이 존재할 수 있음
    상위 개념을 가르키면 상위어, 하위 개념을 표현하면 하위어
    ex. 직업 : 선생, 경찰, 등등
    어휘 분류에 따라 계층화 가능 -> 단어 간의 유사도나 관계로 얻어올 수 있음

언어의 모호성 해소 (word-sense disambiguation, WSD)

단어의 중의성 해소를 통해 의미를 명확히 할 수 있음

  1. 지식 기반 단어 중의성 해소

    사전이나 시소러스 등을 바탕으로 단어의 의미 추론
    (시소러스 : 단어간의 동의어, 유의어, 상하관계를 정의해둔 사전)
    노이즈가 적음 but 데이터베이스 구축이 어려움

  2. 지도 학습 기반 단어 중의성 해소
    ML을 통해 새로운 단어의 의미를 예측하고 분류하는 방법
    quality가 높은 데이터가 필요하다

  3. 비지도 학습 기반 단어 중의성 해소
    Word Sense Induction (WSI)를 많이 이용함
    세부 의미가 같은 맥락을 군집화함

딥러닝 시대에서는 RNN으로 바뀌면서 필요성이 낮아짐 (딥러닝 모델이 자연어 코퍼스를 학습하면서 해당 단어 주변의 문맥을 같이 학습하므로)

컴퓨터는 자연어를 어떻게 이해하는가?

단어를 어떻게 표현하는가

고려해야할 요소

  • 어떤 정보를 압축시킬까
  • 어떤 손실이 발생하는가
  • 손실을 어떻게 줄일수 있는가

임베딩

특징 추출 : 데이터가 어떤 특징을 가지고 있는지, 그리고 이 데이터를 vector로 변환하는 작업

토큰화 작업의 목표 : 임베딩을 만들기 위한 단어 사전의 구축

역할

  1. 자연어의 의미적인 정보 함축
    실제 자연어의 주요 정보를 포함 -> 언어의 복잡성과 모호성까지 설명 가능
    임베딩은 벡터 -> 사칙연산이 가능 : 덧셈 뺄셈을 이용해 단어 사이의 의미적 문법적 관계 도출 가능
    단어 유추 평가 : 임베딩의 품질을 평가하기 위해 사용할 수 있음

  2. 자연어간 유사도 계산
    코사인 유사도를 활용해서 벡터간 유사도를 계산할 수 있음
    • 코사인 유사도 : 두 벡터 간의 코사인 각도를 이용해서 구하는 유사도 (-1 ~ 1, 1에 가까울수록 유사도가 높음)
    • 벡터공간 시각화 : 벡터공간을 t-SNE, PCA와 같은 차원축소 기법으로 2, 3차원으로 시각화 가능
  3. 전이학습
    자연어 처리 작업에서 임베딩은 자연어 처리 모델의 input (품질이 좋을수록 자연어 처리 작업의 학습 속도와 성능 향상)
    이미 만들어진 임베딩을 다른 작업 학습의 input으로 사용하기도 함
    이 방식을 전이 학습
    사전학습 : 대규모 자연어 코퍼스를 활용해서 일반화된 의미, 문법 정보를 학습
    파인 튜닝 : 새로운 작업에 이러한 임베딩을 초기화해서 사용하면 처음 학습하는 것보다 빠르고 성능이 좋음

발전과정

  1. 통계기반
    통계량에서 유의미한 특징을 직접 추출하여 활용

  2. 딥러닝 기반
    대량의 데이터를 바탕으로 입력과 출력 사이의 관계를 스스로 이해할 수 있기 때문에, 사람의 개입없이 모델 스스로 유의미한 특징들을 추출하고 활용
    대신 다량의 데이터가 필요

  3. 전이학습 기반 자연어 처리
    사전학습 이후 파인튜닝을 거치는 전이 학습을 사용하는 패러다임으로 바뀜
    사전학습때에 대규모 데이터로 임베딩 구축 : 일반적 의미, 문법 정보 포함
    파인튜닝 과정에서는 보다 작은 데이터로 임베딩을 포함한 모델 전제 학습
    Downstream task : 파인튜닝 과정에서 해결하고 싶은 자연어 처리 작업

구축 방법

고려사항

  • 자연어를 변활할 때 어떤 정보를 압축할 것인가
  • 정보 압축 과정에서 어떤 손실이 발생할 수 있는가
  • 발생하는 손실은 어떻게 줄일 수 있는가

임베딩 시 고려하는 가설

  • 단어의 출현 빈도
  • 주변 단어와의 관계
  • 단어들의 등장 순서

임베딩 구축 방법 (1) 단어의 출현 빈도

One Hot encoding

자연어를 0과 1로 구별하겠다는 인코딩 방법

자연어 코퍼스 내 전체 단어 집합 크기를 벡터의 차원으로 둠

표현하고 싶은 단어의 인덱스에 1, 나머지 인덱스에는 0

Tensorflow Tokenizer 에서 fit_on_texts 메소드를 이용하면 공백 기준 토큰화 가능

import tensorflow as tf
from tensorflow.keras.preprocessing.text import Tokenizer

target = 'this is example'

tokenizer = Tokenizer()
tokenizer.fit_on_texts([target])

for i, v in tokenizer.word_index.items():
    print(f"{i} : {v}")

encoded = tokenizer.texts_to_sequences([target])[0]
print(encoded)

onehot_encoded = tf.keras.utils.to_categorical(encoded)
print(onehot_encoded)

한계점

  1. 희소문제
    일반적인 자연어 코퍼스에는 최소 몇 만 건의 단어 포함
    one-hot 인코딩을 하면 1개의 값이 1이고 나머지는 0의 값인데 이러한 행렬을 희소행렬이라고 함. 증가하는 크기에 비해 표현의 효율성이 떨어짐
    컴퓨터 성능이 저하됨

  2. 단어의 유사도를 표현하지 못함
    단어 하나 당 벡터 스페이스에서 하나의 축을 가지기 때문에 모든 단어에 대한 유사도가 같아짐. 유사도를 파악할 수 없음

Bag of Word

단어의 순서를 고려하지 않고 단어의 출현 빈도에 집중하는 수치화 방법

만드는 과정

  1. 각 단어에 정수 인덱스 부여, 단어집합 생성
  2. 인덱스의 위치에 단어 토큰의 등장 횟수를 기록하는 벡터
from sklearn.feature_extraction.text import CountVectorizer

doc1 = '프리미어리그는 세계 5대 프로 축구 리그로 꼽히며, 승강제가 이루어진다.'
doc2 = '승격 플레이오프 승리팀이 다음시즌부터 프리미어리그로 승격하게 된다.'
doc3 = '프리미어리그는 전 세계에서 가장 많은 사람이 시청하는 스포츠 리그이다.'

training_documents = [doc1, doc2, doc3]
bow_vectorizer = CountVectorizer()
bow_vectorizer.fit(training_documents)

word_idxes = bow_vectorizer.vocabulary_

for i, v in sorted(word_idxes.items()):
    print(f"{i} : {v})

bow_vector_1 = bow_vectorizer.transform([document_1])
bow_vector_2 = bow_vectorizer.transform([document_2])
bow_vector_3 = bow_vectorizer.transform([document_3])

print(bow_vector_1.toarray())
print(bow_vector_2.toarray())
print(bow_vector_3.toarray())

한계

BoW 방식을 사용하는 문서 - 단어 행렬도 원 핫 인코딩과 마찬가지로 여전히 단어 단위의 압축 방식

  • 희소 문제
  • 단어 순서를 반영하지 못함 : 단어는 문장의 순서에 따라 뜻이 변할 수 있는데 이 과정을 철저히 무시함

TF-IDF

TF-IDF (Term Frequency-Inverse Document Frequency)

단어의 빈도 (TF)와 역문서 빈도(IDF)를 사용해서 문서 - 단어 행렬 내 각 단어들의 중요한 정도를 가중치로 주는 표현 방법

수식

TF-IDF는 TF와 IDF를 곱한 값

$note$

$문서 = d$, $단어 = t$, $문서의\;총\;개수 = n$

TF(d, t) : 특정 문서 d 내에서 특정 단어 t의 등장 횟수 DF(t) : 트정 단어 t가 등장한 문서 수 IDF(d, t) : DF(t)에 반비례하는 값

import pandas as pd
from math import log

doc1 = '나는 사과 바나나 사왔다'
doc2 = '나는 사과 그리고 바나나 사왔다'
doc3 = '대표적인 과일 종류에는 사과 그리고 바나나'
doc4 = '바나나 길다 그리고 노랗다 그리고 바나나 사과 보다 더 맛있다'

training_documents = [doc1, doc2, doc3, doc4]

vocab = list(set(w for doc in training_documents for w in doc.split()))
vocab.sort()

print(vacab)

N = len(training_documents)
def tf(t, d):
    return d.count(t)

def idf(t):
    df = 0
    for doc in training_documents:
        df += t in doc
    return log(N/(df + 1))

def tf_idf(t, d):
    return tf(t, d) * idf(t)

result = []

for i in range(N):
    result.append([])
    d = training_documents[i]
    for j in range(len(vocab)):
        t = vocab[j]
        result[-1].append(tf(t,d))

DTM = pd.DataFrame(result, columns = vocab)
DTM

위의 코드 대신 scikit-learn의 TfidVectorizer를 사용할 수 있다.

장단점

  • 기존 단어-문서 행렬보다 단어 각각 중요도를 고려해서 문서 비교 가능
  • 문서 내 사용빈도가 높지만 의미가 없는 단어들의 중요도를 낮출 수 있음 (불용어) -> 성능 향상
  • 단어의 빈도로 판단하는 표현 방식 -> 맥락적 유사도를 반영하지 않음
  • 단어사전 규모 자체를 축소하지 않기 때문에 계산복잡도가 높음 (희소 문제)

4. 임베딩 구축 방법 (2) 분포 가설과 언어모델

분포가설 (어떤 단어가 같이 쓰였는가)

비슷한 위치 (문맥) 내에서 등장하는 단어들은 비슷한 의미를 가진다는 가설

window(목표 단어의 주변 단어들)에 따라 정의되는 문맥의 의미를 이용 -> 벡터화

PMI (Pointwise Mutual Information)

두 확률변수 사이의 상관성을 계량화하는 단위 (분포 가설의 대표적인 통계량)

$PMI(w_1,w_2)=\log{\displaystyle \frac{p(w_1,w_2)}{p(w_1)p(w_2)}}$

두 확률 변수가 완전히 독립일 경우 0

단어 w1이 등장할 때, 문맥 내에 단어 w2가 자주 등장한다면 PMI값이 커짐

단어들에 대한 PMI값을 임베딩 자체로 사용할 수 있음

ex. google, Word2Vec

(단어가 어떤 순서로 쓰였는가)

통계기반 언어모델 (SLM)

일련의 단어 토큰에 확률을 할당하는 모델

단어 시퀀스 정보를 명시적으로 학습함

Alt text

조건부확률을 이용해, 앞전의 단어가 주어진 경우, 해당 단어가 나타날 확률을 계산해서 진행

실제로는 n-gram이라는 단위를 사용한다.

  • n-gram : n가의 연속적인 단어 시퀀스, 이전 등장한 모든 단어를 고려하지 않고, 이전 n개의 단어만 고려

한계점

  • 희소 문제
    단어의 순서만 고려할 뿐, 빈도를 확률로 추산하므로 결국 방대한 양의 코퍼스가 아니면 희소문제에 당착함

  • n의 설정
    n-gram에서 n을 정하는 것에 trade-off가 발생함
    n이 커질수록 문맥을 많이 고려하므로 모델링 성능이 높아지나,
    코퍼스 내에 해당 문맥이 없을 가능성이 더 높아지므로 확률을 정의할 수 없어질 가능성이 있음
    보통 2~5의 값을 잡으나, 분야마다 다름

    • 백오프(back-off)

      등장 빈도가 낮거나 없으면, 일정 수식을 이용해 n을 줄이는 방법

    • 스무딩(smoothing)

      n-gram 단어 시퀀스의 등장 빈도에 모두 k 만큼 더하는 기법
      등장 확률이 0인 시퀀스도 최소값 k를 가지게 됨

딥러닝 기반 언어 모델

딥러닝 기반 언어모델은 대규모 자연어 코퍼스를 학습하여 알아서 조건부 확률을 찾아냄

여기서 우리가 해줄 것은 대규모 자연어 코퍼스를 어떻게 학습할 것인가

  1. Masked Language Modeling
    문장 중간에 마스크를 씌워서, 해당 마스크에 어떤 단어가 올지 예측하는 과정에서 학습을 전체 다 보고함
    양방향 학습이 가능
    ex. BERT

  2. Next Token Prediction
    단어 시퀀스를 가지고 다음 단어로 어떤 단어가 올지 예측하는 과정에서 학습
    일방향성
    ex. GPT, ELMo