AIFFEL/Going Deeper(NLP)

[Going Deeper(NLP)] 15. NLP Framework의 활용

알밤바 2022. 4. 21. 18:46
728x90
반응형

해당 포스팅은 AIFFEL에서 제공한 학습자료를 통해 공부한 것을 정리한 것임을 밝힙니다.

목차

  1. 다양한 NLP Framework의 출현
  2. Huggingface transformers 개요
  3. Huggingface transformers (1) Model
  4. Huggingface transformers (2) Tokenizer
  5. Huggingface transformers (3) Processor
  6. Huggingface transformers (4) Config
  7. Huggingface transformers (5) Trainer

1. NLP 기술의 발전과 framework

소프트웨어에서 framework란 프로젝트의 뼈대를 이루는 클래스와 인터페이스의 집합을 말한다. 해당 분야의 베스트 프랙티스를 반영하여 확장 가능한 템플릿 형태로 설계되었기 때문에, framework를 이용해 손쉽게 다양한 응용 프로그램을 제작할 수 있다.

최근 NLP 분야에선 transformer 기반의 BERT 등 다양한 pretrained model이 발표되고, 이를 활용한 전이학습(transfer learning)을 통해 다양한 NLP 태스크를 손쉽게 구현하는 흐름이 두드러지고 있다. 이런 NLP 분야의 베스트 프랙티스를 바탕으로 다양한 NLP 분야의 framework가 발표되고 있다.

이러한 NLP framework들은 NLP 분야의 최신 논문들의 리서치 코드를 미리 구현하여 pretrained model을 제공하고 있다. 그래서 framework의 사용자가 아주 손쉽게 이를 가져다가 다양한 태스크에 맞게 finetuning하거나 사용할 수 있게 해준다.

대부분 NLP framework들은 태스크나 데이터셋, 모델에 무관하게 통일적인 인터페이스를 기반으로 설계된 클래스 구조를 가지고 있어서 최소한의 코드 구현만으로도 다양한 변화에 대응할 수 있게 해주는 장점이 있다.


2. 다양한 NLP Framework의 출현

Huggingface transformers Framework를 통해 NLP framework가 어떤 것인지를 구체적으로 살펴보자. 그러나 Huggingface transformers 이전에도 NLP 분야에 널리 알려진 framework들이 많이 있었다. 몇 가지 알아보자.

[참고] Top NLP Libraries to Use 2020

 

2.1 General Framework for NLP

이 framework들은 NLP 문제를 가장 일반적으로 해결할 수 있는 통합적인 프레임워크를 목표로 설계된 것들이다. 대표적으로 AllenNLP, Fairseq, Fast.ai가 있으며, Google의 tensor2tensor 프로젝트도 같은 범주로 생각할 수 있다.

 

📌 AllenNLP

Allen Institute에서 만든 NLP framework이다. ELMO는  GLUE Benchmark Test와 같이 10가지나 되는 다양한 태스크로 구성된 데이터셋을 하나의 모델을 finetune하는 것만으로도 기존의 State-of-the-art 기록을 경신하는 성능을 보여주고자 하였다. 그래서 하나의 모델로 다양한 태스크를 손쉽게 처리할 수 있는 유연한 프로젝트를 구성해야 했고 이를 확장하면서 자연스럽게 NLP framework로 발전하게 되었다. 이후 AllenNLP는 Glue dataset의 baseline 프로젝트  Starting Baseline를 제공하기로 했다.

