CS/Python

파이썬으로 머신러닝 찍먹하기

munsik22 2026. 8. 7. 16:04

🧩 머신러닝이란?

Machine Learning(ML)이란 학습 데이터의 패턴을 학습한 뒤 새로운 데이터에 대해 정확한 추론을 할 수 있는 알고리즘에 초점을 맞춘 AI의 하위 집합이다. (출처)

 

머신러닝(ML, 기계학습)이란 컴퓨터가 일일이 코드로 명령을 받지 않고, 방대한 데이터를 바탕으로 스스로 패턴을 찾아 학습하며 예측이나 결정을 내리게 하는 AI의 핵심 기술 분야다.

🧩 sklearn이란?

Scikit-learn은 NumPy, SciPy 및 matplotlib을 기반으로 구축된 예측 데이터 분석을 위한 간단하고 효율적인 도구이다. (scikit-learn 공식 웹사이트 발췌)

 

scikit-learn(a.k.a sklearn)은 파이썬에서 머신러닝을 쉽게 사용할 수 있게 해주는 대표적인 라이브러리로, 복잡한 머신러닝 알고리즘을 직접 구현할 필요 없이 비교적 간단한 API로 데이터 준비부터 모델 평가까지의 과정을 수행할 수 있게 해주는 도구다. 주로 데이터 분석과 예측 모델 개발이 많이 사용된다.

주요 기능

  • 분류(Classification): 데이터를 범주로 나누기 (예: 이메일이 스팸인지 아닌지?)
  • 회귀(Regression): 연속적인 숫자 예측 (예: 집값, 매출, 온도 예측)
  • 군집화(Clustering): 비슷한 데이터끼리 그룹화 (예: 고객을 구매 성향에 따라 여러 그룹으로 분류)
  • 차원 축소(Dimensionality Reduction): 많은 변수의 데이터를 더 적은 변수로 압축 (예: PCA)
  • 데이터 전처리(Preprocessing): 결측치 처리, 정규화, 인코딩 등
  • 모델 평가 및 검증: 정확도 측정, 교차 검증, 하이퍼파라미터 튜닝

사용 예시

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score

# 예제 데이터 불러오기
iris = load_iris()
X = iris.data
y = iris.target

# 학습용/테스트용 데이터 분리
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

# 모델 생성 및 학습
model = DecisionTreeClassifier()
model.fit(X_train, y_train)

# 예측
y_pred = model.predict(X_test)

# 성능 평가
print(accuracy_score(y_test, y_pred)) # 1.0

🧩 머신러닝 실습하기

sklearn을 사용해 머신러닝 전처리 → 학습 → 평가 → 예측 과정을 진행해 보자. 목표는 다음으로 설정한다.

게임 시작 전 픽/밴/소환사 주문 정보를 보고 팀1이 이길지 예측하기

1. 라이브러리 불러오기

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

from sklearn.model_selection import train_test_split
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline

from sklearn.impute import SimpleImputer
from sklearn.preprocessing import OneHotEncoder

from sklearn.linear_model import LogisticRegression
from sklearn.dummy import DummyClassifier

from sklearn.metrics import (
    accuracy_score,
    classification_report,
    confusion_matrix,
    ConfusionMatrixDisplay,
    roc_auc_score
)

2. 데이터 불러오기 및 타깃 만들기

df = pd.read_csv("dataset/games.csv")
print(df.shape) # (51490, 61)

# winner 이진 변환
  # winner가 1이면 팀1 승리 -> 1
  # winner가 2이면 팀2 승리 -> 0
df["target"] = (df["winner"] == 1).astype(int)

3. 데이터 누수 변수 제거

이번 목표는 게임 시작 전 승패 예측이므로 경기 결과가 나온 뒤에 알 수 있는 정보는 사용하면 안 된다.

# 제외할 누수 변수들
leakage_cols = [
    "gameId",
    "creationTime",
    "gameDuration",
    "winner",
    "target",

    "firstBlood",
    "firstTower",
    "firstInhibitor",
    "firstBaron",
    "firstDragon",
    "firstRiftHerald",

    "t1_towerKills",
    "t1_inhibitorKills",
    "t1_baronKills",
    "t1_dragonKills",
    "t1_riftHeraldKills",

    "t2_towerKills",
    "t2_inhibitorKills",
    "t2_baronKills",
    "t2_dragonKills",
    "t2_riftHeraldKills"
]

