본문 바로가기
IT/Python

[자연어처리] 간단하게 만든 긍정, 부정, 중립 분류 using naive bayes classifier

by Jang HyunWoong 2014. 12. 19.

1. 설명

 

Naive Bayes classification 사용해서 간단하게 댓글을 긍정, 부정, 중립으로 분류하도록 구현해보겠습니다. 

 

보통 P(A|B)라고 할 수 있는데 B가 나왔을 때 A일 확률을 구하는 것입니다. 

 

조건부 확률이라고 하고 있습니다. 


베이지슷 정리를 수학적으로 보면 아래 식(1)과 같습니다. 

 

아래 식에서 x는 분류될 문장이고, c는 분류할 class 라고 생각했습니다. 

x의 확률은 계산하기 어렵기 때문에 베이즈 룰을 통해 아래와 같이 바꿀 수 있습니다.

 

p(c%7Cx)%5Cquad%20%3D%5Cquad%20%5Cfrac%20%7B%20p(x%7Cc)p(c)%20%7D%7B%20p(x)%20%7D%20 (1)

 

클래스가 여러 개고 입력되는 문장의 확률은 다 같은 조건이기 때문에 날려버릴 수 있습니다

 

C%5Cquad%20%3D%5Cquad%20argmaxP(c%7Cx)%5C%5C%20%5Cquad%20%5Cquad%20%5Cquad%20%3D%5Cquad%20argmax%5Cquad%20%5Cfrac%20%7B%20P(x%7Cc)P(c)%20%7D%7B%20P(x)%20%7D%5C%5C%20%5Cquad%20%5Cquad%20%5Cquad%20%3D%5Cquad%20argmax%5Cquad%20P(x%7Cc)P(c)%20 

 

각 클래스에서 가장 높은 값을 가지는 class x가 속하기 때문에 가장 큰 값을 얻습니다.

 

 

입력 문장이 여러 개의 단어로 이루어져 있기 때문에 개수를 n으로 놓고 P(x|c)는 아래식으로 변형할 수 있습니다

 

P(%5Ccombi%20_%7B%201%20%7D%7B%20x%20%7D%2C%5Ccombi%20_%7B%202%20%7D%7B%20x%20%7D%2C%5Ccombi%20_%7B%203%20%7D%7B%20x%20%7D%2C...%5Ccombi%20_%7B%20n%20%7D%7B%20x%20%7D%7Cc)%5Cquad%20%3D%5Cquad%20P(%5Ccombi%20_%7B%201%20%7D%7B%20x%20%7D%7Cc)%5Ccdot%20%5Cquad%20P(%5Ccombi%20_%7B%202%20%7D%7B%20x%20%7D%7Cc)%5Ccdot%20P(%5Ccombi%20_%7B%203%20%7D%7B%20x%20%7D%7Cc)%5Ccdot%20...%5Ccdot%20P(%5Ccombi%20_%7B%20n%20%7D%7B%20x%20%7D%7Cc)%20 

 

변형된 조건부확률을 통해 가장 큰 확률을 얻는 식은 아래와 같습니다.

 

C%5Cquad%20%3D%5Cquad%20argmax%5Cquad%20P(%5Ccombi%20_%7B%201%20%7D%7B%20x%20%7D%2C%5Ccombi%20_%7B%202%20%7D%7B%20x%20%7D%2C%5Ccombi%20_%7B%203%20%7D%7B%20x%20%7D...%2C%5Ccombi%20_%7B%20n%20%7D%7B%20x%20%7D%7Cc)P(c)%5C%5C%20%5Cquad%20%5Cquad%20%5Cquad%20%3D%5Cquad%20argmax%5Cprod%20%7B%20P(x%7Cc)P(c)%20%7D%20 

판별할 클래스는 Class = [긍정, 부정, 중립] 3가이 입니다.

 

확률 P(c), P(x|c)는 빈도수로 계산을 했습니다.

 

P(c) = 1/3

 

 

P(x|c) = count(클래스 c에 있는 x 단어 수) / count(클래스 단어 수)




사용 한 뉴스 기사 : 
http://news.naver.com/main/ranking/read.nhn?mid=etc&sid1=111&rankingType=popular_day&oid=117&aid=0002546333&date=20141210&type=1&rankingSectionId=106&rankingSeq=7



)

 

Class

Word

Positive

최고, 좋다, 굿, 너무, , 예뻐, ….

Negative

최악, 너무, 짜증, 싫다, ….

Neutral

그냥, , 할까, …

 

test = [너무 예뻐, 좋아] 라는 단어가 들어왔을 경우

1) P(pos|t) = P (t1|pos)*P(t2|pos)*P(t3|pos)*P(pos)

2) P(neg|t) = P (t1|neg)*P(t2|neg)*P(t3|neg)*P(neg)

3) P(neu|t) = P (t1|neu)*P(t2|neu)*P(t3|neu)*P(neu)

로 계산을 했습니다.

 

1, 2번만 계산 해보면

 

P(pos|t) = 1/6 * 1/6 * 1/6 * 1/3 = 0.0015432… 의 확률이 나옵니다.

 

P(neg|t) = 1/4 * 0/4 * 0/4 * 1/3 = 0

 

