Posts 나이브 베이지안 분류기(Naive Bayes Classifier)으로 해보는 NLTK
Post
Cancel

나이브 베이지안 분류기(Naive Bayes Classifier)으로 해보는 NLTK

1. 단일문서의 WordCloud


1.1 단일문서 불러오기

1
2
3
4
5
6
import nltk
from konlpy.corpus import kobill

file_ko = kobill.fileids()
doc_ko = kobill.open('1809890.txt').read()
print(doc_ko)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
지방공무원법 일부개정법률안

(정의화의원 대표발의 )

 의 안
 번 호

9890

발의연월일 : 2010.  11.  12.  

발  의  자 : 정의화․이명수․김을동 

이사철․여상규․안규백

황영철․박영아․김정훈

김학송 의원(10인)

제안이유 및 주요내용

  초등학교 저학년의 경우에도 부모의 따뜻한 사랑과 보살핌이 필요

한 나이이나, 현재 공무원이 자녀를 양육하기 위하여 육아휴직을 할 

수 있는 자녀의 나이는 만 6세 이하로 되어 있어 초등학교 저학년인 

자녀를 돌보기 위해서는 해당 부모님은 일자리를 그만 두어야 하고 

이는 곧 출산의욕을 저하시키는 문제로 이어질 수 있을 것임.

  따라서 육아휴직이 가능한 자녀의 연령을 만 8세 이하로 개정하려

는 것임(안 제63조제2항제4호).

- 1 -
...
  • 국회 법률 의안이 패키지에 있음
  • 제 1809890은 육아 휴직을 만 8세 아동까지 대상을 적용한다는 법
  • 그 외에 1809890호 ~ 1809899호 까지 있음


1.2 명사 추출

1
2
3
from konlpy.tag import Okt; t = Okt()
tokens_ko = t.nouns(doc_ko)
print(tokens_ko)
1
['지방공무원법', '일부', '개정', '법률', '안', '정의화', '의원', '대표', '발의', '의', '안', '번', '호', '발의', '연월일', '발', '의', '자', '정의화', '이명수', '김을동', '이사철', '여상규', '안규백', '황영철', '박영아', '김정훈', '김학송', '의원', '인', '제안', '이유', '및', '내용', '초등학교', '저학년', '경우', '부모', '사랑', '필요', '나이', '현재', '공무원', '자녀', '양육', '위', '육아휴직', '수', '자녀', '나이', '만', '세', '이하', '초등학교', '저학년', '자녀', '위', '해당', '부모님', '일자리', '곧', '출산', ...]
  • konlpy를 이용하여 불러온 문서에서 명사만 추출함


1.3 단어의 사용 빈도 확인

1
2
3
4
ko =nltk.Text(tokens_ko, name = '대한민국 국회 의안 제 1809890호')
print(len(ko.tokens))
print(len(set(ko.tokens)))
ko.vocab()
1
2
3
735
250
FreqDist({'육아휴직': 38, '발생': 19, '만': 18, '이하': 18, '비용': 17, '액': 17, '경우': 16, '세': 16, '자녀': 14, '고용': 14, ...})
  • 전체단어는 총 735개이며, 중복을 제거하면 250개임
  • 그중 육아휴직 법안답게 육아휴직 단어가 제일 많이 언급됨


1.4 빈도별 정렬

1
2
3
plt.figure(figsize=(12,6))
ko.plot(50)
plt.show()

  • 단어 빈도가 가장 높은 50를 빈도수를 토대로 그래프를 그려봄
  • 이, 의, 수, 것, 등 빼야할 단어들이 보임

1.4.1 matplotlib 한글 설정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import matplotlib.pyplot as plt
%matplotlib inline

from matplotlib import font_manager, rc
plt.rcParams['axes.unicode_minus'] = False

# 1) windows
f_path = '글꼴이 설치된 위치' # /Users/위치/AppData/Local/Microsoft/Windows/Fonts/NanumGothicCoding.ttf'
font_name = font_manager.FontProperties(fname=f_path).get_name()
rc('font', family = font_name)

# 2) mac
from matplotlib import rc
rc('font', family='AppleGothic')

plt.rcParams['axes.unicode_minus'] = False
  • 해당 코드를 startup.py에 적어서 넣으면 해당 코드를 매번 적을 필요가 없음
  • 윈도우와 맥, os에 맞게 설정을 하면된다.

1.5 Stopword 설정

