이번 코드는 상당히 여러가지 요소들도 많고 기법들도 많이 들어가 있으니 주석을 잘 보시길 바랍니다.
# Day_29_01_popcorn.py
import tensorflow as tf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import gensim
import nltk
from sklearn import model_selection, feature_extraction, linear_model
import re
# popcorn_models
# labeled = train set
# sample = 데이터를 만들어야하는 형태
# testData = test set (id와 리뷰만 있음)
# unlabeld = train set( 분류 안되있는거 사용안 할 것임)
# 팝콘에 5가지 정도의 모델을 만들어서 보여줄 것임
# RNN -> 최종적으로는 CNN으로 만들 것임
# preprocessing
def tokenizing_and_padding(x, vocab_size, seq_len):
tokenizer = tf.keras.preprocessing.text.Tokenizer(num_words=vocab_size)
tokenizer.fit_on_texts(x) # x는 문자열이 들어있는 1차원 배열
# print(tokenizer.num_words) # 2000
# print(len(tokenizer.index_word)) # 88582 -> 너무 많아서 one_hot 으로 만들기 힘들 것이다.
# print(x[1]) # The Classic War ....
x = tokenizer.texts_to_sequences(x) # word를 숫자로 변환
# sentense 분포 확인
# 250는 80%, 500개는 90% 리뷰를 포함
# freqs = sorted([len(t) for t in x])
# plt.plot(freqs)
# plt.show()
x = tf.keras.preprocessing.sequence.pad_sequences(x, maxlen=seq_len)
return x, tokenizer
def make_submission(ids, preds, file_path):
f = open(file_path, 'w', encoding='utf-8')
f.write('"id","sentiment"\n')
for i, p in zip(ids, preds):
f.write('"{}",{}\n'.format(i, p))
f.close()
# 사용 모델: baseline, rnn, cnn, cnn-tf.data
def make_submission_for_deep(ids, x_test, model, tokenizer, seq_len, file_path):
x_test = tokenizer.texts_to_sequences(x_test)
x_test = tf.keras.preprocessing.sequence.pad_sequences(x_test, maxlen=seq_len)
preds = model.predict(x_test)
preds_arg = preds.reshape(-1)
preds_bool = np.int32(preds_arg > 0.5)
# print(preds_bool[:10]) # [1 0 1 1 1 1 1 0 0 0] sub mission 을 보면 ㄷ그렇게 나와있으니깐
make_submission(ids, preds_bool, file_path)
def make_submission_for_word2vec(ids, x_test, model, word2vec, n_features, idx2word, file_path):
x_test = [s.lower().split() for s in x_test]
features = [make_features_for_word2vec(tokens, word2vec, n_features, idx2word) for tokens in x_test]
x_test = np.vstack(features)
preds = model.predict(x_test)
make_submission(ids, preds, file_path)
# 사용 모델 : word2vec, word2vec_nltk
# tokens: [the, in, happy, in, disney]
def make_features_for_word2vec(tokens, word2vec, n_features, idx2word):
# ex
# 리뷰 : [the, in, happy, in]
# the : 1 0 0 0 0 0
# in : 0 0 1 0 0 0
# happy : 0 0 0 0 1 0
# the : 0 0 1 0 0 0
# + --------------
# : 1 0 2 0 1 0 / 4 할 것임 -> 누적 결과 값이 다 다를 것임 + 형평성에 맞게 다 나눠줄 것임
binds, n_words = np.zeros(n_features), 0 # 1차원에 6개의 0이 들어가있음
# 못들어가는 경우도 있음
for w in tokens:
if w in idx2word:
binds += word2vec.wv[w]
n_words += 1
# 리뷰 하나의 대해서 안에 있는 word의 갯수만큼 배열을 더해주고 나눔
return binds if n_words == 0 else binds / n_words
def model_baseline(x, y, ids, x_test):
vocab_size, seq_len = 2000, 200
x, tokenizer = tokenizing_and_padding(x, vocab_size, seq_len)
data = model_selection.train_test_split(x, y, train_size=0.8, shuffle=False)
x_train, x_valid, y_train, y_valid = data
# ------------------------------------------- #
model = tf.keras.Sequential([
tf.keras.layers.Input(shape=[seq_len]),
tf.keras.layers.Embedding(vocab_size, 100), # 입력(2차원), 출력(3차원) 피처 추출하는 것
tf.keras.layers.LSTM(128, return_sequences=False),
tf.keras.layers.Dense(1, activation='sigmoid') # 감성분석 true or false -> 1
])
model.compile(optimizer=tf.keras.optimizers.Adam(lr=0.01),
loss=tf.keras.losses.binary_crossentropy, # 숫자 하나의 배열 사용하니깐
metrics=['acc'])
# 훈련
# step_per_epoch 한번 에 몇번 돌아가는지 알려줘야함
model.fit(x_train, y_train, epochs=10, batch_size=128, verbose=2,
validation_data=(x_valid, y_valid))
# --------------------------------------- 3
make_submission_for_deep(ids, x_test, model, tokenizer, seq_len, 'popcorn_models/baseline.csv')
# tf-idf : Term Frequency-Inverse Document Frequency
# 딥러닝 이전에 머신러닝에서 해볼 것임 과거의 방법들 (성능이 의외로 잘 나옴)
def model_tf_idf(x, y, ids, x_test):
# 3가지 영역으로 나눌 수 있음
# ------------------------------------------- #
# x, y 데이터를 만드는 전처리 부분
vocab_size, seq_len = 2000, 200
x, tokenizer = tokenizing_and_padding(x, vocab_size, seq_len)
print(x.shape) # (1000, 200)
# 싸이킷런에서 보통 문자열을 받기 때문에 다시 변환해줘야함 ㅂㄷㅂㄷ
x = tokenizer.sequences_to_texts(x) # 쓰기 싫지만 꼭 써야함 ( 숫자를 원래의 토큰으로 변환해줌 )
# feature을 추출해주겠다는 것 ( 뽑하내는 방법 Text 중에 여러가지 있음 )
tfidf = feature_extraction.text.TfidfVectorizer(
min_df=0.0, # 머신러닝이여서 별로 비중있게 다루지 않음
analyzer='word', # 단어 기반으로 만들겠다.
sublinear_tf=True, #
ngram_range=(1, 3), # 요 안에서 범위를 찾겠다. 동시에 발생? or not 판단
max_features=5000 # 단어 하나를 숫자 몇개로 표현하겠는가??
)
x = tfidf.fit_transform(x)
print(x.shape) # (1000, 5000)
data = model_selection.train_test_split(x, y, train_size=0.8, shuffle=False)
x_train, x_valid, y_train, y_valid = data
# ------------------------------------------- #
# 딥러닝보다는 약함
linear_regression = linear_model.LogisticRegression(class_weight='balanced') # 사이킷 런에서 제공해주는 것
linear_regression.fit(x_train, y_train)
print('acc :', linear_regression.score(x_valid, y_valid)) # score로 결과를 알 수 있음
# --------------------------------------- 3
# 결과를 예측해서 submission 하는 과정
# tf-ids 알고리즘을 적용한 모델에 대해 서브미션을 만들것임.
# 예측할 때는 predict 함수를 사용할 것임.
x_test = tokenizer.texts_to_sequences(x_test)
x_test = tf.keras.preprocessing.sequence.pad_sequences(x_test, maxlen=seq_len)
x_test = tokenizer.sequences_to_texts(x_test)
x_test = tfidf.fit_transform(x_test)
preds = linear_regression.predict(x_test)
# print(preds) # [1 1 1 ... 0 1 0]
make_submission(ids, preds, 'popcorn_models/tfidf.csv')
# make_submission_for_deep(ids, x_test, model, tokenizer, seq_len, 'popcorn_models/baseline.csv')
def model_word2vec(x, y, ids, x_test):
# 3가지 영역으로 나눌 수 있음
# ------------------------------------------- #
# x, y 데이터를 만드는 전처리 부분
x = [s.lower().split() for s in x]
# gensim에 들어가면 우리가 썼던 거 있음 그걸 이용하면 됨
n_features = 100
word2vec = gensim.models.Word2Vec(
x, workers=4, # 얼마나 빨리 처리할 것인지
size=n_features, # feature size
min_count=40, window=10, sample=0.001
)
idx2word = set(word2vec.wv.index2word) # 계산이 너무 길어지기 때문에 만들어 줘야함
# make_features_for_word2vec 함수 안을 잘 보자
features = [make_features_for_word2vec(tokens, word2vec, n_features, idx2word) for tokens in x]
# x = np.reshape(features, newshape=[-1, n_features]) # (1000, 100)
# x = np.vstack(features) # (1000, 100)
print(x.shape)
data = model_selection.train_test_split(x, y, train_size=0.8, shuffle=False)
x_train, x_valid, y_train, y_valid = data
# ------------------------------------------- #
# 딥러닝보다는 약함
linear_regression = linear_model.LogisticRegression(class_weight='balanced') # 사이킷 런에서 제공해주는 것
linear_regression.fit(x_train, y_train)
print('acc :', linear_regression.score(x_valid, y_valid)) # score로 결과를 알 수 있음
# --------------------------------------- 3
# 결과를 예측해서 submission 하는 과정
make_submission_for_word2vec(
ids, x_test, linear_regression, word2vec, n_features, idx2word,
'popcorn_models/word2vec.csv'
)
def model_word2vec_nltk(x, y, ids, x_test):
# x = [s.lower().split() for s in x]
# 위에 코드보다 조금 더 잘 변환한 것
tokenizer = nltk.RegexpTokenizer(r'\w+') # 단어만을 필터링 하겠다. 한글자 이상
sent_tokens = [tokenizer.tokenize(s.lower()) for s in x]
# print(sent_tokens[0]) # ['with', 'all', 'this', 'stuff', ...
# 어간 추출 해줌
st = nltk.stem.PorterStemmer()
sent_tokens = [[st.stem(w) for w in s] for s in sent_tokens]
# print(sent_tokens[:3])
stop_words = nltk.corpus.stopwords.words('english')
# 불용어를 제외한 sentence
sent_stops = [[w for w in s if w not in stop_words] for s in sent_tokens]
x = sent_stops
# ------------------------------------------------------- #
# 아래 코드는 앞에 나온 함수와 완벽하게 동일함
n_features = 100
word2vec = gensim.models.Word2Vec(x, workers=4, size=n_features, min_count=40, window=10, sample=0.001)
idx2word = set(word2vec.wv.index2word)
features = [make_features_for_word2vec(tokens, word2vec, n_features, idx2word) for tokens in x]
x = np.vstack(features) # (1000, 100)
data = model_selection.train_test_split(x, y, train_size=0.8, shuffle=False)
x_train, x_valid, y_train, y_valid = data
# ------------------------------------------- #
# 딥러닝보다는 약함
linear_regression = linear_model.LogisticRegression(class_weight='balanced') # 사이킷 런에서 제공해주는 것
linear_regression.fit(x_train, y_train)
print('acc :', linear_regression.score(x_valid, y_valid)) # score로 결과를 알 수 있음
# --------------------------------------- 3
# 결과를 예측해서 submission 하는 과정
make_submission_for_word2vec(
ids, x_test, linear_regression, word2vec, n_features, idx2word,
'popcorn_models/word2vec_nltk.csv'
)
# baseline 모델 확장해서 만들었음
def model_rnn(x, y, ids, x_test):
vocab_size, seq_len = 2000, 200
x, tokenizer = tokenizing_and_padding(x, vocab_size, seq_len)
data = model_selection.train_test_split(x, y, train_size=0.8, shuffle=False)
x_train, x_valid, y_train, y_valid = data
# ------------------------------------- #
model = tf.keras.Sequential()
model.add(tf.keras.layers.Input(shape=[seq_len]))
model.add(tf.keras.layers.Embedding(vocab_size, 100))
# model.add(tf.keras.layers.LSTM(64, return_sequences=True)) # layer 추가
# model.add(tf.keras.layers.LSTM(64, return_sequences=False))
cells = [tf.keras.layers.LSTMCell(64) for _ in range(2)] # 위의 코드를 대신 할 수 있음 여러개 만들 수 있음
multi = tf.keras.layers.StackedRNNCells(cells) # 2개를 cell 에 추가해서 추가해준다.
model.add(tf.keras.layers.RNN(multi)) # layer 추가
model.add(tf.keras.layers.Dense(64, activation='relu')) # dense 추가
model.add(tf.keras.layers.Dense(1, activation='sigmoid'))
model.compile(optimizer=tf.keras.optimizers.Adam(lr=0.01),
loss=tf.keras.losses.binary_crossentropy,
metrics=['acc'])
model.fit(x_train, y_train, epochs=10, batch_size=128, verbose=1,
validation_data=(x_valid, y_valid))
# -----------------------------------------#
make_submission_for_deep(ids, x_test, model, tokenizer, seq_len, 'popcorn_models/rnn.csv')
# conv1d 사용
def model_cnn(x, y, ids, x_test):
vocab_size, seq_len = 2000, 200
x, tokenizer = tokenizing_and_padding(x, vocab_size, seq_len)
data = model_selection.train_test_split(x, y, train_size=0.8, shuffle=False)
x_train, x_valid, y_train, y_valid = data
# ------------------------------------- #
input = tf.keras.layers.Input(shape=[seq_len])
embed = tf.keras.layers.Embedding(vocab_size, 100)(input)
embed = tf.keras.layers.Dropout(0.5)(embed) # 전체에 대해서 1/2
# 필터크기가 달라짐에 따라 삐저나가는 정도가 달라짐
conv1 = tf.keras.layers.Conv1D(128, 3, activation='relu')(embed)
conv1 = tf.keras.layers.GlobalAvgPool1D()(conv1) # 3차원 -> 2차원
conv2 = tf.keras.layers.Conv1D(128, 4, activation='relu')(embed)
conv2 = tf.keras.layers.GlobalAvgPool1D()(conv2) # 3차원 -> 2차원
conv3 = tf.keras.layers.Conv1D(128, 5, activation='relu')(embed)
conv3 = tf.keras.layers.GlobalAvgPool1D()(conv3) # 3차원 -> 2차원
concat = tf.keras.layers.concatenate([conv1, conv2, conv3]) # 354
full1 = tf.keras.layers.Dense(256, activation='relu')(concat)
full1 = tf.keras.layers.Dropout(0.5)(full1)
full2 = tf.keras.layers.Dense(1, activation='sigmoid')(full1) # 0과 1 사이의 값으로 변환해줌
model = tf.keras.Model(input, full2)
model.summary()
model.compile(optimizer=tf.keras.optimizers.Adam(lr=0.01),
loss=tf.keras.losses.binary_crossentropy,
metrics=['acc'])
model.fit(x_train, y_train, epochs=10, batch_size=128, verbose=1,
validation_data=(x_valid, y_valid))
# -----------------------------------------#
make_submission_for_deep(ids, x_test, model, tokenizer, seq_len, 'popcorn_models/rnn.csv')
# ---------------------------------------------------------------------- #
popcorn = pd.read_csv("popcorn/labeledTrainData.tsv",
delimiter='\t', # tab 없애줘야함
index_col=0,)
# print(popcorn)
x = popcorn.review.values
y = popcorn.sentiment.values.reshape(-1, 1) # 2차원으로 바꿔줄 것임
# print(x.dtype, y.dtype) # object int64
n_samples = 1000
# 껏다 켰다 해줘야함
# x, y = x[:n_samples], y[:n_samples] # 1000개만 쓰겠다.
test_set = pd.read_csv("popcorn/testData.tsv",
delimiter='\t', # tab 없애줘야함
index_col=0,)
ids = test_set.index.values # numpy로 바꿔줬음
x_test = test_set.review.values
# model_baseline(x, y, ids, x_test)
# model_tf_idf(x, y, ids, x_test)
# model_word2vec(x, y, ids, x_test)
# model_word2vec_nltk(x, y, ids, x_test)
# model_rnn(x, y, ids, x_test)
model_cnn(x, y, ids, x_test)
# baseline: 0.8634
# tf-idf : 0.8742
# word2vec: 0.8244
# nltk : 0.8562
# rnn : 0.8794
# cnn : 0.8662
'인공지능 > RNN' 카테고리의 다른 글
Callback ( check_point와 Earlystopping) (0) | 2020.12.15 |
---|---|
자연어 처리 - 영화 리뷰 분석해서 그래프로 그려보기 (0) | 2020.11.25 |
자연어 처리 ( text에 있는 word에 대한 빈도수 도출하기 ) (0) | 2020.11.24 |
자연어 처리 (Gensim) (0) | 2020.11.24 |
자연어처리( doc2vec ) (0) | 2020.11.22 |