Skip to main content

Few-shot 프롬프팅

Few-shot 프롬프팅은 LLM에게 소수의 입출력 예시를 보여주고 새로운 입력에 대해 동일한 패턴으로 응답하도록 유도하는 기법입니다. 별도의 학습 없이 프롬프트 안의 예시만으로 태스크를 정의할 수 있어, In-Context Learning(문맥 내 학습)의 핵심 메커니즘입니다.

학습 목표

이 문서를 완료하면 다음을 수행할 수 있습니다.
  • Zero-shot, One-shot, Few-shot의 차이를 실습으로 비교할 수 있습니다
  • 효과적인 예시를 선택하는 전략을 적용할 수 있습니다
  • OpenAI API를 사용하여 Few-shot 프롬프팅을 구현할 수 있습니다
  • 각 접근법의 적합한 사용 상황을 판단할 수 있습니다

Zero-shot / One-shot / Few-shot 비교

실습: 감성 분류로 비교하기

1

환경 설정

OpenAI API를 사용하여 각 접근법을 비교합니다.
pip install openai
from openai import OpenAI
client = OpenAI()

def ask_llm(system_prompt, user_prompt, model="gpt-4o-mini"):
    """LLM에게 질문하고 응답을 받는 헬퍼 함수"""
    response = client.chat.completions.create(
        model=model,
        messages=[
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": user_prompt},
        ],
        temperature=0,  # 일관된 결과를 위해 0으로 설정
    )
    return response.choices[0].message.content

# 테스트 데이터
test_reviews = [
    "배송은 빨랐는데 제품 품질이 별로예요",
    "가격 대비 훌륭합니다 재구매 의사 있어요",
    "설명서가 불친절해서 조립에 시간이 오래 걸렸어요",
    "디자인이 사진이랑 똑같아서 만족합니다",
]
2

Zero-shot: 예시 없이 지시만 제공

예시 없이 지시문만으로 태스크를 수행합니다.
system_prompt = """당신은 고객 리뷰 분석기입니다.
주어진 리뷰의 감성을 분류하세요.
출력 형식: JSON {"감성": "긍정|부정|혼합", "핵심_키워드": [...], "신뢰도": 0.0~1.0}"""

for review in test_reviews:
    result = ask_llm(system_prompt, review)
    print(f"리뷰: {review}")
    print(f"결과: {result}\n")
Zero-shot의 특징:
  • 가장 간단하고 토큰 비용이 적습니다
  • 출력 형식이 불안정할 수 있습니다
  • 모델의 사전 학습 지식에 의존합니다
3

One-shot: 예시 1개 제공

1개의 예시를 추가하여 출력 형식과 판단 기준을 보여줍니다.
system_prompt = """당신은 고객 리뷰 분석기입니다.
주어진 리뷰의 감성을 분류하세요.

## 예시
입력: "가격은 좀 비싸지만 성능은 확실히 좋네요"
출력: {"감성": "혼합", "핵심_키워드": ["가격", "성능"], "신뢰도": 0.85}

## 규칙
- 긍정과 부정이 모두 있으면 "혼합"으로 분류합니다
- 핵심_키워드는 최대 3개까지 추출합니다
- 출력은 반드시 JSON 형식이어야 합니다"""

for review in test_reviews:
    result = ask_llm(system_prompt, review)
    print(f"리뷰: {review}")
    print(f"결과: {result}\n")
One-shot의 특징:
  • 출력 형식이 안정화됩니다
  • “혼합” 분류의 기준을 예시로 보여줄 수 있습니다
  • 1개 예시만으로 부족한 경우가 있습니다
4

Few-shot: 예시 3~5개 제공

다양한 패턴의 예시를 포함하여 분류 기준을 명확히 합니다.
system_prompt = """당신은 고객 리뷰 분석기입니다.
주어진 리뷰의 감성을 분류하세요.

## 예시

입력: "배송도 빠르고 포장도 깔끔해요. 100점!"
출력: {"감성": "긍정", "핵심_키워드": ["배송", "포장"], "신뢰도": 0.95}

입력: "제품이 불량이고 교환도 안 해줘요"
출력: {"감성": "부정", "핵심_키워드": ["불량", "교환"], "신뢰도": 0.92}

입력: "가격은 좀 비싸지만 성능은 확실히 좋네요"
출력: {"감성": "혼합", "핵심_키워드": ["가격", "성능"], "신뢰도": 0.85}

입력: "그냥 그래요 특별한 건 없어요"
출력: {"감성": "부정", "핵심_키워드": ["보통"], "신뢰도": 0.60}

입력: "디자인은 이쁜데 실용성이 떨어져요"
출력: {"감성": "혼합", "핵심_키워드": ["디자인", "실용성"], "신뢰도": 0.80}

## 규칙
- 긍정과 부정이 모두 있으면 "혼합"으로 분류합니다
- 핵심_키워드는 최대 3개까지 추출합니다
- 출력은 반드시 JSON 형식이어야 합니다"""

for review in test_reviews:
    result = ask_llm(system_prompt, review)
    print(f"리뷰: {review}")
    print(f"결과: {result}\n")
Few-shot의 특징:
  • 출력 형식이 매우 안정적입니다
  • 경계 사례(edge case)에 대한 판단 기준을 전달할 수 있습니다
  • “그냥 그래요” 같은 애매한 표현의 분류 기준을 학습합니다
5

결과 비교 및 분석

각 접근법의 결과를 정량적으로 비교합니다.
import json

