Skip to content

Latest commit

 

History

History
489 lines (401 loc) · 14.7 KB

File metadata and controls

489 lines (401 loc) · 14.7 KB

CHB-MIT Seizure Detection Dataset - 전처리 정보 총정리

📚 목차

  1. 데이터셋 개요
  2. 원본 데이터 구조
  3. 전처리 파이프라인
  4. 라벨링 및 오버샘플링
  5. 원본 vs DIVER 적용 비교표
  6. 구현 상세

데이터셋 개요

기본 정보

  • 데이터셋명: CHB-MIT Scalp EEG Database
  • 출처: Children's Hospital Boston (CHB) / MIT
  • 목적: 소아 발작(Seizure) 감지
  • 대상: 소아 간질 환자
  • 데이터 타입: 두피 뇌파(Scalp EEG)

서브젝트 정보

  • 전체 환자 수: 21명
  • 환자 번호: chb01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 14, 15, 16, 18, 19, 20, 21, 22, 23, 24
    • 제외된 번호: chb12, chb13, chb17 (데이터 없음)
  • 모든 환자: 발작 환자 (간질 진단)

원본 데이터 구조

파일 구조

/data/datasets/chb-mit-scalp-eeg-database-1.0.0/
├── chb01/
│   ├── chb01-summary.txt       # 메타데이터 (발작 시간 등)
│   ├── chb01_01.edf            # EDF 파일 (recording 1)
│   ├── chb01_02.edf
│   └── ...
├── chb02/
└── ...

Summary 파일 구조

각 환자별 chb{XX}-summary.txt 파일 포함:

File Name: chb01_01.edf
Number of Seizures: 0

File Name: chb01_03.edf
Number of Seizures: 1
Seizure Start Time: 2996 seconds
Seizure End Time: 3036 seconds

채널 정보

전극 시스템

  • 시스템: 10-20 International System
  • Montage 타입: Bipolar (Longitudinal Bipolar / Double Banana)
  • 채널 수: 16개 (고정)

16개 Bipolar 채널

channels = [
    # 좌측 Lateral Chain
    "FP1-F7", "F7-T7", "T7-P7", "P7-O1",

    # 우측 Lateral Chain
    "FP2-F8", "F8-T8", "T8-P8", "P8-O2",

    # 좌측 Parasagittal Chain
    "FP1-F3", "F3-C3", "C3-P3", "P3-O1",

    # 우측 Parasagittal Chain
    "FP2-F4", "F4-C4", "C4-P4", "P4-O2"
]

전극 위치 (10-20 System)

  • Frontal Pole: FP1, FP2
  • Frontal: F3, F4, F7, F8
  • Central: C3, C4
  • Temporal: T7, T8 (구 T3, T4)
  • Parietal: P3, P4, P7, P8 (P7, P8는 구 T5, T6)
  • Occipital: O1, O2

Recording 정보

  • Sampling Rate: 256 Hz
  • 파일 형식: EDF (European Data Format)
  • Recording 길이: 환자/파일마다 다름 (보통 1시간)
  • 데이터 타입: Float (uV 단위)

전처리 파이프라인

파이프라인 구조

전처리는 2단계로 나뉨:

Stage 1 (process1.py): EDF → PKL (정제)
    ↓
Stage 2 (process2.py): PKL → Segments (세그먼트화 + 라벨링)

Stage 1: 원시 데이터 정제 (process1.py)

목적

  • EDF 파일에서 유효한 채널만 추출
  • 메타데이터(발작 시간) 파싱
  • 환자별로 정제된 데이터 저장

처리 과정

  1. Summary 파일 파싱 (process_metadata())

    • chb{p}-summary.txt 읽기
    • 발작 횟수, 시작/종료 시간 추출
    • 시간(초) → 샘플 인덱스 변환: time * 256 - 1
  2. 채널 선택 (drop_channels())

    • Summary 파일에서 유효 채널 목록 확인
    • EDF에서 해당 채널만 추출
    • 누락된 채널은 zero-padding
  3. 저장 (compressed_pickle())

    • 출력 경로: /data/datasets/BigDownstream/chb-mit/processed/
    • 형식: .pkl (pickle)
    • 구조:
      {
          'FP1-F7': np.array([...]),  # shape: (N,) where N = total samples
          'F7-T7': np.array([...]),
          ...
          'metadata': {
              'seizures': int,              # 발작 횟수
              'times': [(start, end), ...], # 발작 시간 (샘플 인덱스)
              'channels': [...]             # 유효 채널 목록
          }
      }

