'전체 글'에 해당하는 글 2177건

문서들 간에 유사도를 구하기 위해서는 문서마다 동일한 단어나 비슷한 단어가 얼마나 사용되었는지를 파악할 수 있다. BoW, DTM, TF-IDF, Word2Vec 등의 방법으로 단어를 수치화 했다면, 유사도 기법을 사용하여 문서의 유사도를 구하는 게 가능하다.


  1. 코사인 유사도(Cosine similarity) 는 두 벡터의 방향에 따라 1~ -1 의 값을 가지며 1에 가까울수록 유사하다.
  2. 유클리드 거리(Euclidean distance) 는 두 점 사이의 직선 거리를 구하여 거리의 값이 작을수록 유사하다.
  3. 자카드 유사도(Jaccard similarity) 는 두개의 집합이 있을 때 합집합에서 교집합의 비율을 구하며, 1에 가까울 수록 유사하다.


문서1 : 저는 사과 좋아요

문서2 : 저는 바나나 좋아요

문서3 : 저는 바나나 좋아요 저는 바나나 좋아요


위 문서간의 유사도 구하기


from sklearn.feature_extraction.text import CountVectorizer
from numpy import dot
from numpy.linalg import norm
import numpy as np
 
corpus = [
    "저는 사과 좋아요",
    "저는 바나나 좋아요",
    "저는 바나나 좋아요 저는 바나나 좋아요",
]
vector = CountVectorizer()
dtm = vector.fit_transform(corpus).toarray()
print(dtm)
# [[0 1 1 1]
#  [1 0 1 1]
#  [2 0 2 2]]
 
def cos_sim(A, B):
    return dot(A, B) / (norm(A)*norm(B))
 
print(cos_sim(dtm[0], dtm[1])) #문서1과 문서2의 코사인 유사도 0.6666666666666667
print(cos_sim(dtm[0], dtm[2])) #문서1과 문서3의 코사인 유사도 0.6666666666666667
print(cos_sim(dtm[1], dtm[2])) #문서2과 문서3의 코사인 유사도 1.0000000000000002
 
def dist(A, B):
    return np.sqrt(np.sum((A-B)**2))
 
print(dist(dtm[0], dtm[1])) #문서1과 문서2의 유클리드 거리 1.4142135623730951
print(dist(dtm[0], dtm[2])) #문서1과 문서3의 유클리드 거리 2.6457513110645907
print(dist(dtm[1], dtm[2])) #문서2과 문서3의 유클리드 거리 1.7320508075688772
 
corp1 = corpus[0].split()
corp2 = corpus[1].split()
corp3 = corpus[2].split()
 
def jaccard(A, B):
    union = set(A).union(set(B))  # 합집합
    intersection = set(A).intersection(set(B))  # 교집합
    return len(intersection) / len(union)
 
print(jaccard(dtm[0], dtm[1])) #문서1과 문서2의 자카드 유사도 1.0
print(jaccard(dtm[0], dtm[2])) #문서1과 문서3의 자카드 유사도 0.3333333333333333
print(jaccard(dtm[1], dtm[2])) #문서2과 문서3의 자카드 유사도 0.3333333333333333
cs


위 코드에서 처럼 문서의 유사도의 성능은 각 문서의 단어들의 수치화 방법, 유사도 방법에 따라 다르다는 것을 알 수 있다.




WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

트랙백  0 , 댓글  0개가 달렸습니다.
secret

문서 단어 행렬(DTM) 의 단점, 중요한 단어에 대해서 가중치를 주지 못하는 단점을 보완한 방법이 단어 빈도-역 문서 빈도(TF-IDF: Term Frequency-Inverse Document Frequency) 이다. 문서의 빈도에 특정 식을 취하여 DTM 내의 각 단어들에 가중치를 주는 방법이다. TF-IDF 의 값이 높을수록 중요도가 높다. TF-IDF 의 특징은 모든 문서에서 자주 등장하는 a 나 the 같은 단어는 중요도가 낮다고 판단하며, 특정 문서에서만 자주 등장하는 단어를 중요도가 높다고 판단한다.



