이제 K-유튜브 제목을 생성해보자!

이제까지 수집한 유튜브 데이터들을 EDA해보며 K-유튜브의 특징들을 알아보았다. 이번 포스팅부터는 KoGPT2 모델을 fine-tuning해 새로운 유튜브 제목을 생성하는 모델을 구현해본다. 우선 GPT 모델에 대해서 알아볼 필요가 있다. GPT는 Gerative pre-training의 약자이다. GPT는 Transformer 모델의 영향을 받아 만들어진 모델이다. GPT의 가장 큰 특징은 대량의 라벨링 되지 않은 데이터를 이용해 사전학습을 할 수 있다는 점이다. 머신러닝에서 가장 큰 난관은 라벨링 된 질 좋은 데이터를 확보하는 것이다. 그렇기 때문에 머신러닝 프로젝트에서는 필연적으로 데이터를 전처리하고 라벨링하는 작업이 가장 긴 시간을 차지한다. 그런 난관을 해결하기 위해 등장한 것이 전이 학습이다. 전이 학습은 미리 대량의 데이터를 학습시켜 놓은 모델을 소수의 라벨링 된 데이터로 fine-tuning하여 다양한 Task에 활용하는 방법이다.
GPT 모델은 전이학습 모델 중에서도 텍스트 생성에 특화된 모델이다. 기본적으로 GPT는 전이학습용 모델이므로 분류나 Quenstion-Answer 문제같은 다양한 Task에 사용가능한 모델이지만 특히 그럴듯한 텍스트를 생성하는 모델로 유명하다. 때문에 소설 텍스트를 학습해서 창작 소설을 쓰거나 챗봇을 만드는 데 많이 사용된다. KoGPT는 그런 GPT 모델을 한국어 처리 Task에 사용할 수 있도록 대량의 한국어 텍스트로 사전학습 시키는 것이다. 이 프로젝트에서 우리는 SKT에서 공개한 KoGPT2 모델을 사용했다.Github 성능이 더 좋다고 알려진 카카오브레인에서 공개한 KoGPT 모델도 존재하지만 용량이 너무 커서 Colab 환경에서 불러올 수 없었고, 또 카카오브레인의 KoGPT 모델의 성능이 오히려 과장되었다는 의견도 존재하므로기사 그냥 사용하기도 쉽고 성능도 별 차이 없을 것이라고 예상되는 KoGPT2 모델을 사용하기로 했다.

KoGPT2 모델 사용하기

우리는 Colab 환경에서 GPU를 사용해 모델을 학습시키기로 했다. KoGPT2 모델은 허깅페이스에 공개되어 Transformers 라이브러리를 사용하면 사전학습된 모델을 불러올 수 있다. Transformers 라이브러리는 허깅페이스에 공개된 모델을 불러와 사용할 수 있게 해주는 라이브러리다. 주로 GPT를 비롯한 Transformer 기반으로 만들어진 모델들이 많이 공개되어 있다. 먼저 Colab 환경에는 Transformers 라이브러리가 기본적으로 설치되어 있지 않으므로, 추가적으로 설치해주어야 한다.

!pip install transformers

!는 Colab 셀에서 터미널 명령을 실행시킬 수 있게 해주는 코드다. 여기서 pip를 사용하면 환경에 설치되어 있지 않은 파이썬 라이브러리들을 설치할 수 있다. KoGPT2 깃허브의 튜토리얼을 참고하면 KoGPT2의 사전학습된 모델과 토크나이저를 불러오는 방법을 쉽게 알 수 있다. KoGPT2의 토크나이저는 약 5만개의 단어에 대해 학습되어 있으며 이모티콘, 이모지등도 사전에 있기 때문에 토크나이즈할 수 있다. 사전학습된 모델과 토크나이저를 불러오는 코드는 다음과 같다.

from transformers import PreTrainedTokenizerFast, GPT2LMHeadModel

MODEL_PATH = 'skt/kogpt2-v2-base'

model = GPT2LMHeadModel.from_pretrained(MODEL_PATH)
tokenizer = PreTrainedTokenizerFast.from_pretrained(MODEL_PATH,
                                                    bos_token='<s>',
                                                    eos_token='</s>',
                                                    pad_token='<pad>',
                                                    unk_token='<unk>',
                                                    mask_token='<mask>')

이 코드들은 KoGPT2 깃허브에서 찾아볼 수 있는 예시 코드이다. bos_token, eos_token은 문장의 시작과 끝을 알려주는 토큰이다. GPT 모델은 기본적으로 eos token이 나올때까지 토큰을 생성해낸다. 따라서 학습시킬 텍스트에는 반드시 문장의 시작과 끝에 bos token, eos token을 붙여줘야 알맞은 길이의 문장을 생성해낸다.pad token은 학습시킬 문장 간의 길이가 다를 때, batch를 생성하기 위해서 벡터의 빈 부분을 채워주는 패딩용 토큰이다. unk token은 사전에 없는 모르는 단어를 매핑하는 token이다.
다음은 K-유튜브 제목 데이터를 불러올 차례이다. fine-tuning을 하기 위해 Pytorch를 사용할 것이므로 pytorch의 dataset 객체로 데이터셋을 만들어 줄 것이다. dataset 객체는 torch의 dataset 객체를 상속시키고 데이터셋의 길이를 반환하는 __len__과 특정 인덱스의 데이터를 반환하는 __getitem__을 정의함으로써 만들 수 있다.

 import pandas as pd
 import torch
 from torch.utils.data import Dataset

 class TitleDataset(Dataset):
     def __init__(self, file_path, tokenizer, max_length):
        df = pd.read_csv(file_path)
        self.docs = df['vid_name']
        self.data = []

        for sent in docs:
            sent = tokenizer.bos_token + sent + tokenizer.eos_token
            encoding_dict = tokenizer(sent, max_length=max_length, padding='max_length')
            self.data.append(torch.tensor(encoding_dict['input_ids']))

    def __len__(self):
        return len(self.data)

    def __getitem__(self, index):
        return self.data[index]