병렬 처리

parameters = [
    (patient, reference_file, start_file, end_file, summary_idx),
    ...
]
with mp.Pool(mp.cpu_count()) as pool:
    pool.starmap(start_process, parameters)

왜 1단계가 필요한가?

  • 느린 EDF 파싱을 한 번만: EDF 읽기는 시간이 오래 걸림
  • 채널 정렬: 환자마다 다른 채널 순서/이름 통일
  • 메타데이터 파싱: Summary 파일의 복잡한 형식 한 번만 처리
  • 재사용성: 다양한 세그먼트 실험에 재사용 가능

Stage 2: 세그먼트화 및 라벨링 (process2.py)

목적

  • 연속 신호를 10초 세그먼트로 분할
  • 발작 여부에 따라 라벨링
  • Train/Validation/Test 분할
  • 클래스 불균형 해소 (오버샘플링)

데이터 분할

test_pats  = ["chb23", "chb24"]           # 2명
val_pats   = ["chb21", "chb22"]           # 2명
train_pats = ["chb01" ~ "chb20"]          # 17명 (12, 13, 17 제외)
Split 환자 수 환자 번호
Train 17명 chb01-20 (12, 13, 17 제외)
Validation 2명 chb21, chb22
Test 2명 chb23, chb24

세그먼트 생성 과정

1) 기본 세그먼트 (모든 신호)

SAMPLING_RATE = 256
SEGMENT_LENGTH = 10  # 초

for i in range(0, signal.shape[1], SAMPLING_RATE * 10):  # 10초 step
    segment = signal[:, i : i + 10 * SAMPLING_RATE]  # (16, 2560)

    # 발작 포함 여부 확인
    label = 0
    for seizure_start, seizure_end in seizure_times:
        if i < seizure_start < i + 10*256 or i < seizure_end < i + 10*256:
            label = 1
            break

    # 저장
    save({"X": segment, "y": label}, f"{filename}-{i}.pkl")

2) 추가 발작 샘플 (오버샘플링)

# 발작 구간만 5초 step으로 추가 샘플링
for idx, (seizure_start, seizure_end) in enumerate(seizure_times):
    # 발작 ±1초 범위
    start = max(0, seizure_start - SAMPLING_RATE)
    end = min(seizure_end + SAMPLING_RATE, signal.shape[1])

    # 5초 step
    for i in range(start, end, 5 * SAMPLING_RATE):
        segment = signal[:, i : i + 10 * SAMPLING_RATE]
        label = 1

        # 파일명에 's-add' 표시로 구분
        save({"X": segment, "y": label}, f"{filename}-s-{idx}-add-{i}.pkl")

출력

  • 경로: /data/datasets/BigDownstream/chb-mit/processed_seg/{train,val,test}/
  • 형식: .pkl
  • 구조: {"X": np.array (16, 2560), "y": int (0 or 1)}
  • 파일명:
    • 기본: chb01_01.edf-0.pkl, chb01_01.edf-2560.pkl
    • 추가: chb01_01.edf-s-0-add-25344.pkl

라벨링 및 오버샘플링

라벨 정의

  • Label 0: 정상 (발작 없음)
  • Label 1: 발작 있음

라벨링 기준

10초 세그먼트 내에 발작 시작 또는 종료 시점이 포함되면 label = 1

예시:
시간:       [0초 -------- 100초(발작) --- 110초 -------- 200초]
세그먼트:   [90-100] [100-110] [110-120]
라벨:       0        1         0

클래스 불균형 문제

문제

전형적인 1시간 recording:
- 총 시간: 3600초
- 발작 구간: ~40초 (1%)
- 정상 구간: ~3560초 (99%)

기본 10초 세그먼트만 사용 시:
- Label 0: 356개
- Label 1: 4개  ← 너무 적음!
- 불균형 비율: 89:1

해결책: 오버샘플링

전략:

  1. 정상 구간: 10초 step (sparse sampling)
  2. 발작 구간: 5초 step (dense sampling) ← 2배 증가