[ 출처 : AllenNLP Guide(https://guide.allennlp.org/building-your-model#1) ]

태스크와 모델을 분리해서 한 가지 모델로 다양한 태스크를 처리하거나 하나의 태스크를 다양한 모델로 처리할 수 있도록 하는 설계는 AllenNLP가 처음 시도한 것은 아니지만, ELMO와 같은 pretrained model의 성공을 바탕으로 NLP framewokr를 완성해 나가려는 AllenNLP의 시도는 이후 많은 아이디어를 제공하였다. AllenNLP는 현재 ELMO 이외에도 BERT 등 다양한 모델의 활용이 가능하다.

단, AllenNLP는 PyTorch 기반으로 설계되었으며 모델이 torch.nn.Module을 상속받는 구조로 설계되었다. Tensorflow나 Keras 기반으로 AllenNLP를 활용하는 것은 어렵다.

 

📌 Fairseq

Fairseq는 Facebook AI Research의 NLP Framework이다. CNN, LSTM 등 전통적인 모델로부터 음성인식/합성 등 sequential한 데이터를 다루는 분야를 두루 다루는 다양한 pretrained model을 함께 제공한다. Pytorch기반으로 설계되었다.

 

📌 Fast.ai

빠르게 배우고 쉽게 모델을 구성할 수 있도록 하이레벨 API와 Application 블록까지 손쉽게 사용할 수 있도록 구성되어 있다. NLP 분야 뿐 아니라 다양한 분야로 확장 가능하다. Pytorch 기반으로 설계되었습니다.

[ 출처 : fast.ai (https://github.com/fastai/fastai) ]

 

📌 tensor2tensor

Google Brain에서 2017년에 transformer 논문을 발표하면서 그 구현체로 함께 공유했던 프로젝트가 바로 tensor2tensor였다. transformer를 중심으로 다양한 태스크와 다양한 모델을 하나의 framework에 통합하려는 시도를 하였다. 이후 2019년부터 Google은 Tensorflow V2 기반으로 pretrained model의 지원을 강화한 trax라는 프로젝트를 생성하면서 20년도부터는 tensor2tensor의 개발을 중단하고 관련 기능을 trax로 통합이관하였다.

 

2.2 Preprocessing Libraries

아래는 전통적으로 사용되었던 NLP 분야의 전처리 관련 framework들이다.

위에서 소개한 framework들처럼 전처리-모델링-태스크 훈련/평가를 통합적으로 설계하여 NLP 태스크를 제너럴하게 수행하게 설계한 것이 아니라 tokenization, tagging, parsing 등 특정 전처리 작업을 위해 설계된 라이브러리에 가깝다.

 

📌 Spacy

 

📌 NLTK

 

📌 TorchText

 

📌 KoNLPy

 

2.3 Transformer-based Framework

📌 Huggingface transformers

Huggingface transformers가 현재 가장 주목을 받고 있는 NLP Framework이다.

사실 Huggingface transformers 라이브러리의 최근 모습은 이미 아주 general한 NLP framework의 모습을 충분히 가지고 있다. 하지만 초기에는 BERT 등 다양한 transformer 기반의 pretrained model을 사용하기 위한 PyTorch 기반의 wrapper 형태로 시작되었다. 그래서 전통적인 모델까지 포괄하려고 했던 이전의 general NLP Framework들에 비해, Huggingface의 transformers는 pretrained model 활용을 주로 지원하며, tokenizer 등 전처리 부분도 pretrained model들이 주로 사용하는 Subword tokenizer 기법에 집중되어 있는 특징이 있다.


3. Huggingface transformers 개요

3.1 Why Huggingface?

(1) 광범위하고 신속한 NLP 모델 지원

Huggingface는 많은 사람들이 최신 NLP 모델들을 더욱 손쉽게 사용하는 것을 목표로 만들기 시작했다고 한다. 그래서 새로운 논문들이 발표될 때마다, 본인들의 framework에 흡수시키고 있다. 또한 pretrained model을 제공하고 dataset과 tokenizer를 더욱 쉽게 이용할 수 있도록 framework화시키고 있는 행보도 보이고 있다. 다른 framework들도 이런 작업을 하지 않는 것은 아니지만, Huggingface의 지원 범위가 가장 광범위하고 최신 논문을 지원하는 속도도 빠르다.

 

(2) PyTorch와 Tensorflow 모두에서 사용 가능

transformers는 기본적으로 PyTorch를 기반으로 만들어져 있다. 많은 utility가 PyTorch 위주로 작성되어있긴 하지만, 최근에는 Tensorflow로도 학습하고 사용할 수 있게끔 계속해서 framework를 확장하고 있는 중이다. 

이렇듯 Huggingface transformers를 바탕으로 Tensorflow와 PyTorch라는 Backend의 한계를 뛰어넘어 어떤 환경에든 쉽게 적용 가능한 표준 framework의 지위를 다져가고 있다.

 

(3) 잘 설계된 framework 구조

HuggingFace의 목표처럼 이 framework는 쉽고 빠르게 어떠한 환경에서도 NLP 모델을 사용할 수 있도록 끊임없이 변화하고 있다. 이것은 또한 사용하기 쉽고 직관적일 뿐더러 모델이나 태스크, 데이터셋이 달라지더라도 동일한 형태로 사용 가능하도록 잘 추상화되고 모듈화된 API 설계가 있기 때문에 가능한 것이다.

 

3.2 시작하기

huggingface의 Transformers 설치해보자.

$ pip install transformers
from transformers import pipeline

classifier = pipeline('sentiment-analysis', framework='tf')
classifier('We are very happy to include pipeline into the transformers repository.')

 

3.3 Huggingface transformers 설계구조 개요

NLP framework가 NLP 모델을 통해 문제를 푸는 과정이 어떻게 진행될 지 생각해보자.

1) Task를 정의하고 그에 맞게 dataset을 가공시킨다.

2) 적당한 model을 선택하고 이를 만든다.

3) model에 데이터를 태워서 학습을 시키고, 이를 통해 나온 weight와 설정(config)들을 저장한다.

4) 저장한 model의 checkpoint는 배포하거나, evaluation을 할 때 사용한다.

 

