* 데이콘(DACON)에서 주최한 '뉴스 토픽 분류 AI 경진대회' 모형을 공유하고자 게시물을 올렸습니다.

* 이 대회는 특정 모형으로 한국어 뉴스 헤드라인 데이터를 학습한 후 주제를 분류하는 것입니다.

 

Bi-LSTM 기법을 이용하여 한국어 뉴스 토픽 분류 모형을 만들었습니다. 임베딩은 Fasttext를 사용하였습니다.

 

먼저 Fasttext 임베딩 코드를 보여드리며 설명한 후 Bi-LSTM 기법을 적용하는 코드를 보여드리며 설명하겠습니다.

*코드는 Google Colaboratory 기준입니다

 

항목은 다음과 같습니다.

 

1. 필요한 패키지 설치

2. 데이터 설명 및 전처리

3. Fasttext 이용, 단어를 벡터로 변환

4. Bi-LSTM 기법 적용

 

그럼 시작하겠습니다.

 

1. 필요한 패키지 설치


[CODE]

!apt-get update 
!apt-get install g++ openjdk-8-jdk python-dev python3-dev 
!pip3 install JPype1-py3 
!pip3 install konlpy 
!JAVA_HOME="C:\Users\tyumi\Downloads"
from konlpy.tag import *  #한국어 형태소 분석
okt=Okt()
!pip3 install fasttext #fasttext 설치
import fasttext
import pandas as pd 
import re #정규표현식
from tqdm import tqdm_notebook
import numpy as np
from keras.layers import LSTM, Activation, Dropout, Dense, Input,Bidirectional
from keras.layers.embeddings import Embedding
from keras.models import Model
from keras import layers
from keras.callbacks import EarlyStopping
import keras
from keras.models import Sequential
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences

 

2. 데이터 설명 및 전처리


[CODE 2-1]

from google.colab import drive
drive.mount('/content/drive') 


test=pd.read_csv('/content/drive/MyDrive/test_data.csv')
train=pd.read_csv('/content/drive/MyDrive/train_data.csv')
sample_sub=pd.read_csv('/content/drive/MyDrive/sample_submission.csv')
topic_dict=pd.read_csv('/content/drive/MyDrive/topic_dict.csv')

 

[Explanation 2-1]

 

[CODE 2-1]은 구글 마운트를 이용해 데이터를 불러오는 과정입니다.

 

[그림 1] train data
[그림 2] 주제

- [그림 1]은 train data 입니다. 45654개의 관측값을 갖고 있는 것을 알 수 있습니다.

- title은 저희가 분석할 뉴스 헤드라인이고 topic_idx는 기사에 할당된 주제입니다.

- [그림 2]에 주제에 대한 설명이 있습니다.

- test data는 9131개의 관측값을 갖고 있습니다.

 

[CODE 2-2]

t_list=[]

for t in range(len(train['title'])):

  train['title'][t]=' '.join(re.compile('[가-힣]+').findall(train['title'][t]))



t_list=[]

for t in range(len(test['title'])):

  test['title'][t]=' '.join(re.compile('[가-힣]+').findall(test['title'][t]))



for i in range(len(train)):

  train['title'][i]=okt.nouns(train['title'][i])



for i in range(len(test)):

  test['title'][i]=okt.nouns(test['title'][i])



data_train = train['title']

data_test = test['title']

 

[Explanation 2-2]

 

- 정규표현식을 이용하여 train, test data의 title에서 한글을 제외한 나머지를 제거합니다.

- okt를 이용하여 처리된 train, test data의 title에서 명사만 추출합니다.

 

[그림 3] data_train

- data_train은 [그림 3]과 같습니다. 이로써 각 문장은 명사 단위로 구성된 리스트가 되었습니다.

 

3. Fasttext 이용, 단어를 벡터로 변환


[CODE 3-1]

with open('/content/drive/MyDrive/data_train.txt', "w") as f:
  for i in range(len(data_train)):
    f.write(' '.join(data_train.iloc[i])+'\n')
  f.close()
with open('/content/drive/MyDrive/data_test.txt', "w") as f:
  for i in range(len(data_test)):
    f.write(' '.join(data_test.iloc[i])+'\n')
  f.close()


model = fasttext.train_unsupervised(input='/content/drive/MyDrive/data_train.txt',model = 'skipgram', lr = 0.05,
dim = 100, ws = 5, epoch = 50,
minn = 1, word_ngrams = 6)

 

[Explanation 3-1]

 

[그림 4]data_train

- fasttext 모델의 입력값 형태로 만들기 위해 데이터를 메모장 파일로 저장합니다.([그림 4]와 같습니다.)

- 이후 data_train 파일로 fasttext 모델을 훈련시킵니다. (초모수 설정은 이명호님의 석사 학위 논문 '단어 임베딩과 LSTM을 활용한 비속어 판별 방법'을 참고하였습니다.)

 

[CODE 3-2]

#정수인덱싱과정
tokenizer = Tokenizer()
tokenizer.fit_on_texts(data_train)
vocab_size=len(tokenizer.word_index)+1
data_train = tokenizer.texts_to_sequences(data_train)
data_test = tokenizer.texts_to_sequences(data_test) 

#모든 문장에서 가장 긴 단어 벡터 길이 구하기
max_len=max(len(l) for l in data_train)

