머신러닝 입문에서 가장 어려운 부분은 알고리즘 이름을 외우는 일이 아니다. 문제를 정의하고, 데이터 누수를 막으며, 같은 기준으로 모델을 비교하는 흐름을 만드는 일이다. Orange의 시각적 워크플로와 Python의 실행 코드를 한 흐름으로 연결하면 이 구조가 선명해진다.
이 글의 목표: Orange로 빠르게 기준 모델을 확인하고, scikit-learn 파이프라인으로 같은 실험을 재현한 뒤, 앙상블과 군집화를 언제 선택할지 판단한다.

전체 정확도가 높아도 희귀 사례를 놓칠 수 있다. 평가지표를 함께 봐야 하는 이유

원본 데이터부터 전처리·교차 검증·오류 분석·최종 선택까지 이어지는 전체 실험 순서
1. 모델보다 먼저 문제 유형을 정한다
데이터에 정답 열이 있고 그 값을 맞히려면 지도학습이다. 정답이 범주라면 분류, 연속적인 수치라면 회귀다. 정답 없이 비슷한 데이터를 묶으려면 군집화 같은 비지도학습을 사용한다.
| 질문 | 문제 유형 | 예시 | 주요 평가 |
|---|---|---|---|
| 정답이 범주인가? | 분류 | 스팸 여부, 이탈 여부 | 정확도, 정밀도, 재현율, F1 |
| 정답이 연속 수치인가? | 회귀 | 가격, 수요량 | MAE, RMSE, 결정계수 |
| 정답 없이 묶고 싶은가? | 군집화 | 고객 세그먼트 | 실루엣 점수, 해석 가능성 |
예를 들어 고객 이탈 여부를 예측하려면 목표 열은 churn이다. 반면 구매 패턴이 비슷한 고객을 발견하려면 목표 열을 두지 않고 군집화를 수행한다. 같은 고객 데이터라도 질문이 달라지면 문제 유형과 평가 방법이 달라진다.
2. Orange는 실험 구조를 눈으로 확인하는 도구다
Orange는 위젯을 선으로 연결해 데이터 분석 흐름을 만든다. 코드를 쓰기 전 열의 역할, 결측치, 전처리, 모델, 평가 결과를 빠르게 확인하기 좋다. 처음에는 다음 순서만 정확히 익히면 된다.
기본 연결: File → Data Table → Select Columns → Impute → Normalize → Test and Score → Confusion Matrix
- File: CSV나 내장 데이터를 불러온다.
- Data Table: 값과 자료형, 결측치를 확인한다.
- Select Columns: 입력 특성과 목표 열을 구분한다.
- Impute·Normalize: 결측치를 처리하고 필요한 모델에만 스케일링을 적용한다.
- Test and Score: 여러 모델을 같은 교차 검증 조건으로 비교한다.
- Confusion Matrix: 어떤 클래스를 어떤 클래스로 틀렸는지 확인한다.
Iris 데이터라면 꽃받침과 꽃잎의 길이·너비가 입력 특성이고, 품종이 목표다. 산점도에서 분포를 본 뒤 로지스틱 회귀, KNN, 결정 트리를 Test and Score에 연결하면 모델별 결과가 한 표에 나온다.
중요: 전체 데이터를 먼저 정규화한 뒤 교차 검증으로 나누면 검증 데이터의 정보가 학습에 섞일 수 있다. 전처리는 각 학습 폴드 안에서 학습되도록 구성해야 한다. Python에서는 Pipeline으로 이 순서를 고정한다.
Orange 단계가 끝났다면 목표 열 지정, 결측치 처리, 평가 방식, 비교할 모델이 화면에 모두 드러나야 한다. 이 네 가지를 확인한 다음 같은 순서를 Python 코드로 옮긴다.
3. Python에서는 전처리와 모델을 하나로 묶는다
Orange에서 확인한 구조를 코드로 옮기면 반복 실행과 저장, 배포가 쉬워진다. 다음 예제는 Iris 데이터를 학습용과 테스트용으로 나누고, 표준화와 로지스틱 회귀를 하나의 파이프라인으로 묶는다.
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split, cross_validate
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(
X, y,
test_size=0.2,
stratify=y,
random_state=42
)
model = make_pipeline(
StandardScaler(),
LogisticRegression(max_iter=1000)
)
scores = cross_validate(
model,
X_train,
y_train,
cv=5,
scoring=["accuracy", "f1_macro"]
)
model.fit(X_train, y_train)
pred = model.predict(X_test)
print(scores["test_accuracy"].mean())
print(classification_report(y_test, pred))stratify=y는 학습용과 테스트용 데이터의 클래스 비율을 비슷하게 유지한다. random_state는 같은 분할을 재현하게 한다. 교차 검증은 한 번의 우연한 분할보다 모델의 평균적인 성능과 변동을 보기 좋다.
4. 정확도 하나로 모델을 고르면 위험하다
정확도는 전체 예측 중 맞힌 비율이다. 그러나 이상 거래처럼 양성 사례가 적은 문제에서는 모든 샘플을 정상으로 예측해도 정확도가 높게 보일 수 있다. 놓치면 큰 문제가 되는 양성을 잘 찾는지는 재현율, 양성이라고 예측한 결과를 얼마나 믿을 수 있는지는 정밀도로 확인한다.
| 지표 | 확인하는 질문 | 중요한 상황 |
|---|---|---|
| 정확도 | 전체 중 얼마나 맞혔나? | 클래스 비율과 오분류 비용이 비슷할 때 |
| 정밀도 | 양성 예측 중 실제 양성은? | 오탐 비용이 클 때 |
| 재현율 | 실제 양성 중 얼마나 찾았나? | 미탐 비용이 클 때 |
| F1 | 정밀도와 재현율의 균형은? | 두 지표를 함께 관리할 때 |
| ROC-AUC | 임계값 전반의 구분 능력은? | 여러 임계값을 비교할 때 |
모델을 선택하기 전 “어떤 오류가 더 비싼가?”를 정한다. 암 선별 검사라면 미탐을 줄이는 재현율이, 자동 차단 시스템이라면 정상 사용자를 막는 오탐을 줄이는 정밀도가 더 중요할 수 있다.
5. 타이타닉과 고객 이탈은 같은 분류라도 질문이 다르다
타이타닉 생존 예측과 통신사 고객 이탈 예측은 모두 이진 분류다. 그러나 전자는 이미 끝난 사건의 제한된 승객 정보로 생존 여부를 맞히는 학습용 문제이고, 후자는 특정 기준일 이전의 고객 정보로 앞으로 이탈할 사람을 찾아 개입하는 운영 문제다. 같은 전처리 코드를 복사하기 전에 예측 시점에 실제로 알 수 있는 정보인지부터 확인해야 한다.
| 확인 항목 | 타이타닉 생존 | 통신사 고객 이탈 |
|---|---|---|
| 목표 | Survived | Churn |
| 대표 수치형 | Age, Fare, SibSp, Parch | tenure, MonthlyCharges, TotalCharges |
| 대표 범주형 | Sex, Embarked, Pclass, Deck | Contract, InternetService, PaymentMethod |
| 파생 변수 | FamilySize, IsAlone, Cabin의 첫 문자 | 월평균 누적요금, 서비스 개수, 계약 유형 |
| 비싼 오류 | 학습 목적에 따라 지표 선택 | 실제 이탈 고객을 놓치는 미탐 또는 과도한 할인 오탐 |
| 특히 조심할 누수 | 정답 이후 기록, 전체 데이터로 결측치 학습 | 해지 이후 생성된 상태·상담 기록, 미래 청구 정보 |
타이타닉의 Cabin은 결측치가 많지만 선실 번호의 첫 글자를 Deck 범주로 단순화해 정보가 있는지 검증할 수 있다. SibSp + Parch + 1로 FamilySize를 만드는 것도 합리적 후보다. 다만 파생 변수를 추가했다는 이유만으로 성능이 좋아졌다고 결론 내리지 않고, 같은 교차 검증 분할에서 기준 모델과 차이를 비교한다.
고객 이탈에서는 월별 계약, 광섬유 인터넷, 높은 월 요금이 이탈 고객에게 자주 나타날 수 있다. 이것은 곧바로 원인이라는 뜻이 아니다. 계약 유형과 요금, 이용 기간이 서로 얽혀 있을 수 있으므로 단일 막대그래프를 인과관계로 해석하지 않는다. 캠페인 대상자를 정할 때는 기본 임계값 0.5를 고정하지 말고, 미탐 비용과 혜택 비용에 맞춰 정밀도·재현율 곡선에서 기준을 고른다.
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
numeric = ["tenure", "MonthlyCharges", "TotalCharges"]
categorical = ["Contract", "InternetService", "PaymentMethod"]
preprocess = ColumnTransformer([
("num", Pipeline([
("imputer", SimpleImputer(strategy="median")),
("scaler", StandardScaler())
]), numeric),
("cat", Pipeline([
("imputer", SimpleImputer(strategy="most_frequent")),
("onehot", OneHotEncoder(handle_unknown="ignore"))
]), categorical)
])
model = Pipeline([
("preprocess", preprocess),
("classifier", LogisticRegression(max_iter=1000))
])이 구조에서는 결측치 대체 기준, 스케일, 범주 인코딩이 학습 데이터 안에서만 학습된다. 테스트 데이터에 처음 보는 결제 방식이 있어도 handle_unknown="ignore"로 변환 단계가 깨지지 않는다. XGBoost 같은 모델을 추가하더라도 같은 분할과 지표를 유지해야 전처리 효과와 모델 효과를 구분할 수 있다.
6. 데이터 특성에 맞춰 첫 모델을 고른다
| 모델 | 강점 | 주의점 | 첫 적용 |
|---|---|---|---|
| 로지스틱 회귀 | 빠르고 계수 해석이 쉬움 | 복잡한 비선형 경계에 한계 | 분류 기준 모델 |
| KNN | 구조가 단순하고 직관적 | 거리 기반이라 스케일 영향 큼 | 작은 데이터의 비교 모델 |
| 결정 트리 | 규칙을 설명하기 쉬움 | 깊어지면 과적합하기 쉬움 | 비선형 관계와 설명이 필요할 때 |
| SVM | 고차원·작은 데이터에서 강력 | 스케일링과 매개변수 영향 큼 | 특성 수가 많은 분류 |
| 나이브 베이즈 | 빠르고 희소 데이터에 유리 | 특성 독립 가정이 강함 | 텍스트 기준 모델 |
처음부터 가장 복잡한 모델을 고를 필요는 없다. 해석 가능한 기준 모델을 먼저 만들고, 교차 검증 결과와 오류 사례를 확인한 뒤 복잡도를 높인다. 학습 점수만 높고 검증 점수가 낮으면 과적합을 의심한다.

