오늘은 그토록 기대하던 머신러닝 파트의 첫 수업이었다.
개념을 정말 확실히 짚고 설명을 해주셔서 좋았다.
어떤 내용을 다뤘는지 복습해보자!
* 예시 코드의 출처는 오즈코딩스쿨로부터...
장점
한계
많이 헷갈리는 개념부터 정리해보자.
👉 딥러닝 ⊂ 머신러닝 ⊂ 인공지능
* 종속 변수에 영향이 클 변수를 선택하는 것이 학습에 중요함.
이를 판별하고, 선택하는 과정이 특징 공학(Feature Engineering)임
1) 기획(문제 정의)
2) 데이터 수집
3) 데이터 정제(재료 준비)
4) 어떤 모델 알고리즘을 사용할 것인가
(여러 모델로 try해보고, 제일 성능이 좋은 것으로 선택)
5) 모델 구축
6) 새로운 데이터(학습에 사용되지 않은 데이터)를 가지고 예측
7) 모델의 판단 결과를 가지고 보완/서비스 공급
머신러닝 모델을 만드는 진짜 목적은 하나다.
학습에 쓰지 않은 데이터에서도 잘 맞히는 것.
이 능력을 일반화(Generalization)라고 한다.
문제는 모델이 학습 데이터에만 과도하게 맞춰지는 경우다.
이를 과적합(Overfitting)이라고 부른다.
👉 그래서 우리는 모델을 평가하기 위한 데이터를 따로 분리해야 한다.
또한 좋은 AI모델을 만들려면 과적합을 지양하는 것을 넘어 억제해야 하고,
이를 위해 다양한 오버피팅 방지 기법이 존재한다.
오늘은 그 중 데이터 분리에 대해 알아보았다.
(이 내용의 깔끔한 정리는 다음 포스팅에서 다루겠다.)
전체 데이터는 명확한 목적을 가진 세 부분으로 분할할 수 있다.
1)은 학습을 시키기 위함이고, 2)와 3)은 평가를 하기 위함이다.
또한,
1), 2)는 모델을 구축하는 과정(학습 진행 과정)에 사용되고, 3)은 모델이 구축된 후(학습 종료)에 사용된다.
하지만 중요한 사실 하나.
데이터 분할에는 ‘황금 비율’이 없다.
핵심은 비율이 아니라
각 세트가 목적을 수행할 만큼 충분한 절대량을 갖는지다.
👉 목표는 신뢰할 수 있는 평가가 가능한 최소한의 데이터 확보다.
# 1. 필요한 라이브러리 및 데이터 로드
(생략)
# 1.1. 특징(X)과 타겟(y) 분리
X = pima_df.drop('outcome', axis=1) # Feature(독립변수)
y = pima_df['outcome'] # Target(종속변수)
# 2. 훈련/테스트 데이터 분할 (8:2 비율) (X, y는 관습적. X: feature Matrix, y: y_hat)
X_train, X_test, y_train, y_test = train_test_split(
X, y,
test_size=0.2,
random_state=42,
stratify=y # 분류 문제이므로 stratify(stratified(층화) sampling) 적용
)
# 3. 분할 결과 확인
print("Original data shape:", X.shape)
print("X_train shape:", X_train.shape)
print("X_test shape:", X_test.shape)
# 3.1. 이전 단계에서 X_train, y_train을 추가로 분할(validation data 만들어야 하니까)
# test_size=0.25 -> 80% 중 25%를 검증셋으로 할당 (전체의 20%)
X_train_final, X_val, y_train_final, y_val = train_test_split(
X_train, y_train,
test_size=0.25,
random_state=42,
# stratify=y_train # 분류 문제이므로 stratify 적용
)
# 3.2. 최종 분할 결과 확인 (6:2:2)
print("X_train_final shape:", X_train_final.shape) # 60%
print("X_val shape:", X_val.shape) # 20%
print("X_test shape:", X_test.shape) # 20%
# 3.3. 알맞게 나눠졌는지 검증!
# 3.3.1. train data 검증
print('y_train_final 빈도 분석:')
display(y_train_final.value_counts().sort_index())
print('\ny_train_final 비율 분석:')
display(y_train_final.value_counts(normalize=True).sort_index())
# 3.3.2. validation data 검증
print('y_val 빈도 분석:')
display(y_val.value_counts().sort_index())
print('\ny_val 비율 분석:')
display(y_val.value_counts(normalize=True).sort_index())
# 3.3.3. test data 검증
print('y_test 빈도 분석:')
display(y_test.value_counts().sort_index())
print('\ny_test 비율 분석:')
display(y_test.value_counts(normalize=True).sort_index())
여기서 의문이 생겼던 점은
1.1. 특징(X)과 타겟(y)를 분리 후에
2. 훈련/테스트 데이터를 분할한다는 점이었다.
Feature를 '문제', Target을 '답지'라고 비유했을 때
문제에 따른 답지가 연결이 되어 있어야 하는데,
이렇게 진행하게 되면 문제에 맞는 답지를 연결해서 훈련용/테스트용으로 나누려면 컴퓨터가 내부적으로 인덱스로 찾아야 하는거 아닌가?
그럼 비용이 너무 커져서 비효율적인거 아닐까?라는 점이었다.
근데 train_test_split()함수를 자세히 살펴보면
매개변수로써 X와 y를 요구하고 있다.
즉, 이 함수를 쓰기 위해서도 1.1. 과정이 선행되어야 하며
이 함수를 쓰게 되면
Feature와 Target이 pair로 움직이기 때문에
Feature와 Target이 잘못 섞여 분리되는 일은 발생하지 않는다고 한다.
이후 다루게 될 Shuffle = True를 쓰더라도 섞이지 않는다고...
시간 순서가 중요한 데이터(Time-Series data)에서는 무작위 섞기가 치명적인 오류를 만든다.
왜냐하면:
미래 데이터로 과거를 예측하는 데이터 누수(Data Leakage)가 발생하기 때문이다
이는 학습 과정에서 답안지를 보여주는 것과도 같다.
올바른 방법은 단 하나.
으로 명확하게 분리하는 것이다.
데이터 누수란,
실제 예측 시점에는 알 수 없는 정보가 학습 과정에 포함되는 현상이다.
1) 전처리 누수(Preprocessing Leakage)
🧐 이게 무슨 말일까?
🔹 먼저 스케일링이 뭘 하는지부터
대표적인 예: StandardScaler
# 공식
z = (x - μ) / σ
👉 즉, 평균과 표준편차라는 ‘기준값’이 필요하다.
🔹 평균이랑 표준편차는 어디서 계산해야 할까?
❌ 잘못된 방법
전체 데이터 (Train + Test)
→ 평균 계산
→ 표준편차 계산
→ 그걸로 Train, Test 전부 변환
👉 이렇게 하면 Test 데이터의 정보가 이미 평균 계산에 들어감.
(이게 바로 데이터 누수(Data Leakage))
✅ 올바른 방법
1단계: Train 데이터만 사용
Train 데이터
→ 평균 계산
→ 표준편차 계산
👉 이 과정을 fit 이라고 부름.
2단계: 같은 기준으로 모두 변환
Train 데이터 → transform
Validation 데이터 → transform
Test 데이터 → transform
⚠️ 평균·표준편차를 새로 계산하지 않는다.
🔥 핵심 차이 요약
| 단계 | 무슨 일? |
|---|---|
| fit() | 통계값 계산 (평균, 표준편차 등) |
| transform() | 이미 계산된 값으로 숫자만 변환 |
| fit_transform() | fit + transform을 동시에 |
2) 시간 누수(Temporal Leakage)
3) 타깃 누수(Target Leakage)
4) 그룹 누수(Group Leakage)
1) 목적을 명확히 하라
2) 데이터 특성을 반영하라
3) 데이터 누수 경계를 지켜라
4) 실제 운영 환경을 반영하라
이 원칙들을 반드시 복기하며 앞으로의 머신러닝에 활용하자.