1
2
3
4
stop_word = ['.', '()', ')', ',', "'", '%', '-', 'X', ').', 'x', '것', '위', '중', '생', '략',
             '의', '자', '에', '안', '번', '호', '을', '이', '다', '만', '로', '가', '를', '액', '세', '제', '수']
ko = [each_word for each_word in ko if each_word not in stop_word]
print(ko)
1
['지방공무원법', '일부', '개정', '법률', '정의화', '의원', '대표', '발의', '발의', '연월일', '발', '정의화', '이명수', '김을동', '이사철', '여상규', '안규백', '황영철', '박영아', '김정훈', '김학송', '의원', '인', '제안', '이유', '및', '내용', '초등학교', '저학년', '경우', '부모', '사랑', '필요', '나이', '현재', '공무원', '자녀', '양육', '육아휴직', '자녀', '나이', '이하', '초등학교', '저학년', '자녀', '해당', '부모님', ...]
  • Stopword를 설정하여 ko에서 제외시킴


1.6 빈도 그리기

1
2
3
4
5
ko =nltk.Text(ko, name = '대한민국 국회 의안 제 1809890호')

plt.figure(figsize=(12,6))
ko.plot(50)
plt.show()

  • stopword로 제외시킨 단어는 없어지고 나오게 된다.


1.7 특정 단어가 발생한 빈도

1
ko.count('초등학교')
1
6
  • count 메서드를 사용해서 특정 단어가 나온 빈도를 확인 할수 있음
  • 초등학교는 총 6번 반복됨


1.8 문서내 특정 단어의 위치

1
2
plt.figure(figsize=(12, 6))
ko.dispersion_plot(['육아휴직', '초등학교', '공무원'])

  • dispersion_plot 메서드를 사용해서 문서내의 특정 단어의 위치를 출력함
  • 육아휴직이란 단어는 후반부에 많이 나오고 공무원과 초등학교는 초반부에 많이 나오는것으로 확인됨


1.9 특정 단어 주변에 나열된 단어들

1
ko.concordance('초등학교')
1
2
3
4
5
6
7
Displaying 6 of 6 matches:
 안규백 황영철 박영아 김정훈 김학송 의원 인 제안 이유 및 내용 초등학교 저학년 경우 부모 사랑 필요 나이 현재 공무원 자녀 양육 육아휴직
 사랑 필요 나이 현재 공무원 자녀 양육 육아휴직 자녀 나이 이하 초등학교 저학년 자녀 해당 부모님 일자리 곧 출산 의욕 저하 문제 것임 따
방공무원법 일부 개정 법률 지방공무원법 일부 다음 개정 항제 이하 초등학교 취학 전 자녀 이하 취학 중인 경우 초등학교 학년 이하 말 자녀 
다음 개정 항제 이하 초등학교 취학 전 자녀 이하 취학 중인 경우 초등학교 학년 이하 말 자녀 부 칙 법 공포 날 시행 신 구조 문대비 표 
 직 임용 휴직 명 다만 경우 대통령령 정 사정 직 명 현행 이하 초등학교 취 이하 취학 중인 경우 학 전 자녀 양육 초등학교 학년 이하 여
명 현행 이하 초등학교 취 이하 취학 중인 경우 학 전 자녀 양육 초등학교 학년 이하 여 여자 공무원 말 자녀 임신 출산 때 현행 현행 지방
  • concordance 메서드를 사용하여 특정 단어의 위치에 단어들을 확인


1.10 WordCloud

1
2
3
4
5
6
7
8
9
10
11
from wordcloud import WordCloud

data = ko.vocab().most_common(200)

wordcloud = WordCloud(font_path='AppleGothic', relative_scaling=0.2,
                      background_color='white').generate_from_frequencies(dict(data))

plt.figure(figsize=(12, 8))
plt.imshow(wordcloud)
plt.axis('off')
plt.show()

  • WordCloud는 font_path를 설정해야 한글이 나옴


2. Naive Bayes Classifier


2.1 Naive Bayes


2.2 나이브 베이즈 분류란?

  • 기계 학습 분야에서 나이브 베이즈 분류는 특성들 사이의 독립을 가정하는 베이즈 정리를 적용한 확률 분류기의 일종
  • 텍스트 분류에 사용됨으로써 문서를 여러 범주중 하나로 판단하는 문제에 대한 대중적인 방법으로 남아있음


2.3 토큰화 (tokenize)

1
2
3
4
5
6
7
from nltk.tokenize import word_tokenize
import nltk