정답 유무와 문제 목표에 따른 모델·앙상블·군집화 선택 지도
7. 앙상블은 여러 모델의 약점을 나눠 갖는다
배깅: 서로 다른 표본으로 병렬 학습
배깅은 학습 데이터를 복원 추출해 여러 모델을 독립적으로 학습하고 결과를 합친다. 결정 트리 여러 개를 사용한 랜덤 포레스트가 대표적이다. 개별 트리의 높은 분산을 줄여 하나의 깊은 트리보다 안정적인 결과를 얻는 것이 목적이다.
부스팅: 앞 모델의 실수를 다음 모델이 보완
부스팅은 약한 모델을 순차적으로 더하며 이전 단계의 오류를 줄인다. 복잡한 패턴을 잘 학습하지만 잡음과 매개변수에 민감할 수 있으므로 학습률, 트리 깊이, 반복 수를 검증 데이터로 조정한다.
보팅: 서로 다른 모델의 예측을 결합
하드 보팅은 다수결, 소프트 보팅은 클래스 확률의 평균으로 결정한다. 소프트 보팅을 사용하려면 각 모델의 확률이 비교 가능한지 확인해야 한다. 비슷한 모델만 여러 개 묶기보다 오류 패턴이 다른 모델을 결합할 때 이점이 커진다.
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
forest = RandomForestClassifier(
n_estimators=300,
max_depth=None,
random_state=42,
n_jobs=-1
)
score = cross_val_score(
forest,
X,
y,
cv=5,
scoring="f1_macro"
)
print(score.mean(), score.std())8. K-means는 평균 중심을 반복해서 갱신한다
K-means는 정답이 없는 데이터를 k개 그룹으로 나눈다. 임의 또는 k-means++ 방식으로 중심을 정하고, 각 샘플을 가장 가까운 중심에 할당한 뒤, 군집별 평균 위치로 중심을 옮긴다. 할당이 안정될 때까지 이 과정을 반복한다.
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
clusterer = make_pipeline(
StandardScaler(),
KMeans(n_clusters=3, n_init="auto", random_state=42)
)
labels = clusterer.fit_predict(X)
scaled_X = clusterer.named_steps["standardscaler"].transform(X)
print(silhouette_score(scaled_X, labels))거리 기반 알고리즘이므로 단위가 큰 특성이 결과를 지배하지 않도록 스케일링한다. k는 엘보 그래프만 보지 말고 실루엣 점수와 업무에서 해석 가능한 군집인지 함께 확인한다. 길쭉하거나 밀도가 다른 군집에는 K-means가 맞지 않을 수 있다.
9. 실수하기 쉬운 부분
- 전체 데이터로 전처리 후 분할: 검증 정보가 학습에 섞인다. 전처리와 모델을 파이프라인으로 묶는다.
- 정확도만 비교: 불균형 데이터에서는 정밀도·재현율·F1과 혼동 행렬을 함께 본다.
- KNN에 원본 단위 사용: 거리 계산 전 스케일을 맞춘다.
- 학습 데이터로 최종 평가: 교차 검증과 별도의 테스트 세트를 구분한다.
- K-means 중심을 중앙값으로 이해: 기본 K-means 중심은 군집 샘플의 평균이다.
- 오래된 예제 그대로 실행: 제거된 데이터셋이나 폐기 예정 API 대신 현재 라이브러리 문서를 확인한다.
10. 10분 검증 실습
같은 분류 데이터로 DummyClassifier, 로지스틱 회귀, 랜덤 포레스트를 비교해 보자. 분할 조건과 교차 검증 폴드는 같게 두고 accuracy, f1_macro, recall_macro를 함께 기록한다. 전체 정확도가 높은 모델과 희귀 클래스를 가장 잘 찾는 모델이 같은지 확인한다.
- 목표 클래스 비율을 먼저 출력한다.
- 전처리와 모델을 각각
Pipeline으로 묶는다. - 세 지표의 평균뿐 아니라 폴드별 편차도 비교한다.
- 최종 후보의 혼동 행렬에서 가장 잦은 오분류 두 개를 적는다.
완료 기준: “점수가 가장 높아서”가 아니라 “어떤 오류를 줄여야 하므로 이 지표와 모델을 골랐다”라고 한 문장으로 설명할 수 있어야 한다.
마지막으로 이 선택을 새 데이터 한 묶음에 적용하고, 예상과 달랐던 사례를 따로 저장한다. 다음 실험은 모델 이름을 바꾸는 데서 시작하지 않고 그 오류가 생긴 데이터와 평가 기준을 다시 확인하는 데서 시작한다.
핵심 요약
- 질문을 분류·회귀·군집화 중 하나로 먼저 정의한다.
- Orange로 실험 흐름을 확인하고 Python 파이프라인으로 재현한다.
- 전처리는 교차 검증 내부에서 수행해 데이터 누수를 막는다.
- 앙상블은 오류 패턴이 다른 모델을 결합할 때 효과가 크다.
- K-means는 스케일링,
k선택, 군집 해석을 함께 검증한다.
'실전 개발 노트 > 개발 가이드' 카테고리의 다른 글
| Redis 토큰 버킷으로 API 레이트 리밋 설계하기: 429 응답부터 Lua 구현까지 (0) | 2026.07.29 |
|---|---|
| 생성형 AI 활용능력 핵심 정리: 프롬프트·검증·보안·저작권 (0) | 2026.07.29 |
| AI 개발 도구 고르는 법: 데이터·모델·실험·온디바이스 보안까지 (0) | 2026.07.28 |
| Python OpenCV 이미지 처리 입문: 밝기·필터·이진화·외곽선까지 (0) | 2026.07.28 |
| Idempotency-Key로 중복 주문을 막는 API 설계와 PostgreSQL 구현 (0) | 2026.07.22 |