
1. 지도학습이란?
**지도학습(Supervised Learning)**은 정답이 있는 데이터를 이용해서 모델을 학습시키는 머신러닝 방법입니다.
지도학습 데이터는 보통 입력 데이터와 정답 데이터로 나눌 수 있습니다.
X = 입력 데이터, 문제 데이터, feature
y = 정답 데이터, target, label
모델은 입력 데이터 X를 보고 정답 데이터 y를 맞히는 규칙을 학습합니다.
예를 들어 다음과 같은 문제가 지도학습에 해당합니다.
꽃의 길이와 너비를 보고 꽃 품종 예측하기
고객 정보를 보고 이탈 여부 예측하기
메일 내용을 보고 스팸 여부 예측하기
식사 금액과 인원 수를 보고 팁 금액 예측하기
주택 정보를 보고 집값 예측하기
지도학습은 크게 분류와 회귀로 나눌 수 있습니다.
| 구분 | 설명 | 예시 |
|---|---|---|
| 분류 | 정답이 카테고리인 문제입니다. | 꽃 품종 예측, 생존 여부 예측, 스팸 여부 분류 |
| 회귀 | 정답이 연속적인 숫자인 문제입니다. | 팁 금액 예측, 집값 예측, 매출 예측 |
이번 글에서는 Seaborn 예제 데이터셋을 이용해서 지도학습의 두 가지 유형을 모두 실습합니다.
iris 데이터셋 → 분류 문제
tips 데이터셋 → 회귀 문제
2. 실습에 사용할 라이브러리
이번 실습에서는 seaborn, pandas, matplotlib, scikit-learn을 사용합니다.
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
머신러닝 모델링에는 scikit-learn을 사용합니다.
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
각 라이브러리의 역할은 다음과 같습니다.
| 라이브러리 | 역할 |
|---|---|
| seaborn | 예제 데이터셋 불러오기, 시각화 |
| pandas | 데이터프레임 처리 |
| matplotlib | 그래프 출력 |
| scikit-learn | 머신러닝 모델 학습과 평가 |
설치가 필요하다면 다음 명령어를 사용할 수 있습니다.
pip install seaborn pandas matplotlib scikit-learn
3. 지도학습의 기본 흐름
지도학습은 보통 다음 순서로 진행합니다.
1. 데이터 불러오기
2. 데이터 구조 확인하기
3. 입력 데이터 X와 정답 데이터 y 분리하기
4. 학습 데이터와 테스트 데이터로 나누기
5. 모델 선택하기
6. 모델 학습하기
7. 테스트 데이터로 예측하기
8. 모델 성능 평가하기
9. 새로운 데이터 예측하기
분류와 회귀 모두 큰 흐름은 같습니다.
다만 정답 데이터의 형태와 평가 지표가 다릅니다.
Part 1. Iris 데이터로 분류 모델 만들기
4. Iris 데이터셋이란?
iris 데이터셋은 붓꽃의 꽃받침과 꽃잎의 길이, 너비 정보를 담고 있는 대표적인 머신러닝 예제 데이터입니다.
Seaborn에서 바로 불러올 수 있습니다.
import seaborn as sns
iris = sns.load_dataset("iris")
데이터를 확인합니다.
iris.head()
데이터는 대략 다음과 같은 형태입니다.
sepal_length sepal_width petal_length petal_width species
0 5.1 3.5 1.4 0.2 setosa
1 4.9 3.0 1.4 0.2 setosa
2 4.7 3.2 1.3 0.2 setosa
3 4.6 3.1 1.5 0.2 setosa
4 5.0 3.6 1.4 0.2 setosa
컬럼의 의미는 다음과 같습니다.
| 컬럼 | 설명 |
|---|---|
| sepal_length | 꽃받침 길이 |
| sepal_width | 꽃받침 너비 |
| petal_length | 꽃잎 길이 |
| petal_width | 꽃잎 너비 |
| species | 붓꽃 품종 |
여기서 species가 모델이 맞혀야 하는 정답 데이터입니다.
5. 분류 문제 정의하기
이번 분류 문제의 목표는 다음과 같습니다.
꽃받침 길이, 꽃받침 너비, 꽃잎 길이, 꽃잎 너비를 이용해서
붓꽃 품종을 예측하는 모델을 만듭니다.
입력 데이터와 정답 데이터는 다음과 같습니다.
X = sepal_length, sepal_width, petal_length, petal_width
y = species
정답인 species는 다음 세 가지 품종을 가집니다.
iris["species"].value_counts()
setosa 50
versicolor 50
virginica 50
정답이 숫자가 아니라 품종 이름이므로 이 문제는 분류 문제입니다.
6. Iris 데이터 간단히 확인하기
모델을 만들기 전에 데이터를 간단히 확인합니다.
iris.info()
기초 통계도 확인합니다.
iris.describe()
품종별 데이터 개수를 확인합니다.
iris["species"].value_counts()
시각화하면 다음과 같습니다.
sns.countplot(data=iris, x="species")
plt.title("Iris Species Count")
plt.show()
품종별 꽃잎 길이와 꽃잎 너비의 관계도 확인할 수 있습니다.
sns.scatterplot(
data=iris,
x="petal_length",
y="petal_width",
hue="species"
)
plt.title("Petal Length and Petal Width by Species")
plt.show()
시각화를 해보면 petal_length와 petal_width가 품종을 구분하는 데 중요한 변수처럼 보일 수 있습니다.
이처럼 간단한 EDA를 통해 어떤 변수가 정답을 구분하는 데 도움이 될지 감을 잡을 수 있습니다.
7. 입력 데이터 X와 정답 데이터 y 분리하기
지도학습에서는 입력 데이터와 정답 데이터를 분리해야 합니다.
X = iris.drop(columns=["species"])
y = iris["species"]
X에는 문제 데이터가 들어갑니다.
sepal_length
sepal_width
petal_length
petal_width
y에는 정답 데이터가 들어갑니다.
species
확인해보겠습니다.
print(X.head())
print(y.head())
8. 학습 데이터와 테스트 데이터 나누기
모델을 학습할 때 모든 데이터를 학습에만 사용하면 안 됩니다.
모델이 처음 보는 데이터에 대해서도 잘 예측하는지 확인해야 하기 때문입니다.
그래서 데이터를 학습용과 테스트용으로 나눕니다.
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.2,
random_state=42,
stratify=y
)
각 옵션의 의미는 다음과 같습니다.
| 옵션 | 설명 |
|---|---|
| test_size=0.2 | 전체 데이터 중 20%를 테스트 데이터로 사용합니다. |
| random_state=42 | 실행할 때마다 같은 방식으로 데이터가 나뉘도록 고정합니다. |
| stratify=y | 정답 클래스 비율이 학습/테스트 데이터에 비슷하게 유지되도록 합니다. |
분리된 데이터 크기를 확인합니다.
print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
print(y_test.shape)
9. 분류 모델 만들기
이번에는 LogisticRegression을 사용합니다.
LogisticRegression은 이름에 Regression이 들어가지만, 실제로는 분류 문제에 많이 사용하는 모델입니다.
스케일링을 함께 적용하기 위해 Pipeline을 사용합니다.
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
clf_model = Pipeline([
("scaler", StandardScaler()),
("classifier", LogisticRegression(max_iter=1000))
])
위 코드는 두 단계를 하나로 묶은 것입니다.
1. StandardScaler로 데이터 스케일 조정
2. LogisticRegression으로 분류 모델 학습
StandardScaler는 각 컬럼의 스케일을 비슷하게 맞춰주는 역할을 합니다.
머신러닝 모델은 변수의 단위나 범위 차이에 영향을 받을 수 있기 때문에 스케일링이 도움이 되는 경우가 많습니다.
10. 분류 모델 학습하기
모델 학습은 fit() 메서드로 진행합니다.
clf_model.fit(X_train, y_train)
이 코드는 모델에게 다음을 학습시키는 과정입니다.
X_train을 보고 y_train을 맞히는 규칙을 찾아라.
즉, 꽃받침 길이, 꽃받침 너비, 꽃잎 길이, 꽃잎 너비를 보고 붓꽃 품종을 맞히는 규칙을 학습합니다.
11. 테스트 데이터로 예측하기
학습이 끝났으면 테스트 데이터로 예측을 수행합니다.
y_pred = clf_model.predict(X_test)
X_test에는 정답 컬럼이 없습니다.
모델은 입력 데이터만 보고 품종을 예측합니다.
예측 결과 일부를 확인합니다.
print(y_pred[:5])
실제 정답과 비교할 수도 있습니다.
result = pd.DataFrame({
"actual": y_test.values,
"predicted": y_pred
})
print(result.head())
12. 분류 모델 평가하기
분류 모델에서는 대표적으로 다음 지표를 사용합니다.
Accuracy
Precision
Recall
F1-score
Confusion Matrix
먼저 정확도를 확인합니다.
from sklearn.metrics import accuracy_score
accuracy = accuracy_score(y_test, y_pred)
print("정확도:", accuracy)
정확도는 전체 데이터 중 모델이 맞힌 비율입니다.
정확도 = 맞힌 개수 / 전체 개수
더 자세한 분류 평가 결과는 classification_report()로 확인할 수 있습니다.
from sklearn.metrics import classification_report
print(classification_report(y_test, y_pred))
출력 결과에는 품종별 Precision, Recall, F1-score가 표시됩니다.
| 지표 | 설명 |
|---|---|
| Precision | 모델이 해당 클래스로 예측한 것 중 실제로 맞은 비율입니다. |
| Recall | 실제 해당 클래스인 것 중 모델이 맞힌 비율입니다. |
| F1-score | Precision과 Recall의 조화평균입니다. |
| Support | 실제 데이터 개수입니다. |
13. 혼동 행렬 확인하기
혼동 행렬은 모델이 어떤 클래스를 어떤 클래스로 예측했는지 보여주는 표입니다.
from sklearn.metrics import confusion_matrix
cm = confusion_matrix(y_test, y_pred, labels=clf_model.classes_)
cm_df = pd.DataFrame(
cm,
index=clf_model.classes_,
columns=clf_model.classes_
)
print(cm_df)
시각화하면 더 보기 쉽습니다.
sns.heatmap(cm_df, annot=True, fmt="d", cmap="Blues")
plt.title("Confusion Matrix")
plt.xlabel("Predicted")
plt.ylabel("Actual")
plt.show()
혼동 행렬에서 대각선 값은 모델이 맞힌 개수입니다.
대각선 밖의 값은 모델이 잘못 예측한 개수입니다.
14. 새로운 Iris 데이터 예측하기
학습된 모델을 이용하면 새로운 데이터도 예측할 수 있습니다.
new_flower = pd.DataFrame({
"sepal_length": [5.1],
"sepal_width": [3.5],
"petal_length": [1.4],
"petal_width": [0.2]
})
prediction = clf_model.predict(new_flower)
print("예측 품종:", prediction[0])
이렇게 하면 새로운 꽃의 측정값을 이용해서 품종을 예측할 수 있습니다.
15. Iris 분류 전체 코드
위 내용을 하나로 합친 전체 코드는 다음과 같습니다.
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
# 1. 데이터 불러오기
iris = sns.load_dataset("iris")
# 2. 입력 데이터와 정답 데이터 분리
X = iris.drop(columns=["species"])
y = iris["species"]
# 3. 학습 데이터와 테스트 데이터 분리
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.2,
random_state=42,
stratify=y
)
# 4. 모델 생성
clf_model = Pipeline([
("scaler", StandardScaler()),
("classifier", LogisticRegression(max_iter=1000))
])
# 5. 모델 학습
clf_model.fit(X_train, y_train)
# 6. 예측
y_pred = clf_model.predict(X_test)
# 7. 평가
accuracy = accuracy_score(y_test, y_pred)
print("정확도:", accuracy)
print()
print(classification_report(y_test, y_pred))
# 8. 혼동 행렬
cm = confusion_matrix(y_test, y_pred, labels=clf_model.classes_)
cm_df = pd.DataFrame(
cm,
index=clf_model.classes_,
columns=clf_model.classes_
)
sns.heatmap(cm_df, annot=True, fmt="d", cmap="Blues")
plt.title("Confusion Matrix")
plt.xlabel("Predicted")
plt.ylabel("Actual")
plt.show()
# 9. 새로운 데이터 예측
new_flower = pd.DataFrame({
"sepal_length": [5.1],
"sepal_width": [3.5],
"petal_length": [1.4],
"petal_width": [0.2]
})
prediction = clf_model.predict(new_flower)
print("예측 품종:", prediction[0])
Part 2. Tips 데이터로 회귀 모델 만들기
16. Tips 데이터셋이란?
이번에는 Seaborn의 tips 데이터셋을 이용해서 회귀 문제를 풀어보겠습니다.
tips = sns.load_dataset("tips")
데이터를 확인합니다.
tips.head()
tips 데이터셋은 식당에서 발생한 식사 금액과 팁 정보를 담고 있습니다.
컬럼의 의미는 다음과 같습니다.
| 컬럼 | 설명 |
|---|---|
| total_bill | 전체 식사 금액 |
| tip | 팁 금액 |
| sex | 성별 |
| smoker | 흡연 여부 |
| day | 요일 |
| time | 점심/저녁 |
| size | 식사 인원 수 |
이번 회귀 문제의 목표는 다음과 같습니다.
식사 금액, 성별, 흡연 여부, 요일, 시간대, 인원 수를 이용해서
팁 금액을 예측합니다.
정답인 tip은 숫자 데이터입니다.
따라서 이 문제는 회귀 문제입니다.
17. Tips 데이터 간단히 확인하기
먼저 데이터 구조를 확인합니다.
tips.info()
기초 통계를 확인합니다.
tips.describe()
팁 금액의 분포를 확인합니다.
sns.histplot(data=tips, x="tip", bins=20, kde=True)
plt.title("Tip Distribution")
plt.show()
전체 식사 금액과 팁 금액의 관계를 확인합니다.
sns.scatterplot(data=tips, x="total_bill", y="tip")
plt.title("Total Bill and Tip")
plt.show()
일반적으로 식사 금액이 커질수록 팁 금액도 증가하는 경향이 있을 수 있습니다.
이처럼 회귀 문제에서는 입력 변수와 정답 숫자 사이의 관계를 확인하는 것이 중요합니다.
18. 입력 데이터 X와 정답 데이터 y 분리하기
tip은 예측해야 하는 정답이므로 y에 넣습니다.
나머지 필요한 컬럼은 X에 넣습니다.
X = tips[["total_bill", "sex", "smoker", "day", "time", "size"]]
y = tips["tip"]
여기서 다음 컬럼은 범주형 변수입니다.
sex
smoker
day
time
머신러닝 모델은 문자열을 그대로 처리하지 못하는 경우가 많기 때문에 숫자로 변환해야 합니다.
이 작업을 인코딩이라고 합니다.
19. 범주형 데이터 인코딩 준비하기
이번에는 ColumnTransformer와 OneHotEncoder를 사용해서 범주형 변수를 자동으로 인코딩하겠습니다.
먼저 숫자형 컬럼과 범주형 컬럼을 나눕니다.
numeric_features = ["total_bill", "size"]
categorical_features = ["sex", "smoker", "day", "time"]
전처리 객체를 만듭니다.
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder
preprocessor = ColumnTransformer(
transformers=[
("num", "passthrough", numeric_features),
("cat", OneHotEncoder(handle_unknown="ignore"), categorical_features)
]
)
여기서 OneHotEncoder(handle_unknown="ignore")는 학습 때 보지 못한 새로운 카테고리가 들어와도 오류가 나지 않도록 해줍니다.
20. 학습 데이터와 테스트 데이터 나누기
회귀 문제에서도 학습 데이터와 테스트 데이터를 분리합니다.
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.2,
random_state=42
)
21. 회귀 모델 만들기
이번에는 RandomForestRegressor를 사용하겠습니다.
from sklearn.ensemble import RandomForestRegressor
from sklearn.pipeline import Pipeline
reg_model = Pipeline([
("preprocessor", preprocessor),
("regressor", RandomForestRegressor(
n_estimators=100,
random_state=42
))
])
RandomForestRegressor는 여러 개의 결정 트리를 이용해서 예측하는 회귀 모델입니다.
복잡한 패턴도 어느 정도 잘 학습할 수 있어서 입문용 실습에도 많이 사용됩니다.
22. 회귀 모델 학습하기
모델을 학습합니다.
reg_model.fit(X_train, y_train)
모델은 입력 데이터와 팁 금액 사이의 관계를 학습합니다.
total_bill, size, sex, smoker, day, time → tip
23. 회귀 모델 예측하기
테스트 데이터로 팁 금액을 예측합니다.
y_pred = reg_model.predict(X_test)
실제값과 예측값을 비교해보겠습니다.
result = pd.DataFrame({
"actual": y_test.values,
"predicted": y_pred
})
print(result.head())
24. 회귀 모델 평가하기
회귀 모델에서는 분류 모델의 정확도 대신 오차 기반 지표를 사용합니다.
대표적인 평가 지표는 다음과 같습니다.
| 지표 | 설명 |
|---|---|
| MAE | 실제값과 예측값 차이의 절댓값 평균입니다. |
| MSE | 실제값과 예측값 차이를 제곱한 값의 평균입니다. |
| RMSE | MSE에 제곱근을 씌운 값입니다. |
| R2 | 모델이 데이터를 얼마나 설명하는지 나타내는 지표입니다. |
코드는 다음과 같습니다.
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
mae = mean_absolute_error(y_test, y_pred)
mse = mean_squared_error(y_test, y_pred)
rmse = mse ** 0.5
r2 = r2_score(y_test, y_pred)
print("MAE:", mae)
print("MSE:", mse)
print("RMSE:", rmse)
print("R2:", r2)
각 지표는 다음과 같이 해석할 수 있습니다.
MAE가 작을수록 예측 오차가 작습니다.
RMSE가 작을수록 예측 오차가 작습니다.
R2는 1에 가까울수록 모델 설명력이 좋습니다.
다만 R2가 항상 높다고 무조건 좋은 모델은 아닙니다.
데이터의 특성과 문제 목적에 따라 여러 지표를 함께 봐야 합니다.
25. 실제값과 예측값 시각화하기
회귀 모델에서는 실제값과 예측값이 얼마나 비슷한지 산점도로 확인할 수 있습니다.
result = pd.DataFrame({
"actual": y_test.values,
"predicted": y_pred
})
sns.scatterplot(data=result, x="actual", y="predicted")
plt.title("Actual vs Predicted Tip")
plt.xlabel("Actual Tip")
plt.ylabel("Predicted Tip")
plt.show()
점들이 대각선에 가까울수록 예측이 잘 된 것입니다.
26. 새로운 Tips 데이터 예측하기
학습된 회귀 모델로 새로운 데이터의 팁 금액을 예측할 수 있습니다.
new_data = pd.DataFrame({
"total_bill": [30.0],
"sex": ["Male"],
"smoker": ["No"],
"day": ["Sun"],
"time": ["Dinner"],
"size": [2]
})
predicted_tip = reg_model.predict(new_data)
print("예측 팁 금액:", predicted_tip[0])
Pipeline 안에 전처리 단계가 포함되어 있기 때문에 새로운 데이터도 자동으로 같은 방식으로 인코딩됩니다.
이 방식은 실무에서도 자주 사용하는 구조입니다.
27. Tips 회귀 전체 코드
위 내용을 하나로 합친 전체 코드는 다음과 같습니다.
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder
from sklearn.pipeline import Pipeline
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
# 1. 데이터 불러오기
tips = sns.load_dataset("tips")
# 2. 입력 데이터와 정답 데이터 분리
X = tips[["total_bill", "sex", "smoker", "day", "time", "size"]]
y = tips["tip"]
# 3. 숫자형 / 범주형 컬럼 구분
numeric_features = ["total_bill", "size"]
categorical_features = ["sex", "smoker", "day", "time"]
# 4. 전처리 설정
preprocessor = ColumnTransformer(
transformers=[
("num", "passthrough", numeric_features),
("cat", OneHotEncoder(handle_unknown="ignore"), categorical_features)
]
)
# 5. 학습 데이터와 테스트 데이터 분리
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.2,
random_state=42
)
# 6. 모델 생성
reg_model = Pipeline([
("preprocessor", preprocessor),
("regressor", RandomForestRegressor(
n_estimators=100,
random_state=42
))
])
# 7. 모델 학습
reg_model.fit(X_train, y_train)
# 8. 예측
y_pred = reg_model.predict(X_test)
# 9. 평가
mae = mean_absolute_error(y_test, y_pred)
mse = mean_squared_error(y_test, y_pred)
rmse = mse ** 0.5
r2 = r2_score(y_test, y_pred)
print("MAE:", mae)
print("MSE:", mse)
print("RMSE:", rmse)
print("R2:", r2)
# 10. 실제값과 예측값 비교
result = pd.DataFrame({
"actual": y_test.values,
"predicted": y_pred
})
sns.scatterplot(data=result, x="actual", y="predicted")
plt.title("Actual vs Predicted Tip")
plt.xlabel("Actual Tip")
plt.ylabel("Predicted Tip")
plt.show()
# 11. 새로운 데이터 예측
new_data = pd.DataFrame({
"total_bill": [30.0],
"sex": ["Male"],
"smoker": ["No"],
"day": ["Sun"],
"time": ["Dinner"],
"size": [2]
})
predicted_tip = reg_model.predict(new_data)
print("예측 팁 금액:", predicted_tip[0])
Part 3. 분류와 회귀 비교하기
28. 분류와 회귀의 차이
지도학습은 정답 데이터의 형태에 따라 분류와 회귀로 나뉩니다.
| 구분 | 분류 | 회귀 |
|---|---|---|
| 정답 데이터 | 카테고리 | 연속적인 숫자 |
| 예시 | 품종, 생존 여부, 스팸 여부 | 가격, 매출, 온도, 팁 금액 |
| 이번 예제 | iris 품종 예측 | tips 팁 금액 예측 |
| 대표 모델 | Logistic Regression, Decision Tree, Random Forest Classifier | Linear Regression, Random Forest Regressor |
| 평가 지표 | Accuracy, Precision, Recall, F1-score | MAE, MSE, RMSE, R2 |
이번 글에서 다룬 예제를 정리하면 다음과 같습니다.
| 데이터셋 | 문제 유형 | 입력 데이터 X | 정답 데이터 y |
|---|---|---|---|
| iris | 분류 | 꽃받침/꽃잎 길이와 너비 | species |
| tips | 회귀 | 식사 금액, 성별, 흡연 여부, 요일, 시간, 인원 수 | tip |