2022년 11월 22일쯤에 우연히 BetterTransformer, Out of the Box Performance for Hugging Face Transformers이 포스트를 발견하였다. 들뜬 마음에, 링크부터 저장하였다. 원래는 무언가 실험을 하고 싶었으나, '토치의 호흡' 강의하드캐리하느라 시간이 없어서 다소곳하게 SLACK 한 구석에다가 링크만 저장해두었다가, 이제서야 보면서 실험을 해보았다.
그런데... 문제는 BetterTransformer을 목적을 아직 잘 모르겠다는 것이다. (아마 필자의 내공이 아직 부족해서 그럴 것이다.) 이유는 이제부터 차차 말해보겠다.
: BetterTransformer로 model을 변환한 뒤, 학습에 쓰려고 하면 ... 다음과 같이 에러가 난다. (상세하게 말해보자면) 개인적으로 학습에 써보려고 Text Classification 에 쓰려고 roberta-base 모델에 적용하려고 했는데 다음과 같이 에러가 났다. 보고 "응...? Training 지원 안 된다고...?" 하는 생각에 10초 이상 멍 때렸던 것 같다.
결론부터 말하면, BetterTransformer는 Inference 전용이다. 필자는 이에 대해서 간과하고 (+ 위 링크의 글들을 제대로 주의깊게 안 읽고) 돌려봤으니 당연한 결과이다.
ValueError: ('Training is not supported for `BetterTransformer` integration.', ' Please use `model.eval()` before running the model.')
: 필자의 경우, Pytorch 2.0과 transformers 4.21.1 버전이 설치된 conda 환경에서 진행했다. 그래서 다음 코드로 BetterTransformer에 필요한 라이브러리만 설치하였다.
pip install accelerate optimum
: 혹시 모르니, 버전을 확인해보자.
torch.__version__
: 먼저, AutoModel, AutoTokenizer를 import해보자.
: 'klue/roberta-base'의 모델과 토크나이저를 다운받아보자.
from transformers import AutoModel, AutoTokenizer
name = 'klue/roberta-base'
tokenizer = AutoTokenizer.from_pretrained(name)
model = AutoModel.from_pretrained(name)
: 예시로는 이전 포스팅의 예시 문장을 들어보자.
: 이번에는 'klue/roberta-base'의 AutoTokenizer로 토크나이징('encode_plus'로) 해보자.
text ="이 족팡매야"
tokenizer.encode_plus(text)
: 다음과 같이 device를 설정해야하는데... 문제는 최적화 문제인지 모르겠지만, mps로 하면 에러가 난다. 그래서 cpu에서 진행했다.
# device = torch.device('mps') if torch.backends.mps.is_available() else torch.device('cpu')
# 위처럼 진행하면, 에러 난다.
device = torch.device('cpu')
device
: 다음과 같이 torch.tensor로 바꿔보자.
print(text)
inputs = tokenizer.encode_plus(text)
data = dict()
data['input_ids'] = torch.tensor(inputs['input_ids'], dtype = torch.long).unsqueeze(0)
data['attention_mask'] = torch.tensor(inputs['attention_mask'], dtype = torch.long).unsqueeze(0)
data['input_ids'].shape, data['attention_mask'].shape
: 위 data 아이들을 model에 다음과 같이 넣고, 나오는 결과물을 'out1'이라고 하자.
out1 = model(data['input_ids'].to(device), data['attention_mask'].to(device), return_dict = False)
# shape을 찍어보자.
# out1[0]: last_hidden_state -> [bs, sl, hid_dim] : 토큰별 임베딩
# out1[1]: pooler_output -> [bs, hid_dim] : 문장 레벨 임베딩
out1[0].shape, out1[1].shape
: 다음 코드처럼 BetterTransformer를 import하고 better_model을 만들어보자.
from optimum.bettertransformer import BetterTransformer
model_name = "roberta-base"
model = AutoModel.from_pretrained(model_name).to(device)
better_model = BetterTransformer.transform(model, keep_original_model=True)
: better_model을 통해 나오는 결과를 'out2' 라고 해보자.
out2 = better_model(data['input_ids'].to(device), data['attention_mask'].to(device), return_dict = False)
# shape을 찍어보자.
# out2[0]: last_hidden_state -> [bs, sl, hid_dim] : 토큰별 임베딩
# out2[1]: pooler_output -> [bs, hid_dim] : 문장 레벨 임베딩
out2[0].shape, out2[1].shape
: out1과 out2는 shape은 동일하지만, 값이 다르다. (어쩌면, BetterTransformer니까 당연)
: 여기서 맺도록 하겠다. 개인적으로 BetterTransformer의 목적이 잘 와닿지 않았기에 쓰면서도 좀 갈등이 많았다. (개인적으로 train 방법이 하나 떠오르기는 하는데 ... 안 해봐서 제대로 될 지도 모르겠다.)
긴 글을 읽어주느라 고맙습니다.
잘 보고 갑니다