IF-IDF 에 적용되는 특정 식은 TF 값 과 IDF 값을 곱하는 것이다. (IDF 값은 DF 값의 역수이다.)

  • tf(d,t) : 특정 문서 d 에서의 특정 단어 t 의 등장 횟수.
  • df(t) : 특정 단어 t가 등장한 문서의 수.
  • idf(d,t) : df(t)에 반비례하는 수. n 을 총 문서 개수라고 할 때, 자연로그 ln(n/(1+df(t))) 


import pandas as pd
from math import log
 
docs = [
  '먹고 싶은 사과',
  '먹고 싶은 바나나',
  '길고 노란 바나나 바나나',
  '저는 과일이 좋아요'
]
 
vocab = list(set(w for doc in docs for w in doc.split()))  # 중복 제거, 단어 토큰화
vocab.sort()  # 오름차순 정렬 : ['과일이', '길고', '노란', '먹고', '바나나', '사과', '싶은', '저는', '좋아요']
 
= len(docs)  # 총 문서 수
 
def tf(t, d):
    return d.count(t)
 
def idf(t):
    df = 0
    for doc in docs:
        df += t in doc  # in 연산. True / False... True = 1
    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 : 문서별 단어 빈도수 구하기
        # [[0, 0, 0, 1, 0, 1, 1, 0, 0], [0, 0, 0, 1, ...
 
tf_ = pd.DataFrame(result, columns = vocab)
""" DTM
   과일이  길고  노란  먹고  바나나  사과  싶은  저는  좋아요
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 j in range(len(vocab)):
    t = vocab[j]
    result.append(idf(t))
 
idf_ = pd.DataFrame(result, index = vocab, columns = ["IDF"])
"""
          IDF
과일이  0.693147
길고   0.693147
노란   0.693147
먹고   0.287682
바나나  0.287682
사과   0.693147
싶은   0.287682
저는   0.693147
좋아요  0.693147
"""
 
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)
"""
        과일이        길고        노란  ...        싶은        저는       좋아요
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
"""
cs


공교롭게도 바나나가 잘렸지만; 문서2 에서의 바나나 tfidf(0.28) 보다, 문서3 에서의 바나나 tfidf(0.57) 가 높은 것으로 보아 문서3 에서의 바나나가 더 중요하다는 것을 인식해야 한다.



TfidfVectorizer 를 이용한 TF-IDF


from sklearn.feature_extraction.text import TfidfVectorizer
 
corpus = [
    'you know I want your love',
    'I like you',
    'what should I do ',
]
 
tfidfv = TfidfVectorizer().fit(corpus)
print(tfidfv.transform(corpus).toarray())
print(tfidfv.vocabulary_)
 
"""
[[0.         0.46735098 0.         0.46735098 0.         0.46735098  0.         0.35543247 0.46735098]
 [0.         0.         0.79596054 0.         0.         0.          0.         0.60534851 0.        ]
 [0.57735027 0.         0.         0.         0.57735027 0.          0.57735027 0.         0.        ]]
{'you': 7, 'know': 1, 'want': 5, 'your': 8, 'love': 3, 'like': 2, 'what': 6, 'should': 4, 'do': 0}
"""
cs




WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

트랙백  0 , 댓글  0개가 달렸습니다.
secret

Bow(Bag of Words) 는 가방 속의 단어를 무작위로 뽑는 것처럼, 단어의 등장 순서를 고려하지 않는 빈도수(frequency) 기반의 단어 표현 방법이다. 빈도수로 유사도를 구하고 그에 따라 분류하는데 사용될 수 있다. 또한 서로 다른 문서들의 Bow 를 결합한 표현 방법을 문서 단어 행렬(DTM: Document-Term Matrix / TDM) 이라고 하는데, 원-핫 벡터와 같이 단어 집합을 줄이기 위해 불용어 처리가 중요하며, 정작 중요한 단어에 대해서 가중치를 주지 못하는 단점이 있다.


Bow 생성 과정은 단어 토큰화, 중복제거, 인덱스 부여, 빈도수 벡터 생성으로 만들 수 있다.


from konlpy.tag import Okt
import re
 
okt = Okt()
text = "정부가 발표하는 물가상승률과 소비자가 느끼는 물가상승률은 다르다."
token = re.sub("(\.)""", text)  # 기호제거
token = okt.morphs(token)
 