train = [('i like you', 'pos'),
         ('i hate you', 'neg'),
         ('you like me', 'neg'),
         ('i like her', 'pos')]
  • 주어진 코퍼스(corpus)에서 토큰(token)이라 불리는 단위로 나누는 작업을 토큰화(tokenization)라고 함.
  • 토큰의 단위가 상황에 따라 다르지만, 보통 의미있는 단위로 토큰을 정의
  • train 데이터는 i like you는 pos(긍정), i hate you는 (부정)으로 라벨링이 된 상태


2.4 전체 말뭉치

1
2
3
all_words = set(word.lower() for sentence in train
                   for word in word_tokenize(sentence[0]))
all_words
1
{'hate', 'her', 'i', 'like', 'me', 'you'}
  • 이중 for문을 한번에 작성 한것.


1
2
3
4
5
6
all_words = []
for sentence in train:
    for word in word_tokenize(sentence[0]):
        all_words.append(word.lower())

set(all_words)
1
{'hate', 'her', 'i', 'like', 'me', 'you'}
  • 위의 코드를 이중 for문으로 작성
  • 해당


2.6 훈련용 데이터에 마킹하기

1
2
3
t = [({word: (word in word_tokenize(x[0]))
   for word in all_words}, x[1]) for x in train]
t
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[({'i': True,
   'like': True,
   'you': True,
   'hate': False,
   'me': False,
   'her': False},
  'pos'),
 ({'i': True,
   'like': False,
   'you': True,
   'hate': True,
   'me': False,
   'her': False},
  'neg'),
 ({'i': False,
   'like': True,
   'you': True,
   'hate': False,
   'me': True,
   'her': False},
  'neg'),
 ({'i': True,
   'like': True,
   'you': False,
   'hate': False,
   'me': False,
   'her': True},
  'pos')]
  • 전체 단어 (‘hate’, ‘her’, ‘i’, ‘like’, ‘me’, ‘you’) 중에 1번쨰 train 데이터에 있으면 true, 없으면 false로 나오게 됨


2.7 나이즈 베이즈 분류기 (Naive Bayes Classifier)

1
2
classifier = nltk.NaiveBayesClassifier.train(t)
classifier.show_most_informative_features()
1
2
3
4
5
6
7
Most Informative Features
                    hate = False             pos : neg    =      1.7 : 1.0
                     her = False             neg : pos    =      1.7 : 1.0
                       i = True              pos : neg    =      1.7 : 1.0
                    like = True              pos : neg    =      1.7 : 1.0
                      me = False             pos : neg    =      1.7 : 1.0
                     you = True              neg : pos    =      1.7 : 1.0
  • NLTK가 제공하는 Naive Bayes 분류기를 사용해서 훈련함
  • i 가 있다 (i = True)는 1.7:1.0의 확률로 pos 하라라는 뜻


2.8 Test 문장 입력

1
2
3
4
5
test_sentence = 'I like Him'

test_sent_features = {word.lower() : (word in word_tokenize(test_sentence.lower()))
                                         for word in all_words}
test_sent_features
1
2
3
4
5
6
{'i': True,
 'like': True,
 'you': False,
 'hate': False,
 'me': False,
 'her': False}
  • 위에서 훈련한 데이터를 토대로 I like Him 이라고 입력함


2.9 분류 결과

1
classifier.classify(test_sent_features)
1
'pos'
  • I like Him은 pos 라고 함


3. 한글에서 나이브 베이지안 분류기 (Naive Bayes Classifier)


3.1 훈련용 문장

1
2
3
4
5
6
7
8
9
from konlpy.tag import Okt
pos_tagger = Okt()

train = [('난 수업이 빨리 마치면 좋겠어' , 'pos'),
        ('내일은 수업이 없어서 좋아','pos'),
        ('내일은 놀러가야지','pos'),
        ('오늘 수업은 정말 지루해','neg'),
        ('수업은 아직 시작도 안했어','neg'),
        ('나는 왜 이런걸 해야하는지 모르겠어','neg')]
  • 위에서 영어로 한것과 마찬가지로 한글로 긍정과 부정을 나누어 표현함


3.2 형태소 분석

1
pos_tagger.pos(train[0][0])
1
2
3
4
5
6
7
[('난', 'Noun'),
 ('수업', 'Noun'),
 ('이', 'Josa'),
 ('빨리', 'Adverb'),
 ('마치', 'Noun'),
 ('면', 'Josa'),
 ('좋겠어', 'Adjective')]
  • 일단 train중 첫번째 한개만 형태소 분석을 해봄
  • 실제론 태그가 붙어 있는 상태에서 사용하는것이 좋음
  • 연습중이니 간략하게 해보자


