Huggingface Transformers의 Trainer
Huggingface Transformers에서는 보다 쉬운 모델 학습을 위해
Trainer
라는 라이브러리를 제공한다.사용하는 것은 많이 하지만, 실제로 모델을 어떻게 받고 데이터를 어떻게 넘겨줘서 → 어떻게
설명대상 코드
Trainer는 어떻게 쓰나?
Transformers Example에 있는
run_clm.py
등에서는 Trainer를 사용해서 학습한다.# Initialize our Trainer trainer = Trainer( model=model, args=training_args, train_dataset=train_dataset if training_args.do_train else None, eval_dataset=eval_dataset if training_args.do_eval else None, tokenizer=tokenizer, # Data collator will default to DataCollatorWithPadding, so we change it. data_collator=default_data_collator, )
위 코드는
Trainer
클래스에 Model, Training 인자, dataset, tokenizer, data collator를 넣어준다.- model:
~~ForCasualLM
,~~ForMaskedLM
같은 CLM/MLM 모델을 넣어준다.
- args:
ModelArguments
라는 DataClass를 기반으로 만들어서 모델 instance를 생성할 때, 아래와 같이 인자를 받아서model_args
로 전달해준다.
class Model Arguments 코드
@dataclass class ModelArguments: """ Arguments pertaining to which model/config/tokenizer we are going to fine-tune, or train from scratch. """ model_name_or_path: Optional[str] = field( default=None, metadata={ "help": "The model checkpoint for weights initialization." "Don't set if you want to train a model from scratch." }, ) model_type: Optional[str] = field( default=None, metadata={"help": "If training from scratch, pass a model type from the list: " + ", ".join(MODEL_TYPES)}, ) config_name: Optional[str] = field( default=None, metadata={"help": "Pretrained config name or path if not the same as model_name"} ) tokenizer_name: Optional[str] = field( default=None, metadata={"help": "Pretrained tokenizer name or path if not the same as model_name"} ) cache_dir: Optional[str] = field( default=None, metadata={"help": "Where do you want to store the pretrained models downloaded from huggingface.co"}, ) use_fast_tokenizer: bool = field( default=True, metadata={"help": "Whether to use one of the fast tokenizer (backed by the tokenizers library) or not."}, ) model_revision: str = field( default="main", metadata={"help": "The specific model version to use (can be a branch name, tag name or commit id)."}, ) use_auth_token: bool = field( default=False, metadata={ "help": "Will use the token generated when running `transformers-cli login` (necessary to use this script " "with private models)." }, )
- train_dataset:
lm_datasets["train"]
라는 형태로 들어가는데, 일종의 dict ~ pandas df 사이의 어딘가에 있는 느낌이다.
datasets = load_dataset( extension, data_files=data_files, cache_dir=model_args.cache_dir ) tokenized_datasets = datasets.map( tokenize_function, batched=True, num_proc=data_args.preprocessing_num_workers, remove_columns=column_names, load_from_cache_file=not data_args.overwrite_cache, ) lm_datasets = tokenized_datasets.map( group_texts, batched=True, num_proc=data_args.preprocessing_num_workers, load_from_cache_file=not data_args.overwrite_cache, )
이때, 위 datasets는 Huggingface datasets 라이브러리를 사용한다.
load_dataset
이라는 함수를 사용해 로드하는데, 해당 코드는 아래를 참고해보자.Dataset은 PyArrow를 캐시로 사용한다. 만약 데이터셋 대상이 PyArrow 타입으로 이미 인코딩 되어있다면 → 해당 파일을 그대로 불러온다.
하지만 txt나 jsonl파일을 넣어줄 경우, PyArrow 형태로 우선 변환 후 처리한다. 이때 처리 시간이 꽤 오래걸려서, 가능하다면 Local에서 처리해주고 PyArrow 형태로 변환한 뒤 학습만 Remote VM에서 처리하는 것을 추천한다.
파일은 jsonl 형태도 지원하지만, 파일 확장자는
.json
이 되어야 한다.
(.jsonl
확장자로 넣을 경우, 지원하지 않는 경우라고 한다.)- tokenizer:
이 옵션을 처음에 봤을때는 Model 학습 단계에서 Trainer level에서 데이터셋을 매 batch마다 tokenize해서 넣어주는줄 알았는데, 실제로는 단순히 Tokenizer의 meta정보를 받기 위해서 들어가는 형태다.
위
tokenized_datasets
에서 tokenize를 진행하고, 이때 tokenized는 사실은 Encoded 되었다, 라고 말하는 것이 더 적합하다. BERT Tokenizer 등에서 {'input_ids': [2, 19017, 8482, 3], 'token_type_ids': [0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1]}
와 같은 형태의 결과물을 저장한다.또한,
lm_datasets
의 경우, 위 dict 처럼 잘려진 상태를 정해진 길이만큼 붙이는 (GPT-2의 경우 1000단위로 붙이고, RoBERTa의 경우 512 단위로 붙이는 등) 과정을 통해 padding을 최대한 줄이는 방향으로 학습한다. (*물론, 이건 모델마다 다르다.)이 과정으로 인해, TEXT 데이터의 경우 txt → encoded json → concated json 형태로 진행하는데, 이 과정에 시간이 굉장히 오래 걸리게 된다.
따라서, 가능하다면 concated json까지 PyArrow로 변환한 뒤 학습을 시작하자.
- data_collator:
default_data_collator
해당 Collator는 아주 심플한 Collator로,
label
이라는 라벨이 있는지를 체크 후 Batch를 만들어주는 역할을 한다.사실 간단하게 설명하면..
이런 형태의 데이터를
[ {'input_ids': [2, 19017, 8482, 3], 'label': 1,, {'input_ids': [2, 1483, 4009, 4358, 4429, 3], 'label': 0} ]
아래와 같이 바꿔주는 느낌이다.
{ 'input_ids': [ [2, 19017, 8482, 3], [2, 1483, 4009, 4358, 4429, 3] ], 'label': [1, 0] }
그러면, Trainer는 어떻게 동작하나?
ㅇ