컴퓨터가 자연어를 이해하려면 텍스트를 숫자로 바꿔야 하는데, 이 과정에서 '임베딩'이라는 방식이 쓰인다. 오늘은 텍스트를 숫자로 바꾸는 다양한 방법과 그 변천사를 알아보고, 각각의 방식이 어떤 의미를 갖는지 깊게 이해해 보자. 우리가 이걸 왜 알아야 할까? 예를 들어 검색 엔진에서 적절한 답을 찾으려면 질문과 정보의 의미적 유사성을 계산해야 하는데, 여기서 임베딩이 핵심적인 역할을 한다. 추천 시스템이나 자연어 처리에서도 마찬가지로 중요하다. 결국 이런 과정을 제대로 이해하면 단순히 모델을 사용하는 데서 그치지 않고, 데이터를 다루고 의미를 압축하는 구조를 설계하는 데까지 응용할 수 있게 된다.
텍스트 임베딩
from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity
smodel = SentenceTransformer('snunlp/KR-SBERT-V40K-klueNLI-augSTS') # 모델 불러오기
dense_embeddings = smodel.encode(['학교', '공부', '운동']) # 세 단어에 대해 임베딩 진행하기
cosine_similarity(dense_embeddings) # 단어간 유사도 계산하기
SentenceTransformer 라이브러리를 통해 서울대학교 자연어처리 연구소에서 개발한 한국어 자연어 처리 모델을 불러온다. 이 모델을 사용하여 학교, 공부, 운동 세 단어의 조합으로 코사인 유사도를 계산해 본다.
array([[1. , 0.59507453, 0.32537565],
[0.59507453, 1.0000001 , 0.54595685],
[0.32537565, 0.54595685, 1.0000002 ]], dtype=float32)
결과는 다음과 같다. 당연히 대각선에 해당하는 건 자기 자신과의 유사도이므로 1이 나올 테고, 나머지 조합에서 유사도를 살펴볼 수 있다. 학교와 공부는 0.595, 학교와 운동은 0.325, 공부와 운동은 0.525의 유사도를 보였다. 이를 통해 학교와 공부가 가장 유사도가 높은 조합임을 확인할 수 있었다.
오늘 이해해 볼 내용을 다 이해하고 나면 돌고 돌아 결국 위와 같은 라이브러리를 사용할 테지만, 그럼에도 과거에 사용했던 다양한 방식들을 알아보자.
원핫 인코딩
가장 기본적인 방식이다. 머신러닝에서 분류 태스크를 할 때, 정답 데이터를 바꾸는 방식 중 하나다. 위에 예시에서 학교, 공부, 운동을 그냥 1, 2, 3으로 변환한다면, 컴퓨터 입장에서는 이 숫자의 크기가 이들의 중요도라고 생각하게 될 것이다. 이러한 오해를 방지하기 위해 단어의 개수만큼 차원을 두고, 자신의 차원을 1로, 나머지를 0으로 두는 방식이다.
학교 = [1, 0, 0]
공부 = [0, 1, 0]
운동 = [0, 0, 1]
하지만 이렇게 하면 2가지 문제점이 발생한다. 먼저 단어의 개수가 많아지면 그만큼 차원이 많아진다. 그래서 각 단어가 가지고 있는 1이라는 숫자 외에는 모두 0이 채워질 텐데, 비효율적인 공간 활용으로 인해 메모리가 감당이 안될 것이다. 그리고 유사도 계산을 할 수 없다. 대표적인 유사도 계산 방식인 Cosine similarity의 계산식은 다음과 같다.
$ \text{cosine_similarity}(\mathbf{A}, \mathbf{B}) = \frac{\mathbf{A} \cdot \mathbf{B}}{\|\mathbf{A}\| \|\mathbf{B}\|} $
그러나 원핫 인코딩 방식은 각 단어의 내적이 0이기 때문에 항상 유사도가 0으로 나온다. 의도하지 않은 관계도 방지할 수 있지만, 그러한 장점으로 인해 의도해야 하는 관계도 모두 무시될 수 있다.
백오브워즈 Bag of Words
백오브워즈는 비슷한 단어가 많이 나오면 비슷한 문장 또는 문서라는 가정에 입각하여 특정 단어의 빈도수로 그 문장이나 문서의 의미를 예측하는 방식이다. 어떤 단어가 문서에 자주 등장한다면, 그 문서의 의미를 그 단어로 설정하는 것인데 이러한 방법의 문제점은 먼저 한국어의 경우 조사가 많이 등장하기 때문에 이들로 설정될 수 있다는 점, 그리고 여러 문서에서 두루 등장하는 단어가 그 문서의 의미를 대표하게 될 경우 문서의 의미를 예측하기 어렵다는 것이다.
TF-IDF
백오브워즈의 단점을 극복하기 위해 새롭게 등장한 것이 바로 TF-IDF (Term Frequency-Inverse Document Frequency)이다. TF-IDF는 특정 단어가 여러 문서에 걸쳐서 등장하는 경우, 그에 대한 중요도를 줄이는 수식을 통해 백오브워즈의 문제를 보완한다.
$ \text{TF-IDF}(w) = \text{특정 단어 w가 등장한 횟수} \times \log{\frac{\text{문서의 개수 N}}{\text{특정 단어 w가 등장한 문서의 수}}} $
다시 돌아가 원핫 인코딩의 단점은 두가지라고 하였다. 하나는 단어 사이의 유사도를 찾을 수 없다는 점, 그리고 차원이 커질 수 있다는 점. TF-IDF는 그중 단어 사이의 연관을 해결할 수 있었으나 여전히 단어가 많아질수록 차원이 커지는 문제는 해결할 수 없었다. 따라서 원핫 인코딩, 백오브워즈, TF-IDF는 차원의 문제를 해결하지 못하여 희소 임베딩(sparse embedding) 방식이라 불리기도 한다.
워드투벡 Word2Vec
지금부터 다룰 것은 희소 임베딩의 반대인 밀집 임베딩(dense embedding)이다. 단어의 개수만큼 차원이 커지는 희소 임베딩과는 달리 벡터의 차원을 100에서 1000 사이의 차원으로 훨씬 압축시킨 형태로 임베딩의 효율성이 증가한다. 그의 선두주자로 대표되는 것이 바로 워드투벡이다. 워드투벡은 특정 단어 주변에 어떤 단어가 있는지 예측하는 모델을 만든다면 단어의 의미를 표현한 임베딩을 모델이 생성할 수 있지 않을까? 하는 가정 아래 개발된 임베딩 방식이다. 마스킹된 단어 주변의 단어들로 마스킹된 단어를 예측하는 방식인 CBoW(Continuous Bag of Words), 그리고 반대로 특정 단어로 주변의 단어를 예측하는 방식인 스킵그램(skip-gram)으로 모델을 학습시킨다. 이렇듯 워드투벡은 기존의 문제점을 머신 러닝 모델을 통해 극복하면서 단어 임베딩의 놀라운 혁신을 이룰 수 있었다.
다시 본론으로 돌아와서, 우리가 지금 하고 싶은 것은 단어 임베딩이 아니라 문장 임베딩이다. 문장으로 물어보고, 그와 유사한 정보를 찾기 위해서이다. 이어지는 포스팅에서는 인공신경망을 활용하여 텍스트를 밀집 임베딩으로 표현하는 기술에 해당하는 모델 중 하나인 BERT에 대해 알아보고, 이를 통해 직접 두 문장사이의 유사도를 계산해 보는 실습과 임베딩 모델 간 비교를 할 예정이다.
'AI' 카테고리의 다른 글
[LLM] LLM Cache로 효율성 확보하기 with ChromaDB (0) | 2024.11.13 |
---|---|
[LLM] RAG로 Hallucination 방지하기 with Llama-index (7) | 2024.11.12 |
[LLM] 트랜스포머 구조 파헤치기 (4) - 인코더와 디코더 (0) | 2024.11.08 |
[LLM] 트랜스포머 구조 파헤치기 (3) - 정규화와 피드 포워드 층 (0) | 2024.11.07 |
[LLM] 트랜스포머 구조 파헤치기 (2) - 어텐션 (Attention) 이해하기 (2) | 2024.11.04 |