Day_42 01. GPT 언어 모델

작성일

12 분 소요

GPT 언어 모델

1. 두 문장 관계 분류 task 소개

1.1 GPT 모델 소개

BERT 는 Transformer 의 Encoder 를 사용했고 GPT 는 Transformer 의 Decoder 를 사용한 모델임

GPT 가 어떻게 언어를 학습을 하냐면?
“발 없는 말이 천리간다” 라는 문장을 학습한다고 가정하자
이 때, “발”이라는 단어가 나왔을 때 이 모델은 다음에 나오기에 가장 적절한 단어가 무엇인지? 어떤 토큰이 가장 적절한지? 를 학습할 수 있음
예를 들어서 “없는” 이 가장 적절한 단어라고 학습하게 되면 “발 없는” 이 나오게 되고 그 다음엔 “발 없는” 다음에 나오기에 가장 적절한 토큰이 어떤 것인지 예측을 하고 그 예측한 것을 통해서 계속해서 자연어를 지속적으로 생성하게 됨

GPT1 모델은 시기적으로 BERT 보다 먼저 나옴
GPT1 모델의 목적은 RNN 과 마찬가지로 기존의 문장이 입력으로 들어갔을 때 그 입력된 것의 context vector 를 출력하고 그 context vector 뒤에 linear layer 를 붙임으로써 분류 task 에 적용하기 위해 설계되었던 모델임
그래서 GPT1 모델의 경우 classification or similarity 를 계산하는 모델이었음

  • [자연어 문장 $\rightarrow$ 분류] 성능이 아주 좋은 디코더인 GPT
  • 덕분에 적은 양의 데이터에서도 높은 분류 성능을 나타냄
  • 다양한 자연어 task 에서 SOTA 달성
  • Pre-train 언어 모델의 새 지평을 열었음 $\rightarrow$ BERT 로 발전의 밑거름
  • 하지만 여전히, 지도 학습을 필요로 하며, labeld data 가 필수임
  • 특정 task 를 위해 fine-tuning 된 모델은 다른 task 에서 사용 불가능

$\rightarrow$ “언어“의 특성 상, 지도학습의 목적 함수는 비지도 학습의 목적함수와 같다! $\rightarrow$ fine-tuning? 필요 없다!!!!!

그런데 GPT 를 개발한 연구진들은 이러한 한계점을 고민을 하다가 이렇게 생각을 하게됨
언어의 특성상 언어의 특성상 우리가 만들고자하는 지도학습의 목적함수가 비지도학습의 목적함수와 같음
그러니까 fine-tuning 에서 만들어지는 목적함수와 pre-trained 모델을 만드는 비지도학습의 목적함수가 같음
무슨말이냐면, 결국에는 fine-tuning 에서 사용되는 label 자체도 그게 하나의 언어라는 것!

예를 들어 감정분석을 보자.
“기쁨”, “슬픔”, “분노” 등이 나올 수 있는데 이 “기쁨”이라는 단어자체가 언어인 것!
“슬픔”이라는 단어 자체도 언어가 됨
그니까 이것을 굳이 fine-tuning 할 필요가 없다라고 여겨지게 됨

엄청 큰 데이터셋을 사용하면 자연어 task 를 자연스럽게 학습!

이 text 에는 영어가 있고 French: 하고 그 영어가 프랑스어로 번역된 예제가 있음
이게 무엇을 의미하냐면?
엄청나게 큰 데이터셋을 사용하면 번역 task 와 같이 자연어 task 들은 자연스럽게 학습할 수 있음

“그는 놀란 마음으로 다음과 같이 말했다.” 라는 sentence 가 제시됨
그러면 “로스트아크에서는 투명 모자가 공짜라고??” 라는 sentence 는 “놀람” 이라는 의미다라는 것을 명확하게 제시하지 않더라도 엄청큰 데이터셋을 통해서 감정에 대한 것까지 자연스럽게 학습할 수 있음
굳이 비지도학습의 과정에서 이루어지는 목점함수랑 fine-tuning 에서 이뤄지는 과정을 구분할 필요가 없다는 것임!
즉, fine-tuning 과정 자체가 필요 없을수도 있음을 가정으로 두게됨
이렇게 GPT1 을 개발한 연구진들은 이 가정을 통해서 다음 모델들을 고안해내게 됨

