Self-Refine와 Self-Consistency: 반복 개선과 표본 투표
두 가지 자기 개선 전략
섹션 제목: “두 가지 자기 개선 전략”에이전트가 외부 도구 없이도 출력 품질을 높이는 방법이 있다. 자신의 출력을 비판하고 수정하는 Self-Refine과, 여러 독립 추론 경로의 다수결을 취하는 Self-Consistency가 그것이다. 두 패턴은 접근 방식이 다르지만 같은 목표를 향한다. 단일 샘플링의 분산을 줄이고 최종 출력의 신뢰도를 높이는 것이다.
Self-Refine: Generate → Critique → Refine
섹션 제목: “Self-Refine: Generate → Critique → Refine”Self-Refine(Madaan et al., NeurIPS 2023)은 세 단계가 반복되는 단순한 루프다.
┌───────────────────────────────────────────────────────────┐│ Self-Refine 루프 ││ ││ 입력 태스크 ││ ▼ ││ ┌──────────┐ ││ │ Generate │ 초기 답변 생성 ││ └────┬─────┘ ││ ▼ ││ ┌──────────┐ ││ │ Critique │ 동일 모델이 답변의 문제점 비판 ││ └────┬─────┘ (명확성, 정확성, 완성도 등) ││ ▼ ││ ┌──────────┐ ││ │ Refine │ 비판을 반영해 답변 수정 ││ └────┬─────┘ ││ ▼ ││ 종료 조건 충족? (max 이터레이션 또는 "충분히 좋음") ││ NO → Critique로 돌아감 ││ YES → 최종 답변 출력 │└───────────────────────────────────────────────────────────┘세 단계 모두 동일한 모델이 수행한다는 점이 핵심이다. 별도 평가 모델이 필요 없고, 추가 학습도 없다. 모델의 Self-Feedback 능력만으로 품질을 끌어올린다.
Python 구현 예시
섹션 제목: “Python 구현 예시”개념 이해용 의사 코드이며 실제 API와 다를 수 있습니다.
def self_refine(model, task: str, max_iterations: int = 3) -> str: """ 동일 모델이 생성→비판→수정을 반복한다. """ # 1단계: 초기 생성 output = model.generate([ {"role": "user", "content": task} ]).text
for i in range(max_iterations): # 2단계: 비판 (Critique) critique_prompt = ( f"아래 답변을 비판하라. 구체적인 문제점과 개선 방향을 제시하라. " f"답변이 충분히 좋으면 '개선 필요 없음'이라고만 답하라.\n\n" f"원래 태스크: {task}\n\n" f"현재 답변:\n{output}" ) critique = model.generate([ {"role": "user", "content": critique_prompt} ]).text
# 종료 조건: 모델이 더 이상 개선 필요 없다고 판단 if "개선 필요 없음" in critique: break
# 3단계: 수정 (Refine) refine_prompt = ( f"아래 비판을 반영해 답변을 개선하라.\n\n" f"원래 태스크: {task}\n\n" f"현재 답변:\n{output}\n\n" f"비판:\n{critique}" ) output = model.generate([ {"role": "user", "content": refine_prompt} ]).text
return outputSelf-Refine의 성능과 실패 모드
섹션 제목: “Self-Refine의 성능과 실패 모드”Self-Refine 논문은 대화 생성, 코드 최적화, 수학 추론 등 7개 태스크에서 평균 약 +20% absolute 개선을 보고했다. 단, 이 수치는 태스크마다 편차가 크고, 평가 기준이 다양하므로 맥락 없이 인용할 때 주의가 필요하다.
가장 흔한 실패 모드는 **아첨적 비판(sycophantic critique)**이다. 모델은 자신이 생성한 답변을 비판할 때 실제로 잘못된 부분이 있어도 “대체로 잘 작성됐습니다” 같은 긍정적 평가를 내놓는 경향이 있다. 이를 방지하기 위해 “반드시 세 가지 구체적 약점을 찾아라”처럼 비판 프롬프트에 의무 비판 조건을 넣는 방식이 사용된다.
Self-Consistency: Fan-Out 샘플링과 다수결
섹션 제목: “Self-Consistency: Fan-Out 샘플링과 다수결”Self-Consistency(Wang et al., ICLR 2023)는 완전히 다른 전략을 취한다. 개선하는 대신 여러 번 독립적으로 샘플링하고, 가장 많이 등장한 답을 채택한다.
┌──────────────────────────────────────────────────────────────┐│ Self-Consistency 흐름 ││ ││ 동일 입력 태스크 ││ ▼ ││ ┌──────────────────────────────────────────────┐ ││ │ Fan-out: N개 독립 샘플 생성 (temperature > 0)│ ││ └──────────────────────────────────────────────┘ ││ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ││ │샘플 1 │ │샘플 2 │ │샘플 3 │ │샘플 N │ ││ │추론:... │ │추론:... │ │추론:... │ │추론:... │ ││ │답: A │ │답: B │ │답: A │ │답: A │ ││ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ ││ └───────────┴───────────┴────────────┘ ││ ▼ ││ 다수결 집계: A(3표) > B(1표) → 최종 답: A │└──────────────────────────────────────────────────────────────┘Self-Consistency의 아이디어는 단순하다. 올바른 추론 경로는 여러 독립 샘플에서도 같은 최종 답으로 수렴하지만, 잘못된 추론 경로는 다양한 오답으로 분산된다. 따라서 다수결이 단일 샘플보다 신뢰할 수 있다.
Python 구현 예시
섹션 제목: “Python 구현 예시”개념 이해용 의사 코드이며 실제 API와 다를 수 있습니다.
from collections import Counter
def self_consistency( model, task: str, n_samples: int = 10, temperature: float = 0.7,) -> str: """ N개의 독립 샘플을 생성하고 다수결로 최종 답을 결정한다. """ answers = []
for _ in range(n_samples): # 각 샘플은 독립적: 이전 샘플의 영향 없음 response = model.generate( messages=[{"role": "user", "content": task}], temperature=temperature, # 다양성 확보를 위해 0보다 큰 값 ) # 최종 답만 추출 (추론 과정은 버림) answer = extract_final_answer(response.text) answers.append(answer)
# 다수결 counter = Counter(answers) majority_answer, vote_count = counter.most_common(1)[0] return majority_answerSelf-Consistency의 검증된 성능 수치
섹션 제목: “Self-Consistency의 검증된 성능 수치”| 벤치마크 | 개선 수치 |
|---|---|
| GSM8K (초등 수학) | +17.9% absolute |
| SVAMP (수학 응용) | +11.0% absolute |
| AQuA (대수 추론) | +12.2% absolute |
수학·논리 추론처럼 정답이 명확히 하나인 태스크에서 특히 효과적이다. 반면 창의적 글쓰기나 열린 질문에서는 “다수결”이라는 집계 방식 자체가 의미 없다. 여러 창의적 답 중 어느 것이 “더 맞다”고 말할 수 없기 때문이다.
두 패턴의 비교
섹션 제목: “두 패턴의 비교”| 차원 | Self-Refine | Self-Consistency |
|---|---|---|
| 핵심 메커니즘 | 반복 비판·수정 | Fan-out 후 다수결 |
| LLM 호출 수 | 이터레이션 × 2~3 | N 샘플 (병렬 가능) |
| 병렬화 가능 여부 | 불가 (순차 의존) | 가능 |
| 적합 태스크 | 오픈엔드, 글쓰기, 코드 품질 | 수학, 논리, 단일 정답 |
| 주요 실패 모드 | 아첨적 비판 | 분산 과다 (N이 작을 때) |
| 비용 구조 | 적은 이터레이션이면 저렴 | N에 비례해 선형 증가 |
실무 적용 팁
섹션 제목: “실무 적용 팁”Self-Refine에서: Critique 단계에 도메인별 루브릭(채점 기준표)을 제공하면 아첨적 비판을 줄일 수 있다. 예를 들어 코드 리뷰라면 “보안 취약점, 성능 병목, 가독성 문제를 각각 하나씩 반드시 찾아라”는 식이다.
Self-Consistency에서: N을 너무 작게(5 미만) 설정하면 다수결의 통계적 신뢰성이 떨어진다. 1020이 실용적 범위다. 또한 온도(temperature)가 너무 낮으면 샘플 간 다양성이 줄어 자동으로 같은 답만 생성되어 다수결 효과가 사라진다. 일반적으로 0.50.8이 적절하다.
다음 챕터에서는 이 두 패턴보다 훨씬 체계적인 탐색 공간 구조를 사용하는 Tree of Thoughts와 Graph of Thoughts를 살펴본다. 더 넓은 가능성 공간을 탐색하되, 비용이 폭발하지 않도록 제어하는 방법을 다룬다.