본문 바로가기
📂 머신러닝 | 딥러닝/◾ NLP

문맥을 고려한 한국어 텍스트 데이터 증강 | Korean Text Augmentation Considering Context, K-TACC 소개 및 활용 코드

by 이 정규 2024. 4. 5.
728x90
반응형

들어가며

안녕하세요! 벌써 봄이 왔네요... 밤낮으로 일교차가 있으나 2주 전에 비교하면 정말 많이 따듯해졌음을 느끼고 있습니다. 이번에 회사에서 3차년도 R&D 과제 중 콘텐츠 분류 모델을 만드는 업무를 맡게됐습니다. 물론 생각보다 목표치를 빨리 달성하게 돼서 이렇게 블로그를 쓸 시간도 생기게 됐네요. 사실 이번 업무에서는 모델을 만든다기보다는 성능 향상이 많이 필요한 상황이었습니다. 

문제

제가 직면한 상황에는 총 3가지 문제가 있었습니다.

1. 데이터 총 개수가 654개로 상당히 작았습니다.
2. 기존에 만들어진 모델이 거의 깡통 모델이었습니다. 
3. 과적합 해결 방안이 필요했습니다.

2번과 3번의 경우는 쉽게 해결이 가능했습니다. 그런데 1번 문제는 데이터를 더 받을 수 있는게 아니라면 해결이 불가능했고, 비전 쪽 과제를 했을 때 사용했던 증강 방법을 사용하기로 결정했습니다. 텍스트 증강은 WordNet을 이용해서 증강이 쉽게 가능하지만, 영어만 지원하기 때문에 한국어에 학습을 시켜봐야 결과가 나오지 않습니다. 부산대나 카이스트에서 한국어 텍스트 데이터 증강에 대한 연구가 진행됐다는 논문을 확인했는데, 실질적으로 깃이나 웹에서 확인할 수는 없었습니다. 부산대의 경우에는 웹 페이지가 구축되어 있지만 사용이 안되는 것으로 확인. 

해결

허깅페이스에서 모델을 가져와 한국어를 학습시키기엔 그럴만한 환경이 아니었기에 조금 더 서칭을 해보았고, 그 결과 Bongseok Yang님의 깃에서 K-TACC를 찾았습니다. 못찾았다면 삽질이 더 길어졌을듯 함 

https://github.com/kyle-bong/K-TACC

 

GitHub - kyle-bong/K-TACC: 문맥을 고려한 한국어 텍스트 데이터 증강

문맥을 고려한 한국어 텍스트 데이터 증강. Contribute to kyle-bong/K-TACC development by creating an account on GitHub.

github.com

 

설치는 레퍼지토리를 작업 환경에 복사해와서 사용하면 됐습니다. 제가 사용한 패키지는 BERT를 이용한 RI(Random Insertion)과 Kiwi 패키지를 이용한 형태소를 분리한 후 부사의 의미를 검색 후 유의어로 교체해주는 SR(Synonym Replacement)입니다. 

예시 문장으로 알아보는 SR, RI, RS, RD 적용했을 때의 결과

반응형

적용 코드

from BERT_augmentation import BERT_Augmentation as bert
from adverb_augmentation import AdverbAugmentation as adverb

bert = bert()
random_masking_replacement = bert.random_masking_replacement
random_masking_insertion = bert.random_masking_insertion

adverb = adverb()
adverb_gloss_replacement = adverb.adverb_gloss_replacement

