이론적 소개
프롬프트 튜닝은 효율적인 매개변수 미세 조정 방법입니다.
핵심 아이디어는 다음과 같습니다.
지식이 풍부한 교과서(사전 훈련된 대형 모델)를 수정하는 대신 기술자는 책의 시작 부분(입력 계층)에 몇 개의 매우 똑똑하고 학습하기 쉬운 스티키 노트 (소프트 프롬프트/가상 토큰(소프트 프롬프트) 또는 가상 토큰)를 추가합니다.
스터키 노트의 내용은 고정된 텍스트가 아니라, 모델이 스스로 학습하고 조정할 수 있는 매개변수(벡터)입니다.
훈련하는 동안 우리는 원래 모델의 대부분의 매개변수를 동결하고 새로 추가된 스티키 노트 매개변수만 훈련합니다.
그러면 모델이 특정 스티키 노트를 볼 때 우리가 예상하는 방식으로 작업을 수행하게 됩니다.
핵심 원리 다이어그램
PLM(사전 학습된 모델)은 변경되지 않고, W(모델 가중치)는 변경되지 않으며, X(모델 입력)는 변경됩니다.
작업 관련 프롬프트 템플릿을 설계하고 프롬프트 임베딩을 미세 조정하여 사전 훈련된 모델이 특정 작업에 적응하도록 안내합니다. 전체 모델 매개변수 대신 소수의 프롬프트(프롬프트 임베딩)만 미세 조정하면 됩니다.
기존의 미세 조정에 비해 다음과 같은 장점이 있습니다.
- • 빠른 학습 및 리소스 절약: 매우 적은 매개변수만 학습됩니다.
- • 작은 저장소: 각각의 새로운 작업은 전체 모델이 아닌, 작은 부착 메모 매개변수만 저장하면 됩니다.
- • 우수한 결과: 많은 작업에서 Prompt-Tuning은 전체 미세 조정과 비슷한 결과를 얻을 수 있습니다.
- • 원래 모델은 영향을 받지 않습니다. 기본 모델은 변경되지 않으므로 다양한 작업에 맞게 다양한 부착 메모를 쉽게 장착할 수 있습니다.
코드를 통한 원칙 해석
이 코드는 Prompt-Tuning의 전체 구현을 보여주고 작동 방식을 설명합니다. (운영 환경에는 PEFT 패키지를 설치하기 위한 pip가 필요하며, 이 코드에서 사용된 PEFT 버전은 0.14.0입니다.)
프롬프트 튜닝 방법은 주로 네 번째와 여덟 번째 단계 에 반영되어 있으므로 주의 깊게 읽어야 합니다.
1단계: 관련 패키지 가져오기
import torch
from datasets import Dataset
from transformers import AutoTokenizer, AutoModelForCausalLM, DataCollatorForSeq2Seq, TrainingArguments, Trainer
from peft import PromptTuningConfig, get_peft_model, TaskType, PromptTuningInit, PeftModel
2단계: 데이터 세트 로드
# Contains 'instruction', 'input' (optional extra input), 'output' (expected answer)
ds = Dataset.load_from_disk("../data/alpaca_data_zh/")
3단계: 데이터 세트 전처리
각 샘플을 input_ids, attention_mask, label을 포함하는 사전으로 처리합니다.
tokenizer = AutoTokenizer.from_pretrained("D:\\git\\model-download\\bloom-389m-zh")
defprocess_func(example):
MAX_LENGTH = 256
prompt = "\n".join(["Human: " + example["instruction"], example["input"]]).strip() + "\n\nAssistant: "
instruction_tokenized = tokenizer(prompt, add_special_tokens=False)
response_tokenized = tokenizer(example["output"] + tokenizer.eos_token, add_special_tokens=False)
input_ids = instruction_tokenized["input_ids"] + response_tokenized["input_ids"]
attention_mask = instruction_tokenized["attention_mask"] + response_tokenized["attention_mask"]
labels = [-100] * len(instruction_tokenized["input_ids"]) + response_tokenized["input_ids"]
# Truncate
iflen(input_ids) > MAX_LENGTH:
input_ids = input_ids[:MAX_LENGTH]
attention_mask = attention_mask[:MAX_LENGTH]
labels = labels[:MAX_LENGTH]
# Return the processed data
return {
"input_ids": input_ids,
"attention_mask": attention_mask,
"labels": labels
}
# The .map() method applies the processing function to all samples in the entire dataset.
tokenized_ds = ds.map(process_func, remove_columns=ds.column_names) # `remove_columns` removes the original columns, keeping only the new columns returned by process_func.
print("\nChecking the results of the 2nd data process:")
print("Input sequence (input_ids decoded):", tokenizer.decode(tokenized_ds[1]["input_ids"]))
target_labels = list(filter(lambda x: x != -100, tokenized_ds[1]["labels"]))
print("Sequence of labels (labels decoded, after filtering -100):", tokenizer.decode(target_labels))
4단계: 모델 및 PEFT 구성 만들기
이 단계는 Prompt-Tuning의 핵심 단계 입니다 . "가상 프롬프트 단어"를 초기화하려면 텍스트를 삽입해야 합니다.
텍스트가 분할된 후, 해당 단어 벡터(임베딩)가 가상 프롬프트 단어의 초기값으로 사용됩니다.
마지막 항목은 num_virtual_tokens가상 프롬프트 단어 임베딩 벡터의 개수를 나타내며 학습이 필요한 유일한 매개변수입니다.
model = AutoModelForCausalLM.from_pretrained("D:\\git\\model-download\\bloom-389m-zh")
# Configure Prompt Tuning
config = PromptTuningConfig(
task_type=TaskType.CAUSAL_LM, # causal language model
prompt_tuning_init=PromptTuningInit.TEXT, # PromptTuningInit.TEXT means to initialize the “virtual tuning word” with a text embedding, which is better than random initialization.
prompt_tuning_init_text="The following is a dialog between a human and a robot.", # Segment the text and use the corresponding word vector (embedding) as the initial value for the virtual prompt.
num_virtual_tokens=len(tokenizer("The following is a conversation between a human and a robot")["input_ids"]), # The number of virtual tokens, equal to the length of the initialized text split, this `num_virtual_tokens` embedding vector of virtual tokens, is the only parameter to be trained!
tokenizer_name_or_path="D:\\git\\model-download\\bloom-389m-zh"
)
# Apply the Prompt Tuning configuration to the base model via the `get_peft_model` function, which adds the learnable Prompt Encoder inside the model and automatically freezes all other parameters of the base model.
model = get_peft_model(model, config)
# Look at the changes to the model structure, there will be an additional prompt_encoder section
print(“PEFT model structure:”, model)
# Check trainable parameters: print and compare the number of trainable parameters to the total number of parameters.
model.print_trainable_parameters() # 'trainable parameters' is much smaller than 'all parameters'.
5단계: 훈련 매개변수 구성
args = TrainingArguments(
output_dir=". /chatbot_prompt_tuning_explained_zh",
per_device_train_batch_size=1,
gradient_accumulation_steps=8, # gradient_accumulation: equivalent to an effective batch size of 1 * 8 = 8, useful for cases with limited video memory
logging_steps=10, # print logging information (e.g., loss) every 10 steps of training
num_train_epochs=1, # number of training rounds
save_steps=100, # Save model checkpoints for every 100 training steps
# learning_rate=1e-3, # Prompt Tuning can usually use a slightly larger learning rate than full fine-tuning
# gradient_checkpointing=True, # Can save memory, a little slower, can be turned on if memory is low.
)
6단계: 트레이너 만들기
trainer = Trainer(
model=model,
args=args,
tokenizer=tokenizer,
train_dataset=tokenized_ds,
data_collator=DataCollatorForSeq2Seq(tokenizer=tokenizer, padding=True),# Data organizer: responsible for composing a batch of samples from the dataset with the necessary padding.
)
7단계: 모델 학습
trainer.train() # Optimize only the parameters of the virtual token, the weights of the base model are frozen.
8단계: 모델 추론 및 효과 표시
프롬프트 튜닝 추론:
- 1. 추론 중에 훈련된 가상 토큰은 자동으로 입력 시퀀스에 추가됩니다.
- 2. 가상 토큰은 모델에 특정 스타일이나 콘텐츠의 응답을 생성하도록 지시하는 암시적 힌트를 제공하는 것과 같습니다.
- 3. 사용자는 이러한 가상 토큰을 볼 필요가 없으며, 모델 내에서만 작동합니다.
표준 프로세스:
- 1. 원래 기본 모델을 로드합니다.
- 2. 훈련된 PEFT 어댑터 가중치를 장착합니다.
- 3. PeftModel.from_pretrained두 가지를 결합하는 데 사용합니다.
# 1. Load the base model
base_model = AutoModelForCausalLM.from_pretrained("D:\\git\\\model-download\\bloom-389m-zh")
# 2. Specify the directory where the PEFT adapter weights are located
peft_model_path = ". /chatbot_prompt_tuning_explained_zh/checkpoint-3357/"
# 3. load the PEFT adapter and apply it to the base model
peft_model = PeftModel.from_pretrained(model=base_model, model_id=peft_model_path)
if torch.cuda.is_available().
peft_model = peft_model.cuda()
print("Model has been moved to the GPU.")
print("The model has been moved to the GPU.") else.
print("CUDA not detected, will run inference on CPU.")
# Prepare the input text
instruction = "What are the tips for the exam?"
input_text = ""
prompt = f "Human: {instruction}\n{input_text}".strip() + "\n\nAssistant: "
print(f"\nInput for reasoning Prompt:\n{prompt}")
# Segment the input text, convert it to tensors, and move it to the device (CPU or GPU) where the model resides
ipt = tokenizer(prompt, return_tensors="pt").to(peft_model.device)
# Use the `.generate()` method to generate the answer, the PEFT model automatically handles the injection of soft prompts
print("Generating responses...")
response_ids = peft_model.generate(**ipt, max_length=128, do_sample=True, top_k=50, top_p=0.95, temperature=0.7)
# Decode the generated token IDs back into text
full_response = tokenizer.decode(response_ids[0], skip_special_tokens=True) # `skip_special_tokens=True` removes special tokens like <|endoftext|>
# Focus on what comes after "Assistant: "
assistant_response = full_response.split("Assistant: ")[-1]
print(f"\n model generated response:\n{assistant_response}")
'AI > AI 툴' 카테고리의 다른 글
Firebase Studio - Google의 AI 프로그래밍 도구, 풀스택 애플리케이션 개발 (1) | 2025.04.21 |
---|---|
awesome-llm-apps: 대규모 언어 모델 애플리케이션 (0) | 2025.04.21 |