transformers는 위와 같은 흐름에 맞추어 설계되어 있다.

task를 정의하고 dataset을 알맞게 가공하는 Processors, 텍스트 데이터를 전처리 할 수 있는 Tokenizer, 다양한 model을 정의한 Model, optimizer와 학습 schedule(warm up 등)을 관리할 수 있는 Optimization, 학습 과정을 전반을 관리하는 Trainer, weight와 tokenizer, model을 쉽게 불러올 수 있도록 각종 설정을 저장하는 Config 등으로 이루어져 있다.


4. Huggingface transformers

4.1 Model

transformers의 가장 핵심적인 부분은 모델이다.

 

기본적으로 모델들은 PretrainedModel 클래스를 상속받고 있다. PretrainedModel 클래스는 학습된 모델을 불러오고 다운로드하고 저장하는 등 모델 전반에 걸쳐 적용되는 메소드들을 가지고 있다. 이런 상속 구조를 가지고 있기 때문에 실제로 사용할 모델이 BERT이건, GPT이건 상관없이 모델을 불러오고 다운로드/저장하는 등의 작업에 활용하는 메소드는 부모 클래스의 것을 동일하게 활용할 수 있게 한다.

 

모델은 두 가지 방식으로 불러올 수 있다.

 

첫 번째로는 task에 적합한 모델을 직접 선택하여 import하고 불러오는 방식이 있다. 모델을 로드할 때 from_pretrained라는 메소드를 사용하며, Huggingface의 pretrained 모델을 불러올 수도, 직접 학습시킨 모델을 불러올 수도 있다.

Huggingface에서 제공하는 pretrained 모델이라면 모델의 이름을 string으로, 직접 학습시킨 모델이라면 config와 모델을 저장한 경로를 string으로 넘겨주면 된다.

from transformers import TFBertForPreTraining
model = TFBertForPreTraining.from_pretrained('bert-base-cased')

print(model.__class__)

Downloading:   0%|          | 0.00/570 [00:00<?, ?B/s]
Downloading:   0%|          | 0.00/502M [00:00<?, ?B/s]
All model checkpoint layers were used when initializing TFBertForPreTraining.

All the layers of TFBertForPreTraining were initialized from the model checkpoint at bert-base-cased.
If your task is similar to the task the model of the checkpoint was trained on, you can already use TFBertForPreTraining for predictions without further training.
<class 'transformers.models.bert.modeling_tf_bert.TFBertForPreTraining'>

 

두 번째 방법은 AutoModel을 이용하는 것이다.

모델에 관한 정보를 처음부터 명시하지 않아도 되어 조금 유용하게 사용할 수 있다.

from transformers import TFAutoModel
model = TFAutoModel.from_pretrained("bert-base-cased")

print(model.__class__)

