한국어 NLP 모델 - hangug-eo NLP model

컴퓨터에게 사람의 말을 알아듣게 하는 것은 매우 어려운 일입니다. 그중에서도 한국어 NLP는 다른 언어들(Ex. 영어, 중국어)보다 훨씬 어려운 편에 속합니다. 그 이유에 대해서 알아봅니다.

1. 교착어

한국어는 교착어에 속합니다. 교착어란 어간에 접사가 붙어 단어를 이루고 의미와 문법적 기능이 정해지는 언어입니다.

예를 들어 나는 밥을 먹다. 라는 문장이 있습니다.
해당 문장에서 과거의 의미를 부여하고 싶을 때, 먹다라는 동사에 -었- 이라는 어미를 붙여 먹었다 라는 단어를 사용합니다.
즉 나는 밥을 먹었다. 라는 문장으로 과거 시제를 표현합니다.

이러한 특징은 하나의 어근에서 비슷한 의미의 수많은 단어가 매우 많이 생성 된다는 점이 있습니다.

아래 표는 좋아하다 라는 동사의 활용을 정리한 것입니다. 언뜻 보기에도 하나의 단어에서 수많은 단어가 파생된 것을 볼 수 있습니다. 이러한 점은 한국어 NLP에서 파싱, 전처리, 모델 학습 등을 어렵게 만듭니다.

한국어 NLP 모델 - hangug-eo NLP model
한국어 교착어 예시

이미지 출처

2. 띄어쓰기

사실 한국어에서 띄어쓰기는 근대에서 도입된 개념입니다. 따라서 서구권 언어에 비해 한국어는 띄어쓰기의 표준이 계속 바뀌고, 비교적 자유분방하다는 특징이 있습니다. 또한 띄어쓰기에 따라 문장의 뜻이 달라지기도 합니다. 대표적인 예시로 아버지가방에들어가신다를 들 수 있습니다.

따라서 단어와 단어 사이 반드시 띄어쓰기를 하는 서구권 언어와 달리 한국어는 추가적으로 띄어쓰기를 정제해주는 과정이 필요합니다.

3. 평서문과 의문문

한국어는 평서문과 의문문의 구분이 모호한 경우가 많이 있습니다.
예를 들어 밥 먹었어? 와 밥 먹었어. 의 경우가 있습니다. 문장 부호가 붙지 않는다면 두 문장 의미의 차이를 알 수 없습니다.

사람이라면 주변 상황이나 말의 억양으로 판별할 수 있겠지만, AI의 경우에는 이를 처리하기가 쉽지 않습니다.

4. 주어 생략

한국어는 동사를 중요시하고 주어가 자주 생략된다는 특징이 있습니다. 위와 같은 예로 밥 먹었어 라는 문장의 경우 주어가 명시되어 있지 않습니다.
주어가 생략된 문장을 AI가 정확히 이해하기란 쉽지 않은 일입니다.

5. 한자 기반의 언어

한국어에는 한자의 조합으로 이루어지는 단어들이 많이 있습니다. 예를 들어 집중이라는 단어의 경우 모을 집(集)과 가운데 중(中)이라는 단어가 합쳐져 만들어집니다. 영어의 concentrate의 경우에도 서브워드들이 합쳐져 하나의 단어를 이루게 됩니다.

  • Concentrate: con(=together) + centr(=center) + ate(= make)
  • 집중(集中): 集(모을 집) + 中(가운데 중)

하지만 한글이 한자를 대체하면서 문제가 발생합니다. 표어 문자인 한자가 표음 문자인 한글로 대체되면서, 읽는 소리는 같을 지라도 형태와 그 뜻은 다른 단어들이 여럿 생겨났습니다. 집 의 경우에도 모을 집(集), 낳을 집(緝), 잡을 집(執) 등의 수 많은 한자 단어가 집이라는 한 글자로 대체되었습니다. 이는 정보의 손실 을 야기하게 됩니다. 사람이라면 문맥을 통해 정보의 손실을 해소할 수 있겠지만, AI의 경우에는 그렇지 못합니다.