게임 시작 전에 첫 킬이나 첫 포탑을 누가 가져갈지 아직 모르기 때문에 firstBlood, firstTower 같은 변수도 제외하였다.

4. 사용할 입력 변수 선택

게임 시작 전 알 수 있는 정보만 사용한다.

# 챔피언 선택 컬럼
champ_cols = [
    "t1_champ1id", "t1_champ2id", "t1_champ3id", "t1_champ4id", "t1_champ5id",
    "t2_champ1id", "t2_champ2id", "t2_champ3id", "t2_champ4id", "t2_champ5id"
]

# 소환사 주문 컬럼
summoner_spell_cols = [
    "t1_champ1_sum1", "t1_champ1_sum2",
    "t1_champ2_sum1", "t1_champ2_sum2",
    "t1_champ3_sum1", "t1_champ3_sum2",
    "t1_champ4_sum1", "t1_champ4_sum2",
    "t1_champ5_sum1", "t1_champ5_sum2",

    "t2_champ1_sum1", "t2_champ1_sum2",
    "t2_champ2_sum1", "t2_champ2_sum2",
    "t2_champ3_sum1", "t2_champ3_sum2",
    "t2_champ4_sum1", "t2_champ4_sum2",
    "t2_champ5_sum1", "t2_champ5_sum2"
]

# 밴 컬럼
ban_cols = [
    "t1_ban1", "t1_ban2", "t1_ban3", "t1_ban4", "t1_ban5",
    "t2_ban1", "t2_ban2", "t2_ban3", "t2_ban4", "t2_ban5"
]

# 최종 특성(feature) 목록
feature_cols = (
    ["seasonId"]
    + champ_cols
    + summoner_spell_cols
    + ban_cols
)

X = df[feature_cols]
y = df["target"]

print("X 크기:", X.shape) # X 크기: (51490, 41)
print("y 크기:", y.shape) # y 크기: (51490,)
  • X = 특성(Feature): 모델이 예측에 사용하는 입력정보, 예측 및 판단의 근거/단서
  • y = 라벨(Label): 모델이 예측하려는 정답, 학습의 목푯값

5. Train / Test 데이터 분리

모델은 학습용 데이터(train data)로 학습하고, 한 번도 보지 못한 테스트 데이터(test data)로 성능을 평가해야 한다.

# Train / Test 데이터 분리
X_train, X_test, y_train, y_test = train_test_split(
    X,
    y,
    test_size=0.2,		# 전체 데이터의 20%를 테스트용으로 사용
    random_state=42,		# 실행할 때마다 같은 방식으로 데이터 분리
    stratify=y			# 팀1 승패 비율을 train과 test에 비슷하게 유지
)

print("훈련 데이터 X_train:", X_train.shape)
print("테스트 데이터 X_test:", X_test.shape)

print("\n훈련 타깃 비율:")
print(y_train.value_counts(normalize=True))

print("\n테스트 타깃 비율:")
print(y_test.value_counts(normalize=True))

'''
훈련 데이터 X_train: (41192, 41)
테스트 데이터 X_test: (10298, 41)

훈련 타깃 비율:
target
1    0.506458
0    0.493542
Name: proportion, dtype: float64

테스트 타깃 비율:
target
1    0.506409
0    0.493591
Name: proportion, dtype: float64
'''

6. Pipeline으로 전처리와 모델 연결하기

Pipeline결측치 처리 → 범주형 데이터 인코딩 → 모델 학습 과정을 하나로 묶는 도구다.

  • 범주형 전처리기 만들기
    • One-Hot Encoding: 챔피언 ID와 같은 범주형 데이터를 머신러닝 모델이 이해할 수 있는 형태로 변환해야 한다.
    • 결측치는 -1로 채우고, OneHotEncoder로 One-Hot Encoding을 적용한다.
    • handle_unknown="ignore": 테스트 데이터에 학습 데이터에서 한 번도 본 적 없는 챔피언 ID가 있어도 오류가 나지 않게 한다.
