2.2.2. Example Selector

“Example Selector”는 주로 프롬프트 엔지니어링에서 few-shot 학습을 최적화하기 위해 사용되는 도구입니다.

LangChain을 활용해 대규모 언어 모델(LLM)을 다룰 때, 모델이 특정 작업을 수행하도록 유도하려면 적절한 프롬프트 설계가 필수적입니다. 특히 few-shot 학습(few-shot learning)에서는 모델에 제공되는 예시(example)가 결과의 품질에 큰 영향을 미칩니다. 그러나 모든 상황에서 고정된 예시를 사용하는 것은 비효율적일 수 있습니다. 작업의 맥락이나 입력 데이터에 따라 적합한 예시가 달라질 수 있기 때문입니다. 이 문제를 해결하기 위해 LangChain은 Example Selector라는 강력한 도구를 제공합니다.

Example Selector는 입력된 쿼리나 작업에 따라 동적으로 적합한 예시를 선택해 프롬프트에 포함시키는 도구입니다. LangChain에서 few-shot 학습을 구현할 때, 모든 예시를 나열하는 대신, 주어진 상황에 가장 관련성 높은 예시를 자동으로 골라내어 모델의 성능을 최적화합니다. 이는 특히 데이터셋이 크거나 예시의 다양성이 중요한 경우에 유용합니다.

예를 들어, 고객 지원 챗봇을 개발한다고 가정해봅시다. 사용자가 “결제 오류”에 대해 질문하면, 결제와 관련된 예시를 제공하는 것이 효과적입니다. 반면 “배송 지연”에 대한 질문에는 배송 관련 예시가 더 적합합니다. Example Selector는 이러한 맥락적 요구를 충족하도록 설계되었습니다.

Example Selector의 주요 유형

LangChain은 다양한 상황에 맞춘 여러 Example Selector를 제공합니다. 아래에서 대표적인 유형을 살펴보겠습니다.

1. Semantic Similarity Example Selector

  • 설명: 입력 쿼리와 예시 간의 의미적 유사성을 계산해 가장 관련성 높은 예시를 선택합니다. 이를 위해 일반적으로 임베딩 모델(예: OpenAI의 텍스트 임베딩)을 사용합니다.
  • 작동 원리: 쿼리와 예시를 벡터로 변환한 뒤, 코사인 유사도(cosine similarity) 같은 메트릭을 활용해 유사도를 측정합니다.
  • 사용 사례: 질문 답변 시스템, 문서 요약 등에서 입력과 의미적으로 가까운 예시가 필요한 경우.
  • 장점: 맥락에 따라 동적으로 예시를 선택하므로 모델의 응답 품질이 향상됩니다.
  • 예제 코드:
from langchain.prompts import FewShotPromptTemplate, PromptTemplate
from langchain.prompts.example_selector import SemanticSimilarityExampleSelector
from langchain_ollama import OllamaEmbeddings
from langchain_chroma import Chroma

# 예시 데이터셋
examples = [
    {"input": "결제 오류가 발생했어요", "output": "결제 오류는 보통 카드 정보가 잘못 입력되었거나 은행 문제일 수 있습니다."},
    {"input": "배송이 늦어졌어요", "output": "배송 지연은 물류 상황에 따라 발생할 수 있으며, 추적 번호로 확인 가능합니다."}
]

# 임베딩 및 벡터 저장소 설정
embeddings = OllamaEmbeddings(model='exaone3.5')
selector = SemanticSimilarityExampleSelector.from_examples(
    examples,
    embeddings,
    Chroma,
    k=1  # 선택할 예시 개수
)

# 프롬프트 템플릿
prompt = FewShotPromptTemplate(
    example_selector=selector,
    example_prompt=PromptTemplate.from_template("입력: {input}\n출력: {output}"),
    prefix="다음 질문에 답변하세요:",
    suffix="\n질문: {query}",
    input_variables=["query"]
)

# 프롬프트 생성
query = "결제 문제가 있어요"
formatted_prompt = prompt.format(query=query)
print(formatted_prompt)
다음 질문에 답변하세요:

입력: 결제 오류가 발생했어요
출력: 결제 오류는 보통 카드 정보가 잘못 입력되었거나 은행 문제일 수 있습니다.

질문: 결제 문제가 있어요

2. Length-Based Example Selector

  • 설명: 예시의 길이를 기준으로 선택합니다. 예를 들어, 너무 긴 예시는 제외하거나 짧은 예시만 선택할 수 있습니다.
  • 작동 원리: 예시 텍스트의 문자 수나 토큰 수를 계산해 조건에 맞는 예시를 필터링합니다.
  • 사용 사례: 프롬프트 길이 제한이 있는 환경(예: 특정 LLM의 토큰 한도)에서 유용합니다.
  • 장점: 간단하고 빠르게 적용 가능합니다.
  • 예제 코드:
from langchain.prompts.example_selector import LengthBasedExampleSelector

examples = [
    {"input": "안녕", "output": "안녕하세요!"},
    {"input": "오늘 날씨 어때요?", "output": "오늘은 맑고 따뜻한 날씨입니다. 외출하기 좋아요."}
]

# 예시 포맷팅 템플릿 생성 (추가)
example_prompt = PromptTemplate.from_template("입력: {input}\n출력: {output}")

selector = LengthBasedExampleSelector(
    examples=examples,
    example_prompt=example_prompt,
    max_length=20  # 최대 길이 (문자 수)
)

prompt = FewShotPromptTemplate(
    example_selector=selector,
    example_prompt=example_prompt,
    prefix="질문에 답하세요:",
    suffix="\n질문: {query}",
    input_variables=["query"]
)

print(prompt.format(query="안녕하세요"))
질문에 답하세요:

입력: 안녕
출력: 안녕하세요!

입력: 오늘 날씨 어때요?
출력: 오늘은 맑고 따뜻한 날씨입니다. 외출하기 좋아요.

질문: 안녕하세요

3. Max Marginal Relevance (MMR) Example Selector

  • 설명: 유사성과 다양성을 동시에 고려해 예시를 선택합니다. 입력과 유사하면서도 서로 중복되지 않는 예시를 골라냅니다.
  • 작동 원리: MMR 알고리즘을 사용해 유사도와 다양성 간 균형을 맞춥니다.
  • 사용 사례: 여러 관점에서 답변을 제공해야 하는 경우(예: 창의적 글쓰기, 복잡한 문제 해결).
  • 장점: 단일 주제에 치우치지 않고 다양한 예시를 활용 가능합니다.
  • 예제 코드:
from langchain_community.vectorstores import FAISS
from langchain_core.example_selectors import MaxMarginalRelevanceExampleSelector
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate

# 1. 예제 데이터 정의
examples = [
    {"input": "행복한", "output": "슬픈"},
    {"input": "높은", "output": "낮은"},
    {"input": "시끄러운", "output": "조용한"},
]

# 2. 예제 포맷 템플릿 설정
example_prompt = PromptTemplate(
    input_variables=["input", "output"],
    template="입력: {input}\n출력: {output}",
)

# 3. MMR 예제 선택기 초기화
example_selector = MaxMarginalRelevanceExampleSelector.from_examples(
    examples,
    embeddings,
    FAISS,               # FAISS 벡터 저장소
    k=2,                 # 선택할 예제 수
)

# 4. FewShot 프롬프트 구성
mmr_prompt = FewShotPromptTemplate(
    example_selector=example_selector,
    example_prompt=example_prompt,
    prefix="모든 입력의 반의어를 제공하세요:",
    suffix="입력: {adjective}\n출력:",
    input_variables=["adjective"],
)

# 5. 실행 예시
print(mmr_prompt.format(adjective="불안한"))
모든 입력의 반의어를 제공하세요:

입력: 시끄러운
출력: 조용한

입력: 높은
출력: 낮은

입력: 불안한
출력:

Example Selector 활용 팁

  1. 작업 맞춤화: 작업의 성격에 따라 적합한 Selector를 선택하세요. 단순한 작업에는 Length-Based를, 복잡한 맥락 이해가 필요한 경우에는 Semantic Similarity를 추천합니다.
  2. 예시 데이터 준비: Selector의 성능은 제공된 예시의 품질에 크게 의존합니다. 명확하고 구체적인 예시를 준비하는 것이 중요합니다.
  3. k 값 조정: 선택할 예시 개수(k)를 조정해 프롬프트의 길이와 정보 밀도를 최적화하세요.
  4. 테스트와 반복: 다양한 쿼리로 Selector를 테스트하며 결과를 비교해 최적의 설정을 찾아보세요.

Example Selector 또 다른 예시

from langchain_chroma import Chroma
from langchain_ollama import OllamaEmbeddings
from langchain_core.example_selectors import MaxMarginalRelevanceExampleSelector, SemanticSimilarityExampleSelector