효과:

기본 세그먼트:
- Label 0: 356개
- Label 1: 4개

오버샘플링 후:
- Label 0: 356개 (동일)
- Label 1: 4 + 8 = 12개  ← 3배 증가
- 불균형 비율: 89:1 → 30:1 (개선)

시각적 예시:

발작 구간: 100-110초

기본 세그먼트 (10초 step):
├─ [100-110초]: label=1
└─ 1개

추가 발작 샘플 (5초 step, 99-111초 범위):
├─ [99-109초]:  label=1  ← 추가
├─ [104-114초]: label=1  ← 추가
└─ 3개 (총 3배)

데이터 분포

각 Split별로:

  • ✅ Label 0과 1이 모두 섞여 있음
  • ✅ Train/Val/Test 모두 발작 환자 포함
  • ⚠️ 여전히 클래스 불균형 존재 (정상 >> 발작)
  • ✅ 오버샘플링으로 어느 정도 완화

원본 vs DIVER 적용 비교표

구분 항목 원본 (CHB-MIT Original) 수정 (DIVER 적용)
데이터셋 전체 서브젝트 수 21명 21명 (동일)
Train 17명 (chb01-20, 12/13/17 제외) 17명 (동일)
Validation 2명 (chb21, chb22) 2명 (동일)
Test 2명 (chb23, chb24) 2명 (동일)
원본 데이터 Sampling Rate 256 Hz 256 Hz (동일)
파일 형식 EDF EDF (동일)
채널 수 16개 (고정) 16개 (동일)
채널 시스템 전극 배치 시스템 10-20 System 10-20 System (동일)
Montage 타입 Bipolar Montage Bipolar Montage (동일)
채널 목록 FP1-F7, F7-T7, T7-P7, P7-O1,
FP2-F8, F8-T8, T8-P8, P8-O2,
FP1-F3, F3-C3, C3-P3, P3-O1,
FP2-F4, F4-C4, C4-P4, P4-O2
동일
ELC 파일 사용 ❌ 없음 사용 (standard_1005.elc)
전처리 Stage 1 입력 경로 /data/datasets/chb-mit-scalp-eeg-database-1.0.0 동일
출력 경로 /data/datasets/BigDownstream/chb-mit/processed 동일
채널 선택 Summary 파일 기반 동일
메타데이터 추출 seizures, times, channels 동일
중간 저장 형식 .pkl (pickle) 동일
전처리 Stage 2 세그먼트 길이 10초 10초 (동일)
슬라이딩 윈도우 10초 step (non-overlapping) 10초 step (동일)
추가 샘플링 발작 구간 ±1초, 5초 step 동일
리샘플링 타겟 Sampling Rate - (256 Hz 유지) 500 Hz
리샘플링 방법 - scipy.signal.resample
Reshape - (16, 5000) → (16, 10, 500)
라벨링 라벨 종류 Binary (0, 1) Binary (0, 1) (동일)
라벨 0 정상 (발작 없음) 동일
라벨 1 발작 있음 동일
라벨링 기준 10초 세그먼트 내 발작 포함 여부 동일
라벨 정보 출처 chb{p}-summary.txt 동일
클래스 불균형 해소 발작 구간 5초 step 오버샘플링 동일
최종 출력 Shape (16, 2560) (16, 10, 500)
16채널, 2560샘플 16채널, 10×1초, 500샘플/초
데이터 구조 {"X": array, "y": int} {"signal": array, "label": int, "elc_info": dict}
저장 형식 .pkl (pickle) LMDB
출력 경로 /data/datasets/BigDownstream/chb-mit/processed_seg/ 서버 업로드 경로
메모리/성능 메모리 요구량 - 200GB+
병렬 처리 CPU 멀티프로세싱 동일
정규화 ❌ 없음 없음 (모델에서 처리)

구현 상세

Shape 변환 과정

# 원본 (256Hz)
signal_256 = np.array (16, 2560)  # 16채널 × 10초 × 256Hz

# Step 1: 리샘플링 (256Hz → 500Hz)
from scipy.signal import resample
signal_500 = resample(signal_256, 5000, axis=1)  # (16, 5000)

