티스토리 뷰

이번 포스트는 전처리에 이어 텍스트 불균형을 해소하기 위해 밟은 절차에 대해 설명하도록 한다.

 

본 프로젝트의 주제인 '스포일러 댓글 분류'에 대한 데이터는 당연히 데이터 불균형 문제가 심할 수 밖에 없다.

 

심지어 레퍼런스로 많이 참고했던 '악플 분류'보다 훨씬 심한 불균형 문제를 가지고 있었고, 불균형을 해소하기 전의 F1-score는 굉장히 낮게 나오는 것을 확인하였다.

 

수집 후 타겟 레이블의 불균형은 0(스포일러가 아님)이 97%, 1(스포일러)가 3%로 이러한 불균형 문제를 해결해주어야만 제대로 된 학습을 시킬 수 있는 상황이었다.

 

처음엔 머신러닝에서 자주 사용하는 Over sampling 기법인 SMOTE와, 학습 시 가중치를 부여할 수 있는 class_weight를 활용해 보았지만 3%라는 아주 적은 데이터를 커버할 정도의 성능이 보이지 않았다.

 

그래서 Vision에서 많이 사용하는 Augmentation 기법을 찾아보았다.

 

그 중 1차적으로 Back Translation 기법을 통해 불균형을 해소시켜주기로 하였다.

 

Back Translation은 기존 텍스트를 외국어로 번역한 뒤 다시 기존의 언어로 번역하는 기법으로, 영어를 기준으로 사용할 수 있는 라이브러리가 있었지만, 한국어는 찾을 수 없었다.

 

또한 간편하게 사용할 수 있는 카카오나 파파고의 번역 API가 있었지만, 일일 글자 수 제한으로 인해 만단위의 데이터를 번역할 수 없는 문제가 있었다.

 

그래서 시간이 오래걸리는 작업이지만 크롤링 소스를 사용하여 번역을 실시해주었다.

 

import pandas as pd
import selenium
from selenium import webdriver
from bs4 import BeautifulSoup
import time
from tqdm import tnrange
from urllib.request import urlopen
import re
import requests
import urllib.request
from tqdm import tqdm
train = pd.read_csv('train_맞춤법후.csv')
test = pd.read_csv('test_맞춤법후.csv')
train_spo = train.loc[train['스포일러'] == 1]
test_spo = test.loc[test['스포일러'] == 1]
train_spo.reset_index(drop = True, inplace = True)
test_spo.reset_index(drop = True, inplace = True)
driver = webdriver.Chrome(executable_path=r'C:\Users\User\Desktop\chromedriver.exe')
driver.maximize_window()

위의 코드는 코랩이 아닌 로컬 기준의 코드이다.

 

먼저 스포일러 데이터만 증강시켜주어야 하므로, 전처리가 끝난 데이터를 불러온 후 스포일러에 해당하는 데이터만 추출하였다. 

 

trans_list = [] # 언어 바꿀때 초기화해야합니다. 
backtrans_list = [] # 초기화 하면 안됩니다. (최종 한글 텍스트의 아웃풋 쌓여있음)
# kor_to_trans 함수 -> 한국어를 외국어로 바꾸는 함수
# trans_to_kor 함수 -> 외국어를 한국어로 바꾸는 함수
# 첫번째 함수를 먼저 수행하면 trans_list에 외국어로 번역된 텍스트가 들어있음
# trans_list를 두번째 함수의 입력값으로 받아서 다시 한글 텍스트의 리스트로 뽑아냄

def kor_to_trans(text_data, trans_lang):
    """
    trans_lang에 넣는 파라미터 값:
    'en' -> 영어
    'ja&hn=0' -> 일본어
    'zh-CN' -> 중국어(간체)
    """
    for i in tqdm(range(len(text_data))):
        try:
            driver.get('https://papago.naver.com/?sk=ko&tk='+trans_lang+'&st='+text_data[i])
            time.sleep(2.5)
            backtrans = driver.find_element_by_xpath('//*[@id="txtTarget"]').text
            trans_list.append(backtrans)
        except:
            driver.get('https://papago.naver.com/?sk=ko&tk='+trans_lang)
            driver.find_element_by_xpath('//*[@id="txtSource"]').send_keys(text_data[i])
            time.sleep(2.5)
            backtrans = driver.find_element_by_xpath('//*[@id="txtTarget"]').text
            trans_list.append(backtrans)

def trans_to_kor(transed_list, transed_lang):
    for i in tqdm(range(len(transed_list))):
        try:
            driver.get('https://papago.naver.com/?sk='+transed_lang+'&tk=ko&st='+transed_list[i])
            time.sleep(2.5)
            backtrans = driver.find_element_by_xpath('//*[@id="txtTarget"]').text
            backtrans_list.append(backtrans)
        except:
            driver.get('https://papago.naver.com/?sk='+transed_lang+'&tk=ko')
            driver.find_element_by_xpath('//*[@id="txtSource"]').send_keys(transed_list[i])
            time.sleep(2.5)
            backtrans = driver.find_element_by_xpath('//*[@id="txtTarget"]').text
            backtrans_list.append(backtrans)

이후 Selenium을 통해 한국어 데이터를 영어, 일본어, 중국어로 번역 시킨 후 리스트에 append 해주는 함수와, 그 리스트의 외국어 데이터를 다시 한국어로 번역시키는 함수를 제작하였다.

 

kor_to_trans(train_spo['리뷰'], 'en') # 한글 -> 영어
trans_to_kor(trans_list, 'en') # 영어 -> 한글

이렇게 적용까지 끝낸 후 1의 값을 갖는 스포일러 데이터를 4배로 증강 시킬 수 있었다. 

 

더 많은 언어들을 적용시켜 보고자 했지만, 대부분 언어들이 비슷하게 번역되어 크게 데이터가 변형되지 않음을 감안하여 3가지의 언어로 번역하였고, 이후 동의어 대체를 통해 Augmentation을 진행할 계획을 세웠다.

 

4배정도를 늘렸음에도 불구하고 성능은 굉장히 좋아져서, 불균형 문제를 해소한다면 꽤나 높은 성능을 기대해도 좋을 것 같다.

반응형
댓글