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
| from konlpy.tag import Kkma, Hannanum,Komoran, Twitter, Okt
from konlpy.utils import pprint
kkma = Kkma()
han = Hannanum()
kom = Komoran()
tw = Twitter()
okt = Okt()
# nlp 라이브러리의 특성을 공부하고 잘 분석해서 상황에 맞는 nlp를 하기 위해 라이브러리를 선별한다.
# 보통 Kkma가 성능이 대체적으로 좋다.
pprint(kkma.sentences(u"네 안녕하세요. 반갑습니다."))
: ['네 안녕하세요.', '반갑습니다.']
# Kkma는 나는 => '날'로 제대로 토큰화가 되는걸 확인할 수 있다.
pprint(kkma.pos(u"하늘을 나는 자동차")) # 품사
: [('하늘', 'NNG'), ('을', 'JKO'), ('날', 'VV'), ('는', 'ETD'), ('자동차', 'NNG')]
print(han.analyze(u"아버지가 방에 들어가신다. 안녕하세요. 하늘을 나는 자동차"))
: [[[('아버지', 'ncn'), ('가', 'jcc')], [('아버지', 'ncn'), ('가', 'jcs')]], [[('방', 'nbu'), ('에', 'jca')], [('방', 'ncn'), ('에',
'jca')]], [[('들', 'pvg'), ('어', 'ecx'), ('가', 'px'), ('시', 'ep'), ('ㄴ다', 'ef')], [('듣', 'pvg'), ('어', 'ecx'), ('가', 'px'),
('시', 'ep'), ('ㄴ다', 'ef')], [('들어가', 'pvg'), ('시', 'ep'), ('ㄴ다', 'ef')]], [[('.', 'sf')], [('.', 'sy')]], [], [[('안녕',
'ncps'), ('하세', 'ncpa'), ('요', 'ncn')], [('안녕', 'ncps'), ('하', 'xsms'), ('세요', 'ef')], [('안녕', 'ncps'), ('하', 'xsms'), ('세',
'ef'), ('요', 'jxf')]], [[('.', 'sf')], [('.', 'sy')]], [], [[('하늘', 'ncn'), ('을', 'jco')]], [[('나', 'ncn'), ('는', 'jxc')], [('나',
'npp'), ('는', 'jxc')], [('나', 'pvg'), ('는', 'etm')], [('나', 'px'), ('는', 'etm')], [('나', 'pvg'), ('아', 'ecs'), ('는', 'jxc')
[('나', 'pvg'), ('아', 'ef'), ('는', 'etm')], [('나', 'px'), ('아', 'ecs'), ('는', 'jxc')], [('나', 'px'), ('아', 'ef'), ('는', 'etm')],
[('날', 'pvg'), ('는', 'etm')]], [[('자동차', 'ncn')], [('자동', 'ncn'), ('차', 'ncn')]]]
print(han.morphs(u"아버지가 방에 들어가신다. 안녕하세요. 하늘을 나는 자동차")) # 형태소 분석
: ['아버지', '가', '방', '에', '들', '어', '가', '시ㄴ다', '.', '안녕', '하', '세', '요', '.', '하늘', '을', '나', '는', '자동차']
print(kom.pos(u"아버지가 방에 들어가신다. 안녕하세요. 하늘을 나는 자동차"))
: [('아버지', 'NNG'), ('가', 'JKS'), ('방', 'NNG'), ('에', 'JKB'), ('들어가', 'VV'), ('시', 'EP'), ('ㄴ다', 'EF'), ('.', 'SF'), ('안녕
세요', 'NNP'), ('.', 'SF'), ('하늘', 'NNG'), ('을', 'JKO'), ('나', 'NP'), ('는', 'JX'), ('자동차', 'NNG')]
# 파일을 읽어와 Corpus를 만드는 함수
def read_data(filename, encoding):
data = []
with open(filename, encoding=encoding) as f:
data = [line.split('\t') for line in f.read().splitlines()]
data = data[1:]
return data
# train_data와 test_data를 나누어 준다. (train, test data가 서로 분리 되어 저장 되어 있었음)
train_data = read_data("ratings_train.txt", 'utf-8')
test_data = read_data("ratings_test.txt", 'utf-8')
import json
import os
# 데이터가 너무 많아 만개 데이터만 학습 시키기 위해 데이터 split.
train_data = train_data[:10000]
test_data = test_data[:1000]
# 토큰화 함수 (토큰과 품사 정보 합치기)
def tokenize(doc):
return ['/'.join(t) for t in okt.pos(doc, norm=True, stem=True)]
# json file 형태로 저장 이미 있으면 불러와 train_docs, test_docs 변수에 저장
if os.path.isfile("train_docs.json"):
with open("train_docs.json", encoding='utf-8') as f:
train_docs = json.load(f)
with open("test_docs.json", encoding='utf-8') as f:
test_docs = json.load(f)
else:
train_docs = [(tokenize(row[1]), row[2]) for row in train_data]
test_docs = [(tokenize(row[1]), row[2]) for row in test_data]
with open("train_docs.json", 'w', encoding='utf-8') as make_file:
json.dump(train_docs, make_file, ensure_ascii=False, indent='\t')
with open("test_docs.json", 'w', encoding='utf-8') as make_file:
json.dump(test_docs, make_file, ensure_ascii=False, indent='\t')
train_docs[0]
: [['아/Exclamation',
'더빙/Noun',
'../Punctuation',
'진짜/Noun',
'짜증나다/Adjective',
'목소리/Noun'],
'0']
tokens = [t for d in train_docs for t in d[0]]
tokens[0]
: '아/Exclamation'
import nltk
text = nltk.Text(tokens, name='NMSC') # 문서를 편리하게 탐색할 수 있는 기능을 제공
text.vocab().most_common(10)
: [('./Punctuation', 4791),
('영화/Noun', 3368),
('하다/Verb', 2829),
('이/Josa', 2624),
('보다/Verb', 2576),
('의/Josa', 2123),
('../Punctuation', 1949),
('가/Josa', 1789),
('에/Josa', 1771),
('을/Josa', 1587)]
# bag of words 만들기
selected_words = [f[0] for f in text.vocab().most_common(10000)]
# 문서마다 bag of words에 포함된 단어가 있는지 카운트 해주는 함수
def term_frequency(doc):
return [doc.count(word) for word in selected_words]
# 학습 시킬 수 있는 형태의 train, test corpus 만들기
train_x = [term_frequency(d) for d, _ in train_docs]
train_y = [c for _, c in train_docs]
test_x = [term_frequency(d) for d, _ in test_docs]
train_y = [c for _, c in train_docs]
import numpy as np
x_train = np.asarray(train_x).astype('float32')
x_test = np.asarray(test_x).astype('float32')
y_train = np.asarray(train_y).astype('float32')
y_test = np.asarray(test_y).astype('float32')
# 모델 만들기
from tensorflow.keras import models, layers, optimizers, losses, metrics
model = models.Sequential()
model.add(layers.Dense(64, 'relu', 10000))
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
model.compile(optimizer=optimizers.RMSprop(lr=0.001), loss=losses.binary_crossentropy, metrics=[metrics.binary_accuracy])
model.fit(x_train, y_train, epochs=10, batch_size=512)
results = model.evaluate(x_test, y_test)
: 1000/1000 [==============================] - 0s 114us/sample - loss: 0.7367 - binary_accuracy: 0.7940
# 리뷰 긍정/부정 추측하는 함수
def predict_pos_neg(review):
token=tokenize(review)
tf = term_frequency(token)
data = np.expand_dims(np.asarray(tf).astype('float32'), axis=0)
score = float(model.predict(data))
if(score > 0.5):
print("[{}]는 {:.2f}% 확률로 긍정 리뷰이지 않을까 추측해봅니다.^^\n".format(review,score*100))
else:
print("[{}]는 {:.2f}% 확률로 부정 리뷰이지 않을까 추측해봅니다.^^\n".format(review,(1-score)*100))
# 예측
predict_pos_neg("올해 최고의 영화! 세번 넘게 봐도 질리지가 않네요.")
: [올해 최고의 영화! 세번 넘게 봐도 질리지가 않네요.]는 99.99% 확률로 긍정 리뷰이지 않을까 추측해봅니다.^^
predict_pos_neg('노잼')
: [노잼]는 96.37% 확률로 부정 리뷰이지 않을까 추측해봅니다.^^
predict_pos_neg("대박 재미있음")
: [대박 재미있음]는 98.86% 확률로 긍정 리뷰이지 않을까 추측해봅니다.^^
predict_pos_neg("대박 재미없음")
: [대박 재미없음]는 73.68% 확률로 부정 리뷰이지 않을까 추측해봅니다.^^
# 예측이 잘 되지 않는 문장
predict_pos_neg("음악이 몰입에 방해가 됩니다")
: [음악이 몰입에 방해가 됩니다]는 54.90% 확률로 긍정 리뷰이지 않을까 추측해봅니다.^^
# 다시 학습하고 나서 예측이 바뀜
predict_pos_neg("음악이 몰입에 방해가 됩니다")
: [음악이 몰입에 방해가 됩니다]는 58.53% 확률로 부정 리뷰이지 않을까 추측해봅니다.^^
|