import tensorflow as tf
import numpy as np
import pandas as pd
path_to_train_file = tf.keras.utils.get_file('train.txt', 'https://raw.githubusercontent.com/e9t/nsmc/master/ratings_train.txt')
path_to_test_file = tf.keras.utils.get_file('test.txt', 'https://raw.githubusercontent.com/e9t/nsmc/master/ratings_test.txt')
train_text = open(path_to_train_file, 'rb').read().decode(encoding='utf-8')
test_text = open(path_to_test_file, 'rb').read().decode(encoding='utf-8')
# 텍스트가 총 몇 자인지 확인
print('Length of text: {} characters'.format(len(train_text)))
print('Length of text: {} characters'.format(len(test_text)))
# 처음 300자를 확인
print(train_text[:300])
'''
Length of text: 6937271 characters
Length of text: 2318260 characters
id document label
9976970 아 더빙.. 진짜 짜증나네요 목소리 0
3819312 흠...포스터보고 초딩영화줄....오버연기조차 가볍지 않구나 1
10265843 너무재밓었다그래서보는것을추천한다 0
9045019 교도소 이야기구먼 ..솔직히 재미는 없다..평점 조정 0
6483659 사이몬페그의 익살스런 연기가 돋보였던 영화!스파이더맨에서 늙어보이기만 했던 커스틴 던스트가 너무나도 이뻐보였다 1
5403919 막 걸음마 뗀 3세부터 초등학교 1학년생인 8살용영화.ㅋㅋㅋ...별반개도 아까움. 0
7797314 원작의
'''
1) 각 텍스트를 개행 문자(\n)로 분리한 다음
2) 헤더에 해당하는 부분(id document label)을 제외한 나머지([1:])에 대해 각 행을 처리
3) 각 행은 탭 문자(\t)로 나눠진 후에 2번째 원소를 정수(integer)로 변환해서 저장
4) np.array로 결과 리스트를 감싸서 네트워크에 입력하기 쉽게 만들기
train_Y = np.array([[int(row.split('\t')[2])]
for row in train_text.split('\n')[1:] if row.count('\t') > 0])
test_Y = np.array([[int(row.split('\t')[2])]
for row in test_text.split('\n')[1:] if row.count('\t') > 0])
print(train_Y.shape, test_Y.shape)
print(train_Y[:5])
'''
(150000, 1) (50000, 1)
[[0]
[1]
[0]
[0]
[1]]
'''
# train 데이터의 입력(X)에 대한 정제(Cleaning)
import re
# From https://github.com/yoonkim/CNN_sentence/blob/master/process_data.py
def clean_str(string):
string = re.sub(r"[^가-힣A-Za-z0-9(),!?\'\`]", " ", string)
string = re.sub(r"\'s", " \'s", string)
string = re.sub(r"\'ve", " \'ve", string)
string = re.sub(r"n\'t", " n\'t", string)
string = re.sub(r"\'re", " \'re", string)
string = re.sub(r"\'d", " \'d", string)
string = re.sub(r"\'ll", " \'ll", string)
string = re.sub(r",", " , ", string)
string = re.sub(r"!", " ! ", string)
string = re.sub(r"\(", " \( ", string)
string = re.sub(r"\)", " \) ", string)
string = re.sub(r"\?", " \? ", string)
string = re.sub(r"\s{2,}", " ", string)
string = re.sub(r"\'{2,}", "\'", string)
string = re.sub(r"\'", "", string)
return string.lower()
train_text_X = [row.split('\t')[1] for row in train_text.split('\n')[1:] if row.count('\t') > 0]
train_text_X = [clean_str(sentence) for sentence in train_text_X]
# 문장을 띄어쓰기 단위로 단어 분리
sentences = [sentence.split(' ') for sentence in train_text_X]
import matplotlib.pyplot as plt
sentence_len = [len(sentence) for sentence in sentences]
sentence_len.sort()
plt.plot(sentence_len)
plt.show()
print(sum([int(l<=25) for l in sentence_len]))
sentence_new = []
for sentence in sentences:
sentence_new.append([word[:5] for word in sentence][:25])
sentences = sentence_new
for i in range(5):
print(sentences[i])
'''
['아', '더빙', '진짜', '짜증나네요', '목소리']
['흠', '포스터보고', '초딩영화줄', '오버연기조', '가볍지', '않구나']
['너무재밓었']
['교도소', '이야기구먼', '솔직히', '재미는', '없다', '평점', '조정']
['사이몬페그', '익살스런', '연기가', '돋보였던', '영화', '!', '스파이더맨', '늙어보이기', '했던', '커스틴', '던스트가', '너무나도', '이뻐보였다']
'''
Tokenizer
는 데이터에 출현하는 모든 단어의 개수를 세고 빈도 수로 정렬해서 num_words
에 지정된 만큼만 숫자로 반환하고 나머지는 0으로 반환합니다.tokenizer.fit_on_texts(sentences)
는 Tokenizer
에 데이터를 실제로 입력합니다.tokenizer.texts_to_sequences(sentence)
는 문장을 입력받아 숫자를 반환합니다.pad_sequences()
는 입력된 데이터에 패딩을 더합니다.pre
는 문장의 앞에 패딩을 넣고, post
는 문장의 뒤에 패딩을 넣습니다. 여기에서는 post를 사용합니다.from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
tokenizer = Tokenizer(num_words=2000)
tokenizer.fit_on_texts(sentences)
train_X = tokenizer.texts_to_sequences(sentences)
train_X = pad_sequences(train_X, padding='post')
print(train_X[:5])
'''
[[ 25 884 8 1111 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0]
[ 588 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0]
[ 71 346 31 35 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0]
[ 106 4 2 869 573 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0]]
'''
model = tf.keras.Sequential([
tf.keras.layers.Embedding(20000, 300, input_length=25),
tf.keras.layers.LSTM(units=50),
tf.keras.layers.Dense(2, activation='softmax')
])
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.summary()
history = model.fit(train_X, train_Y, epochs=5, batch_size=128, validation_split=0.2)
# 테스트
test_sentence = '재미있을 줄 알았는데 완전 실망했다. 너무 졸리고 돈이 아까웠다.'
test_sentence = test_sentence.split(' ')
test_sentences = []
now_sentence = []
for word in test_sentence:
now_sentence.append(word)
test_sentences.append(now_sentence[:])
test_X_1 = tokenizer.texts_to_sequences(test_sentences)
test_X_1 = pad_sequences(test_X_1, padding='post', maxlen=25)
prediction = model.predict(test_X_1)
for idx, sentence in enumerate(test_sentences):
print(sentence)
print(prediction[idx])
'''
['재미있을']
[0.5503682 0.44963175]
['재미있을', '줄']
[0.5536265 0.44637352]
['재미있을', '줄', '알았는데']
[0.5636333 0.43636665]
['재미있을', '줄', '알았는데', '완전']
[0.5324937 0.46750632]
['재미있을', '줄', '알았는데', '완전', '실망했다.']
[0.5324937 0.46750632]
['재미있을', '줄', '알았는데', '완전', '실망했다.', '너무']
[0.5381458 0.46185422]
['재미있을', '줄', '알았는데', '완전', '실망했다.', '너무', '졸리고']
[0.5381458 0.46185422]
['재미있을', '줄', '알았는데', '완전', '실망했다.', '너무', '졸리고', '돈이']
[0.9767985 0.02320149]
['재미있을', '줄', '알았는데', '완전', '실망했다.', '너무', '졸리고', '돈이', '아까웠다.']
[0.97679853 0.0232015 ]
'''
Reference
1) 제로베이스 데이터스쿨 강의자료
2)
https://dschloe.github.io/python/tensorflow2.0/ch7_3_sentimentanalysis/