word2index = {}
bow = []
for voca in token:
    if voca not in word2index.keys():
        word2index[voca] = len(word2index)
        bow.insert(len(word2index) - 11)
    else:
        index = word2index[voca]
        bow[index] += 1
 
print(word2index)  # {'정부': 0, '가': 1, '발표': 2, '하는': 3, '물가상승률': 4, '과': 5, '소비자': 6, '느끼는': 7, '은': 8, '다르다': 9}
print(bow)  # [1, 2, 1, 1, 2, 1, 1, 1, 1, 1]
cs



CountVectorizer 로 Bow 만들기


from sklearn.feature_extraction.text import CountVectorizer
 
corpus = ['you know I want your love. because I love you.']
vector = CountVectorizer()
 
print(vector.fit_transform(corpus).toarray())  # [[1 1 2 1 2 1]]
print(vector.vocabulary_)  # {'you': 4, 'know': 1, 'want': 3, 'your': 5, 'love': 2, 'because': 0}
cs



CountVectorizer 에 불용어 사용하기

# 불용어 사용자 정의
vector = CountVectorizer(stop_words=["the""a""an""is""not"])
 
# CountVectorizer 영문 불용어 사용
vector = CountVectorizer(stop_words="english")
 
# NLTK 영문 불용어 사용
from nltk.corpus import stopwords
sw = stopwords.words("english")
vector = CountVectorizer(stop_words = sw)
cs




WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

트랙백  0 , 댓글  0개가 달렸습니다.
secret

언어 모델


언어 모델(Language Model) 은 단어 순서나 문장이 적합한지를 판단하고 확률을 할당한다. 


  • 통계 기반 언어 모델(SLM: Statistical Languagel Model) : n-gram
  • 인공신경망 기반 언어 모델 : Perceptron, 피드 포워드 신경망 언어 모델 (NNLM: Neural Network Language Model), GPT, Bert


현재는 GPT 나 Bert 같은 인공신경망 기반의 언어 모델이 주로 쓰이나 n-gram 도 꾸준히 자연어 처리에 사용된다.



카운터 기반의 통계적 접근


카운터 기반의 통계학적 언어 모델(SLM) 은 문서에서 이전 단어와 다음 단어의 출현 수를 계산한다. An adorable little boy 가 문서에서 100번 등장하고 다음에 is 가 30번 등장했다면, An adorable little boy 다음에 is 가 나타날 확률은 다음과 같이 나타낼 수 있다.


P(is|An adorable little boy) = count(An adorable little boy is) / count(An adorable little boy) = 30%


카운터에 기반한 확률은 코퍼스 데이터에 위 문장이 존재하지 않는다면 확률은 0 이 되므로 희소 문제(sparsity problem)가 발생하는 단점이 있지만 n-gram 이나 스무딩, 백오프 같은 일반화(generalization) 기법을 사용하여 희소 문제를 완화할 수 있다.



N-gram 언어 모델


코퍼스에서 긴 문장의 확률을 구하려 할 때 발생하는 희소 문제에서, 긴 문장을 줄이면 희소 문제 발생률을 낮출 수 있다. N-gram 은  N  개의 연속적인 단어 나열을 의미한다. "An adorable little boy is spreading smiles" 문장으로 n-gram 을 만들어 보면 아래와 같이 나눌 수 있다.


  • unigrams (N : 1)  an, adorable, little, boy, is, spreading, smiles
  • bigrams (N : 2) an adorable, adorable little, little boy, boy is, is spreading, spreading smiles
  • trigrams (N : 3) an adorable little, adorable little boy, little boy is, boy is spreading, is spreading smiles
  • 4-grams (N : 4) an adorable little boy, adorable little boy is, little boy is spreading, boy is spreading smiles
  • ...


예를 들어 'An adorable little boy is spreading _____' 처럼 spreading 다음의 단어를 예측하고 싶을 때 4-gram 을 사용하면 밑줄을 포함한 앞 단어들의 카운팅 작업을 하게 되며,  다음과 같은 식으로 표현할 수 있다.