GPT 를 개발한 연구진들은 다음과 같은 문제점을 제시함

사람은 새로운 task 를 학습하기 위해 수많은 데이터를 필요로 하지 않음
우리가 감정에 대한 것을 분류한다고 가정했을 때 우리가 어떤 데이터 corpus 를 보면서 corpus 의 이 문장은 “놀람”이다라고 tagging 하고 이건 “기쁨”이다라고 tagging 함으로써 배우지 않음

또 다른 문제점으로 pre-trained 모델을 fine-tuning 함으로써 한 모델을 하나의 task 만으로 활용한다는건 이 행동자체가 바보 같다는 것을 제시하게 됨
pre-trained 모델을 만들어내기 위해서는 엄청나게 많은 resource 가 필요함
엄청나게 많은 GPU 도 필요하고 엄청나게 많은 데이터도 필요함
그렇게 만들어진 pre-trained 모델을 fine-tuning 함으로써 한가지의 task 에만 적용할 수 있는 모델을 새롭게 만들고 그걸로 배포를 한다? 라는 행위자체가 자원의 낭비가 될 수 있다라는 것임

그래서 제안한 방법이 바로 Zero-shot, One-shot, Few-shot 방법임

먼저 Fine-tuning 을 살펴보자.
기존의 언어모델은 pre-trained 된 모델이있고 그 다음에 데이터셋을 하나씩 넣어가면서 gradient 를 업데이트 함으로써 한 가지 task 에 점점 fine-tuning 하게 됨
그래서 한국어로는 미세조정이라고 얘기함
large corpus 로 학습한 pre-trained 모델을 점점 미세조정을 하면서 나의 task 에 맞춰가는 과정으로 만들어가게 됨
하지만 GPT 의 연구진들이 제안한 방법은 바로 Zero-shot, One-shot, Few-shot Learning 임
이 각각의 learning 들은 gradient 가 존재하지 않음
각각의 learning 의 차이점은 어떤 inference 를 날릴 때 내가 원하는 task 에 대한 source(힌트) 를 하나만 주느냐, 아예 안주느냐, 혹은 여러개 주느냐의 차이에 있음
그래서 Zero-shot learning 같은 경우에는 내가 만약 번역 task 를 원한다 그러면 “Translate English to French:” 하고 그 다음에 cheese 라고 입력하게되면 다음에 나올 가장 적절한 단어를 GPT 모델이 예측하게 됨
이 경우에 기존의 번역 모델은 이렇게 번역하는 과정을 학습하기 위해서 fine-tuning 하는 과정을 거쳤어야 함
하지만 저자의 생각은 이미 엄청나게 큰 corpus 내에서 이런 식으로 “English to French:” 에서 번역하는 과정을 학습했기 때문에 cheese 다음에 나올 수 있기에 가장 적절한 토큰들을 예측하는 것을 보면 아마도 French 로 번역된 단어가 나올 것이라고 예측할 수 있음

그래서 Zero-shot learning 같은 경우에는 힌트없이 task 를 수행하는 것임

One-shot learning 은 한가지 예시를 주게 됨

그리고 Few-shot 은 여러가지 예시를 주고서 task 를 수행하게 됨

그래서 저자들은 이 아이디어를 바탕으로 GPT2 모델을 개발하게 됨

기존의 GPT 모델은 파라미터수가 117M 개로 되어있었는데 GPT2 의 경우에는 기존의 11GB 의 text 데이터를 40GB 로 늘리고 그리고 학습가능한 파라미터의 개수도 10배로 늘려서 GPT2 를 개발하게 됨