3.3 훈련용 데이터 형태소 분석

1
2
train_docs = [(pos_tagger.morphs(sentence[0]), sentence[1]) for sentence in train]
train_docs
1
2
3
4
5
6
[(['난', '수업', '이', '빨리', '마치', '면', '좋겠어'], 'pos'),
 (['내일', '은', '수업', '이', '없어서', '좋아'], 'pos'),
 (['내일', '은', '놀러', '가야', '지'], 'pos'),
 (['오늘', '수업', '은', '정말', '지루해'], 'neg'),
 (['수업', '은', '아직', '시작', '도', '안', '했어'], 'neg'),
 (['나', '는', '왜', '이런', '걸', '해야하는지', '모르겠어'], 'neg')]
  • 훈련용 데이터의 형태소 분석 후 긍정 부정 붙이기


3.4 말 뭉치 만들기

1
2
all_words = set([t for d in train_docs for t in d[0]])
all_words
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{'가야',
 '걸',
 '나',
 '난',
 '내일',
 '놀러',
 '는',
 '도',
 '마치',
 '면',
 '모르겠어',
 '빨리',
 '수업',
 '시작',
 '아직',
 '안',
 '없어서',
 '오늘',
 '왜',
 '은',
 '이',
 '이런',
 '정말',
 '좋겠어',
 '좋아',
 '지',
 '지루해',
 '해야하는지',
 '했어'}
  • 형태소 분석 후에 말뭉치 만들기


3.5 훈련용 데이터에 마킹

1
2
3
4
5
def term_exists(doc):
    return {word : (word in set(doc)) for word in all_words}

train_xy = [(term_exists(d), c) for d,c in train_docs]
train_xy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
[({'걸': False,
   '수업': True,
   '는': False,
   '정말': False,
   '마치': True,
   '내일': False,
   '이런': False,
   '해야하는지': False,
   '했어': False,
   '놀러': False,
   '시작': False,
   '나': False,
   '이': True,
   '안': False,
   '아직': False,
   '왜': False,
   '오늘': False,
   '은': False,
   '좋겠어': True,
   '가야': False,
   '좋아': False,
   '지루해': False,
   '도': False,
   '빨리': True,
   '지': False,
   '면': True,
   '없어서': False,
   '모르겠어': False,
   '난': True},
  'pos'),
 ({'걸': False,
   '수업': True,
   '는': False,
   '정말': False,
   '마치': False,
   '내일': True,
   '이런': False,
   '해야하는지': False,
   '했어': False,
   '놀러': False,
   '시작': False,
   '나': False,
   '이': True,
   '안': False,
   '아직': False,
   '왜': False,
   '오늘': False,
   '은': True,
   '좋겠어': False,
   '가야': False,
   '좋아': True,
   '지루해': False,
   '도': False,
   '빨리': False,
   '지': False,
   '면': False,
   '없어서': True,
   '모르겠어': False,
   '난': False},
  'pos'),
 ({'걸': False,
   '수업': False,
   '는': False,
   '정말': False,
   '마치': False,
   '내일': True,
   '이런': False,
   '해야하는지': False,
   '했어': False,
   '놀러': True,
   '시작': False,
   '나': False,
   '이': False,
   '안': False,
   '아직': False,
   '왜': False,
   '오늘': False,
   '은': True,
   '좋겠어': False,
   '가야': True,
   '좋아': False,
   '지루해': False,
   '도': False,
   '빨리': False,
   '지': True,
   '면': False,
   '없어서': False,
   '모르겠어': False,
   '난': False},
  'pos'),
 ({'걸': False,
   '수업': True,
   '는': False,
   '정말': True,
   '마치': False,
   '내일': False,
   '이런': False,
   '해야하는지': False,
   '했어': False,
   '놀러': False,
   '시작': False,
   '나': False,
   '이': False,
   '안': False,
   '아직': False,
   '왜': False,
   '오늘': True,
   '은': True,
   '좋겠어': False,
   '가야': False,
   '좋아': False,
   '지루해': True,
   '도': False,
   '빨리': False,
   '지': False,
   '면': False,
   '없어서': False,
   '모르겠어': False,
   '난': False},
  'neg'),
 ({'걸': False,
   '수업': True,
   '는': False,
   '정말': False,
   '마치': False,
   '내일': False,
   '이런': False,
   '해야하는지': False,
   '했어': True,
   '놀러': False,
   '시작': True,
   '나': False,
   '이': False,
   '안': True,
   '아직': True,
   '왜': False,
   '오늘': False,
   '은': True,
   '좋겠어': False,
   '가야': False,
   '좋아': False,
   '지루해': False,
   '도': True,
   '빨리': False,
   '지': False,
   '면': False,
   '없어서': False,
   '모르겠어': False,
   '난': False},
  'neg'),
 ({'걸': True,
   '수업': False,
   '는': True,
   '정말': False,
   '마치': False,
   '내일': False,
   '이런': True,
   '해야하는지': True,
   '했어': False,
   '놀러': False,
   '시작': False,
   '나': True,
   '이': False,
   '안': False,
   '아직': False,
   '왜': True,
   '오늘': False,
   '은': False,
   '좋겠어': False,
   '가야': False,
   '좋아': False,
   '지루해': False,
   '도': False,
   '빨리': False,
   '지': False,
   '면': False,
   '없어서': False,
   '모르겠어': True,
   '난': False},
  'neg')]
  • 위에서 했던것 처럼 훈련 데이터에 전체 말뭉치에 있으면 True, 없으면 False를 입력함


