로지스틱 회귀 & SVM & SVR 실습 정리

syeom·2026년 5월 14일

멀티캠퍼스 데이터분석 5월 13일 수업 내용 — LogisticRegression으로 신체 능력 분류, SVM/SVR 이론 및 실습, outlier_iqr 함수 모듈화


📌 목차

  1. outlier_iqr() — 극단치 처리 함수 (모듈)
  2. LogisticRegression 실습 — bodyPerformance 데이터
  3. 범주형 데이터 변환 — 4가지 방법 비교
  4. 예측 확률 & decision_function 시각화
  5. 혼동 행렬 히트맵 시각화
  6. 불균형 데이터 처리 — class_weight & 샘플링
  7. 연습문제 — 다중 분류 & average 옵션
  8. SVM (서포트 벡터 머신) — 이론 & 실습
  9. SVR (서포트 벡터 머신 — 회귀) — 이론 & 실습

1. outlier_iqr() — 극단치 처리 함수 (모듈)

outlier.py 파일로 저장해두고 from outlier import outlier_iqr 로 불러와서 사용합니다.

import numpy as np

def outlier_iqr(data, *cols, n=1.5, drop=False):
    """
    Parameters
    ----------
    data : DataFrame — 원본 데이터프레임
    *cols : str — 극단치를 확인할 컬럼명 (복수 가능)
    n : float — 경계 범위 배수 (기본값 1.5)
    drop : bool — True: 제거 / False: 경계값으로 대체 (기본값 False)

    Returns
    -------
    df       : 처리된 DataFrame
    whis_dict: {컬럼명: 극단치 DataFrame} 딕셔너리
    """
    df        = data.copy()
    whis_dict = {}

    for col in cols:
        try:
            q_1, q_3   = np.percentile(df[col], [25, 75])
            iqr        = q_3 - q_1
            upper_whis = q_3 + n * iqr
            lower_whis = q_1 - n * iqr

            print(f"""
                컬럼: {col}
                상단 경계: {upper_whis}
                하단 경계: {lower_whis}
            """)

            upper_flag = df[col] > upper_whis
            lower_flag = df[col] < lower_whis

            print(f"상단 극단치: {upper_flag.sum()}개 / 하단 극단치: {lower_flag.sum()}개")

            whis_dict[col] = df.loc[upper_flag | lower_flag]

            if drop:
                df = df.loc[~(upper_flag | lower_flag)]
            else:
                df.loc[upper_flag, col] = upper_whis
                df.loc[lower_flag, col] = lower_whis

        except Exception as e:
            print(f'Error: {e}')

    return df, whis_dict

💡 2026.05.06 수정 — 두 번째 매개변수를 col*cols (가변 인자) 로 변경하여
여러 컬럼을 한 번에 처리할 수 있도록 개선되었습니다.


2. LogisticRegression 실습 — bodyPerformance 데이터

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, confusion_matrix
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.linear_model import LogisticRegression
import warnings

warnings.filterwarnings('ignore')

body = pd.read_csv('../data/bodyPerformance.csv')
body.info()
body.describe()

컬럼 설명

컬럼설명
age나이
gender성별 (문자열 — M/F)
height_cm
weight_kg무게
body fat_%체지방률
diastolic이완기 혈압
systolic수축기 혈압
gripForce악력
sit and bend forward_cm앉아서 윗몸 앞으로 굽히기
sit-ups counts윗몸 일으키기
broad jump_cm제자리 멀리뛰기
class신체 능력 등급 (A/B/C/D)

3. 범주형 데이터 변환 — 4가지 방법 비교

# 방법 1 — map() + lambda
body['gender'].map(lambda x: 0 if x == 'M' else 1)

# 방법 2 — map() + 딕셔너리 ✅ 권장
body['gender'].map({'M': 0, 'F': 1})

# 방법 3 — replace()
body['gender'].replace('M', 0).replace('F', 1)

# 방법 4 — pd.get_dummies() (원-핫 인코딩)
df = pd.get_dummies(body, columns=['gender'], drop_first=True)

# 방법 5 — np.where()
np.where(body['gender'] == 'M', 0, 1)

target(class) 이진 변환 — A는 1, 나머지는 0

df['class_1'] = np.where(df['class'] == 'A', 1, 0)
df['class_1'].value_counts()