그리고 실제로 GPT2 를 개발하고나서 Zero-shot, One-shot, Few-shot Learning 이 성공적으로 이루어지는 것을 증명하게 됨

GPT2 는 GPT1 의 Decoder 에서 Decoder 구조만 어느정도 다르게 구성이 되어있음

실제로 GPT2 를 이용했을 때

다음 단어 예측 방식에서는 SOTA 성능

기계독해, 요약, 번역 등의 자연어 task 에서는 일반 신경망 수준 $\rightarrow$ 하지만! Zero, One, Few-shot learning 의 새 지평을 제시!

그리고 여기서 연구진들은 멈추지 않았음

GPT3 라는 모델을 만들게 됨

GPT3 는 기존의 파라미터수를 100배 정도로 늘리는 엄청나게 큰 모델을 만들게 됨

사이즈도 늘리고 학습데이터도 기존의 40GB 였던 학습데이터를 570GB 에 달하는 text 데이터를 이용해 학습을 하게됨

GPT3 역시 Transformer 의 Decoder layer 를 사용했음

하지만 GPT2 랑 같은 Decoder 를 사용한 것은 아니고 약간 다른 구조와 initialize 를 조금 고친 그런 모델로 되어있으나 전체적인 구조자체는 Transformer 의 Decoder 를 사용한 것이 동일함

그래서 모델사이즈를 키우고 학습데이터를 키운 그런 차이점이 있음

이렇게 만든 GPT3 를 이용해서 뉴스 기사를 생성하도록 시켰음

이 때 사람 평가자가 사람이 쓴 뉴스랑 GPT3 가 쓴 뉴스를 구분하는 실험을 해봤는데

Control 는 실제 사람이 쓴 뉴스기사이고 100건의 뉴스기사에 대해 88번의 뉴스기사가 사람같이 썼다고 평가자가 평가했고 GPT3 같은 경우는 100건의 뉴스기사중에 52% 에 달하는 뉴스기사를 직접 사람이 쓴거다라고 평가를 내리게 됨

그리고 GPT3 를 활용해서 Zero-shot, One-shot, Few-shot learning 의 대한 실험도 해봤음

수학적 계산에 대한 실험을 해봄

그림에서와 같이 GPT3 가 두개의 숫자를 덧셈하는거에 대해서는 100% 가까운 accuracy 가 나왔다고 함

다음으로 실험한 것은 open domain QA test 를 해봄

기계독해와 같은 경우에는 정답과 관련된 context 가 input 으로 함께 주어지게 됨
하지만 open domain QA 은 어떤식으로 동작하냐면 그런 문서조차 주어지지 않고 질문을 하게 됨
예를 들어 질문이 “물의 끓는점은 몇도인가?” 이런게 질문으로 들어가게 되면 답변으로 “100도” 이런 답변이 나와야 함
그래서 이런 task 는 모델이 하기에 굉장히 어려운 task 임
이걸 수행하기 위해서는 모델 자체가 상식적인 것과 어려운 지식들 이런게 모두 모델에 녹아있어야지만 답변이 가능함

GPT3 의 Few-shot learning 을 사용했을 때 다른 모델 대비 SOTA 성능이 나옴

1.2 GPT 의 응용

이렇게만 보면 GPT3 가 모든 과제를 다 해결할 수 있을것처럼 보이는데 GPT3 는 한계점이 없을까?

GPT3 역시 다음단어를 예측하는 방식으로 학습한 언어모델임

이렇게 학습하는 것으로 정말 모든 문제가 다 해결이 될까?

GPT3 의 문제는 Weight update 가 없다는 것이 문제가 될 수 있음

그렇다는 것은 새로운 지식에 대한 학습이 없다는 것임

그래서 GPT3 를 학습할 때 사용한 large corpus 내에서만 GPT3 가 답변할 수 있음