# 범주형 전처리기 만들기
categorical_features = feature_cols
categorical_transformer = Pipeline(
    steps=[
        ("imputer", SimpleImputer(strategy="constant", fill_value=-1)),
        ("onehot", OneHotEncoder(handle_unknown="ignore"))
    ]
)
  • ColumnTransformer: 컬럼별로 서로 다른 전처리를 적용할 때 사용
# ColumnTransformer 만들기
preprocessor = ColumnTransformer(
    transformers=[
        ("categorical", categorical_transformer, categorical_features)
    ]
)

7. 로지스틱 회귀 모델 만들기

더보기

1. 선형 회귀 (Linear Regression)

[출처] 위키피디아
선형 회귀는 입력 변수 x를 사용해서 연속적인 숫자 값 y를 예측하는 모델이다.

 

[예시]

  • 집 크기 → 집값 예측
  • 공부 시간 → 시험 점수 예측
  • 광고비 → 매출 예측

[수식]

  • x: 입력 특성(feature)
  • w: 회귀 계수(모수) 또는 각 특성의 가중치
  • b: 절편
  • y: 예측 대상

[특징]

  • 예측 결과과 연속적인 숫자로 나옴
  • 해석이 비교적 쉬움
  • 변수와 결과 사이에 선형적인 관계가 있다고 가정
  • 분류보다 회귀 문제에 적합함

2. 로지스틱 회귀 (Logistic Regression)

[출처] 위키피디아
로지스틱 회귀는 선형 결합 결과를 시그모이드(sigmoid) 함수에 대입해 결과를 0~1 사이의 확률로 변환하는 모델이다.

 

[예시] 순서가 없는 범주를 확률로 직접 예측해 분류 문제에 적합하다.

  • 스팸 메일 vs 정상 메일
  • 질병 있음 vs 질병 없음

[수식] 모델 결과가 0.5 이상이면 1로, 0.5 미만이면 0으로 분류한다.

z는 선형 회귀 모델의 수식이다.

[특징]

  • 이진 분류에 적합함
  • 예측 결과를 확률로 표현 가능
  • 학습 속도가 빠름
  • 모델 구조가 비교적 단순함
  • 복잡한 비선형 관계를 학습하기는 어려울 수 있음

3. 신경망 모델 (Neutral Network)

[출처] MathWorks
신경망 모델은 사람 뇌의 신경세포 연결 구조에서 아이디어를 얻은 머신러닝 모델이다.

 

[구조] 입력층 → 은닉층 → 출력층

[수식]

[특징]

  • 복잡한 신경망 관계 학습 가능
  • 복잡한 패턴 찾을 가능성 있음
  • 이미지, 텍스트, 음성 등 복잡한 데이터에서 강력
  • 데이터가 많을수록 성능이 좋아질 가능성이 있음
  • 설정해야 할 값이 많으며 학습 시간이 더 오래 걸릴 수 있음
  • 과적합(Overfitting) 위험이 있으며 결과 해석이 어려울 수 있음

이번 승패 예측 문제에서는 로지스틱 회귀 모델을 선택했다.

  • 선형 회귀: 승패는 0 또는 1이어야 하지만, 선형 회귀로 예측하면 결과가 -0.51.8처럼 나올 수 있어 분류로는 부적합함
  • 로지스틱 회귀: 승패 예측은 이진 분류 문제이므로 로지스틱 회귀가 가장 적합함
  • 신경망 모델: 복잡한 패턴을 학습할 수 있지만 난이도가 어려움
# 로지스틱 회귀 모델
model = LogisticRegression(
    max_iter=1000,
    random_state=42
)

8. 전처리 + 모델을 하나의 Pipeline으로 만들기

# 전처리 + 모델
clf = Pipeline(
    steps=[
        ("preprocessor", preprocessor),
        ("model", model)
    ]
)

 

이제 clf 안에 결측치 처리 → One-Hot Encoding → Logistic Regression 학습 과정이 모두 들어있다.

9. 모델 학습하기

# 모델 학습하기
clf.fit(X_train, y_train)

10. 테스트 데이터 예측하기

# 테스트 데이터 예측하기
y_pred = clf.predict(X_test)
y_prob = clf.predict_proba(X_test)[:, 1]
print(y_prob[:10])

'''
[0.79158912 0.46268761 0.56285979 0.72050867 0.51578715 0.63640129
 0.77212898 0.46479522 0.42304374 0.39547395]
'''