데이터 분할 & 학습

x = df.drop(['class', 'class_1'], axis=1)
y = df['class_1']

X_train, X_test, y_train, y_test = train_test_split(
    x, y, test_size=0.3, random_state=42, stratify=y
)

logR = LogisticRegression()
logR.fit(X_train, y_train)

4. 예측 확률 & decision_function 시각화

# 클래스별 예측 확률
proba = pd.DataFrame(logR.predict_proba(X_train))

# 결정 함수 값 (마진과의 거리 — 양수일수록 A에 가까움)
cs = pd.DataFrame(logR.decision_function(X_train))

df2 = pd.concat([proba, cs], axis=1)
df2.columns = ['Not A', 'A', 'decision_function']

# decision_function 기준으로 정렬
df2.sort_values('decision_function', inplace=True)
df2.reset_index(drop=True, inplace=True)

# 시각화
plt.figure(figsize=(16, 8))

plt.axhline(y=0.5, linestyle='--', color='black', linewidth=3, alpha=0.3)   # 0.5 기준선
plt.axvline(x=0,   linestyle='--', color='black', linewidth=3, alpha=0.3)   # 0 기준선

plt.plot(df2['decision_function'], df2['Not A'], 'r--', label='Not A')
plt.plot(df2['decision_function'], df2['A'],     'b--', label='A')

plt.legend()
plt.xlabel('decision_function')
plt.ylabel('Proba')
plt.show()

💡 decision_function vs predict_proba

  • predict_proba() → 각 클래스별 확률값 (0~1)
  • decision_function() → 결정 경계와의 거리 (양수: 양성, 음수: 음성)
  • decision_function = 0 을 기준으로 양/음 분류

5. 혼동 행렬 히트맵 시각화

pred = logR.predict(X_test)
cm   = confusion_matrix(y_test, pred)

acc  = accuracy_score(y_test, pred)
prc  = precision_score(y_test, pred)
rcll = recall_score(y_test, pred)
f1   = f1_score(y_test, pred)

# 혼동 행렬 히트맵
plt.figure(figsize=(8, 8))
sns.heatmap(
    cm, annot=True, cmap='Blues', fmt='d',
    xticklabels=['pred Negative', 'pred Positive'],
    yticklabels=['actual Negative', 'actual Positive']
)
plt.show()

print('정확도 :', round(acc, 2))
print('정밀도 :', round(prc, 2))
print('재현율 :', round(rcll, 2))
print('F1     :', round(f1, 2))

6. 불균형 데이터 처리 — class_weight & 샘플링

class_weight='balanced' — 모델 내부에서 가중치 부여

logR2 = LogisticRegression(class_weight='balanced')
logR2.fit(X_train, y_train)
pred_2 = logR2.predict(X_test)

# 기본 vs balanced 비교
print('정확도 :', round(acc, 2),  round(accuracy_score(y_test, pred_2), 2))
print('정밀도 :', round(prc, 2),  round(precision_score(y_test, pred_2), 2))
print('재현율 :', round(rcll, 2), round(recall_score(y_test, pred_2), 2))
print('F1     :', round(f1, 2),   round(f1_score(y_test, pred_2), 2))

극단치 제거 후 재학습

from outlier import outlier_iqr

outlier_drop_df, outlier_dict = outlier_iqr(
    df, *df.drop('class_1', axis=1).columns, drop=True
)

X_train, X_test, y_train, y_test = train_test_split(
    outlier_drop_df.drop(['class', 'class_1'], axis=1),
    outlier_drop_df['class_1'],
    test_size=0.3,
    stratify=outlier_drop_df['class_1']
)

logR2.fit(X_train, y_train)
pred_3 = logR2.predict(X_test)

print('정확도 :', round(accuracy_score(y_test, pred_3), 2))
print('정밀도 :', round(precision_score(y_test, pred_3), 2))
print('재현율 :', round(recall_score(y_test, pred_3), 2))
print('F1     :', round(f1_score(y_test, pred_3), 2))

언더 샘플링 & SMOTE — 공통 함수로 실험

from imblearn.under_sampling import RandomUnderSampler
from imblearn.over_sampling import SMOTE