그리고 시기에 따라 달라지는 문제에도 대응할 수 없음
예를 들어 GPT3 한테 “한국의 대통령이 누구야?” 라고 질문하게되면 “이명박 대통령” 이라고 답변이 나오는 것을 확인할 수 있었음
하지만 대통령이라는 존재는 시기에따라서 달라짐
그 말은 GPT3 같은 언어모델도 새로운 데이터에 대한 updqte 가 반드시 필요하다는 것임

또 한편으로는 모델 사이즈와 학습데이터만 계속해서 키우면 모든 문제가 해결될까?

정답은 알 수 없지만 그렇게 생각하지는 않음

뭔가 다른 방향의 연구도 필요하다고 생각함

또한 GPT3 는 글로만 세상을 배우게 됨

하지만 우리가 세상을 인지할 때는 우리의 감각기관을 모두 사용해서 인지를 하게됨

그래서 반드시 언어모델은 단순히 witten language(text 로 된 데이터)만 학습하는 것이 아니라 멀티 모달 에 대한 정보도 학습이 필요할 것이라고 생각함


실습

GPT-2 학습해보기

한국어 코퍼스를 활용해 한국어 GPT-2 모델을 만들 예정

학습데이터는 wiki 데이터를 이용해서 실습하도록 하겠음

path = "/content/my_data/wiki_20190620_small.txt"

기존에는 BERT 를 학습할 땐 BertWordPieceTokenizer 를 사용했음
이번에는 SentencePieceBPETokenizer 를 사용하도록 하겠음

내부적인 알고리즘은 BPE 로 동일하고 tokenizer 의 경우에는 띄어쓰기 부분에 ‘_’ 기호를 추가하는 과정만 다르다고 생각하면 됨

from tokenizers import SentencePieceBPETokenizer
from tokenizers.normalizers import BertNormalizer

tokenizer = SentencePieceBPETokenizer()

tokenizer._tokenizer.normalizer = BertNormalizer(clean_text=True,
handle_chinese_chars=False,
lowercase=False)

tokenizer.train(
    path,
    vocab_size=10000,
    special_tokens=[
        "<s>",
        "<pad>",
        "</s>",
        "<unk>",
    ],
)

마찬가지로 tokenizer 학습을 해줌
학습데이터와 vocab_size 를 지정해줄 수 있음

BERT 와 다른점이 하나 있는데 BERT 는 학습할 때 앞에 [CLS] 토큰과 뒤에 [SEP] 토큰을 부착하게 되어있었음

그렇기 때문에 special token 에 [PAD], [CLS] [SEP], [UNK] 들이 의무적으로 들어갔었음

하지만 GPT2 가 학습하는 과정을 상상해보면 앞에 토큰이 나왔을 때 계속해서 다음 토큰을 예측하는 방식으로 이루어지고 있음

그래서 [CLS] 토큰이나 [SEP] 토큰이 필요하지 않음

하지만 이 경우에 fasttext 도 상상해보면 음절의 시작과 긑을 표시해주는 “<”, “>” 기호 넣은것이 생각나나요?

역시 GPT2 도 문장의 끝과 시작을 표시해주는 special token 을 부착해줄 수 있음

그래서 나중에 생성할 때 문장단위로 생성하도록 control 할 수 있음

special token 에 문장의 시작을 의미하는 <s> 와 문장의 끝을 의미하는 </s> 를 추가해줄 수 있음

tokenizer 를 학습했으니 test 를 해보자.

print(tokenizer.encode("이순신은 조선 중기의 무신이다."))
print(tokenizer.encode("이순신은 조선 중기의 무신이다.").ids)
print(tokenizer.encode("이순신은 조선 중기의 무신이다.").tokens)
print(tokenizer.decode(tokenizer.encode("<s>이순신은 조선 중기의 무신이다.</s>").ids, skip_special_tokens=True))
# SentencePiece를 사용하면, 나중에 decoding 과정에서 '_' 만 ' '로 replace해주면 띄어쓰기 복원이 가능해집니다.

문장에서 띄어쓰기가 들어가는 부분 즉, 어절의 시작부분에는 ‘_’ 기호가 부착이됨

이 문장을 복원하라고 하면 올바르게 복원이 됨

