자연어처리 NLP의 3일차 프로젝트를 진행했습니다.
1-2일차에는 데이터 분석 위주로 진행했고 오늘은 이제 분석을 토대로 어떻게 tokenizer를 진행할 것이고 모델링을 할 것인지에 대해 진행했습니다.
오늘은 전체적인 코드를 한번 작성하겠습니다. 어제 포스팅과 중복된 코드가 있는데 어제 코드에서 문제점이 있어서 수정했습니다.
train_x = train_x.str.replace('[^ㄱ-ㅎㅏ-ㅣ가-힣A-Za-z0-9]', ' ', regex=True)
val_x = val_x.str.replace('[^ㄱ-ㅎㅏ-ㅣ가-힣A-Za-z0-9]', ' ', regex=True)
특수문자를 제거하고 공백으로 대체하는 코드입니다. [ ]
안에 있는 코드는 ^ㄱ-ㅎㅏ-ㅣ가-힣A-Za-z0-9
인데 앞에 ^있으면 해당을 제외한 나머지 문자들을 선택해주는 것을 의미합니다.
from konlpy.tag import Mecab
mecab = Mecab()
def get_nouns(text):
nouns = mecab.nouns(text)
return nouns
명사를 추출하고 띄어쓰기로 해당 명사를 구분 지어줍니다.
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
count_vectorize = CountVectorizer()
count_mecab_vectorizer = CountVectorizer(tokenizer=get_nouns)
train_x_counts = count_vectorize.fit_transform(train_x)
val_x_counts = count_vectorize.transform(val_x)
train_x_mecap_counts = count_mecab_vectorizer.fit_transform(train_x)
val_x_mecp_counts = count_mecab_vectorizer.transform(val_x)
위에 생성한 mecab으로 tokenizer을 진행합니다.
import tensorflow as tf
import numpy as np
from tensorflow.keras.preprocessing import sequence, text
TOP_K = 5000
MAX_SEQUENCE_LENGTH = 500
x_mor_train_str = train_x.apply(lambda x:' '.join(get_nouns(x)))
x_mor_val_str = val_x.apply(lambda x:' '.join(get_nouns(x)))
x_mor_train = train_x.apply(lambda x:get_nouns(x))
x_mor_val = val_x.apply(lambda x:get_nouns(x))
tokenizer_str = text.Tokenizer(num_words = TOP_K, char_level=False)
tokenizer_str.fit_on_texts(x_mor_train_str)
x_mor_train_seq_str = tokenizer_str.texts_to_sequences(x_mor_train_str)
x_mor_val_seq_str = tokenizer_str.texts_to_sequences(x_mor_val_str)
max_length = len(max(x_mor_train_seq_str, key=len))
if max_length > MAX_SEQUENCE_LENGTH:
max_length = MAX_SEQUENCE_LENGTH
print(max_length)
x_mor_train_seq_str = sequence.pad_sequences(x_mor_train_seq_str, maxlen=max_length)
x_mor_val_seq_str = sequence.pad_sequences(x_mor_val_seq_str, maxlen=max_length)
tokenizer = text.Tokenizer(num_words = TOP_K, char_level=False)
tokenizer.fit_on_texts(x_mor_train)
x_mor_train_seq = tokenizer.texts_to_sequences(x_mor_train)
x_mor_val_seq = tokenizer.texts_to_sequences(x_mor_val)
max_length = len(max(x_mor_train_seq_str, key = len))
if max_length > MAX_SEQUENCE_LENGTH:
max_length = MAX_SEQUENCE_LENGTH
print(max_length)
x_mor_train_seq = sequence.pad_sequences(x_mor_train_seq, maxlen=max_length)
x_mor_val_seq = sequence.pad_sequences(x_mor_val_seq, maxlen=max_length)
sequence를 마저 진행해줍니다.
해당 코드는 아직 이해를 하지 못해서 코드만 붙여놓고 나중에 수정해두겠습니다.
import gdown
url = 'https://drive.google.com/file/d/0B0ZXk88koS2KbDhXdWg1Q2RydlU/view?resourcekey=0-Dq9yyzwZxAqT3J02qvnFwg'
output = os.path.join(PATH, 'ko.zip')
gdown.download(url, output, fuzzy=True, quiet=False)
import zipfile
with zipfile.ZipFile(os.path.join(PATH, "ko.zip"), 'r') as zip_ref:
zip_ref.extractall(PATH)
!pip install gensim
from gensim.models import Word2Vec, FastText, KeyedVectors
import logging
logging.basicConfig(format = '%(asctime)s : %(levelname)s : %(message)s', level = logging.INFO)
SIZE = 128
WINDOW = 3
MIN_COUNT = 1
w2v_model = Word2Vec(sentences=x_mor_train, vector_size=SIZE, window=WINDOW, max_vocab_size=None, min_count=MIN_COUNT, workers=4, negative=5, sg=0)
# 아래 함수는 제공합니다.
def get_sent_embeddings(model, embedding_size, tokenized_words):
# 단어 임베딩 및 n_words의 크기가 0인 feature_vec 배열을 0으로 초기화합니다.
# 또한 model.wv.index2word를 사용하여 Word2Vec 모델의 어휘에 단어 세트를 생성합니다.
feature_vec = np.zeros((embedding_size,), dtype='float32')
n_words = 0
index2word_set = set(model.wv.index_to_key)
# 토큰화된 문장의 각 단어를 반복하고 Word2Vec 모델의 어휘에 존재하는지 확인합니다.
# 그렇다면 n_words가 증가하고 단어의 임베딩이 feature_vec에 추가됩니다.
for word in tokenized_words:
if word in index2word_set:
n_words += 1
feature_vec = np.add(feature_vec, model.wv[word])
# Word2Vec 모델의 어휘에 있는 입력 문장에 단어가 있는지 확인합니다.
# 있다면 feature_vec를 n_words로 나누어 입력 문장의 평균 임베딩을 구합니다.
if (n_words > 0):
feature_vec = np.divide(feature_vec, n_words)
return feature_vec
def get_dataset(sentences, model, num_features):
# 각 문장에 대한 임베딩을 보유할 dataset이라는 빈 목록을 초기화합니다.
dataset = list()
# 문장의 각 문장을 반복하고 앞에서 설명한 get_sent_embeddings() 함수를 사용하여 문장에 대한 평균 임베딩을 생성합니다.
# 결과 문장 임베딩이 데이터 세트 목록에 추가됩니다.
for sent in sentences:
dataset.append(get_sent_embeddings(model, num_features, sent))
# 루프에서 생성된 문장 임베딩을 sent_embedding_vectors라는 2차원 배열에 쌓습니다.
sent_embedding_vectors = np.stack(dataset)
return sent_embedding_vectors
pre_trained = Word2Vec.load(os.path.join(PATH, 'ko_updated.bin'))
x_pr_train = get_dataset(train_x.apply(lambda x: mecab.morphs(x)), pre_trained, 128)
x_pr_val = get_dataset(val_x.apply(lambda x: mecab.morphs(x)), pre_trained, 128)
w2v가 진행됐으면 어떤 단어들이 학습됐는지 확인가능합니다. 아래 코드는 결과를 확인하는 코드입니다.
keys = list(w2v_model.wv.key_to_index)
print(keys[:100])
학습된 명사들중 상위 100개만 보여줍니다.
w2v_model.wv['문제']
'문제'라는 단어에 대한 학습 가중치를 확인할 수 있습니다.
모델링을 진행했는데 너무 낮은 결과 값이 나왔습니다.
from sklearn.metrics import accuracy_score
# 모델링 시작
from lightgbm import LGBMClassifier
model = LGBMClassifier()
model.fit(x_pr_train, train_y)
y_pred = model.predict(x_pr_val)
accuracy_score(val_y, y_pred)
아직 튜닝이 부족한지 전처리를 잘못했는지 0.4점대라는 점수를 기록해서 너무 기대 이하의 결과값을 보여줬습니다! 내일은 모델링 튜닝을 좀 더 해보면서 좋은 값이 나오도록 해보겠습니다.
전처리라는 벽을 넘어 이제는 모델링의 벽을 넘어갈 차례인 거 같습니다.
※공부하고 있어 다소 틀린점이 있을 수 있습니다. 언제든지 말해주시면 수정하도록 하겠습니다.
※용어에 대해 조금 공부 더 해서 수정하겠습니다.