35차시. 평가 데이터셋 구축
퀴즈 2 — 유형별로 Dev와 Holdout 나누기
문항마다 type 이 붙은 데이터셋을 유형별로 묶어, 각 유형의 30%(최소 1개)를 Holdout 으로 떼고 나머지를 Dev 로 돌려주는 split_dev_holdout(data, ratio=0.3, seed=42) 를 만드세요. 같은 seed 로 다시 나누면 같은 결과가 나와야 합니다.
빈칸 코드에서 시작합니다.
import random
from collections import defaultdict
def split_dev_holdout(data, ratio=0.3, seed=42):
# 유형별로 묶고, 섞은 뒤 앞쪽 ratio 만큼을 holdout 으로 뗍니다
pass
결과 보기 (예시 풀이)
import random
from collections import defaultdict
def split_dev_holdout(data, ratio=0.3, seed=42):
"""유형별로 나눠 각 유형의 ratio 만큼을 Holdout 으로 떼어 냅니다."""
# 유형별로 묶어야 한쪽 묶음에 특정 유형이 쏠리지 않습니다
by_type = defaultdict(list)
for d in data:
by_type[d["type"]].append(d)
# 시드를 고정하면 몇 번을 돌려도 같은 분할이 나옵니다
rng = random.Random(seed)
dev, holdout = [], []
for t in sorted(by_type):
items = by_type[t][:] # 원본 순서를 건드리지 않게 복사
rng.shuffle(items)
n = max(1, int(len(items) * ratio)) # 유형마다 최소 1개는 Holdout
holdout += items[:n]
dev += items[n:]
return dev, holdout
# 수업 자료의 구성표(단순 사실 15, 표현 변형 10, ...)대로 50문항을 흉내 냅니다
COUNTS = {"fact": 15, "variant": 10, "compound": 8,
"conditional": 5, "absent": 8, "out_of_scope": 4}
data = []
for t, c in COUNTS.items():
for _ in range(c):
data.append({"id": f"Q{len(data) + 1:03d}", "type": t})
dev, holdout = split_dev_holdout(data)
print(f"Dev {len(dev)}문항 / Holdout {len(holdout)}문항")
# 유형마다 Holdout 에 몇 개씩 들어갔는지 확인합니다
per_type = defaultdict(int)
for d in holdout:
per_type[d["type"]] += 1
print("Holdout 유형별:", dict(per_type))
# 같은 시드로 다시 나누면 Holdout 이 똑같아야 합니다
_, again = split_dev_holdout(data)
print("다시 나눠도 같은가:", [d["id"] for d in holdout] == [d["id"] for d in again])
# 두 묶음에 겹치는 문항이 없어야 누출이 없습니다
print("겹치는 문항 수:", len({d["id"] for d in dev} & {d["id"] for d in holdout}))
실행 결과
Dev 37문항 / Holdout 13문항
Holdout 유형별: {'absent': 2, 'compound': 2, 'conditional': 1, 'fact': 4, 'out_of_scope': 1, 'variant': 3}
다시 나눠도 같은가: True
겹치는 문항 수: 0채점 기준: 유형마다 최소 1개가 Holdout 에 들어가고, 두 묶음이 겹치지 않으며, 같은 seed 로 같은 분할이 나오면 정답입니다.