관리 메뉴

솜씨좋은장씨

[DACON] 청와대 청원 : 청원의 주제가 무엇일까? - 1일차 본문

DACON/청와대 청원 : 청원의 주제가 무엇일까?

[DACON] 청와대 청원 : 청원의 주제가 무엇일까? - 1일차

솜씨좋은장씨 2020. 5. 19. 13:57
728x90
반응형

 

[문자] 청와대 청원 : 청원의 주제가 무엇일까?

출처 : DACON - Data Science Competition

dacon.io

그동안 Elasticsearch를 활용하여 검색 시스템을 개발하면서 자연어처리에 대해서 공부를 좀 소홀히 한 느낌이있어

다시 기존에 공부했던 내용을 리마인드 시킬 겸!

데이콘에서 교육용으로 열려있는 청와대 청원 분류 문제를 풀어보기로 했습니다.

 

이 문제는 청와대 청원이 0 : 인권 / 성평등 | 1 : 문화 / 예술 / 체육 / 언론 | 2 : 육아 / 교육 이 세가지 중 어떤 카테고리에 속하는지

분류를 하면되는 문제입니다.

 

모든 과정은 Google Colab의 GPU 환경에서 진행하였습니다.

 

먼저 pandas의 read_csv로 데이터를 불러와 각 카테고리마다 데이터가 몇개씩 존재하는지 확인해 보았습니다.

import pandas as pd

train_data = pd.read_csv("./data/train.csv")
test_data = pd.read_csv("./data/test.csv")
train_data['category'].value_counts().plot(kind='bar')

print(train_data.groupby('category').size().reset_index(name='count'))
   category  count
0         0  13301
1         1  13337
2         2  13362

각각 약 133,300개 씩으로 매우 균등한 분포를 보였습니다.

 

별도의 개수 조정은 없이 그대로 진행해보기로 했습니다.

 

먼저 정규식과 replace를 활용하여 각 데이터에서 특수문자와 개행문자를 제거하고

그 데이터를 clear_text라는 새로운 column을 만들어 저장하였습니다.

from tqdm import tqdm
import re
train_text = list(train_data['data'])

clear_text_list = []

for i in tqdm(range(len(train_text))):
    plain_text = str(train_text[i])

    clear_text = plain_text.replace("\\","").replace("n"," ")

    clear_text = re.sub('[-=+,#/\?:^$.@*\"※~&%ㆍ!』\\‘|\(\)\[\]\<\>`\'…》]', '', clear_text)
    
    clear_text_list.append(clear_text)

train_data['clear_text'] = clear_text_list
train_data

 

 

Google Colab에서 Mecab-ko-dic 쉽게 사용하기

요즘 멀티캠퍼스에서 자연어처리에 대한 교육을 받으며 사용했던 은전한닢 프로젝트 라이브러리인 Mecab-ko-dic을 Google Colab에서 간단한 몇가지 명령어를 통하여 설치하고 사용할 수 있도록 Shell Sc

somjang.tistory.com

형태소 분석을 위한 Mecab을 설치하기 위해 위의 방법대로 Colab에 Mecab을 설치하고 계속 진행하였습니다.

from konlpy.tag import Mecab
mecab = Mecab()

X_train = []

train_clear_text = list(train_data['clear_text'])

for i in tqdm(range(len(train_clear_text))):
    token_data = mecab.morphs(train_clear_text[i])
    token_data = [word for word in token_data if len(word) > 1]
    X_train.append(token_data)
X_train[:1]

mecab의 morphs를 활용하여 각 청원 문장을 토큰화하고 그 중 길이가 2 이상인 단어들만 남깁니다.