P(w|boy is spreading) = count(boy is spreading w) / count(boy is spreading)


boy is spreading 이 문서에서 1,000번 등장하고, boy is spreading 다음에 insults가 500번, smiles 가 200번 등장했다면, insults 의 확률이 50% 로 높은 확률을 가지게 된다. 전체 문장을 고려한 언어 모델보다 카운터는 늘릴 수 있지만, 정확도가 떨어질 수 있다는 것을 알았다.


N-gram 의 n 을 어떻게 설정하느냐에 따라 카운터/성능/정확도가 달라질 수 있다. 

- n 이 커질수록, 카운트 수는 적어지고, 모델 사이즈가 커진다.

- n 이 작을수록, 카운트 수는 많아지지만, 정확도가 현실의 확률분포와 멀어진다. 


언어나 코퍼스에 따라 달라지겠지만 펄플렉서티 지표에 의하면, 일반적으로 가장 효율적인 n 은 4 로 측정되고 있다.


펄플렉서티(PPL: perplexity) 는 언어 모델의 성능을 평가하기 위한 내부 평가 지표로 perplexity(헷갈림) 의 수치가 낮을수록 성능이 좋음을 나타낸다.




WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

NLP, SLM
트랙백  0 , 댓글  0개가 달렸습니다.
secret

조건부 확률이란 주어진 사건(B)이 일어났다는 가정 하에 다른 한 사건(A)이 일어날 확률(P or Pr)을 뜻하며, P(A|B)로 표기한다. 이 때 사건 A가 발생할 확률은 B에 따라 달라진다.


자연어 처리에서 조건부 확률은 언어 모델에서 적합한 단어를 추론하는데 사용될 수 있다. 예를 들어 "나는 학교에 (     )." 라는 문장에서 괄호 안에 어떤 단어가 들어가는 것이 가장 확률이 높은지 등을 파악할 때 사용될 수 있다.


조건부 확률에 나타나는 사건과의 상관 관계를 수식으로 표현하면 다음과 같다.


P(B) = B 사건이 발생할 확률

P(A) = A 사건이 발생할 확률

P(A∩B) = P(A,B) = P(A)P(B|A) = B 와 A 사건이 모두 발생할 확률

P(A|B) = P(A∩B)/P(B) = B 사건이 발생하고 A 사건이 발생할 확률

P(A,B,C,D) = P(A)P(B|A)P(C|A,B)P(D|A,B,C) = D, C, B, A 사건이 모두 발생할 확률 (조건부 확률의 연쇄법칙)

P(x1,x2,x3...xn) = P(x1)P(x2|x1)P(x3|x1,x2)...P(xn|x1...xn-1)



예제1)


전체 중고차 중 70% 가 에어컨이 있고,

전체 중고차 중 40% 가 CD 플레이어가 있다.

전체 중고차 중 90% 가 둘 중 하나 이상을 가지고 있다.

에어컨이 없는 중고차 중 CD 플레이어도 없을 확률은?

에어컨이 없는 중고차 P(B) 중 CD 플레이어도 없는 중고차 P(A) 의 확률 P(A|B)


---------------------------------------------------------------------


공식 P(A|B) = P(A,B)/P(B)

P(B) = 에어컨이 없을 확률 = 0.3

P(A,B) = 둘다 없는 확률 = 0.1

P(A|B) = P(A,B)/P(B) = 0.1 / 0.3 = 1/3


예제2)


"An adorable little boy is spreading smiles" 문장의 확률은?

(문장의 확률은 이전 단어로부터 다음 단어가 등장할 확률들의 곱으로 구성된다.)


---------------------------------------------------------------------


공식 P(x1,x2,x3...xn) = P(x1)P(x2|x1)P(x3|x1,x2)...P(xn|x1...xn-1)

P(An adorable little boy is spreading smiles) =

P(An)×

P(adorable|An)×

P(little|An adorable)×

P(boy|An adorable little)×

P(is|An adorable little boy)×

P(spreading|An adorable little boy is)×

P(smiles|An adorable little boy is spreading)




WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

트랙백  0 , 댓글  0개가 달렸습니다.
secret