6. 부족한 학습 데이터

안그래도 학습도 어려운 한국어인데, 다른 언어에 비해 한국어 데이터도 턱없이 부족합니다.

사이트에서 각 언어별 웹 데이터의 양을 비교한 자료를 살펴보면, 한국어가 다른 언어에 비해 데이터가 확실히 적은 것을 볼 수 있습니다.

한국어 NLP 모델 - hangug-eo NLP model
턱없이 부족한 한국어 학습 데이터

또한 데이터의 개수가 적은 것과 동시에 한국어 데이터셋에 대한 연구와 개발도 부족한 실정입니다. 영어의 경우 SQuAD, GLUE 등 TASK에 따른 다양한 데이터셋에 대한 연구와 개발이 활발합니다. 최근 한국어도 정부나 기업 차원에서 모두의 말뭉치, KorQuAD 처럼 한국어 데이터셋을 개발 및 공개하고 있습니다. 지금부터라도 한국어도 좋은 데이터셋을 확보되었으면 하는 작은 바람이 있습니다 :)

한편, 김 대표는 오늘(16일) 개최하는 ‘if (kakao) 2021’ 컨퍼런스의 '카카오브레인 2.0'세션에서 카카오브레인의 향후 방향성과 주요 연구를 비롯해‘KoGPT’에 관해 직접 설명하는 자리를 가진다. 별도의 신청 절차나 인원 제한 없이 누구나 참여할 수 있으며, 이프 카카오 공식 홈페이지 및 카카오TV를 통해 시청 가능하다. 오전 10시, 이프카카오 공식 홈페이지에서 해당 세션에 관해 자유롭게 소통하는 Q&A 자리도 마련한다. [끝]

한국 경제 뉴스 감정 분류(Sentiment Classification)

한국 경제 뉴스 감정 분류(Sentiment Classification). Contribute to park-gb/financial-news-sentiment-classifier development by creating an account on GitHub.

github.com

6. 코드 설명

👨‍💻들어가며

본 소스코드를 통해 얻어지는 분석 결과 이미지, 데이터셋, 모델은 구글 드라이브 내 소스파일이 있는 위치에서 각 파일이 해당하는 폴더로 저장하도록 작성되었습니다(아래의 그림 2 참고) 예를 들어, 분석 결과 이미지는 figure, 모델은 model, 폴더에 저장됩니다. 따라서 구글 드라이브 연동을 하신 후 소스파일이 있는 위치에서 각 해당하는 폴더를 생성하시거나, 파일 생성 경로를 수정하신 후 코드를 활용하시길 권장합니다.

한국어 NLP 모델 - hangug-eo NLP model
그림 2. 파일 구성

1) 구글 드라이브 연동

코랩 소스파일에서 구글 드라이브를 연동(mount)합니다.

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

아래와 같은 출력문이 나왔다면 정상적으로 mount 된 것입니다.

Mounted at /content/drive/

2) 디렉토리 변경

Colab 노트북 초기 시작 시 노트북이 위치한 디렉토리인 Colab Notebook 위치로 디렉토리를 변경합니다. 이는 추후에 학습한 모델을 저장하거나 분석 결과를 파일로 구글 드라이브에 저장하기 위함입니다.

cd /content/drive/MyDrive/Colab Notebooks

3) 패키지 설치

3-1) Hugging Face 패키지 설치

사전 학습 언어 모델과 tokenizer를 편리하게 불러올 수 있는 Hugging Face 활용을 위한 패키지를 설치합니다.

!pip install transformers

3-2) RAdam 패키지 설치

본 프로젝트에서는 Optimizer로써 Rectified Adam을 사용할 예정입니다.

!pip install tensorflow_addons

4) 패키지 import

import os
import pandas as pd
import numpy as np
import re
from tqdm import tqdm
import urllib.request
import seaborn as sns
import matplotlib.pyplot as plt
import tensorflow_addons as tfa
import tensorflow as tf
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
from transformers import BertTokenizer, TFBertForSequenceClassification
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, f1_score, precision_score, recall_score, \
                            roc_auc_score, confusion_matrix, classification_report, \
                            matthews_corrcoef, cohen_kappa_score, log_loss

