본문 바로가기
공부/AI

[LangChain] Few Shot Prompting, Dynamic Few Shot Prompting

by 웅대 2023. 10. 21.
728x90
반응형

https://growth-coder.tistory.com/254

이전 포스팅에서 prompt에 대해서 알아보았다.

 

이번 포스팅에서는 Few shot prompting에 대해서 알아보자.

Prompt Engineering

prompt engineering 이란 LLM을 애플리케이션에 효율적으로 사용할 수 있도록 prompt를 최적화하는 기술이다.

 

prompt engineering 기술을 활용한다면 모델에게 구체적인 작업을 지시할 수도 있고 질문, 답변 형식도 원하는대로 바꿀 수가 있다.

 

Zero-Shot Prompting과 Few-Shot Prompting

zero shot prompting이란 예시를 주는 것처럼 학습을 하지 않아도 예측을 진행하는 prompting이다.

 

chat gpt에게 다음 문장을 입력해보자.

 
입력
다음 문장을 긍정 또는 부정으로 분류해주세요.
이 영화 너무 별론데?
출력
주어진 문장 "이 영화 너무 별론데?"는 부정적인 감정을 표현하고 있습니다.

그런데 우리가 원하는 응답 형식은 서술형이 아닌 단답형이라고 생각해보자.

 

이럴 경우 입력과 출력의 예시를 학습시켜서 우리가 원하는 응답을 얻을 수 있다.

 

이를 Few Shot Prompting이라고 한다.

예시

너무 재미가 없다... 
부정
이런게 영화?
부정
시간 가는 줄도 모르고 봤네요
긍정
입력
다음 문장을 긍정 또는 부정으로 분류해주세요.
이 영화 너무 별론데?
출력
부정

이제 "긍정" 혹은 "부정"만 출력하도록 학습을 해보자.

from langchain.prompts import ChatPromptTemplate, FewShotChatMessagePromptTemplate
from langchain.chat_models import ChatOpenAI

# 예시
examples = [
    {"input": "너무 재미없다.", "output": "부정"},
    {"input": "이딴게 영화냐?", "output": "부정"},
    {"input": "시간 가는 줄도 모르고 봤어요", "output": "긍정"},
    {"input": "올 해 완벽한 영화", "output": "긍정"},
    {"input": "돈이 아깝다", "output": "부정"},
]

# 예시 prompt
example_prompt = ChatPromptTemplate.from_messages(
    [
        ("human", "{input}"),
        ("ai", "{output}")
    ]
)

# few shot prompt
few_shot_prompt = FewShotChatMessagePromptTemplate(
    example_prompt=example_prompt,
    examples=examples
)

# 최종 prompt 
final_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "You are an AI that classifies positive and negative."),
        few_shot_prompt,
        ("human", "{input}")
    ]
)

chat = ChatOpenAI()
print(chat.predict_messages(final_prompt.format_messages(input = "이거를 돈 받고 판다고?")))
출력
content='부정'

Dynamic Few-Shot Prompting

이제 Dynamic Few Shoht Prompting에 대해서 알아보자.

 

기본적으로 LLM 모델의 경우 처리해야 할 내용이 길수록 더 많은 리소스가 필요하다.

 

위와 같은 Few shot prompting의 example의 개수가 많을 수록 더 많은 리소스가 필요하고 이는 곧 경제적으로 부담이 될 수 있다.

 

한번 token usage를 비교해보자.

 

predict_messages를 아래와 같이 with 구문과 함께 사용하면 token usage를 추적할 수 있다.

from langchain.callbacks import get_openai_callback

with get_openai_callback() as cb:
    chat.predict_messages(final_prompt.format_messages(input = "이거를 돈 받고 판다고?"))
    print(cb)

 

example이 아래처럼 5개일 때 token usage를 확인해보자.

# 예시
examples = [
    {"input": "너무 재미없다.", "output": "부정"},
    {"input": "이딴게 영화냐?", "output": "부정"},
    {"input": "시간 가는 줄도 모르고 봤어요", "output": "긍정"},
    {"input": "올 해 완벽한 영화", "output": "긍정"},
    {"input": "돈이 아깝다", "output": "부정"},
]
Tokens Used: 144
Prompt Tokens: 142
Completion Tokens: 2
Successful Requests: 1
Total Cost (USD): $0.00021699999999999996

총 144개의 토큰이 사용되었다.

 

이번에는 개수를 15개로 늘려보자.

# 예시
examples = [
    {"input": "너무 재미없다.", "output": "부정"},
    {"input": "이딴게 영화냐?", "output": "부정"},
    {"input": "시간 가는 줄도 모르고 봤어요", "output": "긍정"},
    {"input": "올 해 완벽한 영화", "output": "긍정"},
    {"input": "돈이 아깝다", "output": "부정"},
    {"input": "너무 재미없다.", "output": "부정"},
    {"input": "이딴게 영화냐?", "output": "부정"},
    {"input": "시간 가는 줄도 모르고 봤어요", "output": "긍정"},
    {"input": "올 해 완벽한 영화", "output": "긍정"},
    {"input": "돈이 아깝다", "output": "부정"},
    {"input": "너무 재미없다.", "output": "부정"},
    {"input": "이딴게 영화냐?", "output": "부정"},
    {"input": "시간 가는 줄도 모르고 봤어요", "output": "긍정"},
    {"input": "올 해 완벽한 영화", "output": "긍정"},
    {"input": "돈이 아깝다", "output": "부정"},
]
Tokens Used: 360
Prompt Tokens: 358
Completion Tokens: 2
Successful Requests: 1
Total Cost (USD): $0.000541

총 360개의 토큰이 사용되었다.

 