tokniezr 를 저장할 땐 아래 명령어를 사용함

tokenizer.save_model(".")

나만의 tokenizer 가 만들어졌음

tokenizer = SentencePieceBPETokenizer.from_file(vocab_filename="vocab.json", merges_filename="merges.txt")

from_file() 함수를 사용하면 내가 학습한 모델을 그대로 불러와서 tokenizer 로서 사용할 수 있음

print(tokenizer.encode("이순신은 조선 중기의 무신이다."))
print(tokenizer.encode("이순신은 조선 중기의 무신이다.").ids)
print(tokenizer.encode("이순신은 조선 중기의 무신이다.").tokens)
print(tokenizer.encode("<s>이순신은 조선 중기의 무신이다.</s>").tokens)
print(tokenizer.decode(tokenizer.encode("<s>이순신은 조선 중기의 무신이다.</s>").ids, skip_special_tokens=True))

불러와서 test 해보면 동일한 결과를 확인할 수 있음

우리가 원하는 것은 문장의 시작과 끝에 <s>, </s> 를 부착함으로써 문장을 구분해내는 것을 원하고 있음

그런데 이렇게 다 분리되서 나옴

이 이유는 special token 으로 지정이 되어있지 않음

의도적으로 tokenizer 에 add_special_tokens 를 통해 명시적으로 알려줘야 함

tokenizer.add_special_tokens(["<s>", "</s>", "<unk>", "<pad>", "<shkim>"])
tokenizer.pad_token_id = tokenizer.token_to_id("<pad>")
tokenizer.unk_token_id = tokenizer.token_to_id("<unk>")
tokenizer.bos_token_id = tokenizer.token_to_id("<bos>")
tokenizer.eos_token_id = tokenizer.token_to_id("<eos>")

print(tokenizer.encode("<s>이순신은 조선 중기의 무신이다.</s>").ids)
print(tokenizer.encode("<s>이순신은 조선 중기의 무신이다.</s>").tokens)
print(tokenizer.decode(tokenizer.encode("<s>이순신은 조선 중기의 무신이다.</s>").ids, skip_special_tokens=True))

이렇게 등록하고 나면 문장내에 스페셜 토큰을 부착하게 되더라도 올바르게 tokenizing 된 것은 확인할 수 있음

이제 본격적으로 GPT2 를 학습해야 함

from transformers import GPT2Config, GPT2LMHeadModel
# creating the configurations from which the model can be made
config = GPT2Config(
  vocab_size=tokenizer.get_vocab_size(),
  bos_token_id=tokenizer.token_to_id("<s>"),
  eos_token_id=tokenizer.token_to_id("</s>"),
)
# creating the model
model = GPT2LMHeadModel(config)

transformers 에서 BERT 와 마찬가지로 GPT2 에 대해서도 학습을 제공해줌

GPT2 에 대한 껍데기 모델을 만들어야 함

항상 vocab_size 를 우리가 만든 tokenizer 의 vocab_size 로 명확하게 지정해줘야 함

bos_token(문장의 시작을 알려주는 토큰) 과 eos_token(문장의 끝을 알려주는 토큰)을 어떤 것으로 정의했는지 명확하게 저장할 수 있음

다양한 옵션들이 많아서 layer size 를 줄일 수도 있고 내부의 embedding size 도 줄일 수 있고 다양한 설정을 할 수 있음

model.num_parameters()

파라미터 개수도 알 수 있음

GPT2 에 넣어줄 데이터셋도 만들어야 함

import json
import os
import pickle
import random
import time
import warnings
from typing import Dict, List, Optional

import torch
from torch.utils.data.dataset import Dataset

from filelock import FileLock

from transformers.tokenization_utils import PreTrainedTokenizer
from transformers.utils import logging

logger = logging.get_logger(__name__)

