KlueBERT를 활용한 뉴스 세 줄 요약 서비스_2(ft.데이터)

shooting star·2023년 3월 22일
5

들어가며

이번 포스팅에서 다룰 주제는 데이터이다. KlueBERT를 활용한 뉴스 세 줄 요약 프로젝트에서 사용한 데이터의 수집부터 소개 및 데이터 Tokenizer까지 데이터에 대해 전반적으로 살펴보고자 한다.

참고로 해당 프로젝트는 MeCab를 먼저 적용한 후 BPE를 적용한다. 이는 MeCab을 통해 문장을 형태소 단위로 분해한 후에, BPE를 적용하여 더 작은 subword 단위로 분할하여 토큰화하여 모델 학습의 효율성과 정확성을 높일 수 있다. 하지만 무조건 MeCab를 적용한다고 좋은 것은 아니다 NLU Task의 경우는 굳이 사용할 필요가 없으며 주로 NLG Task에서 사용하면 된다.

BPE는 가장 많이 등장하는 바이트 쌍을 새로운 단어로 치환하여 압축하는 작업을 반복한다. 모든 단어를 바이트들의 집합으로 취급하여 자주 등장하는 문자 쌍을 합치면 접두어나 접미어의 의미를 캐치할 수 있고 처음 등장하는 단어는 문장들의 조합을 나타내어 OVV 문제를 해결할 수 있다.

데이터단

1. 데이터 수집

이번 프로젝트에 사용한 데이터는 AIHub의 문서요약 텍스트를 사용했다. 해당 데이터는 신문기사 30만 건, 기고문 6만 건, 잡지 1만 건, 법률 3 만건 데이터로 이루어져 있으며 각각의 문건마다 세 줄 추출요약과 생성요약문으로 구성되어 있어 원문 데이터 40만 건에 총 80만 건의 데이터를 가진 데이터셋이다. 하지만 이 데이터를 모두 사용하지는 않을 것이다.

2. 데이터 소개

우리는 위의 데이터 중 신문기사, 기고문, 법률 데이터 중 세 줄 요약된 요약문 데이터만 사용할 것이므로 데이터의 양이 줄어든다. 실제로 데이터를 불러와 Python 코드를 결과를 냈을 때의 결과는 다음과 같다.

  • train : 325072
  • validation : 40134
  • Test : 2006

여기서 Test 데이터가 적은 이유는 시간 단축을 위해서이다.

3. 데이터 전처리

(1) Text 및 Extracive 추출 및 카테고리 통합

list_dic = []

for file in filenames:
  filelocation = os.path.join(DATAPATH, file)
  
  # 카테고리별 json 파일 읽어오기
  with open(filelocation, 'r') as json_file:
    data = json.load(json_file)['documents']

	
    for x in tqdm (range(len(data))):
      # 데이터에 텍스트 필드를 지정 및 "를 '로 replace
      text = data[x]['text']
      text = str(text).replace('"', "'")
	  
      # 데이터에 추출 필드를 지정 및 None을 0으로 변환
      extractive = data[x]['extractive']
      for index, value in enumerate(extractive):
        if value == None:
          extractive[index] = 0
      """
	  (?<=sentence': ') : 전방탐색 패턴으로, 'sentence': ' 문자열 이전까지의 내용에 대한 매칭을 수행
      (.*?) : 임의의 문자열을 선택하고자 할 때 사용되는 패턴으로 이 패턴은 최소한의 문자열을 선택하기 위해 '?' 문자를 포함
      (?='highlight_indices) : 전방탐색 패턴으로, 'highlight_indices 문자열 다음부터 이전까지의 내용에 대한 매칭을 수행
      즉, 이 정규표현식을 사용하면, "sentence': '"와 "'highlight_indices" 사이의 문자열을 선택하여 추출할 수 있다.
      """
      p = re.compile('(?<=sentence\'\: \')(.*?)(?=\'highlight_indices)')
      
      texts = p.findall(text)

      sentences = []
      
      for t in texts:
        sentence = t[:-3]
        sentences.append(sentence)

      # 텍스트와 추출요약본을 딕셔너리 형태로 저장
      mydict = {}
      mydict['text'] = sentences
      mydict['extractive'] = extractive
      list_dic.append(mydict)