Some layers from the model checkpoint at bert-base-cased were not used when initializing TFBertModel: ['nsp___cls', 'mlm___cls']
- This IS expected if you are initializing TFBertModel from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).
- This IS NOT expected if you are initializing TFBertModel from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).
All the layers of TFBertModel were initialized from the model checkpoint at bert-base-cased.
If your task is similar to the task the model of the checkpoint was trained on, you can already use TFBertModel for predictions without further training.
<class 'transformers.models.bert.modeling_tf_bert.TFBertModel'>

 

bert-base-cased라고 언급된 부분은 Model ID이다. Huggingface가 지원하는 다양한 pretrained model이 있다. 이들 중 어느 것을 선택할지를 결정하기 위해 이 ID를 활용하게 된다.

불러오고자 하는 모델의 ID는 bert-base-cased로서 동일하다. 사용법도 거의 동일하지만 결과적으로 model.__class__를 확인해보면 약간의 차이가 있음을 알 수 있다.

둘 다 동일한 모델 파라미터를 사용하지만 Pretrain, Downstream Task 등 용도에 따라 모델의 Input이나 Output shape가 다를 수 있다. AutoModel을 활용한다면 모델의 상세정보를 확인할 필요없이 Model ID만으로도 손쉽게 모델 구성이 가능하지만, 정확한 용도에 맞게 사용하려면 모델 별 상세 안내 페이지를 참고해서 최적의 모델을 선택하는 것이 좋다.

모델마다 구조는 다르지만 해당 모델 이름을 가진 클래스 (ex.TFBertModel)과 MainLayer class(ex. TFBertMainLayer)와 Attention Class, Embedding Class 등으로 이루어져 있다.

 

4.2 Tokeinzer

내가 가지고 있는 문제를 풀 모델을 정했다면, 이제 모델에 넣을 input을 만들어 줄 차례이다.

 

transformers는 다양한 tokenizer를 각 모델에 맞추어 이미 구비해두었기에 우리가 할 일은 tokenizer를 불러와 사용하는 것 뿐이다. 사용하기 전에 내가 선택한 moel이 어떤 tokenizer를 사용하는지 정도 미리 체크해두자.

 

Pretrained model 기반의 NLP framework를 사용할 때 가장 중요한 두 가지 클래스는 Model과 Tokenizer라고 할 수 있다. 파라미터 구조가 동일한 Model이라 하더라도 Tokenizer가 다르거나 Tokenizer 내의 Dictionary가 달라지면 사실상 완전히 다른 모델이 된다. 그리고 Tokenizer는 어떤 언어를 다루느냐 하는 코퍼스 데이터셋에 따라서도 달라진다.

 

이전 스텝에서 소개했던 Huggingface가 제공하는 모델 종류 중 몇 개만 보자.

  • bert-base-uncased : BERT 모델인데, 108MB 파라미터의 기본 모델이면서, 코퍼스는 영문 대소문자 구분을 없앴다(전체 소문자화)
  • bert-large-cased : BERT 모델인데, 340MB 파라미터의 대형 모델이면서, 코퍼스는 영문 대소문자 구분을 유지했다.
  • bert-base-multilingual-cased : BERT 모델인데, 108MB 파라미터의 기본 모델이면서, 코퍼스는 다국어 대소문자 구분 유지했다.

tokenizer 또한 직접 명시하여 내가 사용할 것을 지정해주거나, Auto Tokenizer를 사용하여 이미 구비된 model에 알맞은 tokenizer를 자동으로 불러올 수도 있다. 이 때 유의할 점은, model을 사용할 때 명시했던 것과 동일한 ID로 tokenizer를 생성해야 한다는 점이다.

 

from transformers import BertTokenizer
tokenizer = BertTokenizer.from_pretrained('bert-base-cased')

 

from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained('bert-base-cased')

 

불러온 tokenizer를 한 번 사용해보자.

encoded = tokenizer("This is Test for aiffel")
print(encoded)

{'input_ids': [101, 1188, 1110, 5960, 1111, 170, 11093, 1883, 102], 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]}


이 경우 BERT의 tokenizer이기 때문에 인코딩이 된 input_ids 뿐만 아니라 token_type_idsattention_mask까지 모두 생성된 input 객체를 받아볼 수 있다.

 

