이 장에서 배울 것
이번 장에서는 지금까지 배운 내용을 하나의 작은 머신러닝 프로젝트로 묶어 본다. 문제를 정의하고, 데이터를 나누고, 기준 모델을 만들고, 여러 모델을 비교한 뒤, 오류 분석과 간단한 집단별 성능 점검까지 수행한다.
목표는 "가장 높은 점수"가 아니다. 목표는 검증 가능한 방식으로 모델을 만들고, 결과를 설명할 수 있는 능력이다. 실제 현업이나 경진대회에서는 코드보다 기록이 더 중요할 때가 많다. 어떤 실험을 했고, 왜 바꿨고, 결과가 정말 좋아졌는지 설명할 수 있어야 한다.
이번 장의 최종 산출물은 네 가지다.
문제 정의서
모델 학습 코드와 제출 파일
실험 기록표
최종 분석 리포트

왜 이 주제가 중요한가
앞 장들에서는 개념을 하나씩 배웠다. 분류와 회귀를 구분했고, 훈련 데이터와 테스트 데이터를 나누었고, 정확도만 믿으면 위험하다는 것도 배웠다. 교차검증, 혼동 행렬, feature importance, 오류 분석도 다루었다.
하지만 실제 프로젝트에서는 이 개념들이 따로 등장하지 않는다. 한 번에 섞여 나온다.
예를 들어 고객 이탈을 예측한다고 하자. 먼저 "무엇을 이탈로 볼 것인가"를 정해야 한다. 그다음 어떤 데이터를 사용할지 정한다. 학습용 데이터와 평가용 데이터를 나누고, 기준 모델을 만든다. 성능이 낮으면 전처리나 모델을 바꾼다. 마지막으로 "전체 성능은 괜찮지만 특정 고객군에서 유독 많이 틀리는가?"를 확인해야 한다.
이 흐름을 한 번 끝까지 해 보면 머신러닝이 훨씬 분명해진다. 모델은 프로젝트의 일부일 뿐이다. 좋은 머신러닝 프로젝트는 문제 정의, 데이터 이해, 평가, 해석, 기록이 함께 움직인다.
핵심 개념 설명
미니 Kaggle형 프로젝트의 구성
Kaggle형 프로젝트는 보통 다음 요소를 갖는다.
문제 설명: 무엇을 예측해야 하는가?
데이터 설명: 각 열이 무엇을 의미하는가?
평가 지표: 어떤 기준으로 성능을 비교하는가?
제출 형식: 예측 결과를 어떤 파일로 제출하는가?
리더보드 또는 점수표: 여러 실험 결과를 비교하는 표
이 책에서는 실제 온라인 경진대회에 참가하지 않고, 로컬 환경에서 작은 경진대회를 흉내 낸다. 데이터를 학습용과 최종 테스트용으로 나누고, 테스트 데이터의 정답은 마지막까지 보지 않는다고 가정한다.
기준 모델부터 시작하기
초보자가 자주 하는 실수는 처음부터 복잡한 모델을 쓰는 것이다. 프로젝트에서는 먼저 기준 모델, 즉 baseline model을 만든다. 기준 모델은 이후 실험의 출발점이다.
기준 모델은 단순해야 한다. 예를 들어 분류 문제라면 로지스틱 회귀, 결정 트리, 또는 항상 가장 많은 클래스를 예측하는 모델을 기준으로 삼을 수 있다. 기준 모델보다 나아지지 않는 복잡한 모델은 좋은 모델이 아니다.
성능 개선은 한 번에 하나씩
전처리도 바꾸고, 모델도 바꾸고, 평가 방식도 바꾸면 무엇 때문에 성능이 달라졌는지 알 수 없다. 실험은 가능한 한 한 번에 하나씩 바꾼다.
예를 들면 다음 순서가 좋다.
기준 모델 학습
표준화 추가
다른 모델 비교
하이퍼파라미터 일부 조정
오류 분석 후 특징 수정
여기서 하이퍼파라미터는 사람이 미리 정하는 설정값이다. 예를 들어 트리의 최대 깊이, kNN의 이웃 수, 랜덤 포레스트의 트리 개수 등이 여기에 해당한다.
교차검증과 테스트 결과를 구분하기
교차검증은 훈련 데이터를 여러 조각으로 나누어 모델을 반복 평가하는 방법이다. 모델 선택에 유용하다. 하지만 최종 테스트 데이터는 마지막 확인용으로 남겨 두어야 한다.
절차는 이렇게 잡는다.
훈련 데이터: 모델 학습과 교차검증에 사용
검증 결과: 모델 선택에 사용
최종 테스트 데이터: 마지막 성능 확인에만 사용
테스트 데이터 점수를 보고 계속 모델을 고치면 테스트 데이터에 맞춘 모델이 된다. 그러면 새 데이터에서 성능이 떨어질 수 있다.
관심 클래스와 평가 지표를 먼저 정하기
분류 프로젝트에서는 "무엇을 positive class로 볼 것인가"를 먼저 정해야 한다. F1, 정밀도, 재현율은 기본적으로 positive class를 기준으로 계산되기 때문이다.
이번 장의 예제인 scikit-learn 유방암 데이터는 원래 라벨이 0=malignant, 1=benign이다. 하지만 우리가 놓치지 말아야 할 관심 대상은 보통 악성 종양이다. 따라서 이 장에서는 악성을 1로 바꾸어 F1과 재현율을 해석한다.
이 과정은 단순한 숫자 변경이 아니다. 평가 질문을 분명히 하는 일이다.
관심 클래스: 악성 종양
positive class: 1
주요 지표: F1, 재현율, 혼동 행렬쉬운 비유 또는 사례
미니 Kaggle형 프로젝트는 요리 대회와 비슷하다.
먼저 심사 기준을 확인해야 한다. 맛을 보는지, 조리 시간을 보는지, 영양 균형을 보는지에 따라 전략이 달라진다. 머신러닝에서도 평가 지표가 정확도인지, F1 점수인지, 평균제곱오차인지에 따라 좋은 모델이 달라진다.
그다음 기본 레시피를 만든다. 이것이 기준 모델이다. 기본 레시피가 있어야 새 재료를 넣었을 때 좋아졌는지 나빠졌는지 비교할 수 있다.
마지막으로 시식평을 본다. 전체 점수만 보면 부족하다. "너무 짜다", "식감은 좋지만 향이 약하다" 같은 구체적인 피드백이 필요하다. 머신러닝에서는 이것이 오류 분석이다. 어떤 사례를 맞혔고, 어떤 사례를 틀렸는지 봐야 다음 개선 방향이 보인다.
직접 해보는 실습
이번 실습은 scikit-learn의 유방암 분류 데이터셋을 사용한다. 목표는 종양이 악성인지 아닌지 분류하는 것이다. 이 데이터는 학습용 예제이며, 실제 의료 판단에 사용해서는 안 된다.
1단계: 문제 정의서 작성
먼저 다음 네 문장을 적는다.
문제 유형: 이진 분류
입력 데이터 X: 종양의 반지름, 질감, 둘레, 면적 등 수치형 특징
예측 대상 y: 악성 여부
평가 지표: 악성 클래스를 기준으로 한 F1 점수, 재현율, 혼동 행렬
F1 점수는 정밀도와 재현율을 함께 고려하는 지표다. 한쪽 클래스의 오답이 중요한 문제에서 정확도보다 더 유용할 수 있다. 의료 예제에서는 특히 실제 악성을 놓치는 거짓음성(FN)을 주의 깊게 봐야 한다.
2단계: 데이터 준비와 분할
import pandas as pd
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
cancer = load_breast_cancer()
X = pd.DataFrame(cancer.data, columns=cancer.feature_names)
# 원래 라벨: 0=malignant, 1=benign
# 이 프로젝트에서는 악성을 관심 클래스 1로 변환한다.
y_original = pd.Series(cancer.target, name="original_target")
y = (y_original == 0).astype(int).rename("is_malignant")
target_names = ["not malignant", "malignant"]
X_train, X_test, y_train, y_test = train_test_split(
X, y,
test_size=0.2,
random_state=42,
stratify=y
)
print(X_train.shape, X_test.shape)
print(y_train.value_counts(normalize=True))
print(y_test.value_counts(normalize=True))stratify=y는 훈련 데이터와 테스트 데이터의 클래스 비율을 비슷하게 유지한다. 분류 문제에서는 자주 사용하는 안전장치다. 여기서는 변환된 y, 즉 악성 여부의 비율을 기준으로 나눈다.
3단계: 기준 모델 만들기
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import f1_score, recall_score, confusion_matrix, classification_report
baseline = Pipeline([
("scaler", StandardScaler()),
("model", LogisticRegression(max_iter=1000, random_state=42))
])
baseline.fit(X_train, y_train)
pred = baseline.predict(X_test)
print("F1:", f1_score(y_test, pred))
print("재현율:", recall_score(y_test, pred))
print(confusion_matrix(y_test, pred))
print(classification_report(y_test, pred, target_names=target_names))Pipeline은 전처리와 모델을 하나로 묶는 도구다. 이렇게 하면 훈련 데이터에 맞춰 표준화를 학습하고, 같은 방식으로 테스트 데이터에 적용할 수 있다. 데이터 누수를 줄이는 좋은 습관이다.
혼동 행렬은 기본적으로 다음 순서로 읽는다.
[[TN, FP],
[FN, TP]]여기서 FN은 실제 악성인데 악성이 아니라고 예측한 경우다. 이 예제에서는 이 값을 특히 조심해서 확인한다.
4단계: 여러 모델을 교차검증으로 비교하기
from sklearn.model_selection import cross_val_score
from sklearn.ensemble import RandomForestClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC
models = {
"logistic": Pipeline([
("scaler", StandardScaler()),
("model", LogisticRegression(max_iter=1000, random_state=42))
]),
"knn": Pipeline([
("scaler", StandardScaler()),
("model", KNeighborsClassifier(n_neighbors=5))
]),
"svm": Pipeline([
("scaler", StandardScaler()),
("model", SVC(kernel="rbf", random_state=42))
]),
"random_forest": RandomForestClassifier(
n_estimators=200,
random_state=42
)
}
results = []
for name, model in models.items():
scores = cross_val_score(
model, X_train, y_train,
cv=5,
scoring="f1"
)
results.append({
"model": name,
"cv_mean_f1": scores.mean(),
"cv_std": scores.std()
})
pd.DataFrame(results).sort_values("cv_mean_f1", ascending=False)평균 점수가 조금 높다고 무조건 좋은 모델은 아니다. 표준편차도 함께 봐야 한다. 표준편차가 크면 데이터 분할에 따라 성능이 흔들린다는 뜻이다.
이번 프로젝트의 F1은 악성 클래스를 기준으로 계산된다. 만약 관심 클래스가 바뀐다면 라벨 변환이나 pos_label 설정도 함께 확인해야 한다.
5단계: 최종 모델 선택과 제출 파일 만들기
교차검증 결과를 보고 최종 모델 하나를 선택한다. 여기서는 예시로 랜덤 포레스트를 선택한다. 실제로는 자신이 실행한 교차검증 결과와 오류 분석을 근거로 선택해야 한다.
final_model = RandomForestClassifier(
n_estimators=200,
random_state=42
)
final_model.fit(X_train, y_train)
final_pred = final_model.predict(X_test)
print("Final F1:", f1_score(y_test, final_pred))
print("Final recall:", recall_score(y_test, final_pred))
print(confusion_matrix(y_test, final_pred))Kaggle형 프로젝트에서는 예측 결과를 파일로 제출한다. 여기서는 테스트 데이터의 행 번호와 예측값을 저장한다. prediction 값은 1이면 악성, 0이면 악성이 아님을 뜻한다.
submission = pd.DataFrame({
"id": X_test.index,
"prediction": final_pred
})
submission.to_csv("submission.csv", index=False)
submission.head()6단계: feature importance 확인하기
랜덤 포레스트는 각 특징이 예측에 얼마나 기여했는지 대략 확인할 수 있다.
importances = pd.Series(
final_model.feature_importances_,
index=X_train.columns
).sort_values(ascending=False)
print(importances.head(10))feature importance는 "원인"을 증명하지 않는다. 모델이 예측할 때 많이 사용한 단서일 뿐이다. 따라서 중요도가 높다고 해서 그 특징이 실제 질병의 원인이라고 말하면 안 된다.
7단계: 오류 사례 보기
error_df = X_test.copy()
error_df["true"] = y_test.values
error_df["pred"] = final_pred
wrong = error_df[error_df["true"] != error_df["pred"]]
print(wrong[["true", "pred"]].head())
print("오답 개수:", len(wrong))
false_negative = error_df[(error_df["true"] == 1) & (error_df["pred"] == 0)]
false_positive = error_df[(error_df["true"] == 0) & (error_df["pred"] == 1)]
print("거짓음성(FN) 개수:", len(false_negative))
print("거짓양성(FP) 개수:", len(false_positive))오답을 보면 모델의 한계가 보인다. 특정 범위의 값에서 자주 틀리는지, 실제 악성을 더 많이 놓치는지, 악성이 아닌 사례를 악성으로 많이 판단하는지 확인한다. 이 과정이 앞 장에서 배운 오류 분석이다.
8단계: 간단한 집단별 성능 점검
이 데이터에는 성별, 나이 같은 보호 속성이 없다. 따라서 실제 공정성 판단은 할 수 없다. 대신 연습용으로 mean radius 값을 기준으로 두 그룹을 나누어 성능이 크게 다른지 확인해 보자.
from sklearn.metrics import accuracy_score
check = X_test.copy()
check["true"] = y_test.values
check["pred"] = final_pred
check["group"] = pd.qcut(check["mean radius"], q=2, labels=["small", "large"])
for group_name, part in check.groupby("group"):
print(group_name)
print("count:", len(part))
print("accuracy:", accuracy_score(part["true"], part["pred"]))
print("f1:", f1_score(part["true"], part["pred"], zero_division=0))
print("recall:", recall_score(part["true"], part["pred"], zero_division=0))집단별 성능 차이가 보이면 원인을 더 살펴봐야 한다. 단, 여기서 나눈 그룹은 실제 보호 집단이 아니다. 실제 프로젝트에서 공정성 점검을 할 때는 나이, 성별, 지역, 장애 여부 같은 민감한 정보의 수집과 사용에 법적·윤리적 검토가 필요하다.
흔한 실수와 오류 분석
첫째, 테스트 데이터를 너무 자주 보는 실수다. 테스트 결과를 본 뒤 계속 모델을 고치면 테스트 데이터에 맞춘 셈이 된다. 최종 테스트는 마지막에 한 번만 확인한다는 원칙을 세운다.
둘째, 기준 모델 없이 복잡한 모델부터 쓰는 실수다. 기준 모델이 없으면 성능 개선을 판단할 기준도 없다. 단순한 모델로 출발해야 개선 과정이 선명해진다.
셋째, 점수 차이를 과장하는 실수다. F1 점수가 0.961에서 0.964로 올랐다고 해서 반드시 더 좋은 모델이라고 말할 수 없다. 교차검증의 표준편차, 데이터 크기, 오류 유형을 함께 봐야 한다.
넷째, feature importance를 원인처럼 해석하는 실수다. 모델이 자주 사용한 특징과 현실의 원인은 다르다. 해석은 항상 조심해야 한다.
다섯째, AI 도구가 만든 코드를 검증하지 않는 실수다. AI가 제안한 코드는 실행될 수 있지만 평가 방식이 틀렸을 수 있다. 특히 데이터 누수, 잘못된 지표, train/test 혼동, positive class 설정을 직접 확인해야 한다.
여섯째, 라벨 의미를 확인하지 않는 실수다. 숫자 0과 1은 그 자체로 좋은 클래스와 나쁜 클래스를 뜻하지 않는다. 데이터 설명을 읽고 어떤 숫자가 어떤 범주인지 확인해야 한다. 관심 클래스를 잘못 잡으면 F1과 재현율 해석이 완전히 달라질 수 있다.
최종 리포트 작성 형식
프로젝트가 끝나면 짧은 리포트를 작성한다. 길게 쓸 필요는 없다. 중요한 것은 판단의 근거다.
다음 형식을 사용해 보자.
1. 문제 정의
- 예측하려는 값:
- 문제 유형:
- 관심 클래스와 라벨 의미:
- 사용한 평가 지표:
2. 데이터와 분할
- 데이터 크기:
- 특징 수:
- train/test 분할 방식:
- 클래스 비율 확인 여부:
3. 실험 기록
- 기준 모델:
- 비교한 모델:
- 교차검증 결과:
- 최종 선택 모델과 이유:
4. 최종 성능
- 테스트 F1:
- 테스트 재현율:
- 혼동 행렬 해석:
- 가장 중요한 특징:
5. 오류 분석
- 자주 틀린 사례의 특징:
- 거짓음성과 거짓양성 중 더 중요한 오류:
- 추가로 필요한 데이터:
6. 공정성 또는 집단별 점검
- 어떤 그룹으로 나누어 보았는가:
- 그룹별 성능 차이:
- 실제 적용 시 주의할 점:
7. AI 사용 로그
- 사용한 질문:
- 생성된 코드:
- 직접 검증한 내용:
- 수정한 부분:AI 도구를 사용했다면 사용 사실을 숨기지 않는다. 대신 스스로 검증한 과정을 함께 남긴다. 좋은 기록은 "AI가 코드를 만들어 주었다"가 아니라 "AI가 제안한 코드를 실행했고, 데이터 누수가 없는지 확인했으며, 지표 계산과 positive class 설정을 직접 검증했다"처럼 써야 한다.
요약
미니 Kaggle형 프로젝트는 머신러닝의 전체 흐름을 압축해서 연습하는 방법이다. 문제 정의, 데이터 설명, 평가 지표, 제출 형식을 먼저 정해야 한다. 그다음 기준 모델을 만들고, 전처리와 모델을 단계적으로 바꾸며 실험한다.
모델 비교에는 교차검증을 사용하고, 최종 테스트 데이터는 마지막 확인용으로 남긴다. 점수만 보지 말고 혼동 행렬, 오답 사례, feature importance를 함께 확인해야 한다. 가능하다면 집단별 성능도 점검한다.
분류 프로젝트에서는 관심 클래스와 라벨 의미를 반드시 확인해야 한다. 같은 F1 점수라도 어떤 클래스를 기준으로 계산했는지에 따라 해석이 달라진다.
최종 결과는 코드만으로 완성되지 않는다. 실험 기록과 리포트가 있어야 프로젝트가 완성된다.
퀴즈
기준 모델을 먼저 만드는 이유는 무엇인가?
교차검증 결과가 좋은 모델을 테스트 데이터에 여러 번 맞춰 보면 왜 문제가 되는가?
F1 점수는 어떤 두 지표를 함께 고려하는가?
feature importance를 해석할 때 조심해야 하는 이유는 무엇인가?
AI 도구를 사용한 경우 로그에 반드시 남겨야 할 내용은 무엇인가?
분류 프로젝트에서 관심 클래스와 라벨 의미를 먼저 확인해야 하는 이유는 무엇인가?
체크리스트
문제 유형을 분류 또는 회귀로 명확히 적었다.
X와 y를 구분했다.
관심 클래스와 라벨 의미를 확인했다.
train/test 분할을 재현 가능하게 설정했다.
기준 모델을 만들었다.
교차검증으로 여러 모델을 비교했다.
최종 테스트 데이터는 마지막 평가에 사용했다.
혼동 행렬 또는 오류 사례를 확인했다.
feature importance를 원인처럼 단정하지 않았다.
집단별 성능 차이를 간단히 점검했다.
AI 사용 로그와 검증 기록을 남겼다.
다음 장 연결
이 장으로 권2의 기본 흐름은 마무리된다. 이제 독자는 작은 머신러닝 프로젝트를 스스로 구성하고 평가할 수 있다. 다음 권에서는 더 복잡한 데이터와 모델을 다루게 된다. 하지만 핵심은 변하지 않는다. 먼저 문제를 정확히 정의하고, 데이터를 의심하며, 평가를 공정하게 설계하고, 결과를 설명해야 한다.
머신러닝 실력은 모델 이름을 많이 아는 데서 끝나지 않는다. 끝까지 검증하고 기록하는 습관에서 시작된다.
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.