프롬프트 엔지니어링
LLM을 DSPy로 최적화: AI 시스템을 구축, 최적화, 평가하는 단계별 가이드
대규모 언어 모델(LLM)의 기능이 계속 확장함에 따라, 강력한 AI 시스템을 개발하는 것이 점점 더 복잡해지고 있습니다. 전통적인 접근 방식은 종종 복잡한 프롬프트 기술, 미세 조정 위한 데이터 생성, 및 도메인별 제약 조건을 준수하기 위한 수동 가이드를 포함합니다. 그러나 이 과정은 번거로울 수 있으며, 오류가 발생하기 쉽고, 인간의 개입에 크게 의존합니다.
여기서 DSPy가 등장합니다. DSPy는 AI 시스템 개발을 간소화하기 위해 설계된 혁신적인 프레임워크입니다. DSPy는 LM 프롬프트와 가중치를 최적화하는 체계적인 접근 방식을 도입하여 개발자가 최소한의 수동 노력으로 복잡한 애플리케이션을 구축할 수 있도록 합니다.
이 포괄적인 가이드에서, 우리는 DSPy의 핵심 원리, 모듈식 아키텍처, 및 제공하는 강력한 기능을 탐구할 것입니다. 또한 DSPy를 사용하여 LLM으로 AI 시스템을 개발하는 방법을 보여주는 실제 예제를 살펴보겠습니다.
DSPy란 무엇이며, 왜 필요한가요?
DSPy는 프로그램의 흐름(모듈)을 각 단계의 매개변수(LM 프롬프트 및 가중치)와 분리하는 프레임워크입니다. 이 분리로 인해 LM 프롬프트 및 가중치를 체계적으로 최적화할 수 있으며, 더 높은 신뢰성, 예측 가능성, 및 도메인별 제약 조건을 준수하면서 복잡한 AI 시스템을 구축할 수 있습니다.
전통적으로, LLM으로 AI 시스템을 개발하는 것은 문제를 단계로 나누고, 각 단계에 대한 복잡한 프롬프트를 생성하고, 미세 조정을 위한 합성 예제를 생성하고, LM이 특정 제약 조건을 준수하도록 수동으로 안내하는 번거로운 과정으로 이루어졌습니다. 이 접근 방식은 시간이 걸리고, 오류가 발생하기 쉽으며, 파이프라인, LM, 또는 데이터의 작은 변경이라도 프롬프트 및 미세 조정 단계의 광범위한 재작업을 필요로 할 수 있습니다.
DSPy는 이러한 도전 과제를 새로운 패러다임을 도입함으로써 해결합니다: 최적화기. 이러한 LM 기반 알고리즘은 지정된 메트릭을 최대화하기 위해 LM 호출의 프롬프트 및 가중치를 조정할 수 있습니다. 최적화 과정을 자동화함으로써, DSPy는 개발자가 최소한의 수동 개입으로 강력한 AI 시스템을 구축할 수 있도록 합니다.
DSPy의 모듈식 아키텍처
DSPy의 핵심에는 복잡한 AI 시스템을 구성할 수 있는 모듈식 아키텍처가 있습니다. 프레임워크는 다양한 프롬프트 기술을 추상화하는 일련의 내장 모듈을 제공합니다. 이러한 모듈은 더 큰 프로그램으로 결합되고 구성될 수 있으며, 개발자가 특정 요구 사항에 맞게 정교한 파이프라인을 구축할 수 있도록 합니다.
각 모듈은 학습 가능한 매개변수를 캡슐화합니다. 모듈이 호출될 때, DSPy의 최적화기는 이러한 매개변수를 미세 조정하여 지정된 메트릭을 최대화하고, LM의 출력이 지정된 제약 조건 및 요구 사항을 준수하도록 보장합니다.
DSPy로 최적화
DSPy는 AI 시스템의 성능 및 신뢰성을 향상시키기 위해 설계된 강력한 최적화기를 도입합니다. 이러한 최적화기는 LM 기반 알고리즘을 사용하여 LM 호출의 프롬프트 및 가중치를 조정하여 지정된 메트릭을 최대화하고, 도메인별 제약 조건을 준수합니다.
DSPy에서 사용할 수 있는 주요 최적화기에는:
- BootstrapFewShot: 이 최적화기는 프롬프트에 최적화된 예제를 자동으로 생성하고 포함하여, 몇몇 샷 학습을 구현합니다.
- BootstrapFewShotWithRandomSearch:
BootstrapFewShot를 여러 번 적용하여 생성된 시연에 대한 랜덤 검색을 수행하고, 최적의 프로그램을 선택합니다. - MIPRO: 각 단계에서 지침 및 몇몇 샷 예제를 생성하며, 지침 생성은 데이터 및 시연에 대한 인식과 의존성을 갖습니다. 베이즈 최적화를 사용하여 모듈 전체의 생성 지침 및 시연 공간을 효과적으로 검색합니다.
- BootstrapFinetune: 프롬프트 기반 DSPy 프로그램을 더 작은 LM에 대한 가중치 업데이트로 증류하여, 기본 LM을 미세 조정하고 효율성을 향상시킵니다.
이러한 최적화기를 활용하여 개발자는 AI 시스템을 체계적으로 최적화할 수 있으며, 높은 품질의 출력을 보장하고, 도메인별 제약 조건 및 요구 사항을 준수합니다.
DSPy 시작하기
DSPy의 강점을 보여주기 위해, 질문-답변을 위한 검색-증강 생성(RAG) 시스템을 구축하는 실제 예제를 살펴보겠습니다.
단계 1: 언어 모델 및 검색 모델 설정
첫 번째 단계는 DSPy 내에서 언어 모델(LM) 및 검색 모델(RM)을 구성하는 것입니다.
설치하려면 다음을 실행합니다:
pip install dspy-ai
DSPy는 여러 LM 및 RM API를 지원하며, 로컬 모델 호스팅도 지원하므로, 원하는 모델을 쉽게 통합할 수 있습니다.
import dspy # LM 및 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
# 데이터셋 로딩
dataset = HotPotQA(train_seed=1, train_size=20, eval_seed=2023, dev_size=50, test_size=0)
# '질문' 필드를 입력으로 지정
trainset = [x.with_inputs('question') for x in dataset.train]
devset = [x.with_inputs('question') for x in dataset.dev]
단계 3: 서명 구축
DSPy는 모듈의 동작을 정의하는 서명을 사용합니다. 이 예제에서는 답변 생성 작업을 위한 서명을 정의하며, 입력 필드(문맥 및 질문) 및 출력 필드(답변)를 지정합니다.
class GenerateAnswer(dspy.Signature): """짧은 팩트 답변으로 질문에 답변합니다.""" context = dspy.InputField(desc='관련 사실을 포함할 수 있음') question = dspy.InputField() answer = dspy.OutputField(desc='일반적으로 1~5 단어 사이')
단계 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 # 검증 메트릭 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 # 최적화기 설정 teleprompter = BootstrapFewShot(metric=validate_context_and_answer) # 프로그램 컴파일 compiled_rag = teleprompter.compile(RAG(), trainset=trainset)
단계 6: 파이프라인 평가
프로그램을 컴파일한 후, 원하는 정확도 및 신뢰성을 달성했는지 확인하기 위해 개발 세트에서 성능을 평가하는 것이 중요합니다.
from dspy.evaluate import Evaluate
# 평가기 설정
evaluate = Evaluate(devset=devset, metric=validate_context_and_answer, num_threads=4, display_progress=True, display_table=0)
# 컴파일된 RAG 프로그램 평가
evaluation_result = evaluate(compiled_rag)
print(f"평가 결과: {evaluation_result}")
단계 7: 모델 기록 검사
더 깊은 이해를 위해, 모델의 최근 생성을 검사할 수 있습니다.
# 모델 기록 검사 turbo.inspect_history(n=1)
단계 8: 예측 수행
파이프라인을 최적화하고 평가한 후, 이제 새로운 질문에 대해 예측을 수행할 수 있습니다.
# 예제 질문
question = "게리 주카브의 첫 번째 책은 어떤 상을 받았나요?"
# 컴파일된 RAG 프로그램을 사용하여 예측
prediction = compiled_rag(question)
print(f"질문: {question}")
print(f"답변: {prediction.answer}")
print(f"검색된 문맥: {prediction.context}")
최소한의 작동 예제: DSPy
이제, GSM8K 데이터셋 및 OpenAI GPT-3.5-turbo 모델을 사용하여 DSPy 내에서 프롬프트 작업을 시뮬레이팅하는 또 다른 최소한의 작동 예제를 살펴보겠습니다.
설정
먼저 환경이 올바르게 구성되었는지 확인합니다:
import dspy from dspy.datasets.gsm8k import GSM8K, gsm8k_metric # LM 설정 turbo = dspy.OpenAI(model='gpt-3.5-turbo-instruct', max_tokens=250) dspy.settings.configure(lm=turbo) # GSM8K 데이터셋에서 수학 질문 로딩 gsm8k = GSM8K() gsm8k_trainset, gsm8k_devset = gsm8k.train[:10], gsm8k.dev[:10] print(gsm8k_trainset)
gsm8k_trainset 및 gsm8k_devset 데이터셋은 각 예제에 질문 및 답변 필드가 있는 목록을 포함합니다.
모듈 정의
다음으로, 체인 오브 사고 모듈을 사용하여 단계별推論을 수행하는 사용자 정의 프로그램을 정의합니다:
class CoT(dspy.Module):
def __init__(self):
super().__init__()
self.prog = dspy.ChainOfThought("질문 -> 답변")
def forward(self, question):
return self.prog(question=question)
모델 컴파일 및 평가
이제 컴파일을 BootstrapFewShot 텔레프롬프트와 함께 수행합니다:
from dspy.teleprompt import BootstrapFewShot # 최적화기 설정 config = dict(max_bootstrapped_demos=4, max_labeled_demos=4) # gsm8k_metric을 사용하여 최적화 teleprompter = BootstrapFewShot(metric=gsm8k_metric, **config) optimized_cot = teleprompter.compile(CoT(), trainset=gsm8k_trainset) # 평가기 설정 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) # 모델 기록 검사 turbo.inspect_history(n=1)
이 예제는 환경 설정, 사용자 정의 모듈 정의, 모델 컴파일, 및 제공된 데이터셋 및 텔레프롬프트 구성과 함께 강력한 최적화를 수행하는 방법을 보여줍니다.
데이터 관리: DSPy
DSPy는 훈련, 개발, 및 테스트 세트와 함께 작동합니다. 데이터의 각 예제에는 일반적으로 세 가지 유형의 값이 있습니다: 입력, 중간 레이블, 및 최종 레이블. 중간 또는 최종 레이블은 선택 사항이지만, 몇 가지 예제 입력은 필수입니다.
예제 객체 생성
DSPy의 예제 객체는 유용한 유틸리티와 함께 Python 사전과 유사합니다:
qa_pair = dspy.Example(question="이것은 질문인가?", answer="이것은 답변입니다.") print(qa_pair) print(qa_pair.question) print(qa_pair.answer)
출력:
Example({'question': '이것은 질문인가?', 'answer': '이것은 답변입니다.'}) (input_keys=None)
이것은 질문인가?
이것은 답변입니다.
입력 키 지정
DSPy에서 예제 객체에는 with_inputs() 메서드가 있으며, 특정 필드를 입력으로 표시하는 데 사용됩니다:
print(qa_pair.with_inputs("question"))
print(qa_pair.with_inputs("question", "answer"))
값은 점 연산자를 사용하여 액세스할 수 있으며, inputs() 및 labels() 메서드는 각각 입력 또는 非입력 키만 포함하는 새로운 예제 객체를 반환합니다.
최적화기: DSPy
DSPy 최적화기는 지정된 메트릭을 최대화하기 위해 DSPy 프로그램의 매개변수(즉, 프롬프트 및/또는 LM 가중치)를 조정합니다. DSPy는 다양한 내장 최적화기를 제공하며, 각 최적화기는 다른 전략을 사용합니다.
사용 가능한 최적화기
- BootstrapFewShot: 제공된 레이블 입력 및 출력 데이터 포인트를 사용하여 몇몇 샷 예제를 생성합니다.
- BootstrapFewShotWithRandomSearch: 생성된 시연에 대한 랜덤 검색과 함께
BootstrapFewShot를 여러 번 적용합니다. - COPRO: 각 단계에서 새로운 지침을 생성하고, 좌표 상승을 사용하여 최적화합니다.
- MIPRO: 베이즈 최적화를 사용하여 지침 및 몇몇 샷 예제를 최적화합니다.
최적화기 선택
시작할 때는 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 어서션
DSPy 어서션은 LM의 계산 제약을 강제하는 자동화된 방법을 제공하여, LM 출력의 신뢰성, 예측 가능성, 및 올바름을 향상합니다.
어서션 사용
검증 함수를 정의하고, 모델 생성 후 어서션을 선언합니다. 예를 들어:
dspy.Suggest(
len(query) <= 100,
"쿼리는 짧고 100자 이하여야 합니다.",
)
dspy.Suggest(
validate_query_distinction_local(prev_queries, query),
"쿼리는 다음에서 구별되어야 합니다: " + "; ".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 어서션은 DSPy 최적화와 함께 작동하며, 특히 BootstrapFewShotWithRandomSearch와 함께 사용할 수 있습니다. 이는 다음과 같은 설정을 포함합니다:
- 어서션과 함께 컴파일
- 어서션과 함께 컴파일 및 추론
결론
DSPy는 언어 모델 및 프롬프트를 최적화하는 체계적인 접근 방식을 제공합니다. 이 예제에서 설명된 단계를 따라, 복잡한 AI 시스템을 쉽게 구축, 최적화, 및 평가할 수 있습니다. DSPy의 모듈식 설계 및 고급 최적화기는 다양한 언어 모델을 효율적이고 효과적으로 통합할 수 있도록 하므로, NLP 및 AI 분야에서 작업하는 모든 사람에게 유용한 도구입니다.
질문-답변 시스템을 구축하는 것에서부터 더 복잡한 파이프라인까지, DSPy는 높은 성능 및 신뢰성을 달성하는 데 필요한 유연성 및 강력을 제공합니다.