tokenizer는 batch 단위로 input을 받을 수도 있다.

batch_sentences = ["Hello I'm a single sentence",
                    "And another sentence",
                    "And the very very last one"]

encoded_batch = tokenizer(batch_sentences)
print(encoded_batch)

{'input_ids': [[101, 8667, 146, 112, 182, 170, 1423, 5650, 102], [101, 1262, 1330, 5650, 102], [101, 1262, 1103, 1304, 1304, 1314, 1141, 102]], 'token_type_ids': [[0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0]], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1]]}

 

이 밖에도 tokeinze할 때에 padding, truncation 등 다양한 옵션을 설정할 수 있으며, 모델이 어떤 프레임워크를 사용하는가(Tensorflow or PyTorch)에 따라 input 타입을 변경시켜주는 return_tensors 인자도 있다.

 

batch = tokenizer(batch_sentences, padding=True, truncation=True, return_tensors="tf")
print(batch)

{'input_ids': <tf.Tensor: shape=(3, 9), dtype=int32, numpy=
array([[ 101, 8667,  146,  112,  182,  170, 1423, 5650,  102],
       [ 101, 1262, 1330, 5650,  102,    0,    0,    0,    0],
       [ 101, 1262, 1103, 1304, 1304, 1314, 1141,  102,    0]],
      dtype=int32)>, 'token_type_ids': <tf.Tensor: shape=(3, 9), dtype=int32, numpy=
array([[0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=int32)>, 'attention_mask': <tf.Tensor: shape=(3, 9), dtype=int32, numpy=
array([[1, 1, 1, 1, 1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1, 0, 0, 0, 0],
       [1, 1, 1, 1, 1, 1, 1, 1, 0]], dtype=int32)>}

 

4.3 Processor

Model, Tokenizer까지 있어도 어떤 Task든 Model에 넣을 수 있는 적절한 입력 형태로 변경해 줄 수 없다.

예를 들어, BERT의 pretraining만 하더라도, 모델에 들어갈 입력 부분을 구성하려면 아래와 같은 과정을 거쳐야 한다.

 

1) 두 개의 문장을 골라서 Next Sentence Prediction을 위해 적절히 배치

2) 15%의 마스킹 포지션을 골라내기 위한 복잡한 과정을 거침 (마킹 대상도 10%, 10%의 확률로 마스킹 대신 다른 예외처리)

3) Next Sentence Prediction을 위해 Segment Embedding을 위한 tensor를 따로 마련해야 함

 

이렇게 복잡한 과정을 위해서는 Tokenizer만으로 부족하다. 그래서 Huggingface에서는 Processor라는 추상 클래스를 하나 추가하였다.

 

아래는 Processor 중 Sequence Classification 태스크를 위한 추상 클래스인 DataProcessor의 코드 예제이다.

class DataProcessor:
    """sequence classification을 위해 data를 처리하는 기본 processor"""

    def get_example_from_tensor_dict(self, tensor_dict):
        """
        tensor dict에서 example을 가져오는 메소드
        """
        raise NotImplementedError()

    def get_train_examples(self, data_dir):
        """train data에서 InputExample 클래스를 가지고 있는 것들을 모으는 메소드"""
        raise NotImplementedError()

    def get_dev_examples(self, data_dir):
        """dev data(validation data)에서 InputExample 클래스를 가지고 있는 것들을 모으는 메소드"""
        raise NotImplementedError()

    def get_test_examples(self, data_dir):
        """test data에서 InputExample 클래스를 가지고 있는 것들을 모으는 메소드"""
        raise NotImplementedError()

    def get_labels(self):
        """data set에 사용되는 라벨들을 리턴하는 메소드"""
        raise NotImplementedError()

    def tfds_map(self, example):
        """
        tfds(tensorflow-datasets)에서 불러온 데이터를 DataProcessor에 알맞게 가공해주는 메소드
        """
        if len(self.get_labels()) > 1:
            example.label = self.get_labels()[int(example.label)]
        return example

    @classmethod
    def _read_tsv(cls, input_file, quotechar=None):
        """tab으로 구분된 .tsv파일을 읽어들이는 클래스 메소드"""
        with open(input_file, "r", encoding="utf-8-sig") as f:
            return list(csv.reader(f, delimiter="\t", quotechar=quotechar))

 

