AI/머신러닝

실무에 쓰는 머신러닝 기초 1주차 (회귀)

edcrfv458 2025. 3. 13. 14:01

목표

  • 휘귀분석 개념
  • 규제(Regularization) 기법
  • 회귀 모델 평가 지표

 

1. 회귀 분석 개요

 

회귀 분석

  • 종속 변수(Y)와 하나 이상의 독립 변수(X) 간의 관계를 추정하여, 연속형 종속 변수를 예측하는 통계/머신러닝 기법
    • 공부한 시간(X)에 따라 시험 점수(y)가 어떻게 변하는 가를 예측
  • 지도 학습에서 분류와 회귀 차이
    • 분류: 결과값이 이산형(클래스 라벨)
    • 회귀: 결과값이 연속형(숫자 값)
    • 사람의 지능적인 작업을 기계가 수행하도록 만드는 광범위한 개념

 

사용 이유

  • 미래 값 예측: 판매가, 주가, 온도 등 실수값 예측에 사용
  • 인과 관계 해석(통계 관점): 특정 독립변수가 종속변수에 미치는 영향력을 해석하기 위해
  • 데이터 기반 의사결정: 추세(trend) 파악, 자원 배분 등

 

대표적 활용 사례

  • 경제: 주식 가격 예측, 판매량 예측
  • 건강: 혈압, 콜레스테롤 수치 예측
  • 제조업: 불량률, 생산량 예측규제(Regularization) 기법
  • 회귀 모델 평가 지표

 

2. 선형 회귀(Linear Regression)

 

가정

  • 독립변수(X)와 종속변수(y)가 선형적(일차 방정식 형태)으로 관계를 맺고 있다고 가정

 

회귀식

  • Y = B_0 + B_1*X_1 + B_2*X_2 + B_n*X_n
    • B_0: 절편(intercept) ➡️ 편향
    • B_i: 각 독립변수의  회귀계수(coefficient) ➡️ 가중치 or 파라미터

 

선형 회귀 모델 학습 과정

  1. 가중치(회귀계수) 초기화
  2. 손실함수(Loss Function) 설정 ➡️ 오차
    1. 주로 MSE(Mean Squared Error) 사용
  3. 최적화
    1. 수학적인 방법(최소자승법), 경사하강법(Gradient Descent) 등을 통해 가중치 업데이트
      1. 최소자승법: 계산을 해서 베타값 찾는 방법
      2. 경사하강법: 경험적으로  베타값 찾는 방법
  4. 학습 완료 후
    1. B_0, B_1 ... 을 얻어 새로운 입력 값에 대한 예측 수행

 

장단점

  • 장점: 해석이 간단하고 구현이 쉬움
  • 단점: 데이터가 선형성이 아닐 경우 예측력이 떨어짐

 

코드

  • sklearn 에서 제공하는 데이터는 전처리가 다 되어있음
    • 당뇨병 데이터 사용
  • LinearRegression: 수학적인 방법
import numpy as np
import pandas as pd
from sklearn.datasets import load_diabets
from sklearn.linear_model import LinearRegression, SGDRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score

# 1. 데이터 호출
diabetes = load_diabetes()
X = diabets.data
y = diabets.target

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

# 3. 선형회귀
model = LinearRegression()
model.fit(X_train, y_train)

# 예측
y_pred = model.predict(X_test)

# 성능
mse = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)

# 평균 비율 오차
def MPE(y_true, y_pred):
	return np.mean((y_true - y_pred) / y_true) * 100
   
print("가중치:", model.coef_)
print("절편:", model.intercept_)
print("MSE:", mse)
print("R2:", r2)
print("평균 비율 오차:", MPE(y_test, y_pred))
  • SGDRegressor: 베타 값을 경험적으로 바꿔가며 학습
    • max_iter: 학습을 반복할 횟수
    • tol: 오차 범위 설정
      • 1e-3은 10^-3
model = SGDRegressor(max_iter=6000, tol=1e-3, random_state=42)
model.fit(X_train, y_train)