def logR_function(x, y, weight=None):
    X_train, X_test, y_train, y_test = train_test_split(
        x, y, test_size=0.3, random_state=42, stratify=y
    )
    model = LogisticRegression(class_weight=weight)
    model.fit(X_train, y_train)
    pred  = model.predict(X_test)

    print('정확도 :', round(accuracy_score(y_test, pred), 2))
    print('정밀도 :', round(precision_score(y_test, pred, average='macro'), 2))
    print('재현율 :', round(recall_score(y_test, pred, average='macro'), 2))
    print('F1     :', round(f1_score(y_test, pred, average='macro'), 2))
    return pred

x = df.drop(['class', 'class_1'], axis=1)
y = df['class_1']

# 언더 샘플링
undersample  = RandomUnderSampler(sampling_strategy=1)
x_under, y_under = undersample.fit_resample(x, y)
under_pred = logR_function(x_under, y_under)

# SMOTE 오버 샘플링
smote = SMOTE(sampling_strategy=1)
x_over, y_over = smote.fit_resample(x, y)
over_pred = logR_function(x_over, y_over)

7. 연습문제 — 다중 분류 & average 옵션

문제

  1. gender 컬럼을 0, 1로 변환
  2. class 컬럼 → A:1, B:2, C:3, D:4로 변환
  3. 극단치 데이터를 경계값으로 대체
  4. LogisticRegression으로 혼동 행렬 및 성능 지표 확인
# 성별 변환
body['gender'] = body['gender'].map({'M': 0, 'F': 1})

# 다중 클래스 변환 — 삼항 연산자 중첩
body['class'] = body['class'].map({'A': 1, 'B': 2, 'C': 3, 'D': 4})

# 극단치 경계값으로 대체
cols = body.drop('class', axis=1).columns
outlier_df, whis_dict = outlier_iqr(body, *cols)

x = outlier_df.drop('class', axis=1)
y = outlier_df['class']

pred = logR_function(x, y)

average 옵션 — 다중 분류 성능 지표

다중 분류에서는 precision_score, recall_score, f1_scoreaverage 매개변수를 지정해야 합니다.

average설명
'macro'각 클래스 점수의 단순 평균 — 모든 클래스를 동등하게 취급
'weighted'각 클래스의 샘플 수 비율로 가중 평균
'micro'혼동 행렬의 전체 합산으로 점수 생성 (이진 분류에서 정확도와 동일)

혼동 행렬 시각화 — 다중 분류

datas = train_test_split(x, y, test_size=0.3, random_state=42, stratify=y)

cm = confusion_matrix(datas[3], pred)   # datas[3] = y_test

plt.figure(figsize=(8, 8))
sns.heatmap(
    cm, annot=True, cmap='Blues', fmt='d',
    xticklabels=[1, 2, 3, 4],
    yticklabels=[1, 2, 3, 4]
)
plt.ylabel('Actual Class')
plt.xlabel('Pred Class')
plt.show()

💡 train_test_split() 의 반환값 순서: X_train(0), X_test(1), y_train(2), y_test(3)
datas[3]y_test


8. SVM (서포트 벡터 머신) — 이론 & 실습

  • 데이터를 분리하는 최적의 결정 경계를 찾고 경계선에서 마진(여유 공간) 을 최대화
  • 마진 밖 데이터에 패널티를 부여
  • 실제 데이터가 선형이 아닌 경우 커널 함수를 이용하여 고차원 공간으로 매핑

주요 매개변수

매개변수기본값설명
C1.0규제 강도의 역수 — 클수록 마진 좁아짐 (과적합 위험)
kernel'rbf'커널 함수 ('linear'/'poly'/'rbf')
gamma-커널 폭 — 클수록 경계 복잡 (과적합), 작을수록 경계 유연
degree3다항식 커널에서의 차수
probabilityFalseTruepredict_proba() 사용 가능 (속도 저하)

주요 속성 & 메서드

이름설명
support_서포트 벡터의 인덱스
support_vectors_서포트 벡터의 실제 데이터값
n_support_클래스별 서포트 벡터 개수
coef_회귀 계수 (linear 커널만 사용 가능)
decision_function()결정 함수값 (마진과의 거리)

SVC 실습 — C 값 비교

import pandas as pd
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score

df = pd.read_csv('../data/classification.csv')

