HuggingFace 실습(PEFT) : 1. Data

2023. 10. 15. 00:01 · Python 및 Torch 코딩 이모저모
목차
  1. 1. Data Loading
  2. 2. Tokenizer
  3. 2-1. 선언
  4. 2-2.  전처리 
  5. 2-3. tokenizer 의 사용
  6. 3. DataLoader 선언
  7. 3-1. DataLoader DataCollator 차이점
  8. 3-2. DataLoader 의 pin_memory 변수
  9. ** Subset 사용하기

허깅페이스를 이용해 PEFT 튜닝 실습을 해보자!

 

허깅페이스에서 제공하는 PEFT 라이브러리를 사용하여 GPT-2 모델을 prefix tuning 해볼 것이다.

prefix tuning 의 개념은 이전에 블로그에서 설명한 글을 참조하자!

https://mari970.tistory.com/53

 

참고할 코드는 아래와 같다.

https://huggingface.co/docs/peft/task_guides/seq2seq-prefix-tuning

 

Prefix tuning for conditional generation

🤗 Accelerate integrations

huggingface.co

 

PEFT 를 이용하여 튜닝을 하는 실습 코드에 대해 포스팅하지만

일반 실습을 하는 사람들도 충분히 도움이 될 수 있는 글이라고 생각한다!


 

from transformers import GPT2Tokenizer, AutoTokenizer, GPT2Model, AutoModelForCausalLM, get_linear_schedule_with_warmup, default_data_collator
from peft import PrefixTuningConfig, get_peft_model, TaskType # 따로 peft install 해야함
from datasets import load_dataset
from torch.utils.data import DataLoader
from tqdm import tqdm
import torch
import os
import argparse

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--epochs', '-e', default=20, type=int,
                        dest='epochs', help='training epoch')
    parser.add_argument('--learning-rate', '-lr', default=1e-2, type=float,
                        dest='lr', help='training learning rate')
    parser.add_argument('--batch-size', '-bs', default=8, type=int,
                        dest='batch_size', help='training batch size')
    parser.add_argument('--max_length', '-ml', default=1004, type=int, # 1024 인데 prefix length=20 이라서,
                        dest='max_length', help='maximum sequence length')
    parser.add_argument('--seed', type=int, default=None)
    parser.add_argument('-save_mode', type=str, choices=['all', 'best'], default='best')
    parser.add_argument('--model_name_or_path', default= 'gpt2-large',
                        dest ='model_name_or_path', help='base model')
    parser.add_argument('--result_dir', default='output',
                        dest = 'result_dir', help='experiment result save directory')

    parser.add_argument('--data_preprocess', default='concat', choices = ['def_clm', 'concat'],
                        dest = 'data', help='data preprocess method for Causal LM')
    args = parser.parse_args()

    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

    peft_config = PrefixTuningConfig(
        task_type=TaskType.CAUSAL_LM, # TaskType.SEQ_2_SEQ_LM, 
        inference_mode=False, 
        num_virtual_tokens=20
    )

    tokenizer = AutoTokenizer.from_pretrained(args.model_name_or_path,
                                              pad_token='<pad>')

    model = AutoModelForCausalLM.from_pretrained(args.model_name_or_path)
    # model = AutoModelForSeq2SeqLM.from_pretrained(model_name_or_path)
    model.resize_token_embeddings(len(tokenizer))

    model = get_peft_model(model, peft_config)
    model.print_trainable_parameters()

    dataset = load_dataset("bigscience/P3", name="xsum_summarize_this_DOC_summary")

    if args.data == 'def_clm':
        def preprocess_func(examples):
            inputs = examples['inputs_pretokenized']
            targets = examples['targets_pretokenized']
            model_inputs = tokenizer(inputs, padding='max_length', truncation=True, max_length= args.max_length, return_tensors='pt') # is_split_into_words = True,
            labels = tokenizer(targets, padding='max_length', truncation=True, max_length= args.max_length, return_tensors='pt')
            labels = labels["input_ids"]
            # labels[labels == tokenizer.pad_token_id] = -100 # ?
            model_inputs["labels"] = labels
            return model_inputs


        tokenized_dataset = dataset.map(
            preprocess_func,
            batched=True,
            num_proc = 1,
            remove_columns=dataset["train"].column_names
            )

    elif args.data == 'concat':
        # Concat inputs and targets for CLM training
        dataset = dataset.map(
        lambda x : {'sent_forclm' : [x['inputs_pretokenized'][i] + x['targets_pretokenized'][i].lstrip() for i in range(len(x['targets_pretokenized']))]},
        batched= True,
        remove_columns=dataset["train"].column_names,
        num_proc = 1)

        tokenized_dataset = dataset.map(
            lambda examples : tokenizer(examples['sent_forclm'], padding='max_length', max_length=args.max_length, truncation=True, return_tensors="pt"),
            batched=True,
            num_proc = 1
            )

    train_dataset = tokenized_dataset['train']
    eval_dataset = tokenized_dataset['validation']

    train_loader = DataLoader(train_dataset, shuffle=True, collate_fn=default_data_collator, batch_size=args.batch_size, pin_memory=True)
    eval_loader = DataLoader(eval_dataset, collate_fn=default_data_collator, batch_size=args.batch_size, pin_memory=True)


    optimizer = torch.optim.AdamW(model.parameters(), lr=args.lr)
    lr_scheduler = get_linear_schedule_with_warmup(
        optimizer = optimizer,
        num_warmup_steps = 8e4,
        num_training_steps = (len(train_loader)*args.epochs)
    )

    train(model, train_loader, eval_loader, optimizer, lr_scheduler, device, args)