여기서 생기는 0의 확률문제는 전체 count 1씩을 더해서 확률이 0이 되는 것을 방지합니다.


smoothing 
방법

P(x|c) = count(클래스 c에 있는 x 단어 수) + 1 / count(클래스 단어 수) + count(모든 클래스 단어 수 중복x)

 

다시 계산하면

1) P(pos|t) = 2/18 * 2/18 * 2/18 * 1/3 = 4.572473…e-4

2) P(neg|t) = 2/16 * 1/16 * 1/16 * 1/3 = 1.627604…e-4

결과 1번 값이 크기 때문에 긍정으로 분류될 수 있습니다.

 

 

2. 생각:

 

인터넷에서 총 200개의 문장을 가져왔고

태그는 (보통명사, 동사, 형용사, 보조동사, 명사추정범주) 를 사용해서 
['NNG', 'VV', 'VA', 'VXV', 'UN']
참고:https://docs.google.com/spreadsheets/d/1OGAjUvalBuX-oZvZ_-9tEfYD2gQe7hTGsgUpiiBSXI8/edit#gid=0

긍정, 부정, 중립으로 나눴습니다.

 

많은 문장을 사용한 것은 아니지만 생각보다 정확하게 분류를 했습니다.

 

bayesian classification은 간단하면서 굉장히 효과적인 방법인 것 같습니다.

 


3. 테스트 방법 :

 

먼저 KoNLPy 패키지가 설치되어 있어야 합니다. 
(
한국어 형태소 분석을 하기 위해 설치했습니다. )
http://konlpy.readthedocs.org/en/latest/


 

설치 후

test.txt에 판별하고 싶은 문장을 저장합니다. 
tester.py
를 실행시킵니다.

 

 

4. 소스

이론을 알아보기 위한 소스이기 때문에 테스트 용입니다. 

 

  1. #-*- coding: utf-8 -*-
  2. from konlpy.tag import *
  3. from konlpy.utils import pprint
  4.  
  5. #make lists
  6. def getting_list(filename, listname):
  7. while 1:
  8. line = filename.readline()
  9. str = unicode(line, 'utf-8')
  10. line_parse = kkma.pos(str)
  11. for i in line_parse:
  12. if i[1] == u'SW':
  13. if i[0] in [u'♡', u'♥']:
  14. listname.append(i[0])
  15. if i[1] in list_tag:
  16. listname.append(i[0])
  17. if not line:
  18. break
  19. return listname
  20.  
  21. #naive bayes classifier + smoothing
  22. def naive_bayes_classifier(test, train, all_count):
  23. counter = 0
  24. list_count = []
  25. for i in test:
  26. for j in range(len(train)):
  27. if i == train[j]:
  28. counter = counter + 1
  29. list_count.append(counter)
  30. counter = 0
  31. list_naive = []
  32. for i in range(len(list_count)):
  33. list_naive.append((list_count[i]+1)/float(len(train)+all_count))
  34. result = 1
  35. for i in range(len(list_naive)):
  36. result *= float(round(list_naive[i], 6))
  37. return float(result)*float(1.0/3.0)
  38.  
  39. # get the data
  40. kkma = Kkma()
  41. f_pos = open('positive.txt', 'r')
  42. f_neg = open('negative.txt', 'r')
  43. f_neu = open('neutral.txt', 'r')
  44. f_test = open('test.txt', 'r')
  45.  
  46. # tag list (보통명사, 동사, 형용사, 보조동사, 명사추정범주)
  47. list_tag = [u'NNG', u'VV', u'VA', u'VXV', u'UN']
  48. list_positive=[]
  49. list_negative=[]
  50. list_neutral=[]
  51.  
  52. # extract test sentence
  53. test_line = f_test.readline()
  54. test_s = unicode(test_line, 'utf-8')
  55. test_list=kkma.pos(test_s)
  56. test_output=[]
  57. for i in test_list:
  58. if i[1] == u'SW':
  59. if i[0] in [u'♡', u'♥']:
  60. test_output.append(i[0])
  61. if i[1] in list_tag:
  62. test_output.append(i[0])
  63.  
  64. # getting_list함수를 통해 필요한 tag를 추출하여 list 생성
  65. list_positive = getting_list(f_pos, list_positive)
  66. list_negative = getting_list(f_neg, list_negative)
  67. list_neutral = getting_list(f_neu, list_neutral)
  68.  
  69. ALL = len(set(list_positive))+len(set(list_negative))+len(set(list_neutral)) #전체 카운트, 함수에 들어가야한다. (all_count)
  70.  
  71. # naive bayes 값 계산
  72. result_pos = naive_bayes_classifier(test_output, list_positive, ALL)
  73. result_neg = naive_bayes_classifier(test_output, list_negative, ALL)
  74. result_neu = naive_bayes_classifier(test_output, list_neutral, ALL)
  75.  
  76. if (result_pos > result_neg and result_pos > result_neu):
  77. print u'긍정'
  78. elif (result_neg > result_pos and result_neg > result_neu):
  79. print u'부정'
  80. else:
  81. print u'중립'
  82.  
  83. f_pos.close()
  84. f_neg.close()
  85. f_neu.close()
  86. f_test.close()
  87.  

 

반응형