processor는 raw data를 가공하여 model에 태울 수 있는 형태를 만들어주는 작업을 해주는 클래스이다.

 

Hugging face는 SQuAD, GLUE 등 가장 대표적인 NLP의 문제들에 쉽게 접근할 수 있도록 processor를 만들어 두었다. 만약 내가 직접 수집한 데이터를 가공하고 싶다면, 내 데이터에 알맞은 processor를 직접 정의해야 한다.

 

Task 별 복잡한 데이터 전처리 작업을 수행하는 processor를 직접 만들 때는 DataProcessor를 상속받아서 만들어주면 된다. 내가 가지고 있는 데이터에 따라서 추가해야 하는 부분이 생길 수도 있다.

 

❗ 주의
raise NotImplementedError()는 지워야한다. 추상 클래스에서 꼭 구현해야 할 부분이 NotImplemented로 남아있다면 우리가 짠 Processor는 Tokenizer와 협력하여 정상 동작하지 않을 것이다.

 

4.4 Trainer

trainer는 모델을 학습시키기 위한 클래스이다. training, fine-tuning, evaluation 모두 trainer class를 이용하여 할 수 있다.

 

tensorflow의 경우 tf.keras.model API를 이용하여서도 Huggingface를 통해 불러온 모델을 활용해 학습이나 테스트를 진행할 수 있다. 그동안 많이 활용해 보았던 moel.fit()이나 model.predict()를 활용하는 것이 가능하다.

다만, TFTrainer를 이용할 경우에는 TrainingArguments를 통해 Huggingface 프레임워크에서 제공하는 기능들을 통합적으로 커스터마이징하여 모델을 학습시킬 수 있다는 장점이 있다.

 

아래 코드는 Huggingface를 통해 불어온 모델은 tf.keras.model API를 이용해 활용하는 경우의 예시이다.

 

import tensorflow as tf
from transformers import TFAutoModelForPreTraining, AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained('bert-base-cased')
model = TFAutoModelForPreTraining.from_pretrained('bert-base-cased')

sentence = "Hello, This is test for bert TFmodel."

input_ids = tf.constant(tokenizer.encode(sentence, add_special_tokens=True))[None, :]  # Batch size 1

optimizer = tf.keras.optimizers.Adam(learning_rate=3e-5)
loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)

model.compile(optimizer=optimizer, loss=loss)
pred = model.predict(input_ids)

print("=====Results=====")
print(pred)

All model checkpoint layers were used when initializing TFBertForPreTraining.

All the layers of TFBertForPreTraining were initialized from the model checkpoint at bert-base-cased.
If your task is similar to the task the model of the checkpoint was trained on, you can already use TFBertForPreTraining for predictions without further training.
=====Results=====
TFBertForPreTrainingOutput(loss=None, prediction_logits=array([[[ -7.402721 ,  -7.362659 ,  -7.4500127, ...,  -6.1955233,
          -5.894807 ,  -6.3672695],
        [ -7.828724 ,  -8.0582285,  -7.864206 , ...,  -6.419408 ,
          -6.3024364,  -6.7624674],
        [-11.549931 , -11.551905 , -11.484696 , ...,  -8.114805 ,
          -8.314197 ,  -9.4444475],
        ...,
        [ -3.2660656,  -3.7416406,  -2.5797932, ...,  -4.0109997,
          -2.4964375,  -3.0753887],
        [-12.231965 , -12.027048 , -11.797831 , ...,  -8.838844 ,
          -9.09165  , -10.497256 ],
        [-10.639945 , -11.074341 , -11.0361   , ...,  -8.148463 ,
         -9.585199 , -10.67151  ]]], dtype=float32), seq_relationship_logits=array([[ 1.6309196 , -0.71684647]], dtype=float32), hidden_states=None, attentions=None)

 

TFTrainer를 사용할 경우에는 학습에 필요한 arguments을 TFTrainingArguments을 통해서 정의해주어야 한다. 아래는 TFTrainer를 사용하여 Huggingface 모델의 학습이 이루어지는 간단한 예시이다.

 