11. 모델 성능 평가

  • Accuracy (정확도)
# 정확도 평가
accuracy = accuracy_score(y_test, y_pred)
print(f"정확도: {accuracy:.4f}") # 정확도: 0.5316

 

테스트 결과 53% 정도의 승패를 맞췄다. 게임 시작 전 픽/밴 정보만으로 승패를 맞히는 것은 쉬운 일이 아니므로 정확도가 높지 않더라도 너무 실망하지는 말자😅

  • Classfication Report
# 분류 리포트
print(classification_report(
    y_test,
    y_pred,
    target_names=["팀2 승리", "팀1 승리"]
))

'''
              precision    recall  f1-score   support

       팀2 승리       0.53      0.51      0.52      5083
       팀1 승리       0.54      0.55      0.55      5215

    accuracy                           0.53     10298
   macro avg       0.53      0.53      0.53     10298
weighted avg       0.53      0.53      0.53     10298
'''
지표 의미
Precision 팀1 승리라고 예측한 경기 중 실제 팀1 승리 비율 (= TP / (TP + FP))
Recall 실제 팀1 승리 경기 중 모델이 팀1 승리라고 맞힌 비율 (= TP / (TP + FN))
F1-score Precision과 Recall의 균형 지표 (= 2 * (Precision * Recall) / (Precision + Recall))
Support 해당 클래스의 실제 데이터 수
  • Confusion Matrix (혼동행렬)
# 혼동행렬
cm = confusion_matrix(y_test, y_pred)
disp = ConfusionMatrixDisplay(
    confusion_matrix=cm,
    display_labels=["팀2 승리", "팀1 승리"]
)
disp.plot(cmap="Blues")
plt.title("Confusion Matrix")
plt.show()

실제값 / 예측값 팀2 승리 예측 팀1 승리 예측
실제 팀2 승리 TP 정확히 팀2 승리를 맞힌 수 FN 팀1 승리로 잘못 예측한 수
실제 팀1 승리 FP 팀2 승리로 잘못 예측한 수 TN 정확히 팀1 승리를 맞힌 수
  • ROC-AUC 점수: 단순히 맞고 틀린 결과뿐 만 아니라 예측 확률의 품질까지 평가하는 지표
# ROC-AUC 점수
auc = roc_auc_score(y_test, y_prob)
print(f"ROC-AUC: {auc:.4f}") # ROC-AUC: 0.5409
ROC-AUC 해석
0.5 무작위 추측 수준
0.6 ~ 0.7 어느 정도 구분 가능
0.7 ~ 0.8 좋은 편
0.8 매우 높은 성능

 

게임 시작 전의 픽/밴 데이터만 사용하는 경우 아주 높은 성능을 기대하기는 어렵다😢

12. DummyClassifier로 기준 성능 확인하기

모델이 실제로 의미 있는 학습을 한 것인지 확인하려면, 단순한 기준 모델과 비교하면 좋다. DummyClassifier는 데이터 패턴을 학습하지 않고 단순하게 예측하는 모델이다.

# DummyClassifier로 기준 성능 확인하기
dummy_clf = Pipeline(
    steps=[
        ("preprocessor", preprocessor),
        ("model", DummyClassifier(strategy="most_frequent"))
    ]
)
dummy_clf.fit(X_train, y_train)
dummy_pred = dummy_clf.predict(X_test)
dummy_accuracy = accuracy_score(y_test, dummy_pred)

print(f"Dummy 모델 정확도: {dummy_accuracy:.4f}")
print(f"Logistic Regression 정확도: {accuracy:.4f}")
# Dummy 모델 정확도: 0.5064
# Logistic Regression 정확도: 0.5316

 

비교 결과 로지스틱 회귀 모델이 단순 무작위 예측보다 더 좋은 성능을 했다고 볼 수 있다.

🧩 머신러닝 과정 정리

  1. 데이터 불러오기
  2. target 만들기
  3. 데이터 누수 변수 제거
  4. feature 선택
  5. train/test 분리
  6. 결측치 처리
  7. 범주형 데이터 One-Hot Encoding
  8. 모델 학습
  9. 테스트 데이터 예측
  10. Accuracy, ROC-AUC, Confusion Matrix로 평가