5) 언어모델 및 Tokenizer 불러오기

Hugging Face에서 KLUE BERT-base 모델과 tokenizer를 불러옵니다.

MODEL_NAME = "klue/bert-base"
model = TFBertForSequenceClassification.from_pretrained(MODEL_NAME, num_labels=3, from_pt=True)
tokenizer = BertTokenizer.from_pretrained(MODEL_NAME)

6) GPU 작동 확인

GPU는 Colab 상단 메뉴바-Runtime-Change Runtime type-Hardware Accelator 순서대로 접근한 후 선택 가능합니다.

device_name = tf.test.gpu_device_name()
if device_name == '/device:GPU:0':
  print("GPU 작동 중")
  mirrored_strategy = tf.distribute.MirroredStrategy()
else:
  print("GPU 미작동 중")

7) 경제 뉴스 감정 데이터셋 다운로드

Github @Ukairia777 님 리포에 올라와 있는 경제 뉴스 감정 데이터셋을 다운로드합니다.

DATASET_URL = "https://raw.githubusercontent.com/ukairia777/finance_sentiment_corpus/main/finance_data.csv"
DATASET_NAME = "finance_data.csv"
urllib.request.urlretrieve(DATASET_URL, 
                           filename = DATASET_NAME
                           )

데이터가 잘 받아졌는지 확인해 봅니다.

Mounted at /content/drive/
0

아래의 그림 3과 같이 데이터 프레임이 출력된다면 데이터셋을 정상적으로 다운로드하신 겁니다.

한국어 NLP 모델 - hangug-eo NLP model
그림 3. 경제 뉴스 감정 데이터셋 일부

8) 데이터 전처리

8-1) 영어 뉴스 기사 칼럼 삭제

한국어 데이터만 사용할 것이기 때문에 영문 데이터를 포함한 칼럼은 삭제합니다.

Mounted at /content/drive/
1

8-2) 라벨 데이터 숫자 치환

replace 함수와 라벨을 리스트에 담아 활용하면 간편하게 라벨을 숫자로 치환할 수 있습니다(그림 4).

Mounted at /content/drive/
2
한국어 NLP 모델 - hangug-eo NLP model
그림 4. 라벨 데이터 숫자 치환

8-3) 결측치 화인

결측치 존재 여부를 확인합니다.

Mounted at /content/drive/
3

아래와 같이 두 칼럼 모두 결측치가 없는 것을 알 수 있습니다(그림 5).

한국어 NLP 모델 - hangug-eo NLP model
그림 5. 결측치 존재 여부 확인

8-4) 중복 데이터 확인

중복된 뉴스 기사가 있는지 확인합니다.

Mounted at /content/drive/
4

아래 그림 6과 같이 중복 데이터가 많이 존재하는 것을 확인할 수 있습니다.

한국어 NLP 모델 - hangug-eo NLP model
그림 6. 중복 데이터 조회

8-5) 중복 데이터 제거

중복도니 뉴스 기사를 제거합니다. drop_duplicates 메소드를 활용하면 특정 칼럼에서 중복된 값을 가지는 행을 제거할 수 있습니다.

Mounted at /content/drive/
5

9) 라벨별 데이터 분포 확인

라벨별 데이터 분포를 막대 그래프로 시각화합니다.

9-1) 라벨별 데이터 개수 확인

먼저 라벨별 데이터 개수 시각화입니다. 데이터프레임의 특정 칼럼이나 행을 기준을 설정하고 value_counts() 메서드를 활용하면 특정 기준별 데이터 개수를 산출해 줍니다.

Mounted at /content/drive/
6

아래 그림 7과 같이 중립적인 뉴스 기사문이 가장 많고, 긍정과 부정적 뉴스 기사문으로 데이터가 분포하는 것을 알 수 있습니다.

한국어 NLP 모델 - hangug-eo NLP model
그림 7. 라벨별 데이터 개수 확인