class TextDataset(Dataset):
    """
    This will be superseded by a framework-agnostic approach soon.
    """

    def __init__(
        self,
        tokenizer: PreTrainedTokenizer,
        file_path: str,
        block_size: int,
        overwrite_cache=False,
        cache_dir: Optional[str] = None,
    ):
        assert os.path.isfile(file_path), f"Input file path {file_path} not found"

        block_size = block_size - tokenizer.num_special_tokens_to_add(is_pair=False)

        directory, filename = os.path.split(file_path)
        cached_features_file = os.path.join(
            cache_dir if cache_dir is not None else directory,
            "cached_lm_{}_{}_{}".format(
                tokenizer.__class__.__name__,
                str(block_size),
                filename,
            ),
        )

        # Make sure only the first process in distributed training processes the dataset,
        # and the others will use the cache.
        lock_path = cached_features_file + ".lock"
        with FileLock(lock_path):

            if os.path.exists(cached_features_file) and not overwrite_cache:
                start = time.time()
                with open(cached_features_file, "rb") as handle:
                    self.examples = pickle.load(handle)
                logger.info(
                    f"Loading features from cached file {cached_features_file} [took %.3f s]", time.time() - start
                )

            else:
                logger.info(f"Creating features from dataset file at {directory}")
                # 여기서부터 본격적으로 데이터셋을 만들기 시작합니다.
                self.examples = []
                text = ""
                with open(file_path, encoding="utf-8") as f:
                    lines = f.readlines()
                    for line in lines:
                        line = line.strip()
                        line = "<s>"+line+"</s>" # 학습 데이터 앞 뒤에 문장 구분 기호를 추가해줍니다.
                        text += line    # 'text' 객체에 모든 학습 데이터를 다 합쳐버립니다 :-)
                tokenized_text = tokenizer.encode(text).ids

                # 모델의 최대 sequence length만큼 데이터를 잘라서 저장합니다.
                for i in range(0, len(tokenized_text) - block_size + 1, block_size):  # Truncate in block of block_size
                    self.examples.append(
                        tokenized_text[i : i + block_size]
                    )
                # Note that we are losing the last truncated example here for the sake of simplicity (no padding)
                # If your dataset is small, first you should look for a bigger one :-) and second you
                # can change this behavior by adding (model specific) padding.

                start = time.time()
                with open(cached_features_file, "wb") as handle:
                    pickle.dump(self.examples, handle, protocol=pickle.HIGHEST_PROTOCOL)
                logger.info(
                    "Saving features into cached file %s [took %.3f s]", cached_features_file, time.time() - start
                )

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

    def __getitem__(self, i) -> torch.Tensor:
        return torch.tensor(self.examples[i], dtype=torch.long)

GPT2 모델은 BERT 학습보다 훨씬 간단함

mask 의 대한 개념도 없었고 Next Sentence Prediction 도 필요하지 않음

그냥 우리가 학습해야하는 sentence 를 통채로 주면 GPT 모델이 이 토큰 다음에 나올 수 있는 토큰은 이거다 다음은 이거다 라는 것을 순차적으로 자동으로 학습하게됨

그래서 sentence 만 만들어줘서 GPT 모델에 넘겨주면 됨

캐싱 관련된 부분을 지나서 file_path(wiki small 데이터) 를 한줄씩 읽어서 strip() 해주고 의도적으로 line 앞뒤에 <s>, </s> 를 넣어주게 됨 그리고 text 라는 하나의 변수에 모든 학습데이터를 통채로 넣게 됨

text 안에 기존의 wiki 데이터와 동일하지만 모든 문장마다 <s>, </s> 이 부착된 형태로 text 가 만들어짐

그 만들어진 것을 tokenizing 하면 tokenizer 에 따라서 vocab_id 로 변환된 데이터가 tokenized_text 로 입력으로 들어가게 됨

그러면 내가 가진 모델의 sequence length 가 있을 건데 BERT 는 512 토큰이 있었고 GPT2 도 그런 block_size 를 가질텐데 tokenized_text 데이터는 전체 wiki 의 데이터를 다가지고 있으니 이거를 block_size 만큼으로 쪼개게 됨