# Step 2: Reshape (10개 1초 세그먼트)
signal_final = signal_500.reshape(16, 10, 500)  # (16, 10, 500)

ELC 파일 구조

elc_info = {
    "channel_names": [
        "FP1-F7", "F7-T7", "T7-P7", "P7-O1",
        "FP2-F8", "F8-T8", "T8-P8", "P8-O2",
        "FP1-F3", "F3-C3", "C3-P3", "P3-O1",
        "FP2-F4", "F4-C4", "C4-P4", "P4-O2"
    ],
    "electrode_pairs": {
        "FP1-F7": ["FP1", "F7"],
        "F7-T7": ["F7", "T7"],
        ...
    },
    "electrode_positions": {
        # standard_1005.elc에서 로드
        "FP1": [x, y, z],
        "F7": [x, y, z],
        ...
    },
    "xyz_id": np.array (16, 3)  # channel_names 순서의 전극 3D 좌표
}

LMDB 저장 구조 (v2-keymodified)

# Key: "{patient}_{recording}_{segment_index}"
key = "chb01_01_0"

# Value: pickled dictionary
value = {
    "sample": np.array (16, 10, 500),  # float32 (v1: "signal")
    "label": int (0 or 1),
    "data_info": {  # v1: "metadata" + "elc_info" 통합
        # ISRUC-compatible fields
        "Dataset": "CHBMIT-Seizure",
        "modality": "EEG",
        "release": "1.0.0",
        "subject_id": "chb01",
        "task": "seizure-detection",
        "resampling_rate": 500,
        "original_sampling_rate": 256,
        "segment_index": 0,
        "start_time": 0.0,
        "channel_names": list,  # 16 bipolar channels
        "electrode_pairs": dict,  # Bipolar pairs
        "electrode_positions": dict,  # 3D coordinates
        "xyz_id": np.array (16, 3),  # channel_names 순서
        # CHBMIT-specific fields
        "segment_id": "chb01_01_0",
        "split": "train",
        "is_oversampled": False
    }
}

파일명 규칙

  • 기본 세그먼트: {patient}_{recording}_{sample_index}
    • 예: chb01_01_0, chb01_01_2560
  • 추가 발작 샘플: {patient}_{recording}_s{seizure_idx}_add_{sample_index}
    • 예: chb01_03_s0_add_25344

참고사항

데이터 품질

  • ✅ EDF 파일 무결성 확인됨
  • ✅ 채널 정렬 완료 (16개 고정)
  • ⚠️ Artifact rejection 없음 (원본 신호 그대로 사용)
  • ⚠️ 정규화 없음 (모델 학습 시 적용)

주의사항

  1. 환자 정보 보호: 환자 번호만 사용 (개인정보 제거됨)
  2. 클래스 불균형: 오버샘플링으로 완화했으나 여전히 존재
  3. ELC 매핑: Bipolar 채널 → 두 전극 위치 모두 저장
  4. 메모리: 전체 데이터셋 크기 고려 시 200GB+ 메모리 권장

데이터셋 크기 추정

환자당 평균:
- Recording 수: ~30개
- Recording 길이: ~1시간
- 기본 세그먼트: ~360개/recording
- 오버샘플링 포함: ~400개/recording

전체 데이터셋:
- Train: 17명 × 30 × 400 = ~204,000 샘플
- Val: 2명 × 30 × 400 = ~24,000 샘플
- Test: 2명 × 30 × 400 = ~24,000 샘플
- 총: ~252,000 샘플

용량 (LMDB):
- 샘플당: ~40KB (16×10×500 float32 + metadata)
- 총: ~10GB (압축 후)

원본 논문 및 참고자료

논문

Shoeb, A. (2009).
Application of Machine Learning to Epileptic Seizure Detection.
In: Proceedings of the 26th International Conference on Machine Learning (ICML).

데이터셋

관련 프로젝트


버전 정보

  • 작성일: 2025-11-21
  • 데이터셋 버전: CHB-MIT v1.0.0
  • 전처리 버전: DIVER 적용 v1.0
  • 작성자: Bohee Lee

라이센스

  • 데이터 사용: 연구 목적으로 자유롭게 사용 가능
  • 인용 필수: 논문 발표 시 원본 논문 인용 필요
  • Open Access: Public dataset