# 정답 레이블 (사람이 판단한 기준)
ground_truth = [
    {"감성": "혼합"},   # 배송 빠르지만 품질 별로
    {"감성": "긍정"},   # 가격 대비 훌륭
    {"감성": "부정"},   # 설명서 불친절
    {"감성": "긍정"},   # 디자인 만족
]

def evaluate_approach(name, system_prompt, test_data, labels):
    """접근법별 정확도 평가"""
    correct = 0
    total = len(test_data)

    for review, label in zip(test_data, labels):
        result = ask_llm(system_prompt, review)
        try:
            parsed = json.loads(result)
            if parsed["감성"] == label["감성"]:
                correct += 1
        except (json.JSONDecodeError, KeyError):
            pass  # 파싱 실패 = 오답 처리

    accuracy = correct / total
    print(f"{name}: {accuracy:.0%} ({correct}/{total})")
    return accuracy

# 각 접근법 평가 (system_prompt는 위에서 정의한 것 사용)
# evaluate_approach("Zero-shot", zero_shot_prompt, test_reviews, ground_truth)
# evaluate_approach("One-shot",  one_shot_prompt,  test_reviews, ground_truth)
# evaluate_approach("Few-shot",  few_shot_prompt,  test_reviews, ground_truth)
일반적인 성능 경향:
접근법형식 준수분류 정확도토큰 비용
Zero-shot낮음보통낮음
One-shot높음보통~높음중간
Few-shot (3~5)매우 높음높음높음

예시 선택 전략

Few-shot의 성능은 어떤 예시를 선택하는가에 크게 의존합니다.

전략 1: 다양성 확보 (Diversity)

모든 클래스/유형을 균등하게 포함합니다.
✅ 긍정 2개 + 부정 2개 + 혼합 1개 = 5개 예시
❌ 긍정 4개 + 부정 1개 = 5개 예시 (편향됨)

전략 2: 경계 사례 포함 (Edge Cases)

모델이 혼동하기 쉬운 사례를 포함합니다.
✅ "그냥 그래요" (부정? 중립?), "비싸지만 좋아요" (혼합)
❌ "최고입니다!" (너무 명확한 긍정만)

전략 3: 유사 예시 선택 (Similarity-based)

테스트 입력과 유사한 예시를 동적으로 선택합니다.
from sentence_transformers import SentenceTransformer, util

model = SentenceTransformer("paraphrase-multilingual-MiniLM-L12-v2")

# 예시 풀 (후보 예시들)
example_pool = [
    {"input": "배송이 너무 느렸어요", "output": '{"감성": "부정"}'},
    {"input": "포장이 꼼꼼했습니다", "output": '{"감성": "긍정"}'},
    {"input": "맛은 좋은데 양이 적어요", "output": '{"감성": "혼합"}'},
    # ... 더 많은 예시
]

def select_similar_examples(query, pool, k=3):
    """입력과 가장 유사한 예시 K개 선택"""
    query_emb = model.encode(query, convert_to_tensor=True)
    pool_inputs = [ex["input"] for ex in pool]
    pool_embs = model.encode(pool_inputs, convert_to_tensor=True)

    scores = util.cos_sim(query_emb, pool_embs)[0]
    top_k = scores.topk(k)

    return [pool[idx] for idx in top_k.indices]

# 사용 예시
query = "배달은 빨랐는데 음식이 식었어요"
selected = select_similar_examples(query, example_pool, k=3)
# → 배송/음식 관련 예시가 우선 선택됨

전략 4: 순서 효과 고려 (Order Effect)

예시의 순서가 결과에 영향을 줄 수 있습니다.
권장 순서:
1. 가장 전형적인 예시를 먼저
2. 경계 사례를 중간에
3. 테스트 입력과 가장 유사한 예시를 마지막에 (recency bias 활용)

언제 어떤 접근법을 사용할까?

상황추천 접근법이유
단순 분류/요약Zero-shot모델이 이미 잘 아는 태스크
특정 출력 형식 필요One-shot~Few-shot형식 안정성 필요
도메인 특화 분류Few-shot (3~5)도메인 용어/기준 전달
복잡한 변환 규칙Few-shot (5~10)다양한 패턴 학습 필요
비용 최적화 중요Zero-shot + 형식 지정토큰 절약
최고 품질 필요Few-shot + CoT예시 + 추론 과정
일반적으로 3~5개가 가장 효과적입니다. 연구에 따르면 예시 수를 늘릴수록 성능이 향상되지만, 5개 이후로는 향상 폭이 줄어듭니다. 10개 이상은 입력 토큰 비용 대비 효율이 낮아집니다. 다만 복잡한 패턴을 학습해야 하는 경우(코드 변환 등)에는 더 많은 예시가 도움될 수 있습니다.
모델은 예시의 패턴을 그대로 학습하므로, 잘못된 예시는 잘못된 결과를 유도합니다. 예시의 정확성은 반드시 검증하세요. 또한 편향된 예시(특정 클래스만 긍정 예시)는 모델의 판단을 왜곡합니다.
간단한 태스크(분류, 형식 변환)에서는 Few-shot으로 충분한 경우가 많습니다. 그러나 대량 데이터 처리, 매우 높은 정확도 요구, 비용 최적화(API 호출 비용 > 학습 비용)가 필요한 경우에는 Fine-tuning이 더 적합합니다. 일반적으로 Few-shot으로 시작하고, 성능이 부족하면 Fine-tuning을 고려하세요.

다음 문서