# 예측
y_pred = model.predict(X_test)

# 성능
mse = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)

# 평균 비율 오차
def MPE(y_true, y_pred):
	return np.mean((y_true - y_pred) / y_true) * 100
   
print("가중치:", model.coef_)
print("절편:", model.intercept_)
print("MSE:", mse)
print("R2:", r2)
print("평균 비율 오차:", MPE(y_test, y_pred))

 

3. 다항 회귀(Polynomial Regression)

  • 비선형적인 관계를 다항식 형태로 모델링
    • ex) 2차 다항식 ➡️ Y = B_0 + B_1 * X + B_2 * X^2
  • 단순 선형항 뿐만 아니라 고차항을 추가해 비선형 패턴을 학습

 

적용 예시

  • 제조 공정에서의 온도와 반응률 관계가 곡선 형태인 경우
  • 건강 데이터에서 나이와 특정 지표(근육량 등)가 단순 선형보다 곡선 형태로 나타나는 경우

 

주의점

  • 고차항을 무작정 늘리면 과적합 문제 발생 가능성 존재
  • 모델 복잡도와 일반화 성능 간의 균형을 맞춰야 함

 

코드

  • 일부러 비선형 데이터로 만듦
    • n_samples: 샘플의 수
    • n_features: 특성의 수
    • noise: 잡음 크기
  • 다항 회귀
    • 비선형 회귀를 할 때는 데이터를 가공해줘야 함 ➡️ PolynomialFeatures
    • degree=2 ➡️ 2차 다항식
import numpy as np
import pandas as pd
from sklearn.datasets import make_friedman1
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score
from sklearn.pipeline import Pipeline

# 1) 비선형 데이터
X, y = make_friedman1(n_samples=1000, n_features=5, noise=1.0, random_state=42)

# 2) 데이터 분리
X_train, X_test, y_train, y_test = train_test_split(
	X, y, test_size=0.2, random_state=42
)

# 3) 다항 회귀
poly_model = Pipeline([
	("Poly", PolynomialFeatures(degree=2, include_bias=False)),
    ("lin_reg", LinearRegression())
])

poly_model.fit(X_train, y_train)

# 예측
y_pred = poly_model.predict(X_test)

# 성능
mse = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)

# 평균 비율 오차
def MPE(y_true, y_pred):
	return np.mean((y_true - y_pred) / y_true) * 100
   
print("가중치:", poly_model.coef_)
print("절편:", poly_model.intercept_)
print("MSE:", mse)
print("R2:", r2)
print("평균 비율 오차:", MPE(y_test, y_pred))

 

4. 회귀 모델 평가 방법

MSE (Mean Squared Error)

  • 예측값과 실제값의 차이를 제곱하여 평균
  • 오차가 클수록 제곱에 의해 더 큰 벌점이 매겨지므로, 큰 오차에 민감
  • 회귀 모델 평가에서 사용

 

MAE (Mean Absolute Error)

  • 예측값과 실제값의 차이를 절댓값으로 측정한 후 평균
  • 예측이 평균적으로 실제값에서 얼마나 벗어났는지 직관적으로 표현

 

RMSE (Root Mean Squared Error)

  • MSE에 root를 씌움
  • MAE와 달리 제곱을 통해 큰 오차에 가중치를 더 주는 특징
  • 오차가 클수록 패널티가 커지므로 큰 오차가 중요한 문제에 자주 사용

 

R-2 Score (결정 계수)

  • 값의 범위
    • 0~1 (음수도 가능)
  • 해석
    • 1에 가까울수록 학습된 모델이 데이터를 잘 설명한다는 의미
    • 0이라면 모델이 종속변수를 전혀 설명하지 못한다는 의미

 

5. 고급 회귀 기법 - Lasso & Ridge Regression

  • 선형 회귀에 규제(Regularization) 항을 추가해 과적합을 방지

 

