1. 자연어의 특성
자연어를 기계가 처리하도록 하기 위하여 먼저 자연어를 기계가 이해할 수 있는 언어로 바꿔야 함.
이는 이전 글에서 작성했던 토큰화 작업의 결과인 단어 사전 Vocabulary를 기계가 이해할 수 있도록 표현해야 한다.
-> 이 과정이 매우 어려움. 하나의 단어가 여러 뜻을 가지기 때문.
2. 단어의 유사성과 모호성
2-1. 단어의 형태
단어에는 다음과 같은 여러 가지 형태가 존재한다.-> _동형어_, _다의어_,_동의어_,_상위어_,_하위어_
동형어: _동형어_란 형태는 같으나 뜻이 다른 단어로 _동음이의어_라고 부른다. ex) 먹는 배, 신체 부위 배
다의어:_다의어_란 하나의 형태가 여러 의미를 지니면서도 그 의미들끼리 서로 연관이 있는 단어. ex) 마음을 먹다, 음식을 먹다, 골을 먹다
동의어: _동의어_는 서로 다른 형태의 단어들이 동일한 의미를 가지는 단어 ex) 개 강아지
상위/하위어: _상위어_와 _하위어_는 말 그대로 계층적 의미를 나타내는 단어 ex) 직업- 선생님, 경찰관 / 교통수단 - 비행기, 버스
--> 이러한 단어의 모호성, 중의성 등을 해결해 줘야만 기계가 정확하게 이해할 수 있다.
2-2. 모호성 해소
이전 거대언어모델들이 출현하기 전에는 _WSD_(word-sense disambiguation) -단어 중의성 해소 방법을 이용하여 처리하였는데, _지식기반_, _지도학습기반_,_비지도학습기반_으로 나뉜다.
2-2-1. 지식기반 중의성 해소
- _지식기반 WSD_는 컴퓨터가 읽을 수 있는 사전이나 시소러스(단어 관계 사전) 등을 바탕으로 단어의 의미를 추론하는 접근 방식
- 방대한 데이터를 수동으로 넣어야 하기 때문에 데이터베이스 구축이 어렵고 시간이 많이 듬
- 사람이 직접 선별하여 넣기 때문에 노이즈가 적음+ 데이터의 편향이 생길 수도 있음
아래는 WordNet을 이용하여 단어 간의 유사도를 확인한 결과.
2-2-2. 지도 학습 기반 단어 중의성 해소
- 각종 기계 학습 알고리즘을 통해 단어 의미를 분류
- 지식기반 wsd도 지도학습의 일종이나, 저것과는 좀 다른 의미로 기계 학습 알고리즘 등을 통하여 학습에 사용되지 않았던 새로운 문장에서 단어 의미를 판별하고자 함.
- 잘 레이블 된 데이터가 대량으로 필요하지만, 충분할 경우 일반화된 환경에서도 좋은 성능을 보임.
2-2-3. 비지도 학습 기반 단어 중의성 해소
- 단어 의미 추론 작업인 WSI(Word Sense Induction)을 가리킴
- 각 단어를 사전적인 의미에 연결하지 않고 세부 의미가 같은 맥락을 군집화
- 레이블 된 데이터가 필요하지 않으며 대규모 자연어 코퍼스로부터 추가 작업 없이 수행이 가능함.
- 사람이 직접 제작한 학습 데이터를 사용하지 않기 때문에 성능을 보장할 수 없음.
컴퓨터가 무한한 표현력의 언어를 이해하게 하기 위해서는 아래 항목들을 고려해야 한다.
1) 자연어를 변환할 때 어떤 정보를 압축시킬 것인가.
2) 정보 압축 과정에서 어떤 손실이 발생할 수 있는가.
3) 발생하는 손실을 어떻게 줄이는 가.
이것들을 임베딩을 이용해서 해결하고자 함. 임베딩은 중요한 특징들을 뽑아내고, 정보를 압축한다.
3. 임베딩 의미
_임베딩(Embedding)_이란 자연어처리에서 특징 추출을 통해 자연어를 수치화=벡터화시키는 과정이자 결과를 의미.
이전에 배웠던 토큰화작업은 이러한 임베딩을 만들기 위한 단어 사전을 구축한 것이다.
즉 토큰화->단어사전생성->임베딩 과정을 거친다.
임베딩은 크게 3가지 역할을 한다. 1) 정보함축 / 2) 유사도 계산 / 3) 전이 학습
3-1. 임베딩의 역할
3-1-1. 자연어의 의미적인 정보 함축
- 임베딩의 본질은 자연어의 중요한 특징을 추출하여 벡터로 압축하는 과정.
- 이러한 임베딩을 거쳐 표현된 문장은 의미적, 문법적, 구조적 정보들을 담고 있어야 함.
- 임베딩의 결과는 벡터인 만큼 사칙 연산이 가능하며, 단어 벡터 간 연산을 통해 단어 사이의 의미적/문법적 관계를 도출 가능.
- 임베딩의 품질을 평가하는 _단어유추평가_(word analogy test)에서 사용된다. ex) 아들-딸+소년=소녀
3-1-2. 자연어 간 유사도 계산
위에서 말했듯 벡터로 표현되어 사칙연산을 할 수도 있지만, 코사인 유사도를 사용하여 두 벡터 간 유사도를 계산하는 것이 가능하다.
이때 코사인 유사도란, 두 벡터 간의 코사인 각도를 이용하여 구하는 유사도인데, -1~+1 사이의 범위를 가지며 값이 1에 가까울수록 유사도가 높은 것을 의미한다. 이는 두 벡터가 가리키는 방향이 얼마나 유사한가를 의미하며 이를 통해 두 단어나 문장이 얼마나 유사한지를 확인할 수 있다.
3-1-3. 전이학습
대부분의 자연어 처리의 작업에서 임베딩된 값은 모델의 입력값으로 사용된다.
이때 임베딩이 잘 되어있을수록 자연어처리의 학습 속도와 성능이 향상되기 때문에 이미 만들어진 임베딩을 받아와 입력값으로 쓰는 전이 학습(transfer learning)을 쓰게 된다.
3-2. 임베딩 기법
3-2-1. 통계 기반의 자연어 처리
자연어 코퍼스 전체의 통계량에서 유의미한 특징들을 직접 추출하여 활용하는 방식으로, 언어학적 지식을 기반으로 특징들을 추출하였다.
3-2-2. 딥러닝 기반의 자연어 처리
RNN 등의 Seq2 Seq 모델이 공개되면서 딥러닝 기반 방법론들이 활용됨. 딥러닝 모델을 이용하여 대량의 데이터를 기반으로 입출력 사이의 관계를 모델이 스스로 이해하게 하여 사람의 개입 없이 유의미한 특징들을 추출하고 활용함.
이에 기존에 사용하던 언어학적 지식들이 점차 불필요해짐.
3-3-3. 전이 학습 기반 자연어 처리
2018년 ELMO라는 모델 이후, 사전 학습 이후 파인튜닝을 거치는 전이학습 방식의 패러다임으로 바뀜.
우선 사전학습으로 대규모 말뭉치로 임베딩을 구축한다. 이후 파인튜닝 과정에서는 구체적인 목적에 따라 작은 데이터로 임베딩을 포함한 모델 전체를 학습한다.
4. 임베딩 구축
임베딩을 만들 때 쓰는 가설은 크게 _단어 출현 빈도_,_주변단어와의 관계_,_단어 등장 순서_로 분류된다.
4-1. 단어 출현 빈도 기반 임베딩
4-1-1. One-Hot Encoding
_One-Hot Encoing1은 자연어를 0과 1로만 구별하는 인코딩 방법으로, 자연어 코퍼스에서 내 전체 단어 집합의 크기를 벡터의 차원으로 두고, 표현하고 싶은 단어의 인덱스에 1을 부여하고, 나머지에는 0을 부여함.
즉 단어 사전이 총 4개라면 각각은 1개의 1을 포함하고 있음.
다만 한계점들이 존재한다.
1) 희소문제(sparsity problem)
자연어 코퍼스는 대량의 데이터를 포함하고 있는데 원핫인코딩을 이용할 경우 단 1개만 1로 표현되고 나머지는 0으로 표현된다. 즉 단어사전이 20000개라면 한 개의 단어를 표현할 때 1개가 1 19999개가 0인데, 이렇게 대부분의 값이 0인 행렬을 _희소행렬(Sparse Metrix)_라고 부른다. 이는 단어가 늘수록 행렬의 크기는 증가하는데, 효율성은 떨어지며, 임베딩을 저장하기 위한 공간도 증가해서 성능이 저하된다.
2) 단어의 유사도 표현
같은 범주에 여러 개의 단어들이 있어도 모든 단어들 간 거리가 동일하기 때문에 유사도를 판단할 수 없음.
4-1-2. Bag of words (Bow)
_BoW_는 단어들의 순서는 고려하지 않고 단어들의 출현빈도에 집중하는 방법.
우선 각 단어에 고유한 정수 인덱스를 부여하여 단어 집합을 만들고, 각 인덱스의 위치에 해당 단어의 등장 횟수를 기록하는 벡터를 생성한다.
일어한 문서들의 BoW들을 결합하여 _문서-단어 행렬(DTM)_을 만드는데 이는 다수의 문장에서 등장하는 각 단어들의 빈도 행렬을 의미한다. 이를 이용하여 서로 다른 문서들을 비교할 수 있게 된다.
BoW도 원핫인코딩보다는 줄었지만 여전희 희소문제를 가지고 있으며, 단어의 순서를 고려하지 않아 의미를 제대로 파악하기 어렵다.(단순하게 빈도수가 높다 해서 중요한 단어가 아님)
4-1-3 TF-IDF
_TF-IDF_는 Term Frequency-Inverse Document Frequency의 약자로 단어의 빈도(TF)와 역문서 빈도(IDF)를 사용하여 문서-단어 행렬 내 각 단어들의 중요한 정도를 가중치로 주는 표현 방법이다.
이는 문서의 유사도를 구하거나, 중요도를 결정하는 작업 등에서 효과적으로 쓰인다.
수식은 다음과 같다. 문서를 d, 단어를 t, 문서의 총개수를 n이라고 할 때
TF(d, t): 특정 문서 d에서 특정 단어 t의 등장 횟수
DF(t): 특정 단어 t가 등장하는 문서 수
IDF(d, t):DF(t)에 반비례하는 값
$$ \text{IDF}(d, t) = \log\left(\frac{n}{\text{DF}(t) + 1}\right) $$
이렇게 나타낼 수 있다. 이걸 이용하면 불용어 같은 것들은 자주 등장하지만, 등장하는 문서 또한 많기 때문에 이 단어는 그 문서를 나타내기에 중요도가 떨어진다고 보아 가중치를 감소시키는 것이다.
로그를 씌우는 이유는 가중치를 줘야 하긴 하는데, 로그를 안 씌우면 가중치 간 격차가 너무 커지기 때문에 조절해 주기 위해서이다.
import pandas as pd
from math import log
doc_1="나는 사과 바나나 사왔다"
doc_2="나는 사과 그리고 바나나 사왔다."
doc_3="대표적인 과일 종류에는 사과 바나나 있다."
doc_4="바나나 길고 노랗다 그리고 바나나 사과 보다 더 맜있다."
documents=[doc_1,doc_2,doc_3,doc_4]
vocab=list(set(w for doc in documents for w in doc.split()))
vocab.sort()
vocab
>> ['과일','그리고','길고','나는','노랗다','대표적인','더','맜있다.','바나나',
'보다','사과','사왔다','사왔다.','있다.','종류에는']
우선 각 문서를 불러온 다음 단어사전을 만든다.(본 실습에서는 간단하게 단어사전을 만들기 위해 토큰화 등의 과정을 건너뛰고 단순 띄어쓰기를 기준으로 잘랐다.)
N=len(documents)
def tf(t,d):return d.count(t)
def idf(t):
df=0
for doc in documents:
df +=t in doc
return log(N/(df+1))
def tf_idf(t,d):
return tf(t,d) * idf(t)
다음으로 tf-idf에 관련된 함수를 수식대로 구현해 준다.
result = []
for i in range(N):
result.append([])
d = documents[i]
for j in range(len(vocab)):
t=vocab[j]
result[-1].append(tf(t,d))
DTM = pd.DataFrame(result,columns=vocab)
DTM
각 단어와 문서를 이용하여 DTM을 계산하면 다음과 같이 생성된다.
이 생성한 DTM은 방금 만든 사실 단순 빈도수만 고려한 BoW를 진행한 것이다.
tf-idf를 이용한 DTM을 구현하기 위해서는 함수만 바꿔주면 된다.
result = []
for i in range(N):
result.append([])
d = documents[i]
for j in range(len(vocab)):
t=vocab[j]
result[-1].append(tf_idf(t,d))
TF_IDF_DTM = pd.DataFrame(result,columns=vocab)
TF_IDF_DTM
이렇게 DTM 만드는 방식을 이용하여 단순 DTM과 TF-IDF 행렬 간 임베딩 방식으로 구한 결과를 이용해 문서 유사도를 계산해 보면 TF-IDF를 이용했을 때가 더 높은 것을 확인할 수 있다.
또한 위의 만든 함수 말고 _Scikit-learn_의 _TfidfVectorizer_을 이용하면 더 조정된 값들을 얻을 수 있기 때문에 다음 실습 글에서 제대로 사용해보도록 하겠다.
결과적으로 TF-IDF의 장단점을 알아보면, 우선 TF-IDF는 기존-단어 문서 행렬(DTM)을 사용하는 것보다 단어 각각의 중요도를 고려하여 문서를 비교할 수 있으며, 사용빈도가 높지만 의미가 없는 단어들의 중요도도 낮출 수 있어 좋다.
하지만 여전히 빈도를 기반으로 하기 때문에 맥락적 유사도를 반영하지 못하며, 이 또한 희소문제를 야기할 수 있다.
4-2. 분포 가설(주변 단어와의 관계)
_분포 가설_이란, 비슷한 위치 내에서 등장하는 단어들은 비슷한 의미를 가진다는 가설이다.
이에 비슷한 상황에서 사용되는 단어들과 비슷한 의미를 가졌을 것으로 추측할 수 있다.
이는 단어빈도와는 다른 개념으로 주변단어들(window사이즈만큼)에 따라 정의되는 문맥을 이용하여 단어를 벡터로 표현하는 것이 목표이다.
분포 가설의 대표적인 통계량을 _PMI_(Pointwise Mutual Information)이라 부르며, 두 확률변수사이의 상관성을 계량화하는 단위이다. $ \text{PMI}(w_1, w_2) = \log\left(\frac{P(w_1 \cap w_2)}{P(w_1) \cdot P(w_2)}\right) $ .
두 단어 $ w_1 $과 $ w_2 $가 같이 나오는 경우가 한 번도 없다면 즉 확률이 독립이라면 값이 0 이 되고 반대로 단어 $ w_1 $이 등장할 때 문맥 내에서 $ w_2 $ 도 자주 등장하면 PMI값이 커진다.
정리하자면, PMI는 두 단어가 얼마나 자주 같이 등장하는지에 관한 정보를 수치화한 것이기 때문에 단어에 중요도를 할당한다고 볼 수 있으며, 이 PMI값 자체로도 임베딩 값으로 사용할 수 있다.
이 PMI는 Word2 Vec이라는 대표적인 임베딩 기법에서 사용된다. (CBOW 외 Skip-gram이 이에 해당함.)
4-3. 단어 등장 순서 기반 임베딩
언어 모델은 단어의 등장 순서를 학습하여 주어진 단어 시퀀스가 얼마나 자연스러운지에 대한 확률을 부여한다.
이때 언어 모델이란 일련의 단어 토큰에 확률을 할당하는 모델들을 말하며 통계기반, 딥러닝 기반으로 나뉜다.
4-3-1. 통계 기반 언어 모델
_통계 기반 언어 모델_은 Statistical Language Model, SLM이라고 부르며, 단어가 n개 주어졌을 때 언어 모델은 n개의 단어가 동시에 나타날 확률을 나타낸다. 즉 $ P(W) = P(w_1,w_2,w_3,...,2_n) $ , 단어 시퀀스를 반환한다.
한마디로 n개의 단어로 구성된 문장에서 (n-1) 개의 단어가 주어졌을 때, n번째에 출현할 단어의 예측 확률은 다음과 같은 조건부 확률이다. $ P(w_n | w_1, w_2, \ldots, w_{n-1}) $
따라서 최종적으로 단어 시퀀스는 아래와 같다.
$$ P(w) = P(w_1) \cdot P(w_2 | w_1) \cdot P(w_3 | w_1, w_2) \cdot \ldots \cdot P(w_n | w_1, w_2, \ldots, w_{n-1}) $$
이 때 통계 기반의 언어모델은 자연어 코퍼스에 해당 단어 시퀀스가 얼마나 자주 등장하는지 빈도를 계산하여 학습에 활용한다. 즉 잘 학습된 언어모델은 어떤 문장이 그럴듯하고 어떤 단어가 오는 게 자연스러운지 학습한다.
실제로 통계 기반 언어 모델은 모든 이전 단어를 사용하는 것이 아닌(문장이 길어질수록 확률이 낮아지기 때문에) _n-gram_이라 하여 n=1인 유니그램, n=2인 바이그램, n=3인 트라이 그램 등 이전 n개의 단어를 고려하여 정하게 된다.
그러나 여전히 단어 순서를 고려하긴 하지만, 빈도를 기반으로 하기 때문에 코퍼스가 엄청나게 크지 않다면 의미가 없을 수 있다. 왜냐면 한 번도 등장하지 않은 패턴에 대해서는 불가능하기 때문이다. 또한 n-gram의 n을 정하는 것도 어렵다.
n이 커지면 문맥을 점점 더 많이 고려하므로 언어 모델링의 성능을 높일 수 있으나 늘릴수록 그 전체 문장이 등장할 확률이 낮아짐.
이러한 것을 방지하기 위하여 _백오프(back-off)_와 _스무딩(smoothing)_이라는 기법이 있는데, 예를 드렁 7 gram으로 설정했는데 특정 7-gram의 단어 시퀀스가 코퍼스 내에 없다면 일정 수식에 따라 7보다 작은 값을 택하여 보는 방식이 있을 것이고, 스무딩은 모든 등장 빈도에 k만큼을 더해 등장확률이 0이 아니라 최소 k가 되도록 설정하는 것이다.
4-3-2. 딥러닝 기반 언어 모델
딥러닝 기반 모델은 입력과 출력 사이의 관계를 유연하게 정의하며 그 자체로 확률 모델로 동작할 수 있다. 따라서 빈도 같은 통계량을 사용하지 않아도 된다.
한 마디로 대규모 학습 코퍼스를 학습하여 수식을 찾아내고 결과를 반환한다.
이러한 딥러닝 기반 모델의 학습 방법은 _Masked Language Modeling_과 _Next Token Prediction_으로 나뉜다.
4-3-2-1. Masked Language Modeling
문장 중간에 마스킹을 하여 해당 마스킹된 자리에 어떤 단어가 올지 예측하는 과정에서 진행문장 전체를 학습하여 중간에 있는 단어를 예측하기 때문에 양방향(bi-directional) 학습이 가능하다. 대표적으로 _BERT_가 있다.
4-3-2-2. Next Token Prediction
주어진 단어 시퀀스를 가지고 다음 단어가 어떤 단어로 올지 예측하는 과정에서 학습하는 것으로 순차적으로 입력받은 뒤 다음 단어를 맞추는 uni-directional 단방향 학습이다. 대표적으로 GPT와 Elmo가 있다.
5. 텍스트 유사도 계산
_텍스트 유사도_란 두 개의 자연어 텍스트가 얼마나 유사한지를 나타내는 방법이다.
유사도 자체가 주관적인 척도이기 때문에 이를 최대한 정량화하는 것이 중요하다.
5-1. 의미기반 유사도
5-1-1. 유클리디안 거리 기반 유사도
_유클리디안 거리_(euclidean distance)기반 유사도 측정은 두 점 사이의 거리를 계산하는 유클리디안 거리를 사용하여 구하며 거리가 가까울수록 유사도가 높다고 판단한다. 자주 사용되는 방법은 아니지만 자연어처리 분야뿐만 아니라 다른 분야에서도 범용적으로 사용되는 거리 측정 기법이다.
특징으로는 단순하게 두 점 사이의 거리를 의미하기 때문에 값에 제한이 없어 1이상이 나올 수도 있다. 따라서 다른 유사도들과 비교하기 위해서는 0~1 사이의 값으로 스케일링하는 과정이 필요하다.
-> 이를 위해 L1 정규화(각 벡터안의 요소값을 모두 더하여 크기가 1이 되도록 조절하는 방법)를 적용한다.
$$ d(p,q) = \sqrt{{(x_2 - x_1)^2 + (y_2 - y_1)^2}} $$
5-1-2. 맨하탄 거리기반 유사도
_맨해튼 거리_는 유클리디안 거리와 다르게 각 좌표의 차를 제곱하여 더하는 것이 아니라, 차의 절댓값을 바로 환산한다.
이는 항상 유클리디안 거리보다 크거나 같기 때문에(최단거리를 구하는 것이 아니기 떄문) 잘 사용하지 않는다.
$$ d = |x_2 - x_1| + |y_2 - y_1| $$
5-1-3. 코사인 유사도
두개의 벡터값에서 코사인 각도를 이용하여 구하는 유사도로, 두 벡터의 방향이 완전하게 동일하면 1, 90도의 각을 이루면 0, 180도로 반대를 이루면 -1을 가진다. 즉 1에 가까울 수록 유사하다는 것을 의미하며, 앞서 설명한 두개의 거리기반 유사도와 다르게 벡터의 방향으로 유사도를 따지기 때문에 자연어 내 유사도 계산에 적합하다.
$$ \text{Cosine Similarity}(\mathbf{A}, \mathbf{B}) = \frac{\mathbf{A} \cdot \mathbf{B}}{\|\mathbf{A}\| \cdot \|\mathbf{B}\|} $$
5-1-4. 자카드 유사도
_자카드 유사도_(Jaccard Similarity)는 두 문장을 각각 단어의 집합으로 만든 뒤 두 집합을 통해 유사도를 측정하는 방식으로, 수치화된 벡터없이 단어 집합 만으로도 계산할 수 있다.
전체 합집합 중 공통의 단어의 개수에 따라 0과 1사이의 값을 가지며 1에 가까울 수록 유사도가 높
$$ \text{Jaccard Similarity}(A, B) = \frac{|A \cap B|}{|A \cup B|} $$
5-2. 문자열 간 유사도
문자열 간 유사도 측정은 주어진 두 문자열 간 편집 거리를 기반으로 형태적인 유사도를 측정하는 방법으로, 맞춤법 검사하 오타 교정 알고리즘등에 사용된다.
5-2-1. 해밍거리
_해밍거리_(Hamming Distance)는 두 개의 길이가 같은 문자열 사이의 거리를 측정하는 방법으로, 둘 중 하나의 문자열에서 몇개의 문자를 바꿔야 두 문자열이 같아지느냐를 측정한다. 이는 수치첮깅ㄴ 차이를 고려하는 것이 아니라 정확히 일치하는가에 대한 여부만 고려한다.
5-2-2. Levenshtein Distance
_Levenshtein Distance_는 _Edit Distance_라고도 부르며 한 string s1을 s2로 변환하는 최소 횟수를 정의한다.
변환하는 방법은 _delete_,_insert_,_substitution_이렇게 3가지가 존재한다.
다음에는 이러한 자연어의 임베딩 및 유사도 계산 실습을 진행할 것이다.
'ML & DL > NLP' 카테고리의 다른 글
[패캠/NLP] 워드 임베딩 (1) | 2023.12.20 |
---|---|
[패캠/NLP] 문장 임베딩 및 유사도 측정 실습 (1) | 2023.12.18 |
형태소 분석과 전처리 실습 (0) | 2023.12.07 |
코퍼스 전처리 및 토큰화 (1) | 2023.12.07 |
[패캠/NLP] HuggingFace 실습 (0) | 2023.11.28 |