MedGemma 파인튜닝을 위한 데이터셋 준비, "병원 데이터 → 학습 데이터" 완전 가이드[pe]
MedGemma 파인튜닝을 위한 데이터셋 준비, "병원 데이터 → 학습 데이터" 완전 가이드[pe]
MedGemma 파인튜닝 데이터셋 핵심 원칙
MedGemma 파인튜닝 데이터는 이미지+텍스트 쌍(pair) 형태로 준비해야 한다.+1
텍스트 전용 27B는 순수 텍스트 데이터, 멀티모달 4B는 X-ray/CT 이미지 + 판독 보고서/질문-답변 쌍이 기본 구조다.
핵심 3원칙:
비식별화 필수: 이름, 주민번호, 병원명 완전 제거
IRB 승인: 병원 데이터 사용 시 연구윤리위원회 승인
품질 > 양: 1000개 고품질 데이터가 10만 개 노이즈 데이터보다 낫다
데이터셋 구조 설계 (3가지 패턴)
패턴 A: X-ray + 판독 보고서 (가장 일반적)
textdataset/ ├── images/ │ ├── cxr_001.png # DICOM → PNG 변환 │ ├── cxr_002.png │ └── ... ├── reports/ │ ├── cxr_001.txt # "심비대(cardiomegaly), 폐렴(pneumonia) 의심" │ ├── cxr_002.txt │ └── ... └── metadata.csv # 이미지ID, 라벨, 난이도 등
패턴 B: 이미지 + VQA (시각적 질문-답변)
text"image": "cxr_001.png", "question": "우심비대가 보이는가?", "answer": "예, 심실 비대(left ventricular hypertrophy) 소견 관찰됨"
패턴 C: 텍스트 전용 (EHR 요약 등)
text"input": "60세 남성, 당뇨 10년차, 최근 HbA1c 9.2%", "output": "당뇨병 대조 불량, 인슐린 치료 고려"
단계별 데이터 준비 절차 (실행 가능)
STEP 1. 원본 의료 데이터 수집 및 비식별화
pythonimport pandas as pd import os from pathlib import Path # 1) 병원 PACS에서 DICOM 추출 (가정) # 실제로는 IRB 승인 + PACS 전문가 협업 필요 raw_data_dir = Path("raw_pacs_export") processed_dir = Path("medgemma_dataset") # 2) 메타데이터 CSV 생성 metadata = [] for dicom_file in raw_data_dir.glob("*.dcm"): metadata.append({ "image_id": dicom_file.stem, "image_path": str(dicom_file), "report_path": None, # 추후 생성 "age": None, # 비식별화로 제거 "sex": None, # 비식별화로 제거 "deidentified": True }) df = pd.DataFrame(metadata) df.to_csv(processed_dir / "metadata.csv", index=False)
STEP 2. DICOM → PNG 변환 + Windowing
pythonimport pydicom import numpy as np from PIL import Image import cv2 def dicom_to_png(dicom_path, output_path, window_center=40, window_width=400): """DICOM → 의료용 PNG 변환""" ds = pydicom.dcmread(dicom_path) # 픽셀 데이터 추출 pixel_array = ds.pixel_array.astype(np.float32) # Windowing 적용 (폐/심장 최적화) min_val = window_center - window_width // 2 max_val = window_center + window_width // 2 windowed = np.clip((pixel_array - min_val) / window_width * 255, 0, 255) # 3채널 RGB로 변환 (MedGemma 요구사항) rgb_image = np.stack([windowed, windowed, windowed], axis=-1).astype(np.uint8) # PNG 저장 (의료 영상 압축 손실 최소화) Image.fromarray(rgb_image).save(output_path, "PNG") return output_path # 벌크 변환 images_dir = processed_dir / "images" images_dir.mkdir(exist_ok=True) for idx, row in df.iterrows(): png_path = images_dir / f"{row['image_id']}.png" dicom_to_png(row['image_path'], png_path) df.at[idx, 'image_png'] = str(png_path)
STEP 3. 판독 보고서 텍스트 정제
pythonimport re def clean_medical_report(report_text): """의료 보고서 텍스트 정제""" # 1) 개인정보 제거 (정규식) patterns = [ r'\b[0-9]{6,}\b', # 주민번호 등 r'[가-힣]{2,4}병원', # 병원명 r'\b[가-힣]{2,4}\s+[가-힣]{2,}\b', # 이름 패턴 ] for pattern in patterns: report_text = re.sub(pattern, '[DEIDENTIFIED]', report_text) # 2) 섹션 분리 (Findings, Impression 위주) sections = re.split(r'Findings?|소견|Impression', report_text, flags=re.IGNORECASE) cleaned = sections[-1].strip()[:2000] # MedGemma 컨텍스트 제한 return cleaned # 보고서 파일 정제 reports_dir = processed_dir / "reports" reports_dir.mkdir(exist_ok=True) for idx, row in df.iterrows(): # 가정: 원본 보고서 파일 존재 report_path = reports_dir / f"{row['image_id']}.txt" with open(report_path, 'w', encoding='utf-8') as f: cleaned_report = clean_medical_report("원본 보고서 텍스트") f.write(cleaned_report) df.at[idx, 'report_path'] = str(report_path)
STEP 4. MedGemma 입력 형식으로 변환 (JSONL)
pythondef create_medgemma_jsonl(df, output_path): """MedGemma 멀티모달 학습용 JSONL 생성""" data = [] for _, row in df.iterrows(): entry = { "image": str(row['image_png']), "messages": [ { "role": "user", "content": [ {"type": "image", "image": str(row['image_png'])}, {"type": "text", "text": "이 영상의 주요 소견을 설명해 주세요."} ] }, { "role": "assistant", "content": [{"type": "text", "text": clean_medical_report(row['report_path'])}] } ] } data.append(entry) # JSONL 저장 (HuggingFace datasets 호환) with open(output_path, 'w', encoding='utf-8') as f: for item in data: f.write(json.dumps(item, ensure_ascii=False) + '\n') create_medgemma_jsonl(df, processed_dir / "train.jsonl")
STEP 5. 데이터셋 분할 및 품질 검증
pythonfrom sklearn.model_selection import train_test_split # 8:1:1 분할 train_df, temp_df = train_test_split(df, test_size=0.2, random_state=42) val_df, test_df = train_test_split(temp_df, test_size=0.5, random_state=42) # 품질 검토 (랜덤 샘플 5개) sample_df = train_df.sample(5) for _, row in sample_df.iterrows(): print(f"ID: {row['image_id']}") print(f"이미지: {row['image_png']}") print(f"보고서 길이: {len(open(row['report_path']).read())}자") print("-" * 50)
STEP 6. HuggingFace datasets 형식으로 최종 변환
pythonfrom datasets import Dataset, DatasetDict # JSONL 로드 dataset = Dataset.from_json("medgemma_dataset/train.jsonl") # train/val/test 분할 train_dataset = dataset.select(range(800)) # 80% val_dataset = dataset.select(range(800, 900)) # 10% test_dataset = dataset.select(range(900, 1000)) # 10% dataset_dict = DatasetDict({ "train": train_dataset, "validation": val_dataset, "test": test_dataset }) dataset_dict.save_to_disk("medgemma_final_dataset") print(f"최종 데이터셋: {dataset_dict}")
실제 의료기관에서 활용 가능한 공개 데이터셋
즉시 사용 가능한 데이터셋:
text1. MIMIC-CXR (가장 추천) - 377,000 흉부 X-ray + 보고서 - https://physionet.org/content/mimic-cxr/2.0.0/ - MedGemma 훈련에 실제 사용됨 2. CheXpert - 224,000 흉부 X-ray + 14개 라벨 - https://stanfordmlgroup.github.io/competitions/chexpert/ 3. PadChest - 160,000 스페인어 X-ray + 보고서 - https://bimcv.cipf.es/bimcv-projects/padchest/
한국어 데이터셋:
text- KCDC 폐렴 X-ray 데이터셋 - 서울대병원 공개 데이터 (제한적)
데이터셋 통계 및 품질 체크리스트
pythondef validate_medgemma_dataset(dataset_dict): """파인튜닝 전 필수 품질 검증""" for split in dataset_dict: ds = dataset_dict[split] print(f"\n{split} 통계:") print(f"- 샘플 수: {len(ds)}") print(f"- 평균 이미지 크기: {np.mean([len(str(ex['image'])) for ex in ds])}") print(f"- 평균 텍스트 길이: {np.mean([len(ex['messages'][1]['content'][0]['text']) for ex in ds])}") # 이상치 제거 long_texts = sum(1 for ex in ds if len(ex['messages'][1]['content'][0]['text']) > 2000) print(f"- 긴 텍스트 비율: {long_texts/len(ds)*100:.1f}%") # 중복 체크 duplicates = len(ds) - len(set([ex['image'] for ex in ds])) print(f"- 중복 이미지: {duplicates}") validate_medgemma_dataset(dataset_dict)
최소 품질 기준:
text✅ 이미지: 512x512 이상 PNG ✅ 텍스트: 50~2000자 사이 ✅ 쌍(pair): 이미지-텍스트 1:1 매칭 ✅ 비식별화: 100% 완료 ✅ 분할: train 80%, val 10%, test 10%
요약
MedGemma 파인튜닝 데이터 준비는 1) DICOM→PNG 변환 + Windowing → 2) 보고서 정제 → 3) 이미지+텍스트 JSONL → 4) HuggingFace DatasetDict 4단계로 진행된다.+1
실제 병원 데이터를 쓸 때는 IRB 승인 → 비식별화 → 품질 검증 절차를 절대 생략하지 말고, MIMIC-CXR 같은 공개 데이터셋으로 먼저 테스트한 뒤 자사 데이터로 전환하는 것이 안전하다.
최종적으로 DatasetDict({"train": 800, "validation": 100, "test": 100}) 형태가 되면 LoRA 파인튜닝에 바로 투입할 수 있다.
댓글
댓글 쓰기