9-2) 라벨별 데이터 비율 확인

라벨별 데이터 분포를 비율로 확인해 보겠습니다. 앞서 살펴본 value_counts() 메서드에서 normalize 옵션을 True로 설정하면 각 데이터별 비율을 산출합니다.

Mounted at /content/drive/
7

아래의 그림 8과 같이, 중립적인 기사문이 전체 중 59.27%, 긍정적인 기사문이 28.22%, 부정적인 기사문이 12.51%를 차지하는 것을 알 수 있습니다.

한국어 NLP 모델 - hangug-eo NLP model
그림 8. 라벨별 데이터 비율

10) Train/Test 데이터 분리

먼저, 입력 데이터와 라벨 데이터를 분리합니다.

Mounted at /content/drive/
8

Train과 Test 데이터는 8:2로 분리하겠습니다. 여기서 strtify 옵션(계층적 데이터 추출 옵션)에 라벨 데이터(y_data)를 입력하면 데이터 분리 이전의 라벨별 비율을 고려하여 데이터를 분리합니다. 이는 본 프로젝트와 마찬가지로 Classification 문제에서 권장되는 옵션입니다.

Mounted at /content/drive/
9

실제로 데이터 비율을 유지하는지 확인해 봅니다.

cd /content/drive/MyDrive/Colab Notebooks
0

데이터 개수로 확인하면 다음과 같습니다.

cd /content/drive/MyDrive/Colab Notebooks
1

비율로 확인해 보겠습니다.

cd /content/drive/MyDrive/Colab Notebooks
2

먼저 훈련 데이터의 경우 아래의 그림 9와 같이 데이터 분포가 정해졌습니다.

한국어 NLP 모델 - hangug-eo NLP model
그림 9. 훈련 데이터의 라벨별 데이터 비율

이어서 테스트 데이터의 경우도 확인해 봅니다.

cd /content/drive/MyDrive/Colab Notebooks
3

아래의 그림 10과 같이 훈련 데이터와 테스트 데이터가 라벨별 데이터 비율이 거의 일치하는 것을 확인할 수 있습니다.

한국어 NLP 모델 - hangug-eo NLP model
그림 10. 테스트 데이터의 라벨별 데이터 비율

11) BERT 입력용 데이터 포맷으로 변경

입력 데이터를 BERT 입력용 포맷으로 변경해야 합니다. 먼저, 입력 데이터의 총길이를 64로 제한하겠습니다.

cd /content/drive/MyDrive/Colab Notebooks
4

BERT의 입력은 token, segment, position으로 구성됩니다. 여기서 token은 텍스트 데이터를 tokenize하고 인덱스 번호를 부여한 것을 말합니다. segment는 문장의 앞뒤 관계를 구분하기 위한 용도입니다. 본 프로젝트에서 사용하는 입력 데이터가 모두 최대 한 문장이므로 segment는 사실상 모두 0이라고 볼 수 있습니다. position은 이름 그대로 단어의 위치를 의미합니다. 이러한 BERT 입력의 구성 요소를 고려하기 위한 함수를 아래와 같이 작성하였습니다.

cd /content/drive/MyDrive/Colab Notebooks
5

위의 함수를 활용하여 훈련 데이터와 테스트 데이터를 BERT 입력 포맷에 맞게 변환해 줍니다.

cd /content/drive/MyDrive/Colab Notebooks
6
cd /content/drive/MyDrive/Colab Notebooks
7

12) BERT 기반 감정 분류 모델링

이제 BERT를 활용하여 감정 분석 모델을 만들어 봅니다.

12-1) BERT 입력 정의

입력을 먼저 정의합니다.

cd /content/drive/MyDrive/Colab Notebooks
8

12-2) BERT 출력 정의

BERT 출력을 정의합니다.

cd /content/drive/MyDrive/Colab Notebooks
9

12-3) 감정 분류 모델 컴파일

