티스토리 뷰
https://www.kaggle.com/competitions/hms-harmful-brain-activity-classification/overview
2024년 2월 12일 현재, 캐글에서 진행하고 있는 컴피티션이다.
필자는 본 경진대회를 진행하면서, 실제로 배운 데이터 분석 요소들을 적용해보려고 한다.
글로 써질 것들은 모델을 학습하고, 만들기 까지의 과정이다.
목적 명확화
일단 첫 번째로 경진대회를 진행하려면 목적과 평기자표를 보아야 한다.
해당 경진대회의 미션은 detect and classify seizures and other types of harmful brain activity.
즉, 발작과 다른 해로운 뇌 활동을 감지하고 분류하는 것이다.
당연하게도 여러개를 분류하는 다중 분류 모델이 목적이고, 주어지는 데이터는 위독한 병원환자들로부터 기록된 뇌파 신호에 대해 훈련된 모델을 개발해야 한다.
electroencephalography (EEG)는 뇌파 신호로 주어지므로 뇌파 신호에 대한 도메인 지식이 필요하다.
Submissions are evaluated on the Kullback Liebler divergence between the predicted probability and the observed target.
또한 평가지표는 쿨백-라이블러 발산(KL-divergence)로 평가된다. 공교롭게도 함수 자체를 제공해주기에, 함수를 가져다 쓰면 된다.
KL-divegence는 거리 개념이 아니다.
그러나 p(x) 분포에서 샘플들의 평균을 통해 p(x)의 기댓값을 할 수 있으므로 p(x)와 q(x|파라미터 세타)(parametic distribution)을 근사시킬 수 있다.
KL-divergence를 minimize 하는 것이 likelihood를 maximize 하는 것과 같다.
즉, 크로스 엔트로피에서 1에서 뺀 값을 구해서 무질서도가 적은 값을 추구하는 것처럼, minimize를 통해 log liklihood를 maximize 한다.
데이터 셋 살펴보기
데이터셋은 중요하므로, 두고두고 보기 위해서 한글로 변환하여 표시한다.
- train.csv: 이 파일은 훈련 세트에 대한 메타데이터를 포함하며, EEG 샘플과 그에 해당하는 스펙트로그램을 설명합니다. 각 행은 특정 50초 길이의 EEG 하위 샘플과 짝을 이루는 10분 길이의 스펙트로그램 하위 샘플을 나타냅니다. 주요 열은 다음과 같습니다.
- eeg_id: 전체 EEG 기록에 대한 고유 식별자입니다.
- eeg_sub_id: EEG의 레이블이 적용되는 50초 길이의 특정 하위 샘플에 대한 식별자입니다.
- eeg_label_offset_seconds: 통합된 EEG 시작과 이 하위 샘플 사이의 시간입니다.
- spectrogram_id: 전체 EEG 기록에 대한 고유 식별자입니다.
- spectrogram_sub_id: 스펙트로그램의 레이블이 적용되는 특정 10분 부분 샘플의 ID입니다.
- spectrogram_label_offset_seconds: 통합된 스펙트로그램 시작과 이 하위 샘플 사이의 시간입니다.
- label_id: 이 레이블 세트에 대한 식별자입니다.
- patient_id: 데이터를 기증한 환자에 대한 식별자입니다.
- expert_consensus: 전문 주석자의 합의 레이블(참조용으로 제공됩니다).
- [seizure/lpd/gpd/lrda/grda/other]_vote: 각 뇌 활동 클래스에 대한 주석자 투표 수 (자세한 내용은 아래 참조).
뇌 활동 클래스:
- seizure: 발작 활동.
- lpd: 측면화된 주기적 방전(특정 뇌 영역의 비정상적인 전기 활동).
- gpd: 일반화된 주기적 방전(전체 뇌의 비정상적인 전기 활동).
- lrda: 측면화된 리듬 델타 활동(특정 뇌 영역의 저주파 활동).
- grda: 일반화된 리듬 델타 활동(전체 뇌의 저주파 활동).
- other: 기타 비특이적 뇌 활동.
- test.csv: 이 파일은 테스트 세트에 대한 메타데이터를 포함하며 eeg_id, spectrogram_id, patient_id를 포함합니다. 훈련 세트와 달리 겹치는 샘플이 없으므로 train.csv의 일부 열은 적용되지 않습니다.
> 즉, eeg_id,spectrogram_id, patient_id로 타깃값인 뇌활동 클래스를 구해야 한다. - sample_submission.csv: 이 파일은 테스트 세트에 대한 예측 결과를 제출하는 예시 형식을 제공합니다. eeg_id와 각 뇌 활동 클래스(seizure, lpd, gpd, lrda, grda, other)에 대한 예측 확률 열을 포함합니다.
>각 뇌활동 클래스에 대한 예측 확률. 즉, 확률로 나타내는 predict_proba를 사용해야 한다. - train_eegs/ 및 test_eegs/: 이 폴더는 실제 EEG 데이터 파일을 포함합니다.
- train_eegs/는 훈련 세트의 겹치는 샘플 데이터를 포함합니다. train.csv를 사용하여 특정 주석이 있는 하위 집합을 선택합니다. 열 이름은 개별 전극 위치를 나타냅니다.
- test_eegs/는 각 테스트 샘플에 대한 50초 길이의 EEG 데이터 파일을 포함합니다.
- train_spectrograms/ 및 test_spectrograms/: 이 폴더는 EEG 데이터로부터 조립된 스펙트로그램을 포함합니다.
- train_spectrograms/는 주석이 있는 훈련 세트의 스펙트로그램을 포함합니다. train.csv를 사용하여 특정 하위 집합을 선택합니다. 열 이름은 주파수와 기록 영역(LL, RL, LP, RP)을 나타냅니다.
- test_spectrograms/는 각 테스트 샘플에 대한 10분 길이의 EEG 데이터로부터 조립된 스펙트로그램을 포함합니다.
- example_figures/: 이 폴더는 개요 탭에 표시된 예시 사례 이미지의 확대 버전을 포함합니다.
분석 방법 수립
일단, 필자는 환자의 구분 방법과, eeg에 대한 도메인 지식이 없다.
따라서 최대한 할 수 있는 통계적 classification을 위해, 각 독립변수에 대해 대립가설을 수립하며 귀무가설과 통계적 지표를 살펴볼 것이다.
추가적으로 데이터 파일, 실제 egg 파일이 들어있는 .praquet 파일에서도 데이터를 뽑을 수 있다.
즉, 통계적인 지표를 일단 살펴보고, 데이터를 확장할 수 있는 방법이 있다면 더 확장해야 한다.
또한 모델링 방법 또한 고려해야할 것이다.
classifition 분석이기에, 일단 머신러닝 모델을 사용해야 하고, 다중 분류이기에 Knn을 쓰거나, K-Means를 써야 할 듯 싶다.
efficientnet에 대해서는 아직 배우지 않았기에, efficientnet에 대해서 학습해가면서 써야 한다.
여기서는 B0부터.
막간을 이용한 캐글 용어
LB = Leader Board standing(리더보드에 제출했을 때의 값)
CV= cross validation(검증했을 때의 값)
데이터 맛보기
import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
train=pd.read_csv('/kaggle/input/hms-harmful-brain-activity-classification/train.csv')
test=pd.read_csv('/kaggle/input/hms-harmful-brain-activity-classification/test.csv')
train.head()
eeg_id eeg_sub_id eeg_label_offset_seconds spectrogram_id spectrogram_sub_id spectrogram_label_offset_seconds label_id patient_id expert_consensus seizure_vote lpd_vote gpd_vote lrda_vote grda_vote other_vote
0 1628180742 0 0.0 353733 0 0.0 127492639 42516 Seizure 3 0 0 0 0 0
1 1628180742 1 6.0 353733 1 6.0 3887563113 42516 Seizure 3 0 0 0 0 0
2 1628180742 2 8.0 353733 2 8.0 1142670488 42516 Seizure 3 0 0 0 0 0
3 1628180742 3 18.0 353733 3 18.0 2718991173 42516 Seizure 3 0 0 0 0 0
4 1628180742 4 24.0 353733 4 24.0 3080632009 42516 Seizure 3 0 0 0 0 0
eeg_id,spectrogram_id의 분포는 범주형으로 취급해도 무방할 것 같고, sub_id는 다르지만, 결국 eeg_id와 spectrogram_id, patient_id와 종속관계로 보이므로, 모델 훈련 과정에서는 독립성을 위해 하나로 통합해도 괜찮아 보인다.
확인을 위해서, id가 다른 녀석들을 뽑아보았다.
train[train['eeg_id'] != 1628180742].head()
eeg_id eeg_sub_id eeg_label_offset_seconds spectrogram_id spectrogram_sub_id spectrogram_label_offset_seconds label_id patient_id expert_consensus seizure_vote lpd_vote gpd_vote lrda_vote grda_vote other_vote
9 2277392603 0 0.0 924234 0 0.0 1978807404 30539 GPD 0 0 5 0 1 5
10 2277392603 1 2.0 924234 1 2.0 134339127 30539 GPD 0 0 5 0 1 5
11 722738444 0 0.0 999431 0 0.0 557980729 56885 LRDA 0 1 0 14 0 1
12 722738444 1 2.0 999431 1 2.0 1949834128 56885 LRDA 0 1 0 14 0 1
13 722738444 2 4.0 999431 2 4.0 3790867376 56885 LRDA 0 1 0 14 0 1
결국 egg_id에 따라 변화하는 건 egg_label_offset_seconds(해당 id 안에서 변화), expert_consensus(id가 변화할때마다 변화) 등이다.
또한 vote에 대한 것도 유심히 살펴보아야 할 필요가 있다. 만약 vote 수가 비등비등한 경우에, 진단을 헷갈렸다는 의미기 때문.
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 106800 entries, 0 to 106799
Data columns (total 15 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 eeg_id 106800 non-null int64
1 eeg_sub_id 106800 non-null int64
2 eeg_label_offset_seconds 106800 non-null float64
3 spectrogram_id 106800 non-null int64
4 spectrogram_sub_id 106800 non-null int64
5 spectrogram_label_offset_seconds 106800 non-null float64
6 label_id 106800 non-null int64
7 patient_id 106800 non-null int64
8 expert_consensus 106800 non-null object
9 seizure_vote 106800 non-null int64
10 lpd_vote 106800 non-null int64
11 gpd_vote 106800 non-null int64
12 lrda_vote 106800 non-null int64
13 grda_vote 106800 non-null int64
14 other_vote 106800 non-null int64
dtypes: float64(2), int64(12), object(1)
memory usage: 12.2+ MB
info를 살펴보면 결측치도 없이 깔끔하다.
따라서 결측치에 대한 전처리는 고려하지 않아도 될 듯 싶다.
분석 결과 : id의 종속관계에 대한 가설 수립 및 일치화
따라서 id를 통한 그룹화로 타깃값 분포 보기
시간에 따라 나열되어 있으므로, 시계열 데이터.
그리고 vote에 대한 인사이트 수립
test.head()
spectrogram_id eeg_id patient_id
0 853520 3911565283 6885
플레이그라운드 경진대회와 달리, 실전이므로 테스트 데이터가 하나밖에 없다.
즉, 테스트를 평가하려면 무조건 validation set을 써야 한다.
그래서 캐글러들이 이 문제에 대해서는 CV vs LB를 비교한 양상을 많이 올려놓았다.
대략적으로, CV vs LB 비율은 1.6:1 인듯 싶다.
즉, CV에서 0.8이 나오면, LB에서는 0.5가 되는 식.
이를 참고하여 목표 CV를 구해보면, 현재 1등이 0.29이므로, CV가 0.5정도 나오면 잘 나오는 평가가 된다.
다음 시간에는 EDA를 진행해보면 될 듯 싶다.