toknized_text[i : i + bolck_size] 로 쪼개서 exmaples 에 넣게 됨

다 넣게되면 우리가 원하는 학습데이터가 완성됨

dataset = TextDataset(
    tokenizer=tokenizer,
    file_path=path,
    block_size=128,
)
from transformers import DataCollatorForLanguageModeling

data_collator = DataCollatorForLanguageModeling(    # GPT는 생성모델이기 때문에 [MASK] 가 필요 없습니다 :-)
    tokenizer=tokenizer, mlm=False,
)

TextDataset 에 tokenizer, file_path, block_size 넣고 만들어 줌

실습을 위해 block_size 를 128 로 줬지만 원래 사이즈는 1024 로 알고 있음

이렇게 데이터셋인 밥이 만들어지면 떠먹여주는 데이터로더가 필요함

transformers 에서 DataCollatorForLanguageModeling 이라는 함수를 제공해주고 있음

이전에는 data_collator 를 mask 를 지원해주는 기능을 넣었었는데 이번에는 mask 가 필요없음

그래서 LanguageModeling 을 위한 함수를 불러왔음

이 함수를 call 할 때 mlm 은 mask 를 의미하는 건데 GPT2 는 mask 가 필요없으므로 False 로 두고 데이터셋을 만들면

print(dataset[0])

학습을 위한 데이터셋이 만들어지게 됨

동일하게 TrainArgument 에 우리가 원하는 argument 를 채워주게 됨

from transformers import Trainer, TrainingArguments

training_args = TrainingArguments(
    output_dir='model_output',
    overwrite_output_dir=True,
    num_train_epochs=50,
    per_device_train_batch_size=64, # 512:32  # 128:64
    save_steps=1000,
    save_total_limit=2,
    logging_steps=100

)

trainer = Trainer(
    model=model,
    args=training_args,
    data_collator=data_collator,
    train_dataset=dataset
)
trainer.train()

BERT 와 마찬가지로 굉장히 쉽게 GPT2 도 학습할 수 있음

trainer.save_model()

모델 저장도 같은 방법으로 할 수 있음

학습을 했으니 test 를 해야죠?

USE_GPU = 1
# Device configuration
device = torch.device('cuda' if (torch.cuda.is_available() and USE_GPU) else 'cpu')
import torch
torch.manual_seed(42)

input_ids = torch.tensor(tokenizer.encode("<s>이순신", add_special_tokens=True).ids).unsqueeze(0).to('cuda')

output_sequences = model.generate(input_ids=input_ids, do_sample=True, max_length=100, num_return_sequences=3)
for generated_sequence in output_sequences:
    generated_sequence = generated_sequence.tolist()
    print("GENERATED SEQUENCE : {0}".format(tokenizer.decode(generated_sequence, skip_special_tokens=True)))

테스트를 할 때는 GPT2 모델 같은 경우에 generate 라는 특별한 함수를 제공해주고 있음

다음 실습에서는 generate 함수에 대한 분석을 보여주겠음

간단히 설명하자면 입력값이 들어가고 GPT2 가 단어를 생성하기에 앞서서 앞에 무슨 단어를 시작으로 할 것이냐를 input_ids 로 넣게 됨

input_ids 이기 때문에 당연히 tokenize 된 vocab_id 가 들어가게 됨

뒷부분에 대해서는 다음 포스팅에서 설명하겠음

do_sample=True 면 sample 을 몇개 반환하라는 의미

작성자

* 김성현 (bananaband657@gmail.com)  
1기 멘토
김바다 (qkek983@gmail.com)
박상희 (parksanghee0103@gmail.com)  
이정우 (jungwoo.l2.rs@gmail.com)
2기 멘토
박상희 (parksanghee0103@gmail.com)  
이정우 (jungwoo.l2.rs@gmail.com)
이녕우 (leenw2@gmail.com)
박채훈 (qkrcogns2222@gmail.com)

CC BY-NC-ND

댓글남기기