def textAug(df: pd.DataFrame, label: str, iter: int):
    '''
    DATAFRAME MUST HAVE ONLY 2 COLS(TEXT AND LABEL).
    TEXT COL IDX IS 0, AND LABEL COL IDX IS 1.
    '''
    print(f'before text augmentation: {len(df)}')
    for _, row in df.iterrows():
        cnt = 0
        if row[1] == label:
            print(f'[{label}] augmentation start')
            for _ in range(iter):
                cnt += 1
                textlabel = row[1]
                textaug = random_masking_insertion(sentence=row[0][:512], ratio=0.8) # text random insertion
                textaug = adverb_gloss_replacement(sentence=row[0][:512]) # adverb random insertion
                if 512 <= len(row[0]) < 1024 :
                    textaug2 = random_masking_insertion(sentence=row[0][512:1024], ratio=0.8)
                    textaug2 = adverb_gloss_replacement(sentence=row[0][512:1024])
                    textaug = textaug + textaug2
                elif 1024 <= len(row[0]) < 1536:
                    textaug3 = random_masking_insertion(sentence=row[0][1024:1536], ratio=0.8)
                    textaug3 = adverb_gloss_replacement(sentence=row[0][1024:1536])
                    textaug = textaug + textaug3
                elif 1536 <= len(row[0]) < 2048:
                    textaug4 = random_masking_insertion(sentence=row[0][1536:2048], ratio=0.8)
                    textaug4 = adverb_gloss_replacement(sentence=row[0][1536:2048])
                    textaug = textaug + textaug4
                elif 2048 <= len(row[0]) < 2560:
                    textaug5 = random_masking_insertion(sentence=row[0][2048:2560], ratio=0.8)
                    textaug5 = adverb_gloss_replacement(sentence=row[0][2048:2560])
                    textaug = textaug + textaug5
                elif 2560 <= len(row[0]) < 3072:
                    textaug6 = random_masking_insertion(sentence=row[0][2560:3072], ratio=0.8)
                    textaug6 = adverb_gloss_replacement(sentence=row[0][2560:3072])
                    textaug = textaug + textaug6
                elif 3072 <= len(row[0]) < 3584:
                    textaug7 = random_masking_insertion(sentence=row[0][3072:3584], ratio=0.8)
                    textaug7 = adverb_gloss_replacement(sentence=row[0][3072:3584])
                    textaug = textaug + textaug7
                elif 3584 <= len(row[0]) < 4096:
                    textaug8 = random_masking_insertion(sentence=row[0][3584:4096], ratio=0.8)
                    textaug8 = adverb_gloss_replacement(sentence=row[0][3584:4096])
                    textaug = textaug + textaug8
                
                df.loc[len(df) + 1] = [textaug, textlabel]
    df.to_csv('textaug completed dataframe.csv', index=False)
    print(f'after text augmentation: {len(df)}')

간단히 코드에 대한 설명을 해보자면 증강을 위해 BERT와 Adverb 객체를 만들어줍니다. textAug 함수에서 받는 파라미터로는 텍스트 - 라벨 구조의 데이터프레임, 증강시키고자 하는 특정 라벨, 함수 안 반복문을 몇 번 돌 것인지가 있습니다.

RI는 텍스트와 비율을 파라미터로 받습니다. 비율이 높아질수록 조금 더 공격적인(?) 증강을 수행합니다. 레포지토리 안에 있던 사용설명서(augmentation.ipynb)를 통해 확인해보겠습니다.

이처럼 비율이 커질수록 한 번에 더 많은 RI를 수행하게 됩니다. 그래서 저는 0.8을 채택하였습니다.

결국은 A라는 텍스트를 받아 RI와 SR을 거친 뒤 데이터프레임의 새로운 열로 추가해주는 걸 반복합니다. 다만 여기서 주의할 점은 기본적으로 길이가 512까지만 받아줍니다. 그렇기 때문에 입력받은 텍스트의 길이의 최대를 저는 4096까지 늘려주는 작업을 했습니다.  결과적으로 654개의 데이터에서 1031개로 늘어났습니다. 

728x90

결론

물론 텍스트 증강으로 저는 테스트 셋에 대한 정확도가 2.1% 향상되어 과제의 3차년도 목표를 달성할 수 있었습니다. 함수만 불러와 쓰는 정도에 그쳤지만, Bongseok Yang님의 코드를 리뷰하며 어떻게 디벨롭이 가능할지 혹은 내 도메인에 맞게 최적화할지 고민하는 시간을 가져보려고 합니다. 이 포스팅을 통해 감사함을 전하며 여기서 끝!

728x90
반응형

댓글