지금은 example의 수가 적기 때문에 비용적으로 부담되지 않지만 수가 커진다면 부담이 될 수 있다.

 

이럴 때 dynamic few shot prompting을 사용하면 부담을 줄일 수 있다.

 

dynamic few shot prompting에 대해서 간단하게 설명을 하자면 모든 example을 사용하는 것이 아니라 

 

입력 값과 가장 비슷한 예시 몇 가지를 뽑아서 사용하는 것이다.

 

이를 구현하기 위해서는 예시를 벡터화해서 벡터 저장소에 모두 저장해두고 입력 값으로 들어온 문장과 비교해서 가장 비슷한 문장을 가져오면 된다.

 

langchain에서는 이를 위한 유용한 라이브러리들을 제공해준다.

 

example selector, 임베딩, Chroma 벡터 스토어를 사용해보자.

from langchain.prompts import SemanticSimilarityExampleSelector # example 선택
from langchain.embeddings import OpenAIEmbeddings # 임베딩 (벡터화)
from langchain.vectorstores.chroma import Chroma # 벡터 저장소
from langchain.callbacks import get_openai_callback
from langchain.prompts import ChatPromptTemplate, FewShotChatMessagePromptTemplate
from langchain.chat_models import ChatOpenAI
# 예시
examples = [
    {"input": "너무 재미없다.", "output": "부정"},
    {"input": "이딴게 영화냐?", "output": "부정"},
    {"input": "시간 가는 줄도 모르고 봤어요", "output": "긍정"},
    {"input": "올 해 완벽한 영화", "output": "긍정"},
    {"input": "돈이 아깝다", "output": "부정"},
    {"input": "너무 재미없다.", "output": "부정"},
    {"input": "이딴게 영화냐?", "output": "부정"},
    {"input": "시간 가는 줄도 모르고 봤어요", "output": "긍정"},
    {"input": "올 해 완벽한 영화", "output": "긍정"},
    {"input": "돈이 아깝다", "output": "부정"},
    {"input": "너무 재미없다.", "output": "부정"},
    {"input": "이딴게 영화냐?", "output": "부정"},
    {"input": "시간 가는 줄도 모르고 봤어요", "output": "긍정"},
    {"input": "올 해 완벽한 영화", "output": "긍정"},
    {"input": "돈이 아깝다", "output": "부정"},
]
# 벡터화
to_vectorize = [" ".join(example.values()) for example in examples]

# 임베딩
embeddings = OpenAIEmbeddings() 

# 벡터 저장소
with get_openai_callback() as cb:
    vectorstore = Chroma.from_texts(to_vectorize, embeddings, metadatas=examples)
    print(f'벡터화\n{cb}')

# example selector
example_selector = SemanticSimilarityExampleSelector(
    vectorstore=vectorstore,
    k=2, # vector store에서 유사한 2개만 가져옴
)

# input과 비슷한 문장 (벡터) 2개 가져옴
print(example_selector.select_examples({"input": "숨도 못 쉴 정도로 재미있다."}))
# 예시 prompt
example_prompt = ChatPromptTemplate.from_messages(
    [
        ("human", "{input}"),
        ("ai", "{output}")
    ]
)
dynamic_few_shot_prompt = FewShotChatMessagePromptTemplate(
    example_prompt=example_prompt,
    example_selector=example_selector
)

# 최종 prompt 
dynamic_final_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "You are an AI that classifies positive and negative."),
        dynamic_few_shot_prompt,
        ("human", "{input}")
    ]
)

chat = ChatOpenAI()
with get_openai_callback() as cb:
    query = dynamic_final_prompt.format_messages(input="숨도 못 쉴 정도로 재미있다.")
    print(chat.predict_messages(query))
    print(cb)

FewShotChatMessagePromptTemplate을 만들 때 기존에는 examples를 직접 넣어줬는데 이번에는 example_selector를 대신 넣어준 모습을 확인할 수 있다.

 

역시 token usage를 출력하도록 했는데 위에서 15개의 example을 직접 넣어준 경우와 토큰 사용량을 비교해보자.

few shot prompting

Tokens Used: 360
Prompt Tokens: 358
Completion Tokens: 2
Successful Requests: 1
Total Cost (USD): $0.000541

dynamic few shot prompting

Tokens Used: 87
Prompt Tokens: 84
Completion Tokens: 3
Successful Requests: 1
Total Cost (USD): $0.000132

거의 4배 정도 토큰을 적게 사용하는 모습을 확인할 수 있다.

 

과정을 정리해보자.

  1. examples를 벡터화해서 벡터 저장소에 저장
  2. example selector에 벡터 저장소를 연결하고 개수 선택
  3. FewShotChatMessagePromptTemplate에 example selector를 연결
  4. ChatPromptTemplate에 FewShotChatMessagePromptTemplate을 연결

참고

https://www.promptingguide.ai/kr

 

프롬프트 엔지니어링 가이드 – Nextra

A Comprehensive Overview of Prompt Engineering

www.promptingguide.ai

https://python.langchain.com/docs/modules/model_io/prompts/prompt_templates/few_shot_examples

 

Few-shot prompt templates | 🦜️🔗 Langchain

In this tutorial, we'll learn how to create a prompt template that uses few-shot examples. A few-shot prompt template can be constructed from either a set of examples, or from an Example Selector object.

python.langchain.com

Dynamic Few-Shot Prompting: ChatGPT 텍스트 분류를 위한 컨텍스트 제한 극복 | by 이리나 콘드라시첸코 | 보통 (medium.com)

 

Dynamic Few-Shot Prompting: Overcoming Context Limit for ChatGPT Text Classification

Recent explosion in the popularity of large language models like ChatGPT has led to their increased usage in classical NLP tasks like…

medium.com

 

728x90
반응형

댓글