붓꽃 분류 모델 실습 가이드
실습 목표
- 첫 번째 머신러닝 모델 직접 구현
- 사이킷런 기본 API 익히기
- 데이터 시각화 및 분석 경험
- 모델 성능 평가 및 해석 능력 습득
실습 환경 설정
필요한 라이브러리
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
from sklearn.preprocessing import StandardScaler한글 폰트 설정 (선택사항)
plt.rcParams['font.family'] = 'Malgun Gothic' # Windows
plt.rcParams['axes.unicode_minus'] = False단계별 실습
1단계: 데이터 로드 및 탐색
# 데이터 로드
iris = load_iris()
print("데이터셋 정보:")
print(f"- 샘플 수: {iris.data.shape[0]}")
print(f"- 특성 수: {iris.data.shape[1]}")
print(f"- 클래스 수: {len(iris.target_names)}")
print(f"- 특성 이름: {iris.feature_names}")
print(f"- 클래스 이름: {iris.target_names}")
# 데이터프레임 생성
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df['species'] = iris.target
df['species_name'] = df['species'].map({0: 'setosa', 1: 'versicolor', 2: 'virginica'})
# 데이터 기본 정보
print("\n데이터 기본 정보:")
print(df.head())
print(f"\n데이터 형태: {df.shape}")
print(f"\n결측치 확인:\n{df.isnull().sum()}")2단계: 탐색적 데이터 분석 (EDA)
# 기본 통계 정보
print("기본 통계 정보:")
print(df.describe())
# 클래스별 분포
print("\n클래스별 분포:")
print(df['species_name'].value_counts())
# 클래스별 평균 특성 값
print("\n클래스별 평균 특성 값:")
print(df.groupby('species_name').mean())3단계: 데이터 시각화
# 1. 히스토그램 - 특성별 분포
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
features = iris.feature_names
for i, feature in enumerate(features):
ax = axes[i//2, i%2]
for species in range(3):
data = iris.data[iris.target == species, i]
ax.hist(data, alpha=0.6, label=iris.target_names[species])
ax.set_title(f'{feature} 분포')
ax.set_xlabel(feature)
ax.set_ylabel('빈도')
ax.legend()
plt.tight_layout()
plt.show()
# 2. 산점도 - 특성 간 관계
fig, axes = plt.subplots(1, 2, figsize=(15, 6))
# 꽃받침 길이 vs 너비
scatter = axes[0].scatter(iris.data[:, 0], iris.data[:, 1], c=iris.target, cmap='viridis')
axes[0].set_xlabel('꽃받침 길이 (cm)')
axes[0].set_ylabel('꽃받침 너비 (cm)')
axes[0].set_title('꽃받침 길이 vs 너비')
# 꽃잎 길이 vs 너비
scatter = axes[1].scatter(iris.data[:, 2], iris.data[:, 3], c=iris.target, cmap='viridis')
axes[1].set_xlabel('꽃잎 길이 (cm)')
axes[1].set_ylabel('꽃잎 너비 (cm)')
axes[1].set_title('꽃잎 길이 vs 너비')
plt.tight_layout()
plt.show()
# 3. 쌍별 그래프 (Pair Plot)
sns.pairplot(df, hue='species_name', markers=["o", "s", "D"])
plt.show()
# 4. 상관관계 히트맵
correlation_matrix = df.iloc[:, :-2].corr()
plt.figure(figsize=(8, 6))
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', center=0)
plt.title('특성 간 상관관계')
plt.show()4단계: 데이터 전처리
# 특성과 타겟 분리
X = iris.data
y = iris.target
# 데이터 분할 (80% 학습, 20% 테스트)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
print(f"학습 세트 크기: {X_train.shape[0]}")
print(f"테스트 세트 크기: {X_test.shape[0]}")
print(f"학습 세트 클래스 분포: {np.bincount(y_train)}")
print(f"테스트 세트 클래스 분포: {np.bincount(y_test)}")
# 특성 스케일링 (일부 알고리즘에 필요)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)5단계: 모델 학습 및 평가
# 여러 모델 정의
models = {
'KNN': KNeighborsClassifier(n_neighbors=3),
'Decision Tree': DecisionTreeClassifier(random_state=42),
'Logistic Regression': LogisticRegression(random_state=42, max_iter=200),
'SVM': SVC(random_state=42)
}
# 모델별 성능 비교
results = {}
for name, model in models.items():
# 스케일링이 필요한 모델들
if name in ['Logistic Regression', 'SVM']:
model.fit(X_train_scaled, y_train)
y_pred = model.predict(X_test_scaled)
else:
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
# 성능 계산
accuracy = accuracy_score(y_test, y_pred)
results[name] = accuracy
print(f"\n=== {name} 모델 결과 ===")
print(f"정확도: {accuracy:.4f}")
print("\n상세 분류 성능:")
print(classification_report(y_test, y_pred, target_names=iris.target_names))6단계: 최적 모델 선택 및 상세 분석
# 최고 성능 모델 선택
best_model_name = max(results, key=results.get)
best_accuracy = results[best_model_name]
print(f"\n최고 성능 모델: {best_model_name}")
print(f"최고 정확도: {best_accuracy:.4f}")
# 성능 비교 시각화
plt.figure(figsize=(10, 6))
models_names = list(results.keys())
accuracies = list(results.values())
bars = plt.bar(models_names, accuracies, color=['skyblue', 'lightgreen', 'lightcoral', 'lightsalmon'])
plt.title('모델별 정확도 비교')
plt.ylabel('정확도')
plt.ylim(0, 1)
# 정확도 값 표시
for bar, accuracy in zip(bars, accuracies):
plt.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.01,
f'{accuracy:.3f}', ha='center', va='bottom')
plt.tight_layout()
plt.show()7단계: 혼동 행렬 (Confusion Matrix) 분석
# 최고 성능 모델로 혼동 행렬 생성
if best_model_name in ['Logistic Regression', 'SVM']:
best_model = models[best_model_name]
best_model.fit(X_train_scaled, y_train)
y_pred_best = best_model.predict(X_test_scaled)
else:
best_model = models[best_model_name]
best_model.fit(X_train, y_train)
y_pred_best = best_model.predict(X_test)
# 혼동 행렬 시각화
cm = confusion_matrix(y_test, y_pred_best)
plt.figure(figsize=(8, 6))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
xticklabels=iris.target_names,
yticklabels=iris.target_names)
plt.title(f'{best_model_name} 모델의 혼동 행렬')
plt.xlabel('예측 클래스')
plt.ylabel('실제 클래스')
plt.show()
# 분류 오류 분석
print("\n분류 오류 분석:")
incorrect_indices = np.where(y_test != y_pred_best)[0]
if len(incorrect_indices) > 0:
print(f"총 {len(incorrect_indices)}개 오분류")
for idx in incorrect_indices:
actual = iris.target_names[y_test[idx]]
predicted = iris.target_names[y_pred_best[idx]]
print(f"실제: {actual}, 예측: {predicted}")
else:
print("완벽한 분류 - 오분류 없음!")8단계: 새로운 데이터 예측
# 새로운 샘플 예측 예제
new_samples = np.array([
[5.1, 3.5, 1.4, 0.2], # Setosa 같은 특성
[6.2, 2.9, 4.3, 1.3], # Versicolor 같은 특성
[7.3, 2.9, 6.3, 1.8] # Virginica 같은 특성
])
print("\n새로운 샘플 예측:")
for i, sample in enumerate(new_samples):
if best_model_name in ['Logistic Regression', 'SVM']:
sample_scaled = scaler.transform([sample])
prediction = best_model.predict(sample_scaled)[0]
probabilities = best_model.predict_proba(sample_scaled)[0] if hasattr(best_model, 'predict_proba') else None
else:
prediction = best_model.predict([sample])[0]
probabilities = best_model.predict_proba([sample])[0] if hasattr(best_model, 'predict_proba') else None
print(f"샘플 {i+1}: {sample}")
print(f" 예측: {iris.target_names[prediction]}")
if probabilities is not None:
print(f" 확률: {dict(zip(iris.target_names, probabilities))}")
print()9단계: 모델 저장 및 불러오기
import joblib
# 모델 저장
model_filename = f'iris_best_model_{best_model_name.replace(" ", "_").lower()}.pkl'
joblib.dump(best_model, model_filename)
print(f"모델이 '{model_filename}'에 저장되었습니다.")
# 스케일러도 저장 (필요한 경우)
if best_model_name in ['Logistic Regression', 'SVM']:
scaler_filename = 'iris_scaler.pkl'
joblib.dump(scaler, scaler_filename)
print(f"스케일러가 '{scaler_filename}'에 저장되었습니다.")
# 모델 불러오기 예제
loaded_model = joblib.load(model_filename)
print(f"모델이 '{model_filename}'에서 불러와졌습니다.")실습 체크리스트
데이터 탐색 ✓
- 데이터셋 기본 정보 확인
- 클래스 분포 확인
- 특성별 통계 분석
- 시각화를 통한 패턴 파악
모델 개발 ✓
- 데이터 전처리 (분할, 스케일링)
- 여러 모델 비교 실험
- 최적 모델 선택
- 성능 평가 및 해석
결과 분석 ✓
- 혼동 행렬 해석
- 분류 오류 분석
- 새로운 데이터 예측
- 모델 저장/불러오기
확장 실습 과제
1. 하이퍼파라미터 튜닝
from sklearn.model_selection import GridSearchCV
# KNN 하이퍼파라미터 튜닝
param_grid = {'n_neighbors': [3, 5, 7, 9, 11]}
grid_search = GridSearchCV(KNeighborsClassifier(), param_grid, cv=5)
grid_search.fit(X_train, y_train)
print(f"최적 k 값: {grid_search.best_params_}")2. 교차 검증
from sklearn.model_selection import cross_val_score
# 5-fold 교차 검증
cv_scores = cross_val_score(best_model, X_train, y_train, cv=5)
print(f"교차 검증 점수: {cv_scores}")
print(f"평균 점수: {cv_scores.mean():.4f} (+/- {cv_scores.std() * 2:.4f})")3. 특성 중요도 분석
# 결정 트리의 특성 중요도
if best_model_name == 'Decision Tree':
feature_importance = best_model.feature_importances_
feature_names = iris.feature_names
plt.figure(figsize=(10, 6))
plt.barh(feature_names, feature_importance)
plt.title('특성 중요도')
plt.xlabel('중요도')
plt.show()학습 성과 요약
이 실습을 통해 다음을 경험했습니다:
- 데이터 이해: 붓꽃 데이터셋의 구조와 특성
- 시각화: 다양한 그래프를 통한 데이터 탐색
- 모델 비교: 여러 알고리즘의 성능 비교
- 성능 평가: 정확도, 혼동 행렬 등 평가 지표
- 실무 적용: 새로운 데이터 예측 및 모델 저장
이 실습은 머신러닝의 전체 워크플로우를 경험하는 첫 번째 실습입니다. 각 단계를 차근차근 따라하며 개념을 익혀보세요.