본문 바로가기
  • 머킹이의 머신로그
프로젝트/개인 프로젝트

[자연어 개인 프로젝트] Transformer 전처리 하기, KoBART

by 머킹 2023. 10. 6.
728x90

Transformer 데이터 전처리 하기, 코랩 안 끊기게 설정하기

 

안녕하세요 머킹입니다.

정말 오랜만에 프로젝트 이슈를 들고 왔습니다.

왜 이렇게 늦었냐! 하면 지난 글부터 이어져있는 문제인데요.

 

저는 로컬로 드디어 okt를 돌릴 수 있게 됐고

기쁜 마음에 (구린 장비인 것도 잊고) 연휴를 다녀왔습니다.

 

그리고 제가 확인해 본 결과 토큰화 된 데이터는 3백만 개가 맞았습니다.

그래서 엄청나게 방대한 양을 병렬처리하고,

PAD 같은 특별한 토큰을 씌우고, 텐서화를 진행시키는데..!!!!!!

엄청난 길이 이슈로 결국 로컬로 돌리는 것을 포기했습니다.

이게 말이 되는 시간인가요 ㅎㅎㅋㅋㅋㅋㅋ

 

그래서 동료분들의 조언으로 다시 코랩으로 돌아갔습니다. 
그리고 코랩에 돌아가서 코랩이 계속 유지되는 코드를 개발자도구 콘솔에 입력했습니다.

function ClickConnect(){
    console.log("코랩 연결 끊김 방지");
    document.querySelector("colab-toolbar-button#connect").click()
}
setInterval(ClickConnect, 60 * 1000)

코랩 연결 끊김이 제대로 잘 작동되는 것을 볼 수 있었습니다.


다시 데이터 구글 드라이브에 올리고 전처리를 잘 진행하는가 했더니!

너무 많은 데이터를 처리해서인지 코랩이 끊기더라고요.

그래서 고안해 본 결과 데이터를 1/3씩 쪼갰습니다.

import csv
import torch
from torch.utils.data import DataLoader, TensorDataset
from konlpy.tag import Okt
from tqdm import tqdm 

# Okt 형태소 분석기 초기화
okt = Okt()

# CSV 파일 경로 설정
csv_file = '/content/drive/MyDrive/jeju/parallel_data.csv'

# 데이터를 저장할 리스트
parallel_data = []

# CSV 파일에서 1/3 데이터만 읽어오기
def read_partial_csv_data(csv_file, fraction=1/3):
    with open(csv_file, 'r', encoding='utf-8') as csvfile:
        reader = csv.DictReader(csvfile)
        for row in tqdm(reader, total=fraction * 3142768, desc="Reading Partial Data"):
            parallel_data.append({
                'Utterance': row['Utterance'],
                'Original': row['Original'],
                'Translation': row['Translation']
            })

# 추가 후처리 작업 (SOS, EOS, PAD 토큰 추가)
max_len = 32  # 문장의 최대 길이를 지정합니다.
for item in parallel_data:
    item['Original'] = '[SOS] ' + item['Original'] + ' [EOS]'
    item['Translation'] = '[SOS] ' + item['Translation'] + ' [EOS]'

# 나머지 데이터 처리 코드
# (단어를 정수로 매핑하는 사전 생성, 토큰화, 텐서로 변환, DataLoader 생성 등)

if __name__ == "__main__":
    # 1/3 데이터만 읽어오기
    read_partial_csv_data(csv_file, fraction=1/3)

word_to_index = {}
index_to_word = {}

# 특수 토큰을 사전에 추가합니다.
special_tokens = ['[PAD]', '[SOS]', '[EOS]']
for token in special_tokens:
    word_to_index[token] = len(word_to_index)
    index_to_word[len(word_to_index) - 1] = token

# 토큰화 및 후처리 함수
def tokenize_and_preprocess(sentence, max_len):
    tokens = okt.morphs(sentence)[:max_len-2]  # 최대 길이를 고려하여 자르기
    tokens = ['[SOS]'] + tokens + ['[EOS]']
    tokens += ['[PAD]'] * (max_len - len(tokens))
    return tokens

# 데이터를 토큰화 및 후처리하여 텐서로 변환하는 함수
def process_data(data, max_len):
    tokenized_data = [tokenize_and_preprocess(item['Original'], max_len) for item in tqdm(data, desc="Tokenizing Data")]
    tensor_data = [torch.tensor(tokens_to_indices(tokens, max_len)).long() for tokens in tqdm(tokenized_data, desc="Converting to Tensors")]
    return tensor_data

# 토큰을 정수 인덱스로 매핑하는 함수
def tokens_to_indices(tokens, max_len):
    indices = [word_to_index.get(token, word_to_index['[PAD]']) for token in tokens]
    indices += [word_to_index['[PAD]']] * (max_len - len(indices))
    return indices

# 문장의 최대 길이를 계산합니다.
jeju_lengths = [len(tokenize_and_preprocess(item['Original'], max_len)) for item in parallel_data]

# 데이터를 토큰화 및 후처리하여 텐서로 변환
jeju_tensors = process_data(parallel_data, max_len)

# DataLoader를 사용하여 데이터를 배치로 만듭니다.
batch_size = 32  # 배치 크기 조정 가능
jeju_loader = DataLoader(TensorDataset(*jeju_tensors), batch_size=batch_size, shuffle=True)

# 데이터 사이즈 확인
jeju_data_size = len(jeju_loader.dataset)
print(f"제주도 사투리 데이터의 전체 크기: {jeju_data_size} 문장")

# 배치 수 계산
jeju_batches = len(jeju_loader)
print(f"제주도 사투리 데이터의 배치 수: {jeju_batches} 배치")

열심히 쪼개서 진행 중인 저의 컴퓨터...

생각보다 학원 컴이 좋지는 않더라고요 ㅎ.. 왜 다들 클라우드를 쓰는지.. 알겠습니다.

 

그리고 hugging face에서 모델을 파인튜닝해서 쓰고 싶어서

이리저리 알아본 결과 역시 설계가 중요하다는 것을 알았습니다.

모델에 따라서 전처리 방법이 다르니까 역시 설계를 꼼꼼하게 해야 한다는 것을 절실히 깨달았습니다.

 

그러던 중 KoBART라는 모델로 제주도 - 표준어 번역한 것을 봐서

KoBART라는 모델에 관심을 가지게 되었는데요.

이런 모델이라고 합니다.

원래 계획대로라면 여러 가지 모델을 비교해 보고 가장 성능이 뛰어난 모델을 

배포하는 것이 목적이었는데..........

 

지금은 트랜스포머도 시간 내에 돌릴 수 있을지 고민되네요.

개인 프로젝트를 하면서 가장 많이 느낀 게 삽질의 연속이라는 점인데요.

그래도 삽질을 하는 만큼 얻어지는 것도 있을 거라 생각하며

오늘도 열심히 코드를 돌려보겠습니다.