LLM(대형 언어 모델)의 기능이 계속 확장됨에 따라 해당 잠재력을 활용하는 강력한 AI 시스템을 개발하는 것이 점점 더 복잡해지고 있습니다. 기존 접근 방식에는 도메인별 제약 조건을 준수하기 위한 복잡한 프롬프트 기술, 미세 조정을 위한 데이터 생성, 수동 지침이 포함되는 경우가 많습니다. 그러나 이 프로세스는 지루하고 오류가 발생하기 쉬우며 사람의 개입에 크게 의존할 수 있습니다.
엔터 버튼 DSPy, LLM으로 구동되는 AI 시스템 개발을 간소화하도록 설계된 혁신적인 프레임워크입니다. DSPy는 LM 프롬프트와 가중치를 최적화하기 위한 체계적인 접근 방식을 도입하여 개발자가 최소한의 수동 노력으로 정교한 애플리케이션을 구축할 수 있도록 합니다.
이 종합 가이드에서는 DSPy의 핵심 원리, 모듈식 아키텍처, DSPy가 제공하는 다양한 강력한 기능을 살펴보겠습니다. 또한 실제 사례를 살펴보고 DSPy가 LLM을 사용하여 AI 시스템을 개발하는 방식을 어떻게 변화시킬 수 있는지 보여줍니다.
DSPy란 무엇이며 왜 필요한가요?
DSPy는 프로그램 흐름을 분리하는 프레임워크입니다(modules)를 각 단계의 매개변수(LM 프롬프트 및 가중치)에서 추출합니다. 이러한 분리를 통해 LM 프롬프트 및 가중치를 체계적으로 최적화할 수 있으므로 신뢰성, 예측 가능성이 뛰어나고 도메인별 제약 조건을 준수하는 복잡한 AI 시스템을 구축할 수 있습니다.
전통적으로 LLM을 사용하여 AI 시스템을 개발하려면 문제를 여러 단계로 나누고, 각 단계에 대한 복잡한 프롬프트를 작성하고, 미세 조정을 위한 합성 예제를 생성하고, 특정 제약 조건을 준수하도록 LM을 수동으로 안내하는 힘든 프로세스가 필요했습니다. 이 접근 방식은 시간이 많이 걸릴 뿐만 아니라 오류가 발생하기 쉬웠습니다. 파이프라인, LM 또는 데이터를 조금만 변경해도 프롬프트와 미세 조정 단계를 광범위하게 재작업해야 할 수 있기 때문입니다.
DSPy는 새로운 패러다임을 도입하여 이러한 과제를 해결합니다. 옵티 마이저. 이러한 LM 기반 알고리즘은 극대화하려는 측정항목에 따라 LM 통화의 프롬프트와 가중치를 조정할 수 있습니다. 최적화 프로세스를 자동화함으로써 DSPy는 개발자가 최소한의 수동 개입으로 강력한 AI 시스템을 구축할 수 있도록 지원하여 LM 출력의 신뢰성과 예측 가능성을 향상시킵니다.
LangChain vs. DSPy
LLM 프레임워크 중 가장 대중적으로 알려진 LangChain과 오늘 소개할 DSPy를 비교해보면서, DSPy의 특징을 더 구체적으로 살펴보겠습니다.
DSPy | LangChain | |
출시일 | 2022.12 | 2022.10 |
프롬프트 처리 | 프롬프트 자동 최적화 | 수동 프롬프트 엔지니어링 |
강점 | 자동 최적화로 성능/안정성 향상 복잡한 파이프라인 구축 용이 |
방대한 통합 기능 (DB, API, 벡터 스토어 등) 유연한 체인 구성 |
생태계/커뮤니티 | 상대적으로 작은 커뮤니티 관련 정보 찾기 어려움 |
크고 활발한 커뮤니티 방대한 관련 문서 |
적합한 프로젝트 | 복잡한 다단계 추론 파이프라인, 성능/안정성 자동 최적화가 중요한 경우 |
다양한 외부 도구/데이터통합이 중요하고, 유연한 구성이 필요한 경우 |
DSPy의 모듈형 아키텍처
DSPy의 핵심에는 복잡한 AI 시스템의 구성을 용이하게 하는 모듈식 아키텍처가 있습니다. 프레임워크는 다음과 같은 다양한 프롬프트 기술을 추상화하는 내장 모듈 세트를 제공합니다. dspy.ChainOfThought 그리고 dspy.ReAct. 이러한 모듈을 결합하여 더 큰 프로그램으로 구성할 수 있으므로 개발자는 특정 요구 사항에 맞는 복잡한 파이프라인을 구축할 수 있습니다.
각 모듈은 지침, Few-Shot 예제 및 LM 가중치를 포함하여 학습 가능한 매개변수를 캡슐화합니다. 모듈이 호출되면 DSPy의 최적화 프로그램은 이러한 매개변수를 미세 조정하여 원하는 메트릭을 최대화하여 LM의 출력이 지정된 제약 조건과 요구 사항을 준수하도록 보장할 수 있습니다.
DSPy로 최적화
DSPy는 AI 시스템의 성능과 신뢰성을 향상시키도록 설계된 다양하고 강력한 최적화 도구를 도입합니다. 이러한 최적화 프로그램은 LM 기반 알고리즘을 활용하여 LM 통화의 프롬프트와 가중치를 조정하고 도메인별 제약 조건을 준수하면서 지정된 지표를 최대화합니다.
DSPy에서 사용할 수 있는 주요 최적화 프로그램 중 일부는 다음과 같습니다.
- 부트스트랩 FewShot: 이 옵티마이저는 모델로 전송된 프롬프트 내에 최적화된 예시를 자동으로 생성하고 포함시켜 시그니처를 확장하고 퓨샷 학습을 구현합니다.
- 부트스트랩 FewShotWithRandomSearch: 적용 BootstrapFewShot 생성된 데모에 대해 무작위 검색을 여러 번 수행하고 최적화를 통해 최상의 프로그램을 선택합니다.
- 미프로: 데이터 인식 및 데모 인식 명령 생성을 통해 각 단계에서 명령 및 몇 장의 예제를 생성합니다. 베이지안 최적화를 사용하여 모듈 전체에서 생성 지침 및 데모 공간을 효과적으로 검색합니다.
- 부트스트랩 Finetune: 프롬프트 기반 DSPy 프로그램을 더 작은 LM에 대한 가중치 업데이트로 추출하여 효율성 향상을 위해 기본 LLM을 미세 조정할 수 있습니다.
개발자는 이러한 최적화 도구를 활용하여 AI 시스템을 체계적으로 최적화하여 도메인별 제약 조건과 요구 사항을 준수하면서 고품질 출력을 보장할 수 있습니다.
DSPy 시작하기
DSPy의 강력한 기능을 설명하기 위해 질문 답변을 위한 RAG(검색 증강 생성) 시스템을 구축하는 실제 사례를 살펴보겠습니다.
1단계: 언어 모델 및 검색 모델 설정
첫 번째 단계에는 DSPy 내에서 언어 모델(LM)과 검색 모델(RM)을 구성하는 작업이 포함됩니다.
DSPy를 설치하려면 다음을 실행하세요.
pip install dspy-ai
DSPy는 여러 LM 및 RM API는 물론 로컬 모델 호스팅도 지원하므로 선호하는 모델을 쉽게 통합할 수 있습니다.
import dspy
# Configure the LM and RM
turbo = dspy.OpenAI(model='gpt-3.5-turbo')
colbertv2_wiki17_abstracts = dspy.ColBERTv2(url='http://20.102.90.50:2017/wiki17_abstracts')
dspy.settings.configure(lm=turbo, rm=colbertv2_wiki17_abstracts)
2단계: 데이터 세트 로드
다음으로 일반적으로 다중 홉 방식으로 답변되는 복잡한 질문-답변 쌍 모음이 포함된 HotPotQA 데이터 세트를 로드합니다.
from dspy.datasets import HotPotQA
# Load the dataset
dataset = HotPotQA(train_seed=1, train_size=20, eval_seed=2023, dev_size=50, test_size=0)
# Specify the 'question' field as the input
trainset = [x.with_inputs('question') for x in dataset.train]
devset = [x.with_inputs('question') for x in dataset.dev]
DSPy는 서명을 사용하여 모듈의 동작을 정의합니다. 이 예에서는 입력 필드(컨텍스트 및 질문)와 출력 필드(답변)를 지정하여 답변 생성 작업에 대한 서명을 정의합니다.
class GenerateAnswer(dspy.Signature):
"""Answer questions with short factoid answers."""
context = dspy.InputField(desc="may contain relevant facts")
question = dspy.InputField()
answer = dspy.OutputField(desc="often between 1 and 5 words")
4단계: 파이프라인 구축
RAG 파이프라인을 DSPy 모듈로 구축할 것입니다. 이는 하위 모듈(dspy.Retrieve 및 dspy.ChainOfThought)을 선언하는 초기화 메소드(__init__)와 응답 제어 흐름을 설명하는 전달 메소드(forward)로 구성됩니다. 이 모듈을 사용하는 질문입니다.
class RAG(dspy.Module):
def __init__(self, num_passages=3):
super().__init__()
self.retrieve = dspy.Retrieve(k=num_passages)
self.generate_answer = dspy.ChainOfThought(GenerateAnswer)
def forward(self, question):
context = self.retrieve(question).passages
prediction = self.generate_answer(context=context, question=question)
return dspy.Prediction(context=context, answer=prediction.answer)
5단계: 파이프라인 최적화
파이프라인이 정의되었으면 이제 DSPy의 최적화 도구를 사용하여 파이프라인을 최적화할 수 있습니다. 이 예에서는 훈련 세트와 검증용 지표를 기반으로 모듈에 대한 효과적인 프롬프트를 생성하고 선택하는 BootstrapFewShot 최적화 프로그램을 사용합니다.
from dspy.teleprompt import BootstrapFewShot
# Validation metric
def validate_context_and_answer(example, pred, trace=None):
answer_EM = dspy.evaluate.answer_exact_match(example, pred)
answer_PM = dspy.evaluate.answer_passage_match(example, pred)
return answer_EM and answer_PM
# Set up the optimizer
teleprompter = BootstrapFewShot(metric=validate_context_and_answer)
# Compile the program
compiled_rag = teleprompter.compile(RAG(), trainset=trainset)
6단계: 파이프라인 평가
프로그램을 컴파일한 후에는 원하는 정확성과 신뢰성을 충족하는지 확인하기 위해 개발 세트에서 성능을 평가하는 것이 필수적입니다.
from dspy.evaluate import Evaluate
# Set up the evaluator
evaluate = Evaluate(devset=devset, metric=validate_context_and_answer, num_threads=4, display_progress=True, display_table=0)
# Evaluate the compiled RAG program
evaluation_result = evaluate(compiled_rag)
print(f"Evaluation Result: {evaluation_result}")
7단계: 모델 기록 검사
모델의 상호 작용을 더 깊이 이해하려면 모델 기록을 검사하여 가장 최근 세대를 검토할 수 있습니다.
# Inspect the model's history
turbo.inspect_history(n=1)
8단계: 예측하기
파이프라인이 최적화되고 평가되었으므로 이제 이를 사용하여 새로운 질문에 대한 예측을 할 수 있습니다.
# Example question
question = "Which award did Gary Zukav's first book receive?"
# Make a prediction using the compiled RAG program
prediction = compiled_rag(question)
print(f"Question: {question}")
print(f"Answer: {prediction.answer}")
print(f"Retrieved Contexts: {prediction.context}")
DSPy를 사용한 최소 작업 예
이제 다음을 사용하여 또 다른 최소 작업 예제를 살펴보겠습니다. GSM8K 데이터 세트 DSPy 내에서 프롬프트 작업을 시뮬레이션하는 OpenAI GPT-3.5-turbo 모델.
설정
먼저 환경이 올바르게 구성되었는지 확인하세요.
import dspy
from dspy.datasets.gsm8k import GSM8K, gsm8k_metric
# Set up the LM
turbo = dspy.OpenAI(model='gpt-3.5-turbo-instruct', max_tokens=250)
dspy.settings.configure(lm=turbo)
# Load math questions from the GSM8K dataset
gsm8k = GSM8K()
gsm8k_trainset, gsm8k_devset = gsm8k.train[:10], gsm8k.dev[:10]
print(gsm8k_trainset)
The gsm8k_trainset 그리고 gsm8k_devset 데이터세트에는 질문 및 답변 필드가 있는 각 예시의 예시 목록이 포함되어 있습니다.
모듈 정의
다음으로, 단계별 추론을 위해 ChainOfThought 모듈을 활용하는 사용자 정의 프로그램을 정의하십시오.
class CoT(dspy.Module):
def __init__(self):
super().__init__()
self.prog = dspy.ChainOfThought("question -> answer")
def forward(self, question):
return self.prog(question=question)
모델 컴파일 및 평가
이제 다음과 같이 컴파일하십시오. 부트스트랩FewShot 텔레프롬프터:
from dspy.teleprompt import BootstrapFewShot
# Set up the optimizer
config = dict(max_bootstrapped_demos=4, max_labeled_demos=4)
# Optimize using the gsm8k_metric
teleprompter = BootstrapFewShot(metric=gsm8k_metric, **config)
optimized_cot = teleprompter.compile(CoT(), trainset=gsm8k_trainset)
# Set up the evaluator
from dspy.evaluate import Evaluate
evaluate = Evaluate(devset=gsm8k_devset, metric=gsm8k_metric, num_threads=4, display_progress=True, display_table=0)
evaluate(optimized_cot)
# Inspect the model's history
turbo.inspect_history(n=1)
이 예에서는 제공된 데이터 세트와 텔레프롬프터 구성을 사용하여 환경을 설정하고, 사용자 정의 모듈을 정의하고, 모델을 컴파일하고, 성능을 엄격하게 평가하는 방법을 보여줍니다.
DSPy의 데이터 관리
DSPy는 교육, 개발 및 테스트 세트로 작동합니다. 데이터의 각 예에는 일반적으로 입력, 중간 레이블, 최종 레이블이라는 세 가지 유형의 값이 있습니다. 중간 또는 최종 라벨은 선택사항이지만 몇 가지 예시 입력이 있어야 합니다.
예제 객체 생성
DSPy의 예제 객체는 Python 사전과 유사하지만 유용한 유틸리티가 함께 제공됩니다.
qa_pair = dspy.Example(question="This is a question?", answer="This is an answer.")
print(qa_pair)
print(qa_pair.question)
print(qa_pair.answer)
출력:
Example({'question': 'This is a question?', 'answer': 'This is an answer.'}) (input_keys=None)
This is a question?
This is an answer.
입력 키 지정
DSPy에서 예제 객체에는 특정 필드를 입력으로 표시하는 with_inputs() 메서드가 있습니다.
print(qa_pair.with_inputs("question"))
print(qa_pair.with_inputs("question", "answer"))
도트 연산자를 사용하여 값에 액세스할 수 있으며, inputs() 및 labels()와 같은 메서드는 각각 입력 키 또는 비입력 키만 포함하는 새 예제 객체를 반환합니다.
DSPy의 옵티마이저
DSPy 최적화 프로그램은 DSPy 프로그램의 매개변수(예: 프롬프트 및/또는 LM 가중치)를 조정하여 지정된 메트릭을 최대화합니다. DSPy는 각기 다른 전략을 사용하는 다양한 내장 최적화 프로그램을 제공합니다.
Optimizer
프롬프트 최적화 또는 모델 가중치 미세 조정을 위한 모듈
하지만 LLM은 동일한 입력에 대해서도 항상 같은 방식으로 응답하지 않을 수 있습니다. 응답의 일관성을 높이고 정확도를 더욱 개선하기 위해, DSPy의 핵심 기능인 프롬프트 최적화를 진행해 보겠습니다.
DSPy는 프롬프트 최적화를 위한 다양한 Optimizer 모듈을 제공합니다. Optimizer는 우리가 정의한 프로그램(모듈과 시그니처)을 입력으로 받아, 더 나은 성능을 내도록 프롬프트(내부 지시사항 및 예제 포함)를 자동으로 조정해 주는 강력한 도구입니다. 주요 Optimizer들은 다음과 같습니다.
종류 | 동작 원리 | 추천 사용 경우 |
LabeledFewShot | 사용자가 입력한 예제들 중에서 무작위로 k개를 선택 | 가장 간단한 Few Shot 방식 |
BootstrapFewShot | 사용자가 입력한 예제들을 바탕으로 고품질 예제를 생성하고 메트릭을 통과한 예제들을 사용 | 예제의 개수가 매우 적은 경우(약 10개) |
BootstrapFewShotWithRandomSearch | BootstrapFewShot을 여러번 수행해 다양한 예제 조합 생성 후 가장 성능이 좋은 조합을 찾음 | 예제가 약 50개 이상인 경우 |
KNNFewShot | 입력과 가장 유사한 예제 k개를 탐색 후 BootstrapFewShot 수행 | 입력에 따라 동적으로 가장 관련 높은 예제를 선택하고자 하는 경우 |
COPRO | 프롬프트의 설명문을 자동 개선, 즉 Signature를 개선 | 설명문을 최적화하고자 할 때 |
MIPROv2 | 베이지안 최적화를 통해 프롬프트의 설명문과 예제를 모두 최적화 | Zero Shot으로 시작하거나 예제가 200개 이상인 경우 |
이번 예제에서는 초기 결과가 어느 정도 괜찮고, 사용할 예제 데이터가 많지 않으므로 BootstrapFewShot 옵티마이저를 선택하여 프롬프트에 포함될 예제를 최적화해 보겠습니다(설명문/지시사항은 최적화 대상에 포함되지 않습니다).
이를 위해 먼저 학습 데이터셋(trainset)과 검증 데이터셋(devset)을 준비해야 합니다.
사용 가능한 최적화 프로그램
- 부트스트랩FewShot: 제공된 레이블이 지정된 입력 및 출력 데이터 포인트를 사용하여 몇 장의 예제를 생성합니다.
- 부트스트랩FewShotWithRandomSearch: 생성된 데모에 대해 무작위 검색을 통해 BootstrapFewShot을 여러 번 적용합니다.
- 코프로: 각 단계에 대한 새로운 지침을 생성하고 개선하여 좌표 상승으로 최적화합니다.
- 미프로: 베이지안 최적화를 사용하여 지침과 몇 장의 예시를 최적화합니다.
최적화 도구 선택
어디서부터 시작해야 할지 잘 모르겠으면 BootstrapFewShotWithRandomSearch를 사용하세요.
데이터가 매우 적은 경우(예제 10개) BootstrapFewShot을 사용하세요.
약간 더 많은 데이터(예제 50개)를 보려면 BootstrapFewShotWithRandomSearch를 사용하세요.
더 큰 데이터세트(예시 300개 이상)의 경우 MIPRO를 사용하세요.
BootstrapFewShotWithRandomSearch를 사용하는 방법은 다음과 같습니다.
from dspy.teleprompt import BootstrapFewShotWithRandomSearch
config = dict(max_bootstrapped_demos=4, max_labeled_demos=4, num_candidate_programs=10, num_threads=4)
teleprompter = BootstrapFewShotWithRandomSearch(metric=YOUR_METRIC_HERE, **config)
optimized_program = teleprompter.compile(YOUR_PROGRAM_HERE, trainset=YOUR_TRAINSET_HERE)
최적화된 프로그램 저장 및 로드
최적화 프로그램을 통해 프로그램을 실행한 후 나중에 사용할 수 있도록 저장하세요.
optimized_program.save(YOUR_SAVE_PATH)
저장된 프로그램 로드:
loaded_program = YOUR_PROGRAM_CLASS()
loaded_program.load(path=YOUR_SAVE_PATH)
DSPy 어설션은 LM에 대한 계산 제약 조건 적용을 자동화하여 LM 출력의 신뢰성, 예측 가능성 및 정확성을 향상시킵니다.
어설션 사용
검증 함수를 정의하고 해당 모델 생성 후 어설션을 선언합니다. 예를 들어:
dspy.Suggest(
len(query) <= 100,
"Query should be short and less than 100 characters",
)
dspy.Suggest(
validate_query_distinction_local(prev_queries, query),
"Query should be distinct from: " + "; ".join(f"{i+1}) {q}" for i, q in enumerate(prev_queries)),
)
어설션을 사용하여 프로그램 변환
from dspy.primitives.assertions import assert_transform_module, backtrack_handler
baleen_with_assertions = assert_transform_module(SimplifiedBaleenAssertions(), backtrack_handler)
또는 프로그램에서 직접 어설션을 활성화합니다.
baleen_with_assertions = SimplifiedBaleenAssertions().activate_assertions()
어설션 기반 최적화
DSPy Assertions는 다음과 같은 설정을 포함하여 특히 BootstrapFewShotWithRandomSearch와 함께 DSPy 최적화와 함께 작동합니다.
- 어설션을 사용한 컴파일
- 어설션을 사용한 컴파일 + 추론
결론
DSPy는 언어 모델과 프롬프트를 최적화하기 위한 강력하고 체계적인 접근 방식을 제공합니다. 이 예제에 설명된 단계를 따르면 복잡한 AI 시스템을 쉽게 구축, 최적화 및 평가할 수 있습니다. DSPy의 모듈식 설계와 고급 최적화 기능을 사용하면 다양한 언어 모델을 효율적이고 효과적으로 통합할 수 있어 NLP 및 AI 분야에서 일하는 모든 사람에게 유용한 도구가 됩니다.
간단한 질의 응답 시스템을 구축하든 더 복잡한 파이프라인을 구축하든 DSPy는 높은 성능과 안정성을 달성하는 데 필요한 유연성과 견고성을 제공합니다.