DSPy의 Example: 데이터를 담는 기본 그릇
DSPy의 Example: 데이터를 담는 기본 그릇
DSPy로 작업하다 보면 수많은 데이터 항목을 주고받게 돼요. 그런데 이 데이터들이 정확히 어떤 형태로 흘러다니는지, 어떻게 읽고 고칠 수 있는지가 사실 제일 기본이면서도 중요한 부분이에요. 오늘은 DSPy 데이터의 핵심 자료형인 Example을 하나씩 뜯어서, 만들고 읽고 바꾸는 방법을 정리해 볼게요.
⚠️ 원문에 "이 페이지는 오래되었고 DSPy 2.5에선 완전히 정확하지 않을 수 있다"는 경고가 있어요. 핵심 개념 자체는 유효하니 기본 구조를 이해하는 용도로 보시면 좋아요.
Example이 뭔가요
DSPy에서 작업을 하다 보면 훈련셋(training set), 개발셋(dev set), 테스트셋(test set)을 다루게 돼요. 전통적인 머신러닝과 비슷한데, 보통은 라벨이 훨씬 적어도(아예 없어도) 효과적으로 쓸 수 있다는 게 큰 차이점이에요.
이때 데이터의 핵심 자료형이 바로 Example입니다. 훈련셋이나 테스트셋의 개별 항목을 Example 객체로 표현해요. 그리고 DSPy 모듈이 반환하는 값들은 Prediction이라는 타입인데, 이건 Example의 특별한 하위 클래스예요.
쉽게 말하면 Example은 파이썬의 dict와 비슷하지만, 몇 가지 유용한 유틸리티가 더 붙어 있는 존재라고 보면 돼요.
Example 만들기
DSPy를 쓰다 보면 평가나 최적화 실행을 아주 자주 하게 됩니다. 그때 개별 데이터 포인트가 Example 타입이에요. 아주 간단하게 만들어 볼게요.
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)
Output:
Example({'question': 'This is a question?', 'answer': 'This is an answer.'}) (input_keys=None)
This is a question?
This is an answer.
보시다시피 필드 키와 값 타입은 자유롭게 정할 수 있어요. 다만 보통은 값이 문자열인 경우가 많죠.
object = Example(field1=value1, field2=value2, field3=value3, ...)
입력 키(Input Keys) 지정하기
전통적인 머신러닝에서는 "입력(input)"과 "라벨(label)"이 분리돼 있어요. DSPy의 Example도 이 구분을 지원하는데, with_inputs() 메서드를 쓰면 특정 필드를 입력으로 표시할 수 있어요. 나머지 필드는 그냥 메타데이터나 라벨로 취급되는 거죠.
# Single Input.
print(qa_pair.with_inputs("question"))
# Multiple Inputs; be careful about marking your labels as inputs unless you mean it.
print(qa_pair.with_inputs("question", "answer"))
이런 유연성 덕분에 다양한 DSPy 시나리오에 맞게 Example 객체를 커스터마이징할 수 있어요.
한 가지 주의할 점은 with_inputs()를 호출하면 새로운 복사본이 반환된다는 거예요. 원본 객체는 그대로 남아 있어요.
요소 접근과 갱신
값은 .(점) 연산자로 접근할 수 있어요. 예를 들어 Example(name="John Doe", job="sleep") 객체에서 object.name처럼 접근하는 거죠.
특정 키만 뽑거나 제외하고 싶을 땐 inputs()와 labels() 메서드를 써요. 각각 입력 키만, 혹은 비입력 키만 담은 새로운 Example 객체를 반환해 줍니다.
article_summary = dspy.Example(article= "This is an article.", summary= "This is a summary.").with_inputs("article")
input_key_only = article_summary.inputs()
non_input_key_only = article_summary.labels()
print("Example object with Input fields only:", input_key_only)
print("Example object with Non-Input fields only:", non_input_key_only)
Output
Example object with Input fields only: Example({'article': 'This is an article.'}) (input_keys=None)
Example object with Non-Input fields only: Example({'summary': 'This is a summary.'}) (input_keys=None)
키를 아예 제외하려면 without()을 써요.
article_summary = dspy.Example(context="This is an article.", question="This is a question?", answer="This is an answer.", rationale= "This is a rationale.").with_inputs("context", "question")
print("Example object without answer & rationale keys:", article_summary.without("answer", "rationale"))
Output
Example object without answer & rationale keys: Example({'context': 'This is an article.', 'question': 'This is a question?'}) (input_keys=None)
값을 바꾸는 것도 마찬가지로 . 연산자로 새 값을 할당하면 돼요.
article_summary.context = "new context"
Example 반복(Iteration)하기
Example 클래스의 반복 동작도 딕셔너리처럼 동작해요. keys(), values() 같은 메서드를 그대로 지원하죠.
for k, v in article_summary.items():
print(f"{k} = {v}")
Output
context = This is an article.
question = This is a question?
answer = This is an answer.
rationale = This is a rationale.
더 알아보기 (Learn more)
- 내장 데이터셋 활용 — HotPotQA 같은 데이터셋을 바로 불러오는 방법
- 커스텀 데이터셋 만들기 — 우리만의 데이터를
Example리스트로 만드는 방법 - 공식: DSPy Examples 문서