if __name__ == '__main__':
    main()

위는 참고 사이트에서 조금 바꾼 코드이다. 이를 레퍼런스로 데이터를 로딩하는 것 부터 토크나이즈 하는 전처리 과정까지 알아보자!

 

(train() 은 다음 블로그에서 소개할 것이다.)

 

1. Data Loading

dataset = load_dataset("bigscience/P3", name="xsum_summarize_this_DOC_summary")

P3 데이터셋, 그 중에 XSUM (summarization 요약) 데이터를 사용할 것이다.

허깅페이스에서 제공하는 데이터셋을 링크로 받아와서 load_dataset 함수를 이용해 사용할 수 있다.

원하는 데이터셋이 있는 허깅페이스 사이트에 들어가면 아래와 같은 화면을 볼 수 있는데 "</> Use in dataset library" 라는 버튼을 누르면 위 처럼 써야하는 코드를 바로 확인할 수 있다!

https://huggingface.co/datasets/bigscience/P3

 

** P3 데이터셋은 Public Pool of Prompts 로, 일반 데이터셋에 프롬프트 문장을 추가한 데이터셋이다.

 

2. Tokenizer

2-1. 선언

tokenizer = AutoTokenizer.from_pretrained(args.model_name_or_path, pad_token='<pad>')

위와 같이 GPT-2 의 학습된 tokenizer 를 불러오기 위해 from_pretrained 함수를 사용하는데, 

pre-trained 된 tokenizer 는 PAD 토큰이 학습되어있지 않아서 위와 같이 pad_token='<pad>' 를 추가해주어야 한다. 

 

** 기존에 학습된 토크나이저를 사용하다 보면 패딩해야 하는데 막 pad 토큰이 없어서 따로 지정을 해줘야 한다는 오류가 나기도 한다.

 

ValueError:
Asking to pad, but the tokenizer does not have a padding token. Please select a token to use as 'pad_token' '(tokenizer.pad_token = tokenizer.eos_token e.g.)' or add a new pad token via 'tokenizer.add_special_tokens({'pad_token': '[PAD]'})'.

Error 메세지에서 친절하게 어떻게 해결하는지 방법도 알려준다. 

필자가 위에 기재한 코드는 tokenizer 초기부터 add 해주는 방법이고, 위와 같이 이후에 add_special_tokens() 을 사용할 수도 있다!

 