객체를 선언할 때, file_path, tokenizer, max_length를 인자로 받는다. file_path에서 csv 파일의 경로를 받아 pandas를 이용해 데이터를 불러온다. 그 후 문장에 bos token과 eos token을 붙여 인자로 받은 tokenizer로 벡터화한다. 토크나이즈를 진행할 때, 입력받은 max_length에 맞춰 padding을 해준다. padding은 앞서 설명한 padding token으로 채워진다. 그리고 토큰으로 벡터화한 문장을 data 리스트에 tensor로 변형하여 추가한다. __len__과 __getitem__에는 data의 길이와 인덱스에 맞는 원소를 반환하는 함수를 정의한다.
dataset 객체는 torch의 DataLoader를 이용해 batch화 할 수 있다. DataLoader는 batch size와 shuffle 여부를 인자로 받아 batch로 묶인 데이터로더를 반환한다. DataLoader는 iterator의 역할을 해서 매 iter마다 batch화 된 텐서를 반환한다. 이번 프로젝트에서는 Colab 환경의 GPU 용량에 맞춰 batch size를 32로 정했다. batch size가 클 수록 학습은 빨라지지만 사용되는 메모리의 양은 커진다. 최종적으로 모델을 fine-tuning을 시키는 코드는 다음과 같다.

 from torch.utils.data import DataLoader

 device = 'cuda' if torch.cuda.is_available() else 'cpu'
 EPOCHS = 4
 BATCH_SIZE = 32

 # Dataloader 생성
 train_dataloader = DataLoader(dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=0)

 # Optimizer 생성
 optimizer = torch.optim.Adam(model.parameters(), lr=3e-5)
 model = model.to(device)
 model.train()

 for epoch in range(EPOCHS):
     for data in train_dataloader:
        data = data.to(device)
        optimizer.zero_grad()

        outputs = model(data, labels=data)
        loss = outputs.loss
        loss.backward()
        optimizer.step()

    # print loss & save checkpoint
    print(f'epoch: {epoch}, loss: {loss.detach().item()}')
    model.save_pretrained(f'model_file/model_{epoch}.bin')

model에 data를 넣고 Adam으로 모델의 파라미터를 업데이트한다. Pytorch는 backward 메소드를 이용해 역전파를 한 번에 편리하게 구할 수 있다. 그후 optimizer의 step 메소드를 사용하여 모델을 업데이트한다. 그리고 매 epoch마다 loss를 프린트하고 중간 모델을 세이브한다. GPT 모델의 fine-tunnig은 2~4 epoch의 학습을 권장하므로 이번 프로젝트에선 4 epochs로 모델을 튜닝했다.

학습된 모델의 결과

최종적으로 학습된 모델은 생각보다 좋은 출력물을 보여줬다. 시작할 단어를 입력하면 정말 그럴듯한 유튜브 제목을 생성해냈다. 생성 결과는 다음과 같다.

batch_size=32, epochs=4, learning_rate=3e-5로 fine-tuning 되어 저장된 모델을 불러와 사용했다. temperature는 모델의 output을 보정해서 좀 더 나올 확률이 낮은 단어도 출력하게하는 인자다. temperature 값이 높아질수록 좀 더 창의적인 문장이 나올 가능성이 높아진다. 생성 모델에서 temperature를 사용하지않고 계속 확률이 가장 높은 토큰만을 출력하게 만들면 동일한 단어가 계속 출력되는 모델이 될 수 있다.

출력된 제목들을 보면 매우 흥미롭다. 위 사진에서는 원래 데이터에서 매우 자주쓰인 단어인 '한국'을 input으로 주고 10개의 문장을 생성한 것인데, 대부분의 문장이 자연스러울 뿐만 아니라 체크해본 결과 원본 데이터의 제목을 그대로 출력하지도 않았다. 또한 대부분의 문장이 '~한 이유' 라는 식으로 문장을 끝맺고 있는 것도 재밌다. 크롤링하면서 직접 본 유튜브 제목들의 특징과 일치한다.

우리는 앞으로 이 모델을 web으로 서빙하는 과정까지 구현하여 단어와 temperature을 입력하면 적절한 제목들을 출력해주는 웹 어플리케이션을 만들어볼 계획이다. AWS와 django를 이용해서 간단하게 서빙해볼 것이다.

+ Recent posts