Ridge(릿지) 회귀

  • 오차에 가중치를 제곱합(L2 Norm)을 더함
    • 가중치가 큰 것이 나오면 오차를 크게 만들어 모델을 일반화
    • 앞에 람다 값이 있음
  • 효과: 가중치가 너무 커지지 않도록 방지 (가중치 값을 부드럽게 줄임)

 

Lasso(라쏘) 회귀

  • 릿지와 같지만 가중치의 절댓값합(L1 Norm)을 패널티로 추가
  • 효과: 가중치를 0으로 만들어 변수 선택(Feature Selection) 효과
    • 앞에 람다 값이 있음

 

코드

  • 릿지
    • alpha가 람다 값
import numpy as np
import pandas as pd
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.linear_model import Ridge, Lasso
from sklearn.metrics import mean_squared_error, r2_score

# 1. 데이터 로드
housing = fetch_california_housing()
X = housing.data
y = housing.target

# 2. 데이터 분리
X_train, X_test, y_train, y_test = train_test_split(
	X, y, test_size=0.2, random_state=42
)

# 3. Ridge 회귀
# alpha = 1.0 (규제 세기)는 필요에 따라 조정
ridge_reg = Ridge(alpha=1.0, random_state=42)
ridge_reg.fit(X_train, y_train)

# 예측
y_pred = ridge_reg.predict(X_test)

# 성능
mse = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)

# 평균 비율 오차
def MPE(y_true, y_pred):
	return np.mean((y_true - y_pred) / y_true) * 100
   
print("가중치:", ridge_reg.coef_)
print("절편:", ridge_reg.intercept_)
print("MSE:", mse)
print("R2:", r2)
print("평균 비율 오차:", MPE(y_test, y_pred))
  • 라쏘
# Lasso 회귀
# alpha=0.1 정도로 조금 낮춤(기본 1.0)
# alpha 너무 크면 가중치가 0이 되어 과소적합의 위험
lasso_reg = Lasso(alpha=0.1, random_state=42, max_iter=1000)
lasso_reg.fit(X_train, y_train)

# 예측
y_pred = lasso_reg.predict(X_test)

# 성능
mse = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)

# 평균 비율 오차
def MPE(y_true, y_pred):
	return np.mean((y_true - y_pred) / y_true) * 100
   
print("가중치:", lasso_reg.coef_)
print("절편:", lasso_reg.intercept_)
print("MSE:", mse)
print("R2:", r2)
print("평균 비율 오차:", MPE(y_test, y_pred))

 

실습

import numpy as np
import pandas as pd
from sklearn.datasets import load_diabetes
from sklearn.linear_model import Ridge, Lasso
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score

diabetes = load_diabetes()
X, y = diabetes.data, diabetes.target

feature_names = diabetes.feature_names
df = pd.DataFrame(X, columns=feature_names)
df['target'] = y 

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

ridge = Ridge(alpha=1.0, random_state=42)
ridge.fit(X_train, y_train)

y_pred_ridge = ridge.predict(X_test)

mse = mean_squared_error(y_test, y_pred_ridge)
r2 = r2_score(y_test, y_pred_ridge)

def MPE(y_true, y_pred):
    return np.mean((y_true - y_pred_ridge) / y_true) * 100

print("가중치:", ridge.coef_)
print("절편:", ridge.intercept_)
print("MSE:", mse)
print("R2:", r2)
print("평균 비율 오차:", MPE(y_test, y_pred_ridge))

lasso = Lasso(alpha=0.1, random_state=42, max_iter=1000)
lasso.fit(X_train, y_train)

y_pred_lasso = lasso.predict(X_test)

mse = mean_squared_error(y_test, y_pred_lasso)
r2 = r2_score(y_test, y_pred_lasso)

def MPE(y_true, y_pred):
    return np.mean((y_true - y_pred_lasso) / y_true) * 100

print("가중치:", lasso.coef_)
print("절편:", lasso.intercept_)
print("MSE:", mse)
print("R2:", r2)
print("평균 비율 오차:", MPE(y_test, y_pred_lasso))