Ssul's Blog
[NLP, Fine-Tuning] 허깅페이스(Huggingface) 사용법 본문
0. 허깅페이스는 무엇인가?(huggingface.co)
허깅페이스(Hugging Face)는 인공 지능(AI) 분야에서 자연어 처리(NLP)를 중심으로 한 다양한 딥러닝 모델과 도구들을 제공하는 회사. 오픈소스 라이브러리인 'Transformers'를 통해 유명해짐. 이 라이브러리는 다양한 전처리 방법, 모델 아키텍처(BERT, GPT, T5 등), 그리고 후처리 방법을 포함하여 NLP 분야에서 광범위하게 사용.
- AI관련 깃허브 느낌
- 내가 만든 모델/데이터셋을 Public, private로 올릴수 있고, Public일 경우 누구나 내가 올려놓은 데이터셋, 모델을 사용할수 있음
- 당연히 다른 사람이 만든 언어모델이 Public으로 공개되어 있다면, 나는 해당 모델을 기반으로 파인 튜닝이 가능함.
1. 허깅페이스에서 데이터셋 가져오기
: 알고 있는 3가지의 데이터셋을 가져오기 방법공유.
첫번째는 깃허브 git clone 처럼 올려져있는 데이터셋/모델 가져와서 사용하는 방법
두번째는 로컬에 있는 자기 데이터 사용하는 방법
세번째는 허깅페이스 허브에서 제공하는 데이터 사용하는 방법
1-1. 허깅페이스 다른사람 public 데이터셋 또는 내 허깅페이스 데이터셋 가져오기
- 허깅페이스 나의 데이터셋 페이지에서, 데이터셋 생성(id/test-데이터셋 주소)
- 데이터셋은 아래와 같이, train / valid / test 파일 업로드
from datasets import load_dataset
load_dataset("허깅페이스id/test") #데이터셋 주소(git주소와 동일한 개념)
- load_dataset 함수 가져오고,
- 허깅페이스에 올린, 데이터셋 주소 입력
- 자동으로 train/vaild/test 구분하여 가져오게 됨. 아래와 같이 train/validation/test를 DatasetDict형태로 가져옴
1-2. 로컬데이터셋 가져오기
emotions_local = load_dataset("csv", data_files="train.txt", sep=";",
names=["text", "label"])
- 로컬에 있는 txt파일에서 데이터셋 가져오기
dataset_url = "https://huggingface.co/datasets/transformersbook/emotion-train-split/raw/main/train.txt"
emotions_remote = load_dataset("csv", data_files=dataset_url, sep=";",
names=["text", "label"])
- 온라인에 있는 txt파일에서 데이터셋 가져오기
1-3. 허깅페이스 허브가 제공하는 데이터셋 가져오기
from datasets import load_dataset
from huggingface_hub import list_datasets
all_datasets = [ds.id for ds in list_datasets()]
print(f"현재 허브에는 {len(all_datasets)}개의 데이터셋이 있습니다.")
print(f"처음 10개 데이터셋: {all_datasets[:10]}")
# emotion 데이터셋이 다운로드되지 않으면 SetFit/emotion을 사용합니다.
emotions = load_dataset("emotion")
- 허깅페이스 허브가 제공하는 emotion데이터 가져오기
- load_dataset함수에 이미 정해져있는 데이터셋 키워드를 입력하면 가져올수 있습니다.
2. 모델 가져오기
- 다른 사람의 허깅페이스에 공개된 라마2-7b모델을 가져오기
from transformers import AutoModel
model_ckpt = "TinyPixel/Llama-2-7B-bf16-sharded"
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = AutoModel.from_pretrained(model_ckpt).to(device)
- TinyPixel라는 분이 올려놓은 라마2 7b모델을 가져오는 코드입니다. 끝
3. 훈련하기
- 모델은 문장을 입력받으면, 6가지 감정(슬픔, 즐거움, 사랑,...)중 1개로 분류하는 모델(예: i love you를 입력받으면, 출력은 2(love를 의미)
- 두가지 형태의 훈련방법. 하나는 가져온 Pretrain모델은 업데이트 없이 학습하기. 두번째는 pretrain모델도 업데이트 하는 구조
3-1. Pretrain모델은 고정, 뒤에 classifier만 업데이트
모델/토크나이저 가져오기 코드
from transformers import AutoModel
from transformers import AutoTokenizer
model_ckpt = "distilbert-base-uncased"
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = AutoModel.from_pretrained(model_ckpt).to(device)
tokenizer = AutoTokenizer.from_pretrained(model_ckpt)
- pretrain모델 가져오고, 모델에 사용된 토크나이져도 함께 가져옵니다.
- transformers의 AutoModel, AutoTokenizer는 모델명 입력만으로, 자동으로 가져옵니다.
Pretrain모델의 output확인하기
- clasifier를 붙이기 위해서는 Pretrained모델이 어떤 구조의 값을 뱉어내는지 알아야 합니다.
- 직접 i love you를 입력해서 출력값을 확인합니다
text = "i love you"
# 파이토치 텐서로 리턴
inputs = tokenizer(text, return_tensors="pt")
print(f"입력 텐서 크기: {inputs['input_ids'].size()}")
#입력 텐서 크기: torch.Size([1, 5])
inputs
#{'input_ids': tensor([[ 101, 1045, 2293, 2017, 102]]), 'attention_mask': tensor([[1, 1, 1, 1, 1]])}
- 토크나이저를 통해, 5개의 토큰으로 분리되어 입력됨을 확인가능
- 시작토큰, i, love, you, 끝토큰 5개로 구성된것으로 예상됨
inputs = {k:v.to(device) for k,v in inputs.items()}
with torch.no_grad():
outputs = model(**inputs)
print(outputs)
- 모델에 입력. 출력된 구조를 보면 3차원으로 구성되어 있고, last_hidden_state라는 값을 확인할 수 있음
- 출력된 데이터구조를 살펴보면 [1, 5, 768]
- [배치, 토큰, 임베딩차원]으로 1개배치, 5개 토큰, 768차원으로 해석이 가능함
- 이는 시작토큰/i/love/you/끝토큰 5개의 토큰이 입력되면, 모델은 5개의 토큰을 각각 768차원으로 임베딩해서 출력해주는 구조
classifier붙이기
- output을 확인하였으니, 뒤에 classifier를 붙이자.
- NLP에서 classifier를 보통 시작토큰([CLS])에 학습시킨다.(*허깅페이스 관련글이므로 자세한 내용은 생략)
- 그렇기 때문에 CLS의 결과값만 가지고, classifier를 이어 붙이고, 데이터를 학습시키면, 0-5사이의 분류값으로 학습이 가능하다
def extract_hidden_states(batch):
# 모델 입력을 GPU로 옮깁니다.
inputs = {k:v.to(device) for k,v in batch.items()
if k in tokenizer.model_input_names}
# 마지막 은닉 상태를 추출합니다.
with torch.no_grad():
last_hidden_state = model(**inputs).last_hidden_state
# [CLS] 토큰에 대한 벡터를 반환합니다.
return {"hidden_state": last_hidden_state[:,0].cpu().numpy()}
- 결과값 중에서, CLS=시작토큰 의 결과값 뽑아내는 함수 선어
- last_hidden_state[:,0]이 빨강색. 시작토큰의 Pretrain모델의 output 결과값
emotions_hidden = emotions_encoded.map(extract_hidden_states, batched=True)
- 입력한 토큰에 대한 토큰별 768차원의 벡터를 결과값으로 주던 것을, extract_hidden_states함수를 적용하여, cls토큰의 768차원만 반환
X_train = np.array(emotions_hidden["train"]["hidden_state"])
X_valid = np.array(emotions_hidden["validation"]["hidden_state"])
- emotions_hidden의 값은, Pretrain모델을 통과해서, 그 결과값 중 CLS토큰의 결과값만 가져옴
- 해당 데이터를 train/validation으로 구분
# 데이터 로더 생성
train_dataset = TensorDataset(X_train, y_train)
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
valid_dataset = TensorDataset(X_valid, y_valid)
valid_loader = DataLoader(valid_dataset, batch_size=32)
# 분류기 정의 (768 -> 6, 감성 분류를 위한 레이블 개수)
classifier = nn.Linear(768, 6)
loss_fn = nn.CrossEntropyLoss()
optimizer = optim.Adam(classifier.parameters(), lr=2e-5)
# 모델을 GPU로 옮깁니다 (GPU 사용 가능한 경우)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
classifier.to(device)
# 훈련 함수
def train(model, data_loader, loss_fn, optimizer, device):
model.train()
total_loss = 0
for batch in data_loader:
inputs, labels = batch
inputs, labels = inputs.to(device), labels.to(device)
# Forward pass
outputs = model(inputs)
loss = loss_fn(outputs, labels)
# Backward and optimize
optimizer.zero_grad()
loss.backward()
optimizer.step()
total_loss += loss.item()
return total_loss / len(data_loader)
# 검증 함수
def validate(model, data_loader, loss_fn, device):
model.eval()
total_loss = 0
with torch.no_grad():
for batch in data_loader:
inputs, labels = batch
inputs, labels = inputs.to(device), labels.to(device)
outputs = model(inputs)
loss = loss_fn(outputs, labels)
total_loss += loss.item()
return total_loss / len(data_loader)
# 훈련 및 검증 루프
num_epochs = 3
for epoch in range(num_epochs):
train_loss = train(classifier, train_loader, loss_fn, optimizer, device)
valid_loss = validate(classifier, valid_loader, loss_fn, device)
print(f'Epoch {epoch+1}/{num_epochs}, Train Loss: {train_loss}, Valid Loss: {valid_loss}')
- classifier = nn.Linear(768, 6): 입력된 값의 cls토큰의 최종값 768입력. 이를 6개중 1개의 값으로 뽑아내는 classifier 만들기
- loss_fn = nn.CrossEntropyLoss(): softmax와 X_pred과 y_train의 손실값 계산
- 위 코드를 통해서, pretrain모델의 파라미터는 변화가 없이, classifier파라미터 값만 분류를 잘하는 방향으로 업데이트
3-2. Pretrain모델, classifier 모두 업데이트
- 이미 두개가 합쳐진 모델을 허깅페이스에서 제공
from transformers import AutoModelForSequenceClassification
num_labels = 6
model = (AutoModelForSequenceClassification
.from_pretrained(model_ckpt, num_labels=num_labels)
.to(device))
- AutoModelForSequenceClassification을 가져옵니다
from sklearn.metrics import accuracy_score, f1_score
def compute_metrics(pred):
labels = pred.label_ids
preds = pred.predictions.argmax(-1)
f1 = f1_score(labels, preds, average="weighted")
acc = accuracy_score(labels, preds)
return {"accuracy": acc, "f1": f1}
- 주요 매트릭 정의
- 예측된 결과값을 label과 비교하여, 주요 정확도 및 손실을 확인합니다
from transformers import Trainer, TrainingArguments
batch_size = 64
logging_steps = len(emotions_encoded["train"]) // batch_size
model_name = f"{model_ckpt}-finetuned-emotion"
training_args = TrainingArguments(output_dir=model_name,
num_train_epochs=2,
learning_rate=2e-5,
per_device_train_batch_size=batch_size,
per_device_eval_batch_size=batch_size,
weight_decay=0.01,
evaluation_strategy="epoch",
disable_tqdm=False,
logging_steps=logging_steps,
push_to_hub=True,
save_strategy="epoch",
load_best_model_at_end=True,
log_level="error")
- 트레이너의 주요 옵션을 설정합니다.
from transformers import Trainer
trainer = Trainer(model=model, args=training_args,
compute_metrics=compute_metrics,
train_dataset=emotions_encoded["train"],
eval_dataset=emotions_encoded["validation"],
tokenizer=tokenizer)
trainer.train();
- 학습을 실행합니다
preds_output = trainer.predict(emotions_encoded["validation"])
- 훈련을 마친 모델로 예측을 합니다.
4. 훈련한 모델 허깅페이스에 업로드
: trainer를 통해 학습한 모델을 허깅페이스에 업로드 합니다(git push와 유사)
trainer.push_to_hub(commit_message="Training completed!")
- 업로드 끝.
5. 업로드한 모델 사용하기(또는 남의 모델 사용하기)
from transformers import pipeline
# 허브 사용자 이름
model_id = "yourid/distilbert-base-uncased-finetuned-emotion"
classifier = pipeline("text-classification", model=model_id)
- 모델을 가져옵니다
custom_msg = "i love you"
preds = classifier(custom_msg, top_k=None)
preds
- i love you를 입력해봅니다
- label2의 확률값이 가장 높음을 알수 있습니다 > love의 감성이 가장 높음을 확인
preds_sorted = sorted(preds, key=lambda d: d['label'])
preds_df = pd.DataFrame(preds_sorted)
plt.bar(labels, 100 * preds_df["score"], color='C0')
plt.title(f'"{custom_msg}"')
plt.ylabel("Class probability (%)")
plt.show()
- 그래프로도 확인
6. 정리
- 허깅페이스는 깃허브와 유사하다. ai학습을 할때 사용되는 모델, 데이터셋을 업로드하고, 함께 사용할수 있다.
- 허깅페이스를 통해 쉽게 pretrain모델을 가져올수 있고,
- 모두를 업데이트하는 학습을 하거나, 일부만 업데이트 하는 학습을 할수 있다.
- 나의 모델을 업로드하고, 공개된 다른 모델들을 쉽게 가져와서 바로 사용할 수 있다.
NLP를 공부한다면, 허깅페이스는 필수!!
'AI & ML > 사용하기' 카테고리의 다른 글
[Langchain #1] 기본채팅부터 커스텀parser사용 (0) | 2024.02.13 |
---|---|
Fine-tuning with LoRA(PEFT)로 스팸문자 분류기 만들기 (2) | 2024.02.04 |
[ChatGPT] OpenAI function_call 제대로 이해하기 (0) | 2024.02.02 |
[ChatGPT] OpenAI의 function_call활용해서, 반말 챗봇 만들기 (0) | 2024.01.11 |
[NLP, Fine-Tuning] ChatGPT 만들기(w Llama2) (0) | 2024.01.02 |