그렇지만 이렇게 vocab 을 1개 추가하면 vocab size 가 커져서 나중에 모델안에서 embedding layer 를 거칠 때 (예를들어 GPT-2 의 wte layer) Index out of range 문제가 뜬다.

이를 해결하기 위해서

model.resize_token_embeddings(len(tokenizer))

를 사용한다.

 

2-2.  전처리 

tokenized_dataset = dataset.map(
            lambda examples : tokenizer(examples['sent_forclm'], padding='max_length', max_length=args.max_length, truncation=True, return_tensors="pt"),
            batched=True,
            num_proc = 1)

Huggingface dataset .map() 함수 를 사용하는데, batched =True 를 이용하여 batch 단위로 map() 함수를 사용하여 데이터 처리 속도를 빠르게 할 수 있다.

https://huggingface.co/docs/datasets/about_map_batch

 

Batch mapping

 

huggingface.co

 

2-3. tokenizer 의 사용

padding

boolean 으로, True 를 하면 batch 안에서 제일 긴 길이에 대해 <pad> 토큰으로 패딩을 해주고, ‘max_length’ 로 하면 다른 변수인 max_length 에서 설정한 최대 길이로 패딩을 해준다.

 

truncation

도 또한 또한 패딩과 관련있는 변수인데, 최대 길이를 넘어가는 데이터가 있을 때 잘라주는 역할을 한다.

 

return_tensors

'tf' 는 tensorflow, 'pt' 는 Pytorch, 'np' 는 numpy 로, 각 라이브러리에 맞는 자료형으로 출력해준다.

 

return_attention_mask

attention_mask는 tokenizer 를 통과하면 토큰화된 출력은 [input_ids’] 과 [’attention_mask’] 로 이루어져 있는데 이때 input_ids 는 토큰화된 문장이고, ‘’attention_mask’ 는 실제 토큰이 존재하는 부분과 <pad> 토큰으로 이루어진 부분을 1 과 0 으로 표현하여 실제 문장 부분을 표시한다.

 

3. DataLoader 선언

train_loader = DataLoader(train_dataset, shuffle=True, collate_fn=default_data_collator, 
						  batch_size=args.batch_size, pin_memory=True)

3-1. DataLoader DataCollator 차이점

Data loader: 프로그램을 실행하기 위해 하드디스크에 있는 파일을 RAM 에 올리는 과정이 필요한데 그 과정을 한다.

Data collator : 데이터셋 리스트에서 batch 를 만들어내는 역할을 한다.

 

DataLoader 는 보통 Pytorch 에서 구현한 기능이고

DataCollator 는 HuggingFace 라이브러리에서 구현된 함수이다.

 

DataLoader 에 있는 collate_fn 이라는 함수(? 아니면 변수) 를 이용해 batch 로 묶을 수 있다. → dataset 이 variable length 를 가지면 (padding 을 하기 위해) collate_fn 을 꼭 사용해야 한다고 한다.

 

** 요즘 보통 data loader 가 아니라 data collator 를 사용하는 예제가 많이 보인다. 아직 이 둘이 쓰임새 차이를 확인하지 못했다 ㅜㅠ..

 

3-2. DataLoader 의 pin_memory 변수

학습을 시키기 위해 GPU 에 데이터를 로드해줘야 하는데 이때 빠르게 데이터를 옮기기 위해 사용하는 것이 pin memory 이다.

즉, 원래 지피유에 데이터를 올리기 위해서는 DRAM(pageable memory) 에서 pinned memory 로 옮겨지고 이후 VRAM(GPU 전용 메모리) 에 옮기는 과정이 필요한데,

옮기는 과정이 매우 많기 때문에 느려지게 된다.

이를 보완하기 위해 Pytorch 의 DataLoader 에서는 pin_memory 라는 옵션을 이용하여 DRAM 을 거치지 않고 바로 VRAM 으로 할당시켜주는 역할을 한다.

https://mopipe.tistory.com/191

 