드롭아웃을 설정하고 감정 분류 모델의 output layer를 설정합니다. 3가지 감정 중 1가지 감정으로 텍스트를 분류하기 때문에 다중 클래스 분류 문제입니다. 이에, output layer의 활성화 함수는 softmax를 사용하였습니다.

!pip install transformers
0

옵티마이저는 Rectified Adam을 사용하였습니다.

!pip install transformers
1

모델을 컴파일합니다. 본 프로젝트에서는 라벨이 0, 1, 2 중 하나를 예측하는 모델을 사용하기 때문에 손실함수로는 Sparse Categorical Crossentropy를 사용하였습니다.

!pip install transformers
2

Early Stopping을 추가하였습니다. patience 옵션을 5로 설정함으로써 모델 학습 시 성능이 개선되지 않는 횟수가 5회를 초과하면 학습을 멈추도록 구현하였습니다.

!pip install transformers
3

모델 학습 증 최고 성능이 나왔을 때만 모델이 저장되도록 checkpoint에 save_best_only 옵션을 True로 설정하였습니다. 테스트 데이터의 손실값이 최소가 되도록 만드는 것이 목표이기 때문에 monitor 변수는 val_loss를, mode는 최솟값을 의미하는 min을 설정하였습니다.

!pip install transformers
4

Early Stop과 Model Checkpoint를 콜백 함수로 설정합니다.

!pip install transformers
5

12-4) 감정 분류 모델 학습

100번의 Epoch 동안 32개의 배치 크기에서 모델을 학습시킵니다.

!pip install transformers
6

저는 17번의 Epoch 도중 학습이 조기종료되었습니다(그림 11).

한국어 NLP 모델 - hangug-eo NLP model
그림 11. 감정 분류 모델 학습 중 성능 모니터링

13) 감정 분류 모델 성능 평가

학습 중 최고 성능을 보인 모델을 불러옵니다.

!pip install transformers
7

테스트용 입력 데이터를 기반으로 최고 성능의 모델로 라벨을 예측합니다.

!pip install transformers
8

Multi-class 평가 지표 중 하나인 분류 리포트를 출력합니다.

!pip install transformers
9


분류 정확도는 85.2%, F1 점수는 0.851로 나왔습니다(그림 12).

한국어 NLP 모델 - hangug-eo NLP model
그림 12. 분류 리포트 결과

Confusion Matrix(혼동 행렬)로 모델의 예측과 정답 간의 차이를 시각화하면 아래 그림 13과 같습니다. 데이터를 분류하기 전과 유사한 비율을 갖는 라벨 예측 결과가 나타났습니다.

!pip install tensorflow_addons
0
한국어 NLP 모델 - hangug-eo NLP model
그림 13. Confusion MAtrix

Multi-class Classification 시 성능 평가를 위하여 주로 활용되는 평가지표를 활용하였습니다. Accuracy, Precion, Recall, F1과 함께, Cohen's Kappa Coeffient, Matthews_corrcoef, Log Loss 등 다중 분류 문제에서 주로 활용되는 평가 지표를 활용하였습니다.

!pip install tensorflow_addons
1

평가지표별로 Key-Value 형태의 Dictionary에 저장하고, 종합한 평가지표는 데이터프레임으로 저장합니다.

!pip install tensorflow_addons
2

종합적인 평가 지표는 엑셀로 저장합니다.

!pip install tensorflow_addons
3

평가 결과는 아래와 같습니다.

MetricScoreAccuracy0.852Precision0.851Recall0.852F1 Score0.851ROC AUC Score0.920Cohen's Kappa Coefficient0.728Matthews Correlation Coefficient0.729Log Loss0.555

Last Updated @2022-05-31


오늘은 사전 학습 언어 모델인 KLUE BERT-base과 오픈소스 데이터를 활용하여 한국 경제 뉴스 기사의 감정 분류 프로젝트에 대해 알아봤습니다. 포스팅 내용에 오류가 있거나 보완할 점이 있다면 아래에 댓글 남겨주시면 감사드리겠습니다. 그럼 오늘도 즐겁고 행복한 하루 보내시길 바랍니다. 고맙습니다 :)