2021.12.19 - [Python과 확률] - 자연어 처리를 위한 TF-IDF
여기서 이어집니다.
tf-idf는 간단하게 위키에서 이전에 짰던 소스를 참고하여, 나이브 베이즈 분류기를 만들어 보도록 하겠다.
이 조건부 확률을 이용한 베이즈 정리의 파생은, 연속형 변수에도 가능하고, 지금 예제와는 다른 다항 출력에서도 적용 가능하다.
정말 귀인으로 예상되는 분의 사이트를 하나 발견했는데, 정리가 무척 잘 되어있다.
(다만 수학식은 조건부 수식 P(A|B)에 익숙해져있다면 눈에 조금 안들어 올 수도 있다.)
https://zephyrus1111.tistory.com/80?category=858748
사실 여기 파이썬으로 전부 구현된 케이스가 존재하긴 하나, 나는 일단 Native로 작성하는 것이 목적이고, 스스로 학습의 의미도 있으니, 아이디어는 좀 차용할 수 있어도 기본적인 소스는 모두 내가 손수 짜려고 한다.
때문에 위의 Article 부터 들어간다면, 치트를 사용하는 느낌이 들 수도 있고, 이제 막 재미를 붙히기 시작했다면 너무 진도가 빠를 수 있으니, 우리는 이전 베이즈 정리(조건부확률) -> tf-idf로 이어지는 위키독스를 참고해서 예제를 짜보도록 하겠다.
마침 파이썬으로 작성된 소스도 없다! (물론 확률 계산된 정답은 있다....;;ㅋㅋ)
아마 간단한 식의 코테라면 설명을 좀 더 간소화해서 던져주지 않을까 싶다... 1~2번문제로 적당하려나?
직접 차근차근 조건부 확률을 이해하고, 주어진 서술형 문장에서 파이썬 소스로 구현 가능한지 한번 보도록 하자!
베이즈 정리는 이미 이전시간에 다뤘으므로, 간단하게 보고 지나가면 될 것 같고, 만약 까먹었다면
P(you|정상메일)의 확률은 '총 정상 메일의 전제하에 you가 들어간 비율' 이라는 점만 유의한다면, 소스를 작성하는데 어렵지 않다.
https://github.com/YooSungHyun/probability/blob/main/tfidf_naive_base_spam_classi.ipynb
docs = ['me free lottery','free get free you','you free scholarship','free to contact me','you won award','you ticket lottery']
label = ['Y','Y','N','N','N','Y']
vocab = list(set(w for doc in docs for w in doc.split()))
vocab.sort()
N = len(docs) # 총 문서의 수
from math import log
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)
tf_result = []
for i in range(N): # 각 문서에 대해서 아래 명령을 수행
tf_result.append([])
d = docs[i]
for j in range(len(vocab)):
t = vocab[j]
tf_result[-1].append(tf(t, d))
print(vocab)
print(tf_result)
idf_result = []
for j in range(len(vocab)):
t = vocab[j]
idf_result.append(idf(t))
print(vocab)
print(idf_result)
tfidf_result = []
for i in range(N):
tfidf_result.append([])
d = docs[i]
for j in range(len(vocab)):
t = vocab[j]
tfidf_result[-1].append(tfidf(t,d))
print(vocab)
print(tfidf_result)
첫번째 코드블럭은 tfidf를 구하는 방식으로, idf의 방식은 평이하게 log(N/df+1)이다.
(이전에 tf-idf 장에서 이야기 했듯, idf의 기준은 다른 공식으로도 사용 가능하니, 필요하다면 수정해서 쓸 수도 있을 것이다.)
import collections
label_cnt = collections.Counter(label)
p_label = collections.defaultdict(float)
for key in label_cnt.keys():
p_label[key] = label_cnt[key]/len(label)
어짜피 우리는 출력이 2개짜리인 베르누이 나이브 베이즈 분류 모형이긴 하나, 추후에 다항으로 확장시킬 것도 혹시나 고려해서, p_label dict를 통해, 들어온 label의 종류만큼 확률을 구할 수 있도록 처리하였다.
p_label[a 레이블] = a 레이블 개수 / 전체 레이블 개수
input_text = ['you','free','lottery']
output_dict = collections.defaultdict(float)
for idx, item in enumerate(label):
for label_word in p_label.keys():
for i in range(len(input_text)):
if item==label_word:
output_dict[label_word] = sum(tfidf_result[idx]) + output_dict[label_word]
output_dict[str(i)+'_'+label_word] = tfidf_result[idx][vocab.index(input_text[i])] + output_dict[str(i)+'_'+label_word]
실제로 Input이 주어졌을때, 실제 문장 테이블에서 label 조건에 맞는 전체 수와 각 label의 수를 구하게 된다.
이 부분에서, '정상/스팸 메일 중 특정 단어 비율' 을 구하기위한 선작업을 한다고 생각하면 된다.
output_dict[label_word] = 스팸 혹은 정상이었던 메일의 총합
output_dict[str(i)+'_'+label_word] = 'n번째_spam/정상' case에 따른 총 word 합
이 둘을 구하면 각 case(정상/스팸)별로 output_dict[str(i)+'_'+label_word] / output_dict[label_word] 을 구해주면, 조건부확률이 완성되게 된다.
Pandas 등을 쓰면 index나 column명을 활용해서 좀 더 직관적으로 소스를 짤 수 있겠으나, 딕셔너리로 구현하자니 tf-idf 쪽 수정이 커져서 리스트로 활용하려면 위와같이 소스가 좀 더러워진다.
output = collections.defaultdict(lambda : 1.0)
for label_word in p_label.keys():
for _, value in enumerate(output_dict):
if value in p_label.keys():
continue
if value.split('_')[1] == label_word:
output[label_word] = (output_dict[value]+1/output_dict[label_word]+1) * output[label_word]
output[label_word] = p_label[label_word]* output[label_word]
각 case(정상/스팸)별로 전체 for문을 돌려서 최종 확률을 구하게 된다.
if max(output):
print('Spam!!!')
결과는 max 해주면된다.
결과는?
------------------------------------------------------------------
귀인분의 소스를 보다보니, 연속형 나이브 베이즈 분류나, 혼합형 소스, 다항 베이즈에도 관심이 좀 생겨서, 마르코프 체인으로 넘어가기 전에 이번주에는 이 부분들을 좀 짚어보고 넘어가보자 한다. (핵심으로 빠르게 치고나가야하는데 자꾸 딴 곳에 한 눈 파는 것 같네...ㅠㅠ)
소스를 보아하니 Arima를 다루듯이 재활용 가능한 값들은 뭔가 Train으로 저장까지 해놓고 사용하고자 하셨던데, 아직 소스를 구체적으로 보진 않았다. 또한 소스를 구체적으로 보기 시작하면 내 사상이 그 쪽 방향으로 치우칠까 우려되어 선뜻 보기도 두렵다.
지금은 간단한 예제를 보고 넘어가지만 좀 더 구체적으로 보고 이후 시간들에 추가적으로 정리해보고자 하겠다.
'Python과 확률' 카테고리의 다른 글
조건부 확률부터 마르코프까지 - 5-2) HMM Forward Algorithm (0) | 2021.12.27 |
---|---|
조건부 확률부터 마르코프까지 - 4) 마르코프 체인 (0) | 2021.12.25 |
자연어 처리를 위한 TF-IDF (0) | 2021.12.19 |
조건부 확률부터 마르코프까지 - 2) 베이즈 정리 (0) | 2021.12.16 |
조건부 확률부터 마르코프까지 - 1) 조건부 확률과 독립 사건 (0) | 2021.12.14 |
댓글