# 데이터 분포 확인
sns.pairplot(data=df, hue='success')

x = df.drop('success', axis=1)
y = df['success']

X_train, X_test, y_train, y_test = train_test_split(
    x, y, test_size=0.3, stratify=y, random_state=42
)

svc   = SVC()          # C = 1.0 (기본값)
svc2  = SVC(C=0.5)    # C = 0.5 (마진 넓어짐)

svc.fit(X_train, y_train)
svc2.fit(X_train, y_train)

pred  = svc.predict(X_test)
pred2 = svc2.predict(X_test)

print('정확도 :', round(accuracy_score(y_test, pred), 2),  round(accuracy_score(y_test, pred2), 2))
print('정밀도 :', round(precision_score(y_test, pred), 2), round(precision_score(y_test, pred2), 2))
print('재현율 :', round(recall_score(y_test, pred), 2),    round(recall_score(y_test, pred2), 2))
print('F1     :', round(f1_score(y_test, pred), 2),        round(f1_score(y_test, pred2), 2))

9. SVR (서포트 벡터 머신 — 회귀) — 이론 & 실습

  • 입실론(ε) 튜브 안에 있는 데이터는 오차로 보지 않음
  • 튜브 데이터에만 패널티를 부여

주요 매개변수

매개변수설명
kernel'linear'/'rbf'/'poly'
epsilon오차 허용 폭 (튜브 크기)
C규제 강도 역수
gamma커널 폭 (rbf, poly에서 사용)
degree다항식 차수 (poly에서 사용)
max_iter최대 반복 횟수 (수치 불안정 시 사용)
tol수렴 판단 기준 (기본값 0.001)

SVR 실습 — 커널 종류별 성능 비교

import numpy as np
from sklearn.svm import SVR
from sklearn.metrics import mean_squared_error, r2_score

# 랜덤 데이터 생성 (사인 함수 + 노이즈)
x = np.sort(5 * np.random.rand(40, 1), axis=0)
y = np.sin(x)
y[::5] += 3 * (0.5 - np.random.rand(len(y[::5]), 1))   # 노이즈 추가

svr_rbf  = SVR(kernel='rbf',    gamma='auto', epsilon=0.1)
svr_lin  = SVR(kernel='linear')
svr_poly = SVR(kernel='poly',   gamma='auto')

svr_rbf.fit(x, y)
svr_lin.fit(x, y)
svr_poly.fit(x, y)

pred_rbf  = svr_rbf.predict(x)
pred_lin  = svr_lin.predict(x)
pred_poly = svr_poly.predict(x)

# 결과 DataFrame으로 정리
index  = ['RBF', 'Linear', 'Poly']
result = pd.DataFrame(index=index, columns=['MSE', 'R2'])

for pred, idx in zip([pred_rbf, pred_lin, pred_poly], index):
    result.loc[idx, 'MSE'] = round(mean_squared_error(y, pred), 2)
    result.loc[idx, 'R2']  = round(r2_score(y, pred) * 100, 2)

result

커널 종류별 사용 가능 매개변수

커널사용 가능 매개변수
linearC, epsilon
rbfC, epsilon, gamma
polyC, epsilon, gamma, degree

📎 핵심 개념 요약

개념설명
LogisticRegression분류 모델 — 확률값으로 클래스 판단
predict_proba()클래스별 예측 확률 반환
decision_function()결정 경계와의 거리 반환
class_weight='balanced'불균형 데이터에 자동 가중치 부여
np.where(조건, 참, 거짓)조건에 따라 값 선택 (벡터화)
average='macro'다중 분류 — 클래스별 점수 단순 평균
average='weighted'다중 분류 — 샘플 수 비율로 가중 평균
average='micro'다중 분류 — 혼동 행렬 전체 합산
SVC서포트 벡터 머신 (분류)
SVR서포트 벡터 머신 (회귀)
C (SVM)규제 강도 역수 — 클수록 마진 좁아짐
kernel='rbf'가우시안 커널 — 비선형 분류에 가장 많이 사용
epsilon (SVR)오차 허용 폭 (튜브 크기)
support_vectors_서포트 벡터의 실제 데이터값
datas[3]train_test_split() 반환값 중 y_test
profile
공부 기록

0개의 댓글