3.6 Naive Bayes 훈련

1
2
classifier = nltk.NaiveBayesClassifier.train(train_xy)
classifier.show_most_informative_features()
1
2
3
4
5
6
7
8
9
10
11
Most Informative Features
                      내일 = False             neg : pos    =      2.3 : 1.0
                       이 = False             neg : pos    =      2.3 : 1.0
                      가야 = False             neg : pos    =      1.4 : 1.0
                       걸 = False             pos : neg    =      1.4 : 1.0
                       나 = False             pos : neg    =      1.4 : 1.0
                       난 = False             neg : pos    =      1.4 : 1.0
                      놀러 = False             neg : pos    =      1.4 : 1.0
                       는 = False             pos : neg    =      1.4 : 1.0
                       도 = False             pos : neg    =      1.4 : 1.0
                      마치 = False             neg : pos    =      1.4 : 1.0
  • 내일 = False면 neg : pos가 2.3 : 1.0 즉, 내일이라는 단어가 있으면 neg일 확률이 높음


3.7 테스트문장 형태소 분석

1
2
3
test_sentence = '수업 마치면 놀러 가야지'
test_docs = pos_tagger.morphs(test_sentence)
test_docs
1
['수업', '마치', '면', '놀러', '가야', '지']
  • 테스트문장을 입력 후 형태소 분석 하였음
  • 수업 마치면 놀러 가야지 라는 문장은 긍정일까 부정일까

3.8 테스트 데이터 마킹

1
2
test_xy = term_exists(test_sentence)
test_xy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{'걸': False,
 '수업': False,
 '는': False,
 '정말': False,
 '마치': False,
 '내일': False,
 '이런': False,
 '해야하는지': False,
 '했어': False,
 '놀러': False,
 '시작': False,
 '나': False,
 '이': False,
 '안': False,
 '아직': False,
 '왜': False,
 '오늘': False,
 '은': False,
 '좋겠어': False,
 '가야': False,
 '좋아': False,
 '지루해': False,
 '도': False,
 '빨리': False,
 '지': True,
 '면': True,
 '없어서': False,
 '모르겠어': False,
 '난': False}
  • 훈련용 데이터의 말뭉치를 기준으로 테스트 데이터에 True, False 마스킹을 함

3.9 테스트

1
classifier.classify(test_xy)
1
'pos'
  • 수업 마치면 놀러가야지 라는 테스트 문장은 긍정으로 나왔다.


3.10 다른 문장 테스트

1
2
test_sentence = '오늘은 수업 언제 마칠려나'
classifier.classify(term_exists(pos_tagger.morphs(test_sentence)))
1
'neg'
1
2
test_sentence = '내일이 기다려 진다'
classifier.classify(term_exists(pos_tagger.morphs(test_sentence)))
1
'pos'
1
2
test_sentence = '내일은 또 어떤 일이 있을까'
classifier.classify(term_exists(pos_tagger.morphs(test_sentence)))
1
'pos'
1
2
test_sentence = '내일이 안왔으면 좋겠다'
classifier.classify(term_exists(pos_tagger.morphs(test_sentence)))
1
'pos'
  • 어느정도 맞추는것 같지만, 내일이 안왔으면 좋겠다 라는 문장이 pos로 나오는것을 보면 완전하지 않은것으로 파악됨
  • 당연히 훈련용 데이턱 적으니 완벽하진 않을듯..
This post is licensed under CC BY 4.0 by the author.