[pytorch, 딥러닝] pin_memory 란 무엇인가?

Pin memory란 무엇인가? 우리가 모델을 제작을 함에 있어서 GPU를 사용하려면 GPU에 데이터를 로드를 해줘야 하는데, 이때 빠르게 데이터를 옮기기 위해 사용하는 것이 pin memory입니다(대부분의 데이

mopipe.tistory.com

 

** Subset 사용하기

디버깅을 할 때 큰 데이터셋으로 작성한 코드가 잘 돌아가는지 확인하는 것은 시간이 너무 많이 걸린다. 데이터셋을 작게 줄여서 사용하거나, 원하는 데이터의 인덱스만 뽑아서 사용하고 싶을 때 torch 의 Subset 을 사용할 수 있다.

from torch.utils.data import DataLoader, Subset

num_train_idxs = list(range(8))
train_dataset = Subset(dataset['train'], num_train_idxs) 
eval_dataset = Subset(dataset['validation'], num_train_idxs)

train_loader = DataLoader(train_dataset, shuffle=True, collate_fn=default_data_collator, batch_size=args.batch_size, pin_memory=True)
eval_loader = DataLoader(eval_dataset, collate_fn=default_data_collator, batch_size=args.batch_size, pin_memory=True)

기존에 load_dataset() 으로 로드한 dataset 이 있을 때, 다음과 같이 index 리스트를 만들어 Dataset class 를 샘플링할 수 있다.

하지만 실수해서는 안되는 부분이 이렇게 Subset 한 train_이나 eval_dataset 의 class 는 Dataset 이 아니라 Subset 이다.

Subset 은 이후에 DataLoader 를 이용해 로드를 해야 비로소 샘플링된 상태가 된다.

'Python 및 Torch 코딩 이모저모' 카테고리의 다른 글

리눅스에 파이썬 새로운 버전 설치하기!  (0) 2023.10.25
HuggingFace 실습(PEFT) : 2. Train  (0) 2023.10.17
GGML 개념 정리  (1) 2023.10.09
*list 와 **dict : unpacking 방법  (0) 2023.08.09
Pytorch Data Parallel  (0) 2021.12.30
  1. 1. Data Loading
  2. 2. Tokenizer
  3. 2-1. 선언
  4. 2-2.  전처리 
  5. 2-3. tokenizer 의 사용
  6. 3. DataLoader 선언
  7. 3-1. DataLoader DataCollator 차이점
  8. 3-2. DataLoader 의 pin_memory 변수
  9. ** Subset 사용하기
'Python 및 Torch 코딩 이모저모' 카테고리의 다른 글
  • 리눅스에 파이썬 새로운 버전 설치하기!
  • HuggingFace 실습(PEFT) : 2. Train
  • GGML 개념 정리
  • *list 와 **dict : unpacking 방법
섬섬옥수수
섬섬옥수수
컴공 AI 개발자가 되기 위한 노역입니다
섬섬옥수수
아날로그 인간의 컴공 되기
섬섬옥수수
전체
오늘
어제
  • 분류 전체보기
    • 백준 단계별 코딩 테스트
    • KB 논문 정리
    • Memory network 논문 정리
    • LLM 관련 논문 정리
    • Python 및 Torch 코딩 이모저모
    • Clustering 관련 논문 정리
    • 머신러닝 이모저모
    • 암호학

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • efficient and effective vocabulary expansion towards multilingual large language models
  • ragas
  • 티스토리챌린지
  • e5-v
  • 심재형
  • 백준
  • 인공지능융합기반시스템개론
  • CUDA
  • constituency tree
  • dependency tree
  • 문제풀이
  • eeve
  • 오블완
  • vocabulary expansion
  • 하드웨어
  • 코딩테스트
  • PEFT
  • GIT
  • 소프트웨어
  • 이화여대

최근 댓글

최근 글

hELLO · Designed By 정상우.v4.2.0
섬섬옥수수
HuggingFace 실습(PEFT) : 1. Data
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.