실제로 모델이 구동되어 성능을 내기 위한 프로젝트 구성은 Model, Tokenizer 및 데이터셋 구성이 TFTrainingArguments를 통해서 TFTrainer에 어떻게 반영되는지 확인해보자.

 

from dataclasses import dataclass, field
from enum import Enum
from typing import Dict, Optional
import tensorflow as tf
import tensorflow_datasets as tfds
from transformers import (
    TFAutoModelForSequenceClassification,
    TFTrainer,
    TFTrainingArguments,
    AutoConfig,
    AutoTokenizer,
    glue_convert_examples_to_features,
)

# TFTrainingArguments 정의
training_args = TFTrainingArguments(
    output_dir='./results',              # output이 저장될 경로
    num_train_epochs=1,              # train 시킬 총 epochs
    per_device_train_batch_size=16,  # 각 device 당 batch size
    per_device_eval_batch_size=64,   # evaluation 시에 batch size
    warmup_steps=500,                # learning rate scheduler에 따른 warmup_step 설정
    weight_decay=0.01,                 # weight decay
    logging_dir='./logs',                 # log가 저장될 경로
    do_train=True,                        # train 수행여부
    do_eval=True,                        # eval 수행여부
    eval_steps=1000
)

# model, tokenizer 생성
model_name_or_path = 'bert-base-uncased'
with training_args.strategy.scope():    # training_args가 영향을 미치는 model의 범위를 지정
    model = TFAutoModelForSequenceClassification.from_pretrained(
            model_name_or_path,
            from_pt=bool(".bin" in model_name_or_path),
        )
tokenizer = AutoTokenizer.from_pretrained(
        model_name_or_path,
    )

Downloading:   0%|          | 0.00/570 [00:00<?, ?B/s]
Downloading:   0%|          | 0.00/511M [00:00<?, ?B/s]
All model checkpoint layers were used when initializing TFBertForSequenceClassification.

Some layers of TFBertForSequenceClassification were not initialized from the model checkpoint at bert-base-uncased and are newly initialized: ['classifier']
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
Downloading:   0%|          | 0.00/28.0 [00:00<?, ?B/s]
Downloading:   0%|          | 0.00/226k [00:00<?, ?B/s]
Downloading:   0%|          | 0.00/455k [00:00<?, ?B/s]

 

위와 같이 Huggingface 프레임워크 구조에 따라 Model과 Tokenizer를 간단히 생성하였다.

여기까지는 기존에 살펴본 것과 큰 차이는 없지만 model 생성 시에 training_args의 scope 안에서 진행했다는 것이 눈에 띈다. 이 부분은 TFTrainer 사용 시 결정적으로 중요하다. 이 with 구문을 생략하면 TFTrainer에 전달하고픈 옵션이 제대로 전달되지 않아 결과적으로 모델이 오동작하게 되는 경우가 생길 수 있다.

 

# 데이터셋 생성
ds, info = tfds.load('glue/mrpc', with_info=True)
train_dataset = glue_convert_examples_to_features(ds['train'], tokenizer, 128, 'mrpc')
train_dataset = train_dataset.apply(tf.data.experimental.assert_cardinality(info.splits['train'].num_examples))

# TFTrainer 생성
trainer = TFTrainer(
    model=model,                          # 학습시킬 model
    args=training_args,                  # TFTrainingArguments을 통해 설정한 arguments
    train_dataset=train_dataset,   # training dataset
)

# 학습 진행
trainer.train()

# 테스트
test_dataset = glue_convert_examples_to_features(ds['test'], tokenizer, 128, 'mrpc')
test_dataset = test_dataset.apply(tf.data.experimental.assert_cardinality(info.splits['test'].num_examples))
trainer.evaluate(test_dataset)

{'eval_loss': 0.4480868798715097}

 

이후 데이터셋을 생성하여 model, training_args과 함께 TFTrainer에 전달하는 것으로 학습을 위한 준비가 마무리된다. 이후 trainer.train()을 호출하면 실제 학습이 진행된다.

728x90
반응형