커스텀 데이터셋 만들기

커스텀 데이터셋 만들기

앞서 Example 객체를 다루는 법과 HotPotQA 클래스로 HuggingFace 데이터셋을 Example 리스트로 불러오는 법을 봤어요. 그런데 실무에서는 이런 정형화된 데이터셋이 오히려 드물어요. 대부분 "우리만의 데이터셋"을 마주하게 되죠. 그럴 때 어떻게 데이터셋을 만들고, 어떤 형식으로 만들어야 할까요? 이번 글에서 정리해 볼게요.

출처: 공식 문서 — Creating a Custom Dataset

⚠️ 원문에 "이 페이지는 오래되었고 DSPy 2.5에선 완전히 정확하지 않을 수 있다"는 경고가 있어요. 커스텀 데이터를 Example 리스트로 만드는 기본 패턴은 여전히 유효해요.

데이터셋은 Example의 리스트, 만드는 두 가지 방법

DSPy에서 우리의 데이터셋은 Example의 리스트예요. 이걸 만드는 방법은 크게 두 가지가 있습니다.

  • 권장: 파이써닉한 방법(Pythonic Way) — 기본 파이썬 유틸리티와 로직 활용
  • 심화: DSPy의 Dataset 클래스 사용

권장: 파이써닉한 방법

Example 객체의 리스트를 만들려면, 단순히 소스에서 데이터를 불러와 파이썬 리스트로 구성하면 돼요. 3개 필드(context, question, summary)를 담은 예시 CSV sample.csv를 Pandas로 불러와서 데이터 리스트를 만들어 볼게요.

import pandas as pd

df = pd.read_csv("sample.csv")
print(df.shape)

Output:

(1000, 3)
dataset = []

for context, question, answer in df.values:
    dataset.append(dspy.Example(context=context, question=question, answer=answer).with_inputs("context", "question"))

print(dataset[:3])

Output:

[Example({'context': nan, 'question': 'Which is a species of fish? Tope or Rope', 'answer': 'Tope'}) (input_keys={'question', 'context'}),
 Example({'context': nan, 'question': 'Why can camels survive for long without water?', 'answer': 'Camels use the fat in their humps to keep them filled with energy and hydration for long periods of time.'}) (input_keys={'question', 'context'}),
 Example({'context': nan, 'question': "Alice's parents have three daughters: Amy, Jessy, and what’s the name of the third daughter?", 'answer': 'The name of the third daughter is Alice'}) (input_keys={'question', 'context'})]

이 방법은 꽤 단순하죠. 그럼 DSPy의 방식, 즉 "DSPythonic"하게 데이터셋을 로드하는 건 어떻게 생겼는지도 함께 볼게요.

심화: DSPy의 Dataset 클래스 사용 (선택)

아까 정의한 Dataset 클래스의 힘을 빌려 전처리를 끝내볼게요. 할 일은 다음과 같습니다.

  • CSV에서 데이터를 데이터프레임으로 로드
  • 데이터를 train, dev, test 스플릿으로 나누기
  • _train, _dev, _test 클래스 속성 채우기 (이 속성들은 딕셔너리 리스트이거나 HuggingFace Dataset 같은 매핑에 대한 이터레이터여야 동작해요)

이 모든 건 __init__ 메서드만으로 처리돼요. 우리가 구현해야 할 유일한 메서드가 바로 이 __init__입니다.

import pandas as pd
from dspy.datasets.dataset import Dataset

class CSVDataset(Dataset):
    def __init__(self, file_path, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        
        df = pd.read_csv(file_path)
        self._train = df.iloc[0:700].to_dict(orient='records')

        self._dev = df.iloc[700:].to_dict(orient='records')

dataset = CSVDataset("sample.csv")
print(dataset.train[:3])

Output:

[Example({'context': nan, 'question': 'Which is a species of fish? Tope or Rope', 'answer': 'Tope'}) (input_keys={'question', 'context'}),
 Example({'context': nan, 'question': 'Why can camels survive for long without water?', 'answer': 'Camels use the fat in their humps to keep them filled with energy and hydration for long periods of time.'}) (input_keys={'question', 'context'}),
 Example({'context': nan, 'question': "Alice's parents have three daughters: Amy, Jessy, and what’s the name of the third daughter?", 'answer': 'The name of the third daughter is Alice'}) (input_keys={'question', 'context'})]

코드를 한 단계씩 이해해 볼게요.

  • DSPy의 기본 Dataset 클래스를 상속받아요. 이걸로 유용한 데이터 로드/처리 기능을 전부 물려받죠.
  • CSV의 데이터를 DataFrame으로 로드해요.
  • DataFrame에서 첫 700행을 train 스플릿으로 잡고, to_dict(orient='records') 메서드로 딕셔너리 리스트로 변환한 뒤 self._train에 할당해요.
  • DataFrame에서 다음 300행을 dev 스플릿으로 잡고, 마찬가지로 to_dict(orient='records')로 변환해 self._dev에 할당해요.

이렇게 Dataset 기본 클래스를 쓰면 커스텀 데이터셋 로딩이 엄청 쉬워져요. 매번 새로운 데이터셋마다 보일러플레이트 코드를 직접 작성할 필요가 없어지죠.

⚠️ 주의할 점이 하나 있어요. 위 코드에서는 _test 속성을 채우지 않았는데, 그 자체로는 에러를 내지 않아요. 다만 test 스플릿에 접근하려고 하면 에러가 나요.

dataset.test[:5]

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-59-5202f6de3c7b> in <cell line: 1>()
----> 1 dataset.test[:5]

/usr/local/lib/python3.10/dist-packages/dspy/datasets/dataset.py in test(self)
    51     def test(self):
    52         if not hasattr(self, '_test_'):
---> 53             self._test_ = self._shuffle_and_sample('test', self._test, self.test_size, self.test_seed)
    54 
    55         return self._test_

AttributeError: 'CSVDataset' object has no attribute '_test'

이걸 막으려면 _testNone이 아니고 적절한 데이터로 채워져 있는지만 확인하면 돼요.

Dataset 클래스의 메서드를 오버라이드하면 클래스를 더욱 커스터마이징할 수도 있어요.

정리하면, Dataset 기본 클래스는 최소한의 코드로 커스텀 데이터셋을 로드·전처리할 수 있는 간결한 방법을 제공합니다.

더 알아보기 (Learn more)