해당 코드는 원본 데이터에서 모든 카테고리를 통합하여 Text와 Extracive를 추출하고 딕셔너리 형태로 저장하는 과정이다. 여기에서 유의해서 보아야 할 부분은 다음과 같다.

  • extractive에 None로 잘 못 지정된 부분을 0으로 바꿔준다.
  • 정규 표현식을 사용하여 "sentence': '"와 "'highlight_indices" 사이의 문자열을 선택하여 추출한다.

이렇게 구현한 이유는 원본 데이터에 오류가 있기 때문에 보다 더 정확하게 데이터를 불러오기 위함이다.

(2) 학습가능한 형태로 전환(MeCab)

def data_preprocess(set_name):
  with open("/content/drive/MyDrive/인공지능/추출요약/data/raw_data/{}.json".format(set_name), 'r') as f:
    data = json.load(f)

    list_dic = []
    for x in tqdm(range(len(data))):
      text = data[x]['text']
      extractive = data[x]['extractive']

      sentences = []
      for sentence in text:
        sentence_morph = ' '.join(mecab.morphs(sentence))
        sentences.append(sentence_morph)

      extractives = []
      for e in extractive:
        extractives.append(sentences[e])

      src = [i.split(' ') for i in sentences]
      tgt = [i.split(' ') for i in extractives]

      mydict = {}
      mydict['src'] = src
      mydict['tgt'] = tgt
      list_dic.append(mydict)

    jsonfilelocation = '/content/drive/MyDrive/인공지능/추출요약/data/json_data/' + set_name
    os.makedirs(jsonfilelocation, exist_ok=True)

    temp = []
    DATA_PER_FILE = 50

    for i,a in enumerate(tqdm(list_dic)):
      if (i+1)%DATA_PER_FILE!=0:
        temp.append(a)
      else:
        temp.append(a)
        filename = 'korean.'+ set_name + '.' + str(i//DATA_PER_FILE)+'.json'
        with open(os.path.join(jsonfilelocation, filename), "w", encoding='utf-8') as json_file:
          json.dump(temp, json_file, ensure_ascii=False)
          temp = []

      #마지막에 남은 데이터 있으면 추가로 append
      if len(temp) != 0:
        filename = 'korean.'+ set_name + '.' + str(i//DATA_PER_FILE + 1)+'.json'
        with open(os.path.join(jsonfilelocation, filename), "w", encoding='utf-8') as json_file:
          json.dump(temp, json_file, ensure_ascii=False)

해당 작업은 매우 간단하다. 우선 Text 데이터와 Extractive의 index에 해당하는 문장을 추출한다. 그러고는 MeCab로 형태소 분석 후에 BPE Tokenizer를 진행한다. Tokenizer를 후에는 token 단위로 json 파일에 저장한다.

(3) feature 생성 후 .pt 파일로 저장(BPE)

import glob
from os.path import join as pjoin
import sys

from multiprocess import Pool
sys.path.append('/content/drive/MyDrive/인공지능/추출요약')
from SRC.prepro.data_builder import _format_to_bert

def format_to_bert(args):
    if (args.dataset != ''):
        datasets = args.dataset
    else:
        datasets = ['train', 'valid', 'test']
    for corpus_type in datasets:
        a_lst = []
        for json_f in glob.glob(pjoin(args.raw_path, corpus_type, '*' + corpus_type + '.*.json')):
            real_name = json_f.split('/')[-1]
            a_lst.append((json_f, args, pjoin(args.save_path, real_name.replace('json', 'bert.pt'))))
        # print(a_lst)
        pool = Pool(args.n_cpus)
        for d in pool.imap(_format_to_bert, a_lst):
            pass

        pool.close()
        pool.join()

format_to_bert 함수를 이용하여 data_builder에 있는 _format_to_bert를 실행한다. 이를 통해서 feature를 생성한 후. pt 파일로 저장한다. format_to_bert 함수도 data_builder에 있지만 원활한 진행을 위해서 Colob에 가져와서 사용하였다.

마치며

이번 포스팅은 데이터에 대해서 수집, 소개, 전처리까지 전반적으로 살펴보았다. 다음 시간에는 이번 프로젝트의 꽃인 모델링에 대해서 살펴보도록 하겠다.

github로 이동하기 : KlueBERT를 활용한 뉴스 세 줄 요약 서비스

0개의 댓글