📝언어 모델
언어모델은 단어에 확률을 부여해서 다음 나올 단어에 대해 유추를 하거나 할 수 있습니다.
언어 모델을 만드는 방법은 크게는 통계를 이용한 방법과 인공 신경망을 이용한 방법으로 구분할 수 있습니다.
최근에는 통계를 이용한 방법보다는 인공 신경망을 이용한 방법이 더 좋은 성능을 보여주고 있습니다.
📝언어 모델 종류
- 통계적 언어 모델 (SLM)
- 말 그대로 통계를 내어서 확률적으로 다음 언어를 유추하는 방법입니다. 수학에 가장 유사한 것으로는 조건부확률이 있습니다. 해당 모델의 문제점으로는 통계에 들어가지 않는 데이터는 추가적인 기법을 적용하지 않는 이 영원히 나오지 않습니다.
- N-gram 언어 모델
- SLM의 경우는 모든 데이터를 카운트하면서 유추하지만 N-gram의 경우 문장을 N개 단위로 나누어 분석하며, 빠르고 적은 데이터로도 학습 가능하지만 긴 문맥을 반영하지 못한다. 정밀한 단계에서는 SLM에 비해 빈약하며 SLM처럼 통계에 들어가지 않는 데이터는 추가적인 기법을 적용하지 않는 이 영원히 나오지 않습니다.
- 한국어 언어 모델
- 한국어의 경우 띄어쓰기 단위로 토큰화하면 문제가 발생할 수 있어서 토큰화가 중요하며 어순이 중요하지 않고 띄어쓰기가 제대로 이루어지지도 않기 때문에 어렵다.
📝언어 모델 평가 (PPL)
어떤 언어 모델이 정확하지 평가하기 위한 지표이다.
📝단어 표현 방식
단어의 표현 방법은 크게 국소 표현(Local Representation) 방법과 분산 표현(Distributed Representation) 방법으로 나뉩니다. 국소 표현 방법은 해당 단어 그 자체만 보고, 특정값을 맵핑하여 단어를 표현하는 방법이며, 분산 표현 방법은 그 단어를 표현하고자 주변을 참고하여 단어를 표현하는 방법입니다.
예를 들어 puppy(강아지), cute(귀여운), lovely(사랑스러운)라는 단어가 있을 때 각 단어에 1번, 2번, 3번 등과 같은 숫자를 맵핑(mapping)하여 부여한다면 이는 국소 표현 방법에 해당됩니다. 반면, 분산 표현 방법의 예를 하나 들어보면 해당 단어를 표현하기 위해 주변 단어를 참고합니다. puppy(강아지)라는 단어 근처에는 주로 cute(귀여운), lovely(사랑스러운)이라는 단어가 자주 등장하므로, puppy라는 단어는 cute, lovely한 느낌이다로 단어를 정의합니다
Bag of Words
단어들의 순서는 전혀 고려하지 않고, 단어들의 출현 빈도(frequency)에만 집중하는 텍스트 데이터의 수치화 표현 방법입니다. Bag of Words를 직역하면 단어들의 가방이라는 의미입니다
from konlpy.tag import Okt
okt = Okt()
def build_bag_of_words(document):
# 온점 제거 및 형태소 분석
document = document.replace('.', '')
tokenized_document = okt.morphs(document)
word_to_index = {}
bow = []
for word in tokenized_document:
if word not in word_to_index.keys():
word_to_index[word] = len(word_to_index)
# BoW에 전부 기본값 1을 넣는다.
bow.insert(len(word_to_index) - 1, 1)
else:
# 재등장하는 단어의 인덱스
index = word_to_index.get(word)
# 재등장한 단어는 해당하는 인덱스의 위치에 1을 더한다.
bow[index] = bow[index] + 1
return word_to_index, bow
doc1 = "정부가 발표하는 물가상승률과 소비자가 느끼는 물가상승률은 다르다."
vocab, bow = build_bag_of_words(doc1)
print('vocabulary :', vocab)
# vocabulary : {'정부': 0, '가': 1, '발표': 2, '하는': 3, '물가상승률': 4, '과': 5, '소비자': 6, '느끼는': 7, '은': 8, '다르다': 9}
print('bag of words vector :', bow)
# bag of words vector : [1, 2, 1, 1, 2, 1, 1, 1, 1, 1]
단어에 번호를 매긴 후에 얼마나 빈도수가 있는지를 추가 시킵니다.
문서 단어 행렬(Document-Term Matrix, DTM)
문서 단어 행렬(Document-Term Matrix, DTM)이란 다수의 문서에서 등장하는 각 단어들의 빈도를 행렬로 표현한 것을 말합니다. 쉽게 생각하면 각 문서에 대한 BoW를 하나의 행렬로 만든 것으로 생각할 수 있으며, BoW와 다른 표현 방법이 아니라 BoW 표현을 다수의 문서에 대해서 행렬로 표현하고 부르는 용어입니다.
문서1 : 먹고 싶은 사과
문서2 : 먹고 싶은 바나나
문서3 : 길고 노란 바나나 바나나
문서4 : 저는 과일이 좋아요
과일이 | 길고 | 노란 | 먹고 | 바나나 | 사과 | 싶은 | 저는 | 좋아요 | |
문서1 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 0 |
문서2 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 |
문서3 | 0 | 1 | 1 | 0 | 2 | 0 | 0 | 0 | 0 |
문서4 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
위 예시를 DTM으로 변환시켰을때는 해당 표와 같습니다. 이렇게 하는 경우 원-핫 인코딩처럼 필요 없는 공간 차지가 존재하게 됩니다. 또한 영어에서는 The가 많이 나오는데 The가 중요한 단어는 아니기 때문에 이에 대한 처리가 필요합니다.
TF-IDF(단어 빈도-역 문서 빈도, Term Frequency-Inverse Document Frequency)
이번에는 DTM 내에 있는 각 단어에 대한 중요도를 계산할 수 있는 TF-IDF 가중치에 대해서 알아보겠습니다.
TF-IDF를 사용하면, 기존의 DTM을 사용하는 것보다 보다 많은 정보를 고려하여 문서들을 비교할 수 있습니다.
TF-IDF가 DTM보다 항상 좋은 성능을 보장하는 것은 아니지만, 많은 경우에서 DTM보다 더 좋은 성능을 얻을 수 있습니다.
- tf(d,t)
- 특정 문서 d에서의 특정 단어 t의 등장 횟수
- df(t)
- 특정 단어 t가 등장한 문서의 수
- idf(t)
- df(t)에 반비례하는 수로 log(n1+df(t))라는 수식을 사용해서 만드는데 문서 n이 커질수록 기하급수적으로 커지기 때문에 log를 이용합니다.
from math import log # IDF 계산을 위해
import pandas as pd # 데이터프레임 사용을 위해
docs = [
'먹고 싶은 사과',
'먹고 싶은 바나나',
'길고 노란 바나나 바나나',
'저는 과일이 좋아요'
]
vocab = list(set(w for doc in docs for w in doc.split()))
vocab.sort()
# 총 문서의 수
N = len(docs)
def tf(t, d):
return d.count(t)
def idf(t):
df = 0
for doc in docs:
df += t in doc
return log(N / (df + 1))
def tfidf(t, d):
return tf(t, d) * idf(t)
result = []
# 각 문서에 대해서 아래 연산을 반복
for i in range(N):
result.append([])
d = docs[i]
for j in range(len(vocab)):
t = vocab[j]
result[-1].append(tf(t, d))
tf_ = pd.DataFrame(result, columns=vocab)
print(tf_)
# 과일이 길고 노란 먹고 바나나 사과 싶은 저는 좋아요
# 0 0 0 0 1 0 1 1 0 0
# 1 0 0 0 1 1 0 1 0 0
# 2 0 1 1 0 2 0 0 0 0
# 3 1 0 0 0 0 0 0 1 1
result = []
for i in range(N):
result.append([])
d = docs[i]
for j in range(len(vocab)):
t = vocab[j]
result[-1].append(tfidf(t, d))
tfidf_ = pd.DataFrame(result, columns=vocab)
print(tfidf_)
# 과일이 길고 노란 ... 싶은 저는 좋아요
# 0 0.000000 0.000000 0.000000 ... 0.287682 0.000000 0.000000
# 1 0.000000 0.000000 0.000000 ... 0.287682 0.000000 0.000000
# 2 0.000000 0.693147 0.693147 ... 0.000000 0.000000 0.000000
# 3 0.693147 0.000000 0.000000 ... 0.000000 0.693147 0.693147
직접 코드로 구현한 부분이긴 하지만 라이브러리 제공하니까 그걸로 사용하면 됩니다.
코사인 유사도(Cosine Similarity)
코사인 유사도는 두 벡터 간의 코사인 각도를 이용하여 구할 수 있는 두 벡터의 유사도를 의미합니다. 두 벡터의 방향이 완전히 동일한 경우에는 1의 값을 가지며, 90°의 각을 이루면 0, 180°로 반대의 방향을 가지면 -1의 값을 갖게 됩니다. 즉, 결국 코사인 유사도는 -1 이상 1 이하의 값을 가지며 값이 1에 가까울수록 유사도가 높다고 판단할 수 있습니다. 이를 직관적
으로 이해하면 두 벡터가 가리키는 방향이 얼마나 유사한가를 의미합니다.

여기에서 벡터가 의미하는 건 문장이나 문서를 벡터(방향과 크기)로 변환시켜서 나타내는 것입니다. 그래서 해당 문서끼리 얼마나 같은 방향에 있냐 이정도로 유사도를 따질 수 있습니다.
🔗 참고 및 출처
https://thebook.io/080328/0364/