PEFT documentation
컨텍스트 인식 프롬프트 튜닝: 적대적 방법을 통한 인컨텍스트 러닝 발전
컨텍스트 인식 프롬프트 튜닝: 적대적 방법을 통한 인컨텍스트 러닝 발전

Context-Aware Prompt Tuning (CPT)는 컨텍스트 임베딩만 정제하여 few-shot 분류를 향상시키기 위해 설계되었습니다. 이 접근법은 In-Context Learning (ICL), Prompt Tuning (PT), 적대적 최적화의 아이디어를 결합하여 모델 적응이 매개변수 효율적이고 효과적이도록 하는 데 초점을 맞춥니다. CPT에서는 특정 컨텍스트 토큰 임베딩만 최적화되고, 모델의 나머지 부분은 고정(frozen)된 상태로 유지됩니다. 과적합을 방지하고 안정성을 유지하기 위해 CPT는 제어된 섭동을 사용하여 컨텍스트 임베딩에 허용되는 변경을 정의된 범위 내에서 제한합니다. 또한, 컨텍스트 끝부분에 있는 예시가 이전 예시보다 우선시되는 recency bias 현상을 해결하기 위해 CPT는 감쇠 손실 계수를 적용합니다.
해당 논문의 초록은 다음과 같습니다:
Large Language Models(LLM)은 최적화 기반 접근법 또는 In-Context Learning(ICL)을 사용하여 few-shot learning을 수행할 수 있습니다. 최적화 기반 방법은 제한된 데이터로 대량의 파라미터를 업데이트해야 하므로 과적합(overfitting) 문제가 발생하기 쉽습니다. 반면, ICL은 과적합을 피할 수 있지만 일반적으로 최적화 기반 방법보다 성능이 낮으며, 데모 예시의 선택, 순서, 형식에 매우 민감합니다. 이러한 문제를 해결하기 위해 ICL, Prompt Tuning(PT), 적대적 공격에서 영감을 받은 Context-aware Prompt Tuning(CPT)을 소개합니다. CPT는 입력 앞에 예시를 연결하는 ICL 전략을 기반으로 하며, 반복적 최적화를 통해 컨텍스트 임베딩을 정교화하는 PT 유사 학습을 결합하여 학습 예시에서 더 깊은 통찰력을 추출하도록 확장했습니다. 우리의 접근법은 컨텍스트 내 예시의 고유한 구조를 고려하여 특정 컨텍스트 토큰을 신중하게 수정합니다. PT 유사 최적화로 컨텍스트를 업데이트하는 것 외에도, CPT는 적대적 공격에서 영감을 받아 컨텍스트에 존재하는 라벨에 기반하여 입력을 조정하되, 사용자가 제공한 데이터의 고유한 가치를 보존합니다. 최적화过程中的 강건성과 안정성을 보장하기 위해, 토큰 임베딩이 원래 값에 가깝도록 유지하여 컨텍스트의 품질을 보호하는 projected gradient descent 알고리즘을 사용합니다. 우리의 방법은 다양한 LLM 모델을 사용하여 여러 분류 작업에서 기존 베이스라인을 능가하는 우수한 정확도를 입증했으며, few-shot learning의 과적합 문제를 효과적으로 해결했습니다.
CPT로 모델을 학습하는 단계별 가이드를 확인하려면 예제를 참고하세요.
작동 원리
Context-aware Prompt Tuning은 사용자가 원하는 텍스트 프롬프트를 사용할 수 있다는 점에서 Prompt Tuning과 유사합니다. 이 방식에서는 프롬프트 토큰에만 적용되는 두 번째 토큰 임베딩을 통해 해당 프롬프트의 토큰만 학습 가능하도록 만듭니다. CPT 모델을 쿼리할 때, 사용자의 ‘소프트’ 프롬프트가 일반 입력의 임베딩 앞에 붙습니다. CPT는 Prompt Tuning과 달리, 특정 부분을 학습하지 않도록 표시함으로써 소프트 프롬프트를 문맥화할 수 있다는 점에서 다릅니다. CPT 전용 토큰 임베딩 행렬은 베이스 모델의 임베딩 행렬로 초기화된 정적 임베딩과 학습 가능한 토큰별 델타 값으로 구성됩니다.
CPT는 지시사항과 예시, ground-truth/labels가 포함된 프롬프트를 사용할 때 유용합니다. 지시사항은 최적화되지 않을 수 있으므로 학습 중에 최적화하는 것이 이점이 있습니다(프롬프트 튜닝과 유사하게). 하지만 프롬프트에 포함된 예시/labels는 변경되어서는 안 됩니다. 이는 프롬프트가 끝나는 위치와 예시가 시작되는 위치를 명시적으로 정의함으로써 강제할 수 있습니다. CPT는 프롬프트 내 예시에 대해 모델의 표준 다음 토큰 예측 손실에 토큰 손실을 추가하여 주어진 예시를 최적의 상태로 활용합니다.
CPT는 프롬프트의 서로 다른 섹션을 표시하기 위해 다섯 가지 다른 type id를 사용합니다:
- 0: separator: 완전히 무시되며, 문장 시작/종료 토큰에 유용합니다
- 1: 입력 마커: 추가 정보를 포함하여 입력 측 템플릿 전체를 나타내는 프롬프트를 설명하는 토큰
- 2: 입력 쿼리: 예시의 사용자 입력을 설명하는 토큰
- 3: 출력 마커: 예시의 예상 출력을 시작하는 토큰
- 4: label: 예시의 라벨에 속하는 토큰
이 내용은 매우 추상적이므로, CPT 프롬프트와 그에 해당하는 타입 식별자의 예를 보여 드리겠습니다. 간단함을 위해 각 단어 = 1 토큰이라고 가정합니다:
prompt_text = "Example 1: Input: 1+1 Answer: 2 Example 2: 1+3 Answer: 4"
token_ids = [15, 2, 11, 9, 1, 9, 11, 3, 11, 8, 9, 4, 2, 4, 9, 11, 3, 6, 8, 9, 10]
# BOS, Example 1 : Input : 1 + 1 Answer : 2 Example 2 : 1 + 3 Answer : 4
type_ids = [0, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 4, 5, 5, 5, 6, 6, 6, 7, 7, 8]
# 1, 1, 1, 2, 2, 2, 3, 3, 4token_ids는 기본 모델의 토크나이저로 인코딩된 프롬프트이며 ~CPTConfig.cpt_token_ids에 해당합니다. type_ids는 프롬프트 토큰 유형 ID로, 프롬프트의 서로 다른 섹션을 정의하며 ~CPTConfig.cpt_tokens_type_mask에 해당합니다.
개별 id는 학습 과정에 서로 다른 영향을 미칩니다:
- 0: 완전히 무시되며, 손실에 기여하지 않고 이러한 토큰은 학습되지 않습니다. 기술적 이유로만 존재하며, 그렇지 않으면 매개변수 공간을 무의미하게 확장하게 되는 토큰(예: EOS 토큰)에 유용합니다
- 1과 3: 이러한 토큰은 포맷팅에 고려되며, 임베딩 델타의 최대 노름은
opt_projection_format_epsilon()으로 제어됩니다. 노름은 이 유형의 학습된 델타 임베딩이 가리킬 수 있는 최대 크기를 제어합니다. 노름이 높을수록 의미의 변화 가능성이 커집니다. - 2: 이러한 토큰은 예시의 입력으로 간주되며, 임베딩 델타의 최대 노름은
opt_projection_epsilon()에 의해 제어됩니다. 노름은 이 유형의 학습된 델타 임베딩이 가리킬 수 있는 최대 크기를 제어합니다. 노름이 높을수록 의미의 변화 가능성이 커집니다. - 4: 프롬프트의 라벨 토큰은 학습 데이터의 표준 손실에 추가적인 다음 토큰 예측 목표로 추가됩니다. 그 외의 모든 소프트 프롬프트 토큰은 손실에서 마스킹됩니다. 라벨의 의미를 유지하기 위해 라벨 토큰의 델타 임베딩은 절대 변경되지 않습니다.
두 번째 예시의 type_ids 숫자가 4만큼 이동된 것을 눈치채셨을 수 있습니다. CPT는 프롬프트에 제공한 예시의 손실에 지수 가중치를 적용하며, 마지막 예시가 가장 덜 감쇠되고 첫 번째 예시의 손실이 가장 많이 감쇠됩니다. 이를 통해 실제 입력 쿼리에 가까운 예시에 더 큰 가중치를 부여할 수 있으며, opt_weighted_loss_type()가 "decay"로 설정된 경우 opt_loss_decay_factor()로 제어됩니다. 예시를 4만큼 이동시키면 서로 다른 예시를 나열하여 손실이 각 예시에 서로 다른 감쇠 가중치를 부여할 수 있습니다.
cpt_tokens_type_mask는 소프트 프롬프트 자체만을 설명합니다. 학습 시에는 각 배치와 함께 input_type_mask 텐서를 추가로 전달하여 실제 입력의 토큰에 타입 ID를 할당해야 합니다(위와 같이 0-4의 ID를 사용하며, 패딩 또는 문장 종료 토큰은 0으로 표시합니다). CPT는 프롬프트 내 예시의 수만큼 이 ID를 자동으로 이동시키므로, 사용자가 직접 이동시킬 필요가 없습니다. 이는 프롬프트의 예시 목록 끝에 입력을 추가하는 것과 동일합니다. ID는 4를 기준으로 모듈로(modulo)로 해석된다는 점을 유의하세요. 4의 배수로 이동하는 것은 무해하지만, 그 외의 오프셋은 손실에 기여하는 토큰을 조용히 변경합니다. input_type_mask가 전달되지 않으면, CPT는 모든 입력 토큰을 타입 4로 가정하고 전체 입력 시퀀스에 대해 손실을 계산하는 방식으로 폴백됩니다.
벤치마크 개요
이 방법에 대한 벤치마크는 아직 없습니다. 실험 구성을 기여해 주셔도 되지만, 먼저 여기에서 이슈를 생성해 주세요.
API
CPTConfig
클래스 peft.CPTConfig
< source >( task_type: Optional[Union[str, TaskType]] = Nonepeft_type: Optional[Union[str, PeftType]] = Noneauto_mapping: Optional[dict] = Nonepeft_version: Optional[str] = Nonebase_model_name_or_path: Optional[str] = Nonerevision: Optional[str] = Noneinference_mode: bool = Falsenum_virtual_tokens: int = Nonetoken_dim: int = Nonenum_transformer_submodules: Optional[int] = Nonenum_attention_heads: Optional[int] = Nonenum_layers: Optional[int] = Nonemodules_to_save: Optional[list[str]] = Nonecpt_token_ids: list[int] | None = Nonecpt_mask: list[int] | None = Nonecpt_tokens_type_mask: list[int] | None = Noneopt_weighted_loss_type: typing.Literal['none', 'decay'] | None = 'none'opt_loss_decay_factor: float | None = 1.0opt_projection_epsilon: float | None = 0.1opt_projection_format_epsilon: float | None = 0.1tokenizer_name_or_path: str | None = None )
Context-aware Prompt Tuning(CPT)을 위한 PeftConfig를 확장하는 CPT Configuration 클래스
이 클래스는 CPT에 필요한 추가 매개변수를 도입합니다. 예를 들어:
- 토큰 유형 마스크
- Prompt tuning 초기화
- 손실 가중
- Projection 설정
자세한 내용은 다음 논문을 참고하세요: https://huggingface.co/papers/2410.17222
CPTEmbedding
CPTEmbedding은 PEFT의 Context-aware Prompt Tuning(CPT)을 위해 설계된 커스텀 임베딩 레이어입니다. 임베딩을 초기화하고, 프롬프트별 투영을 적용하며, 레이블 마스크를 사용하여 손실을 계산합니다.
calculate_loss
< source >( base_model_outputlabelscpt_type_maskconfig ) → ModelOutput
선택적으로 지수 감쇠를 적용하여 CPT 모델의 손실을 계산합니다.
forward
< source >( indices ) → torch.Tensor
프롬프트 임베딩을 계산하고 델타 조정을 적용합니다.
epsilon 기반 투영을 델타 임베딩에 적용하여 그 노름을 제어합니다.
CPT 토큰 유형 마스크에 따라 토큰 그래디언트를 선택적으로 업데이트하도록 backward 훅을 설정합니다.