내장 데이터셋 활용하기
내장 데이터셋 활용하기
DSPy에서 데이터를 직접 불러오는 건 생각보다 간단해요. 사실 "데이터셋"이 무엇인지만 알면 끝인데, 바로 Example 객체들의 리스트가 곧 데이터셋이거든요. 이번엔 DSPy가 기본으로 제공하는 내장 데이터셋을 어떻게 불러오고 다루는지 살펴볼게요.
⚠️ 원문에 "이 페이지는 오래되었고 DSPy 2.5에선 완전히 정확하지 않을 수 있다"는 경고가 있어요. 내장 데이터셋을 여는 기본 흐름을 이해하는 데는 충분해요.
데이터셋은 Example의 리스트
DSPy에서 우리만의 데이터를 쓰는 건 아주 쉽습니다. 데이터셋은 그냥 Example 객체의 리스트거든요. DSPy를 잘 쓰는 법은 이미 존재하는 데이터셋을 잘 찾아내고, 우리 파이프라인에 맞게 새롭게 재활용하는 데서 나와요. DSPy는 이런 전략을 특히 강력하게 만들어 주죠.
편의를 위해 DSPy는 현재 다음과 같은 데이터셋을 기본으로 제공해요.
- HotPotQA (멀티홉 질의응답)
- GSM8k (수학 문제)
- Color (기본 색상 데이터셋)
HotPotQA 불러오기
HotPotQA는 질문-답변 쌍의 모음이에요. train, dev, test 스플릿을 원하는 크기로 잘라서 불러올 수 있어요.
from dspy.datasets import HotPotQA
dataset = HotPotQA(train_seed=1, train_size=5, eval_seed=2023, dev_size=50, test_size=0)
print(dataset.train)
Output:
[Example({'question': 'At My Window was released by which American singer-songwriter?', 'answer': 'John Townes Van Zandt'}) (input_keys=None),
Example({'question': 'which American actor was Candace Kita guest starred with ', 'answer': 'Bill Murray'}) (input_keys=None),
Example({'question': 'Which of these publications was most recently published, Who Put the Bomp or Self?', 'answer': 'Self'}) (input_keys=None),
Example({'question': 'The Victorians - Their Story In Pictures is a documentary series written by an author born in what year?', 'answer': '1950'}) (input_keys=None),
Example({'question': 'Which magazine has published articles by Scott Shaw, Tae Kwon Do Times or Southwest Art?', 'answer': 'Tae Kwon Do Times'}) (input_keys=None)]
방금 우리는 훈련셋(5개)과 개발셋(50개)을 불러왔어요. 훈련셋의 각 Example은 질문과 (사람이 주석한) 답변만 담고 있죠. 리스트에 Example 객체들이 들어간 걸 확인할 수 있어요. 다만 한 가지 유의점은 입력 키가 자동으로 설정되지 않는다는 것입니다. 이건 우리가 직접 지정해 줘야 해요.
trainset = [x.with_inputs('question') for x in dataset.train]
devset = [x.with_inputs('question') for x in dataset.dev]
print(trainset)
Output:
[Example({'question': 'At My Window was released by which American singer-songwriter?', 'answer': 'John Townes Van Zandt'}) (input_keys={'question'}),
Example({'question': 'which American actor was Candace Kita guest starred with ', 'answer': 'Bill Murray'}) (input_keys={'question'}),
Example({'question': 'Which of these publications was most recently published, Who Put the Bomp or Self?', 'answer': 'Self'}) (input_keys={'question'}),
Example({'question': 'The Victorians - Their Story In Pictures is a documentary series written by an author born in what year?', 'answer': '1950'}) (input_keys={'question'}),
Example({'question': 'Which magazine has published articles by Scott Shaw, Tae Kwon Do Times or Southwest Art?', 'answer': 'Tae Kwon Do Times'}) (input_keys={'question'})]
DSPy는 보통 아주 최소한의 라벨링만 요구해요. 파이프라인이 여섯 일곱 단계로 복잡해도, 초기 질문과 최종 답변에 대한 라벨만 있으면 돼요. 중간 단계의 라벨은 DSPy가 직접 부트스트래핑해서 채워 줍니다. 그리고 파이프라인을 조금이라도 바꾸면, 거기에 맞춰 부트스트래핑된 데이터도 함께 바뀌어요.
심화: DSPy의 Dataset 클래스 내부 들여다보기 (선택)
방금 HotPotQA 데이터셋 클래스를 이용해 데이터를 불러왔는데, 이게 실제로 어떻게 동작하는지 궁금하지 않나요? HotPotQA 클래스는 Dataset 클래스를 상속받아요. Dataset 클래스가 소스에서 불러온 데이터를 훈련-테스트-개발 스플릿으로 변환하는 일을 도맡고, 그 결과는 전부 Example의 리스트예요. HotPotQA 클래스에서는 __init__ 메서드만 구현하면 되는데, 여기서 HuggingFace에서 가져온 데이터를 _train, _test, _dev 변수에 채워 넣어요. 나머지 처리는 Dataset 클래스의 메서드들이 알아서 해 줍니다.

그럼 Dataset 클래스의 메서드들은 HuggingFace 데이터를 어떻게 변환할까요? 차근차근 살펴볼게요. 위 예시에서 .train, .dev, .test 메서드로 스플릿에 접근했는데, train() 메서드의 구현을 한번 볼게요.
@property
def train(self):
if not hasattr(self, '_train_'):
self._train_ = self._shuffle_and_sample('train', self._train, self.train_size, self.train_seed)
return self._train_
보시다시피 train() 메서드는 일반 메서드가 아니라 **프로퍼티(property)**예요. 이 프로퍼티는 먼저 _train_ 속성이 존재하는지 확인하고, 없으면 _shuffle_and_sample() 메서드를 호출해서 self._train(HuggingFace 데이터셋이 로드된 변수)을 처리해요. 이제 _shuffle_and_sample() 메서드를 볼게요.
def _shuffle_and_sample(self, split, data, size, seed=0):
data = list(data)
base_rng = random.Random(seed)
if self.do_shuffle:
base_rng.shuffle(data)
data = data[:size]
output = []
for example in data:
output.append(Example(**example, dspy_uuid=str(uuid.uuid4()), dspy_split=split))
return output
_shuffle_and_sample() 메서드는 두 가지 일을 해요.
self.do_shuffle이 True면 데이터를 섞어요.- 섞인 데이터에서
size크기만큼 샘플을 잘라내요. - 샘플링된 데이터를 순회하며 각 요소를
Example객체로 변환해요. 이Example에는 예시 데이터뿐 아니라 고유 ID와 스플릿 이름도 함께 담겨요.
이렇게 원시 예시를 Example 객체로 바꿔 두면, Dataset 클래스가 나중에 표준화된 방식으로 처리할 수 있어요. 예를 들어 PyTorch DataLoader가 사용하는 collate 메서드는 각 항목이 Example이길 기대하죠.
정리하면 Dataset 클래스가 필요한 모든 데이터 처리를 담당하고, 다른 스플릿에 접근할 수 있는 간단한 API를 제공해요. 이것이 원시 데이터 로드 방법만 정의하면 되는 HotpotQA 같은 데이터셋 클래스들과 구분되는 지점입니다.
더 알아보기 (Learn more)
- DSPy의 Example — 데이터의 기본 단위인
Example구조와 입력 키 지정 - 커스텀 데이터셋 만들기 — 내장이 아니라 우리 데이터로 데이터셋을 만드는 방법
- 공식: DSPy Built-in Datasets 문서