#max_len에 맞춰서 패딩하기
X_train = pad_sequences(data_train, maxlen = max_len)
X_test = pad_sequences(data_test, maxlen = max_len)

 

[Explanation 3-2]

 

- Tokenizer를 이용해서 data_train/data_test 에 있는 고유한 단어에 번호를 할당합니다. 

- 즉, data_train/data_test 를 정수 인덱싱 벡터로 변환하는 것입니다.

- 모든 문장의 단어 벡터 길이는 같아야 됩니다. 같지 않는 경우, 0으로 패딩해서 길이를 맞춰줍니다.

- 따라서 모든 문장에서 최대 단어 벡터 길이를 구한 후 패딩합니다.

 

[CODE 3-3]

#임베딩행렬 구하기
dd=list(tokenizer.word_index.keys())
embedding_matrix=[np.zeros(shape=(100,),dtype=float)]
for i in range(len(dd)):
  embedding_matrix.append(model.get_word_vector(dd[i]))
embedding_matrix=np.array(embedding_matrix)

 

[Explanation 3-3]

 

- 학습시킨 fasttext model로 data_train을 구성하는 각 단어의 벡터를 구합니다.

- 모두 합친 벡터를 embedding_matrix라고 합니다.

 

[그림 5] embedding_matrix

단어 단어벡터
대통령 [-0.04712268, -0.03183127, -0.24798341, ..., 0.10834852, -0.00924296, -0.08876684]
[-0.01681509, -0.02997315, -0.04271703, ..., 0.1574153 , 0.00186034, -0.07043125]

 

- 위의 표는 단어벡터를 생성한 예시입니다. 각 단어벡터는 15차원으로 구성됩니다.

- [그림 5]는 embedding_matrix로, (24418, 100) 차원을 가졌습니다. 24418은 data_train을 구성하는 고유한 단어의 수를

   나타냅니다. 100은 각 단어의 벡터 차원을 의미합니다.

 

4. Bi-LSTM  계층 구현


앞에서 구한 embedding_matrix를 이용해 Bi-LSTM 계층을 구현하겠습니다.

 

[CODE 4-1]

target = pd.get_dummies(train['topic_idx']).values

x_train=X_train
x_test=X_test
y_train=target

 

[Explanation 4-1]

 

- 뉴스 기사의 주제 분류를 목적으로 분석하기 때문에 'topic_idx'를 Y변수로 놓습니다.

- topic_idx를 더미 변수로 변환합니다.

- 앞에서 구한 embedding_matrix를 임베딩 계층의 가중치로 입력할 것입니다.

 

[CODE 4-2]

max_len=15
vocab_size=24418
embedding_dim=100
model_5=keras.Sequential([
keras.layers.Embedding(vocab_size, embedding_dim, weights=[embedding_matrix],input_length=max_len,trainable=True), 
keras.layers.Bidirectional(LSTM(100, dropout=0.2, recurrent_dropout=0.2,return_sequences=True)),
keras.layers.Bidirectional(LSTM(100, dropout=0.2, recurrent_dropout=0.2,return_sequences=True)),
keras.layers.Bidirectional(LSTM(100, dropout=0.2, recurrent_dropout=0.2)),
keras.layers.Dense(7, activation='softmax')])

 

[Explanation 4-2]

 

- Keras는 인공 신경망 층을 구성하기 위해 Sequential()을 사용합니다.

- 첫번째 층은 Embedding 계층으로, 정수 인덱싱한 x_train 을 밀집 벡터로 맵핑합니다.

- 설명드리자면, 이 밀집 벡터는 신경망의 학습 과정에서 가중치가 학습되는 것과 같은 방식으로 훈련됩니다. 훈련 과정에서 단어는 모델이 풀고자하는 작업에 맞는 값으로 업데이트 됩니다.

- 두번째 층은 Bidirectional 계층이 감싼 LSTM 계층으로, 첫번째 요소값 100은 LSTM 계층을 거치며 출력될 차원값을 의미합니다.

- 세 개의 Bi-LSTM 계층을 쌓은 후 나오는 결과에 softmax함수를 적용합니다. 

- 이를 통해 각 관측값이 7개의 주제에 할당될 확률을 구할 수 있습니다.

 

[그림 6] model_5.summary() 결과

- [그림 6]은 model_5.summary() 결과로, 각 계층의 출력 차원을 확인하실 수 있습니다.

 

[CODE 4-3]

es = EarlyStopping(monitor='val_loss', mode='min', verbose=1, patience=4)
mc = ModelCheckpoint('best_model.h5', monitor='val_acc', mode='max', verbose=1, save_best_only=True)

from sklearn.model_selection import train_test_split
X_train, X_valid, y_train, y_valid = train_test_split(X_train, y_train, test_size=0.2, random_state=1000)

from tensorflow.keras.utils import to_categorical
model_5.compile(optimizer='sgd', loss='categorical_crossentropy', metrics=['accuracy'])
history = model_5.fit(X_train, y_train, epochs=15, callbacks=[es, mc], validation_data=(X_valid,y_valid),batch_size=60, validation_split=0.2)

predictions = model_5.predict(x_test)

pred_1=[]
for p in predictions:
  pred_1.append(np.argmax(p))

sample_sub['topic_idx']=pred_1
sample_sub.to_csv('Fasttext와3개BILSTM계층.csv',index=False)

[Explanation 4-3]

 

- 예측 결과 정확도가 81% 나왔습니다. 

+ Recent posts