examples = [
   {
       "question": "스티브 잡스와 아인슈타인 중 누가 더 오래 살았나요?",
       "answer": """이 질문에 추가 질문이 필요한가요: 예.
추가 질문: 스티브 잡스는 몇 살에 사망했나요?
중간 답변: 스티브 잡스는 56세에 사망했습니다.
추가 질문: 아인슈타인은 몇 살에 사망했나요?
중간 답변: 아인슈타인은 76세에 사망했습니다.
최종 답변은: 아인슈타인
""",
   },
   {
       "question": "마이크로소프가 창립했을 때, 스티브 잡스는 몇살이었나요?",
       "answer": """이 질문에 추가 질문이 필요한가요: 예.
추가 질문: 마이크로소프트 설립일은 언제인가요?
중간 답변: 마이크로소프트는 1975년 4월 4일 빌 게이츠와 폴 앨런에 의해 설립되었다.
추가 질문: 스티브 잡스는 몇 년에 태어났나요?
중간 답변: 스티브 잡스는 1955년 2월 24일에 미국 캘리포니아주 샌프란시스코에서 태어났습니다.
최종 답변은: 20살 (1975년 - 1955년)
""",
   },
   {
       "question": "율곡 이이의 어머니가 태어난 해의 통치하던 왕은 누구인가요?",
       "answer": """이 질문에 추가 질문이 필요한가요: 예.
추가 질문: 율곡 이이의 어머니는 누구인가요?
중간 답변: 율곡 이이의 어머니는 신사임당입니다.
추가 질문: 신사임당은 언제 태어났나요?
중간 답변: 신사임당은 1504년에 태어났습니다.
추가 질문: 1504년에 조선을 통치한 왕은 누구인가요?
중간 답변: 1504년에 조선을 통치한 왕은 연산군입니다.
최종 답변은: 연산군
""",
   },
   {
       "question": "올드보이와 기생충의 감독이 같은 나라 출신인가요?",
       "answer": """이 질문에 추가 질문이 필요한가요: 예.
추가 질문: 올드보이의 감독은 누구인가요?
중간 답변: 올드보이의 감독은 박찬욱입니다.
추가 질문: 박찬욱은 어느 나라 출신인가요?
중간 답변: 박찬욱은 대한민국 출신입니다.
추가 질문: 기생충의 감독은 누구인가요?
중간 답변: 기생충의 감독은 봉준호입니다.
추가 질문: 봉준호는 어느 나라 출신인가요?
중간 답변: 봉준호는 대한민국 출신입니다.
최종 답변은: 예
""",
   },
]

# Vector DB 생성 (저장소 이름, 임베딩 클래스)
embeddings_model = OllamaEmbeddings(model="exaone3.5")
chroma = Chroma("example_selector", embeddings_model)

example_selector = SemanticSimilarityExampleSelector.from_examples(
   # 여기에는 선택 가능한 예시 목록이 있습니다.
   examples,
   # 여기에는 의미적 유사성을 측정하는 데 사용되는 임베딩을 생성하는 임베딩 클래스가 있습니다.
   embeddings_model,
   # 여기에는 임베딩을 저장하고 유사성 검색을 수행하는 데 사용되는 VectorStore 클래스가 있습니다.
   Chroma,
   # 이것은 생성할 예시의 수입니다.
   k=1,
)

question = "구글이 창립했을 때, 빌 게이츠는 몇살이었나요?"

# 입력과 가장 유사한 예시를 선택합니다.
selected_examples = example_selector.select_examples({"question": question})




from langchain_core.prompts.few_shot import FewShotPromptTemplate
from langchain_core.prompts import PromptTemplate

example_prompt = PromptTemplate.from_template(
   "Question:\n{question}\nAnswer:\n{answer}"
)



prompt = FewShotPromptTemplate(
   example_selector=example_selector,
   example_prompt=example_prompt,
   suffix="Question:\n{question}\nAnswer:",
   input_variables=["question"],
)

from langchain_ollama import ChatOllama

llm_model = ChatOllama(model='exaone3.5', temperature=0)

# 체인 생성
chain = prompt | llm_model
answer = chain.invoke({"question": question})
print(answer.content)
이 질문에 대한 정확한 답변을 위해서는 몇 가지 추가 정보가 필요합니다:

구글 창립 날짜: 구글은 1998년 9월 6일에 창립되었습니다.
빌 게이츠의 생년월일: 빌 게이츠는 1955년 10월 28일에 태어났습니다.

이제 계산해보겠습니다:

1998년 (구글 창립) – 1955년 (빌 게이츠 출생) = 43세

따라서, 구글이 창립되었을 때 빌 게이츠는 43세였습니다.
이 글은 카테고리: 랭체인 (LangChain)에 포함되어 있습니다. 고유주소를 북마크하세요.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다