[['신혼',  '부부',  '위한',  '주택',  '정책',  '보다',  '보육',  '시설',  '늘려',  '세요',  '국민',  '세금',
.... 이하 생략

 

from keras.preprocessing.text import Tokenizer
max_words = 35000
tokenizer = Tokenizer(num_words = max_words) 
tokenizer.fit_on_texts(X_train) 
X_train_vec = tokenizer.texts_to_sequences(X_train) 
X_test_vec = tokenizer.texts_to_sequences(X_test)

토큰화한 단어들 중 35,000개만 사용하여 정수 인코딩을 수행합니다.

import numpy as np
from keras.utils import np_utils

y_train_data = list(train_data['category'])

y_train_vec = np_utils.to_categorical(y_train_data, num_classes=3)

y_train_vec

라벨 데이터는 keras.utils의 np_utils에 있는 to_categorical을 활용하여 Ont-Hot 인코딩을 실시합니다.

array([[0., 0., 1.],
       [1., 0., 0.],
       [0., 1., 0.],
       ...,
       [0., 0., 1.],
       [0., 0., 1.],
       [1., 0., 0.]], dtype=float32)

 

import matplotlib.pyplot as plt

print("청원의 최대 길이 :" , max(len(l) for l in X_train_vec)) 
print("청원의 평균 길이 : ", sum(map(len, X_train_vec))/ len(X_train_vec)) 
plt.hist([len(s) for s in X_train_vec], bins=50) 
plt.xlabel('length of Data') 
plt.ylabel('number of Data') 
plt.show()

모든 벡터의 길이를 같은 길이로 맞추기 위해서 청원 데이터의 평균 길이를 구합니다.

from keras_preprocessing.sequence import pad_sequences
max_len = 120
X_train_vec = pad_sequences(X_train_vec, maxlen=max_len)
X_test_vec = pad_sequences(X_test_vec, maxlen=max_len)

위에서 구한 평균 길이 값을 참고하여 pad_sequences를 통해 모든 데이터의 길이를 동일하게 120으로 맞추어줍니다.

 

Embedding, LSTM / Bi-LSTM 레이어로 구성된 모델을 활용하여 분류를 실행합니다.

참고 : layer 이름 E - Embedding, L - LSTM, Bi-L - Bi-LSTM

  layers optimizer batch_size epochs validation_split 형태소분석 max_words max_len accuracy
1 E(100) + L(128) adam 32 5 0.1 mecab (morphs) 35,000 120 0.8436
2 E(100) + L(128) adam 32 3 0.1 mecab (morphs) 35,000 120 0.8256
3 E(100) + L(128) adam 32 2 0.1 mecab (morphs) 35,000 120 0.857
4 E(100) + L(128) adam 32 1 0.1 mecab (morphs) 35,000 120 0.8614
5 E(100) + Bi-L(128) adam 32 2 0.1 mecab (morphs) 35,000 120 0.859
6 E(100) + Bi-L(128) adam 32 1 0.1 mecab (morphs) 35,000 120 0.8652

결과는 위와 같았습니다.

 

이번에는 mecab의 nouns를 활용하여 명사만 추출한 후에 위에서 가장 좋았던 4번, 6번과 같은 조건으로 테스트를 해보았습니다.

  layers optimizer batch_size epochs validation_split 형태소분석 max_words max_len accuracy
7 E(100) + L(128) adam 32 1 0.1 mecab (nouns) 35,000 72 0.8628
8 E(100) + Bi-L(128) adam 32 1 0.1 mecab (nouns) 35,000 72 0.862

결과는 위와 같았습니다.

 

7번, 8번에서 optimizer만 adam에서 rmsprop으로 바꾸어 학습한 후 제출해보았습니다.

  layers optimizer batch_size epochs validation_split 형태소분석 max_words max_len accuracy
9 E(100) + L(128) rmsprop 32 1 0.1 mecab (nouns) 35,000 72 0.8684
10 E(100) + Bi-L(128) rmsprop 32 1 0.1 mecab (nouns) 35,000 72 0.8608

조금 더 좋은 결과가 나온 것을 확인했습니다.

 

이번엔 프로그래머스 Dev-Matching : 자연어처리 과제 도전때 가장 좋은 결과를 내었던 CNN-LSTM 모델을 사용해보았습니다.

참고 : layer 이름 E - Embedding, L - LSTM, Bi-L - Bi-LSTM, Cv1D - Conv1D, MP1D - MaxPooling1D, D - Dropout

  layers optimizer batch_size epochs validation_split 형태소분석 max_words max_len accuracy
11 E(100) + D(0.2) + Cv1D(256) + MP1D(4) + L(128) adam 32 5 0.1 mecab (nouns) 35,000 72 0.8498
12 E(100) + D(0.2) + Cv1D(256) + MP1D(4) + L(128) adam 32 3 0.1 mecab (nouns) 35,000 72 0.8608
13 E(100) + D(0.2) + Cv1D(256) + MP1D(4) + L(128) adam 32 1 0.1 mecab
(nouns)
35,000 72 0.8672

앗 그런데 categorical_crossentropy를 사용했어야했는데 binary_crossentropy를 사용하여 결과를 냈습니다.

그런데 결과가 잘 나온게 신기합니다.

다시 categorical_crossentropy로 변경하여 결과를 도출해보았습니다.

  layers optimizer batch_size epochs validation_split 형태소분석 max_words max_len accuracy
16 E(100) + D(0.2) + Cv1D(256) + MP1D(4) + L(128) adam 32 5 0.1 mecab (nouns) 35,000 72 0.8454
17 E(100) + D(0.2) + Cv1D(256) + MP1D(4) + L(128) adam 32 3 0.1 mecab (nouns) 35,000 72 0.8588
18 E(100) + D(0.2) + Cv1D(256) + MP1D(4) + L(128) adam 32 1 0.1 mecab
(nouns)
35,000 72 0.8688

위와 같은 결과를 얻을 수 있었습니다.

 

LSTM 레이어를 활용한 모델에서 128 -> 64로 변경하여 시도해보았습니다.

  layers optimizer batch_size epochs validation_split 형태소분석 max_words max_len accuracy
14 E(100) + 
L(64)
adam 32 1 0.1 mecab (nouns) 35,000 72 0.8644
15 E(100) +
Bi-L(64)
adam 32 1 0.1 mecab (nouns) 35,000 72 0.8688

 

이번에는 padding의 길이를 72에서 120으로 변경해보았습니다.

  layers optimizer batch_size epochs validation_split 형태소분석 max_words max_len accuracy
19 E(100) + D(0.2) + Cv1D(256) + MP1D(4) + L(128) adam 32 1 0.1 mecab (nouns) 35,000 120 0.8728
20 E(100) +
Bi-L(64)
adam 32 1 0.1 mecab (nouns) 35,000 120 0.8648
21 E(100) + 
L(64)
adam 32 1 0.1 mecab
(nouns)
35,000 120 0.8644

위와 같은 결과를 얻을 수 있었습니다.

 

오늘은 여기까지!

앞으로 여러 방법들을 동원하여 성능을 더 높여볼 생각입니다!

부족하지만 읽어주셔서 감사합니다.

언제든지 이상한 점이나 피드백 있으면 가감없이 댓글 남겨주세요!

Comments