머신러닝

머신러닝 - 선형 회귀 기초

haniru 2026. 1. 9. 19:38

사이킷런 설치

 

1. 머신러닝의 기본 개념 및 원리

  • 머신러닝의 정의: 모델 내부의 **가중치(Weight)**들이 실수값을 가지며, 데이터를 통해 이 값을 최적화하는 과정이다.
  • 입력 데이터: 일반적으로 데이터(Feature)와 정답(Label)의 쌍으로 구성된다.
  • 추론(Inference): 학습된 모델을 통해 새로운 데이터에 대한 예상값을 도출하는 과정이다.

2. 학습 방식에 따른 분류

머신러닝은 데이터의 성격과 학습 목표에 따라 세 가지로 분류된다.

지도 학습 (Supervised Learning)

  • 특징: 데이터와 함께 명확한 **정답표(Label)**를 제공한다.
  • 목표: 입력값에 대한 정확한 정답을 예측하는 것이다.

비지도 학습 (Unsupervised Learning)

  • 특징: 정답 없이 데이터 자체만 보고 학습한다.
  • 핵심 기술: 비슷한 데이터끼리 묶는 **클러스터링(군집화)**이 대표적이다.

강화 학습 (Reinforcement Learning)

  • 특징: 보상(Reward)과 패널티를 통해 학습한다. 아무것도 모르는 상태에서 확률적인 선택을 반복하며 최적의 경로를 찾는다.
  • 비유: 영화 '엣지 오브 투모로우', 벽돌깨기 게임.
  • 사례: 구글 딥마인드의 알파고(AlphaGo). 확률 기반의 의사결정이 중요하다.

3. 인공지능의 수준 분류

  • 약인공지능 (Weak A.I.): 특정 영역에서만 지능을 발휘하는 AI (현재 대부분의 AI).
  • 강인공지능 (Strong A.I.): 인간 수준의 범용적인 지능을 가진 AI.

4. 실무 및 주요 사례

  • 알파고(AlphaGo): 딥마인드에서 개발했으며, 수많은 기보 데이터를 학습하여 바둑의 확률적 승률을 계산한다.
  • 머신러닝 실무의 핵심:
    • 이미 다양한 모델이 시중에 공개되어 있다.
    • 따라서 양질의 데이터 확보, 데이터 전처리, 상황에 맞는 적재적소의 알고리즘 선택 능력이 더 중요하다.

5. 머신러닝의 수학적 본질

머신러닝은 결국 오차를 줄여나가는 반복적인 최적화 과정이다.

  • 오차(Error) 계산: 실제값과 예측값의 차이를 구한다.
  • 반복 학습: 오차가 0이 될 때까지 파라미터(Weight)를 수정하며 반복한다.
  • 점화식의 관점: 이전 단계의 파라미터를 바탕으로 다음 단계의 파라미터를 갱신하는 과정은 수학의 점화식 원리와 유사하다.

 

6. 회귀(Regression) vs 분류(Classification)

지도 학습은 출력값(정답)의 형태에 따라 크게 두 가지로 나뉜다.

구분 회귀 (Regression) 분류 (Classification)
출력값 형태 연속적인 실수(Value) 이산적인 카테고리(Label)
예시 주택 가격 예측, 키 예측 개와 고양이 구분, 스팸 메일 판별
특징 데이터 간의 상관관계를 파악함 데이터를 특정 기준(결정 경계)으로 나눔
  • 분류의 수치화: AI는 '개', '고양이'라는 글자 자체를 이해하지 못하므로, 학습 시에는 이를 숫자(예: 개=0, 고양이=1)로 변환하는 과정이 필요하다.

7. 선형 회귀(Linear Regression)의 원리

데이터를 가장 잘 설명하는 직선을 찾는 과정이다.

주요 변수 및 용어

  • 독립변수 (X): 원인이 되는 데이터. 주택 면적, 부모의 키 등 (특성, Feature, Property).
  • 종속변수 (Y): 결과가 되는 데이터. 주택 가격, 자녀의 키 등.
  • 피어슨 상관계수: 두 변수가 양의 관계인지 음의 관계인지, 혹은 관계가 없는지 나타내는 척도다.

 

수학적 모델 (가설)

단순한 직선 방정식에서 시작하여 다변수로 확장된다.

  1. 일변수 회귀 (단순 선형 회귀): 특성이 1개일 때
  2. 다변수 회귀 (다중 선형 회귀): 특성이 여러 개일 때

  1. 벡터 표기 (행렬 연산):
    • W: 가중치(Weight), 기울기, 계수(Coefficient).
    • b: 편향(Bias), 절편(Intercept), 때로는 노이즈를 포함하는 개념.
    • W의 T: 가중치 행렬의 전치(Transpose).

 

 


8. 오차와 차원

오차(Residual, 잔차)

실제 정답과 모델이 예측한 값 사이의 거리다. 머신러닝의 목적은 이 잔차의 합을 최소화하는 최적의 Wb를 찾는 것이다.

고차원의 세계

  • 2차원: 선(Line)으로 데이터를 설명한다.
  • 3차원: 평면(Plane)으로 데이터를 설명한다.
  • 4차원 이상: 인간의 눈으로 시각화할 수 없는 영역이며, 이를 **하이퍼플레인(Hyperplane, 초평면)**이라고 부른다.

 

코드

 

공부시간에 따른 시험성적 예측

1. 데이터의 차원 (Shape)

  • scikit-learn에서 독립변수 X는 반드시 2차원 행렬 형태여야 한다. [163] 처럼 대괄호를 두 번 쓴 이유는 행(데이터 개수)과 열(특성 개수)을 맞추기 위함이다.

2. 가중치(W)와 편향(b)

  • regr.coef_: 입력값 X가 결과값 y에 주는 영향력을 의미한다. 위 예시에서는 "1시간 공부할 때마다 오르는 점수"가 된다.
  • regr.intercept_: X가 0일 때의 기본값이다. "공부를 아예 안 했을 때의 기본 점수"로 해석할 수 있다.

3. 결정계수

  • 이 점수는 0에서 1 사이의 값을 가진다.
  • 1에 가까울수록 "선형 회귀 직선이 데이터를 완벽하게 설명한다"는 뜻이며, 필기에서 언급한 **오차(Residual)**가 매우 적다는 것을 의미한다.

 

  • W (Coefficient): 기울기
  • b (Intercept): 절편/편향
  • 머신러닝의 목적은 수많은 X, y 쌍을 보고 최적의 W, b를 찾아내어, 새로운 X가 들어왔을 때 정확한 y를 **추론(Inference)**하는 것이다.

 

import numpy as np
from sklearn import linear_model

# 1. 모델 객체 생성
regr = linear_model.LinearRegression()

# 2. 데이터 준비 (X는 반드시 2차원 배열 형태여야 함)
# [공부 시간(시간)]
X = [[2], [4], [6], [8], [10]]
# [시험 점수(점)]
y = [40, 55, 70, 92, 98]

# 3. 모델 학습 (데이터 피팅)
regr.fit(X, y)

# 4. 결과 추출
coef = regr.coef_            # 가중치 (W, 기울기)
intercept = regr.intercept_  # 편향 (b, 절편)
score = regr.score(X, y)     # 결정계수 (R-squared)

# 5. 결과 출력
print(f"회귀 방정식: y = {coef[0]:.2f} * X + {intercept:.2f}")
print(f"모델 설명력(R²): {score:.2%}")

# 6. 새로운 데이터로 추론(Prediction)
new_study_time = [[7]]
predicted_score = regr.predict(new_study_time)
print(f"7시간 공부했을 때 예상 점수: {predicted_score[0]:.1f}점")

 

데이터 시각화

 

  • 파란색 점과 빨간색 선 사이의 수직 거리가 바로 **오차(잔차)**다.
  • 선형 회귀는 이 거리들의 제곱 합을 최소화하는 직선을 찾는 알고리즘이다.

 

import matplotlib.pyplot as plt
# 실제 수집된 데이터를 파란색(blue) 다이아몬드(D) 모양의 점으로 표시
plt.scatter(X, y, color='blue', marker='D')
y_pred = regr.predict(X)
plt.plot(X, y_pred, 'r:') # 모델이 예측한 값(y_pred)을 빨간색(r) 점선(:)으로 잇는다.

 

분석 결과 해석

  • 회귀 방정식: y = 7.65X + 25.10
    • 기울기가 7.65이므로 공부 시간이 1단위 증가할 때마다 점수는 약 7.65점 상승한다고 해석할 수 있다.
    • 절편이 25.10이므로 공부를 전혀 하지 않아도 기본적으로 얻는 점수(Bias)가 약 25점임을 의미한다.
  • 관계 점수(Score): 0.9803 (98.03%)
    • 데이터가 직선에 매우 가깝게 분포하고 있으며, 이 모델이 현재 데이터를 매우 높은 정확도로 설명하고 있다는 증거다.

 

새로운 공부 시간으로 시험점수 예측

1. 모델을 활용한 추론 (Inference)

학습된 가중치(W)와 편향(b)을 사용하여 본 적 없는 새로운 데이터에 대한 결과값을 예측하는 단계다.

  • 예측 과정: 모델에 167이라는 값을 입력하면, 내부적으로 y = Wx + b 연산을 수행한다.
  • 외삽(Extrapolation)의 위험: 현재 예시에서 공부 시간 167은 학습 데이터 범위(2~10시간)를 심하게 벗어나 있다. 이렇게 데이터 범위를 크게 벗어난 예측은 현실적인 제약(예: 하루는 24시간, 점수 만점은 100점 등)을 반영하지 못하므로 주의가 필요하다.

2. 결정계수 (R^2 Score, Coefficient of Determination)

모델이 실제 데이터를 얼마나 잘 설명하는지 나타내는 지표다.

  • 계산 방법: regr.score(X, y)와 sklearn.metrics의 r2_score(y, y_pred)는 동일한 수치를 반환한다.
  • 수식적 의미: 전체 분산 중 모델이 설명하는 분산의 비율을 의미한다.
  • 점수 해석:
    • 1.0: 모델이 데이터를 완벽하게 설명함.
    • 0.5: 데이터 변동성의 절반 정도를 설명함.
    • 0.0: 데이터를 평균값으로 예측하는 것과 차이가 없음.
new_study_time = [[167]]
result = regr.predict(new_study_time)
print(f'공부 시간이 {new_study_time}시간 이므로 시험점수는 {result.round(1)}점으로 추정됨')

from sklearn.metrics import r2_score
print(f"데이터와 선형 회귀 직선의 r square 점수: {r2_score(y, y_pred):.3}")
단계 주요 작업 코드 예시
1. 데이터 준비 독립변수($X$)와 종속변수($y$) 설정 X = [[...]], y = [...]
2. 모델 생성 알고리즘 객체 선언 regr = LinearRegression()
3. 모델 학습 데이터를 모델에 피팅 regr.fit(X, y)
4. 모델 평가 $R^2$ 등으로 성능 검증 regr.score(X, y)
5. 결과 시각화 산포도 및 회귀선 출력 plt.scatter(), plt.plot()
6. 예측(추론) 새로운 데이터 입력 및 결과 도출 regr.predict(new_data)

 

다중 선형 회귀 코드: 성적 예측

import numpy as np
from sklearn import linear_model

# 모델 객체 생성
regr = linear_model.LinearRegression()

# 데이터 설정: [공부 시간(시간), 하루 커피 섭취량(잔)]
X = [
    [10, 0], [8, 1], [9, 0], [7, 2], [12, 1], [5, 3], 
    [11, 0], [6, 2], [8, 4], [4, 1], [10, 2], [7, 0]
]
# 정답 데이터: [시험 점수]
y = [95, 88, 92, 80, 98, 65, 96, 75, 82, 60, 91, 85]

# 학습 (Fit)
regr.fit(X, y)

# 결과 분석
print(f'각 특성의 가중치(W1, W2): {regr.coef_}')
print(f'기본 점수(절편, b): {regr.intercept_:.2f}')
print(f'모델의 설명력(Score): {regr.score(X, y):.4f}')

# 새로운 데이터 추론: [13시간 공부하고 커피 안 마신 사람, 3시간 공부하고 커피 5잔 마신 사람]
new_students = [[13, 0], [3, 5]]
predictions = regr.predict(new_students)
print(f'예상 점수들: {predictions.round(1)}')

 

수동 가설 설정 및 시각화

import matplotlib.pyplot as plt
import numpy as np

# 실제 관측 데이터 (예: 광고비 대비 매출)
x_data = np.array([1, 2, 3, 4, 5]) 
y_data = np.array([3.1, 5.2, 8.9, 11.5, 15.1]) 

# 1. 실제 데이터 산포도
plt.scatter(x_data, y_data, color='green', marker='s', label='Actual Data')

# 2. 내가 직접 세운 가설 1: y = 3x (단순 3배 비례할 것이다)
plt.plot(x_data, 3.0 * x_data, 'r--', label='Hypothesis: y=3x')

# 3. 내가 직접 세운 가설 2: y = 2.5x + 1 (조금 더 완만할 것이다)
plt.plot(x_data, 2.5 * x_data + 1, 'b-', label='Hypothesis: y=2.5x+1')

plt.xlabel('Advertising Cost')
plt.ylabel('Sales')
plt.legend()
plt.grid(True)
plt.show()

 

평균 제곱 오차(MSE, Mean Squared Error)

1. 평균 제곱 오차(MSE)의 의미

머신러닝에서 모델의 성능을 평가할 때 사용하는 대표적인 **손실 함수(Loss Function)**다.

  • 오차(Error): 실제값(y)과 예측값(y 햇)의 차이다.
  • 제곱하는 이유: 1. 오차가 음수일 때 합산하면 오차가 상쇄되는 문제를 방지한다. 2. 오차가 클수록 더 큰 페널티를 부여한다(제곱이 되기 때문).
  • 평균: 전체 데이터 개수로 나누어 데이터 규모와 상관없이 평균적인 오차 수준을 파악한다.

2. 비슷한 지표: 평균 절대 오차(MAE)

MSE와 비슷하지만, 제곱 대신 절댓값을 사용하는 **평균 절대 오차(MAE, Mean Absolute Error)**가 있다.

  • 특징: 오차의 크기를 그대로 반영한다. MSE보다 이상치(Outlier)에 덜 민감하다.

import numpy as np
from sklearn.metrics import mean_absolute_error, mean_squared_error

# 실제값과 예측값
y = np.array([1.2, 2.4, 2.5, 4.6, 5.4])
y_hat = np.array([1, 2, 3, 4, 5])

# 1. MAE (Mean Absolute Error) 수동 계산
mae_manual = np.abs(y_hat - y).mean()

# 2. MSE (Mean Squared Error) 수동 계산
diff = (y_hat - y) ** 2
mse_manual = diff.sum() / len(y)

# 3. RMSE (Root Mean Squared Error) 수동 계산
# MSE에 루트를 씌워 실제 데이터와 단위를 맞춤
rmse_manual = np.sqrt(((y_hat - y) ** 2).mean())


print(f"MAE (평균 절대 오차): {mae_manual:.3f}")
print(f"MSE (평균 제곱 오차): {mse_manual:.3f}")
print(f"RMSE (제곱근 평균 제곱 오차): {rmse_manual:.3f}")

# 4. 사이킷런 라이브러리 활용
mae_sklearn = mean_absolute_error(y, y_hat)
mse_sklearn = mean_squared_error(y, y_hat)
rmse_sklearn = np.sqrt(mse_sklearn)

print(f"MAE (평균 절대 오차): {mae_sklearn:.3f}")
print(f"MSE (평균 제곱 오차): {mse_sklearn:.3f}")
print(f"RMSE (제곱근 평균 제곱 오차): {rmse_sklearn:.3f}")
지표 수식 특징 장점 단점
MSE 오차 제곱의 평균 미분이 쉬워 최적화에 유리함 큰 오차에 지나치게 민감함
MAE 오차 절댓값의 평균 직관적이며 이상치에 강함 모델 학습 시 미분이 까다로움
RMSE MSE에 루트를 씌움 실제 정답과 단위가 같아 해석이 쉬움 MSE의 민감도를 그대로 가짐

 

경사 하강법을 이용해 단순 선형 회귀 모델의 가중치와 편향 구하기

from sklearn import linear_model  # 사이킷런의 선형 모델 모듈을 가져옴
import numpy as np                # 배열 계산을 위해 넘파이를 가져옴

# X: 커피 잔 수, y: 총 결제 금액 (단위: 천원)
# 학습에 사용할 데이터 정의 (X: 원인/입력, y: 결과/타겟)
X = np.array([1, 2, 3, 4, 5])
y = np.array([8, 13, 18, 23, 28])

w, b = 0, 0
learning_rate, epoch = 0.01, 2000
n = len(X)

for i in range(epoch):
    y_pred = w * X + b
    error = y_pred - y
    # 경사하강법 업데이트 (평균값 기준)
    w = w - learning_rate * (error * X).mean()
    b = b - learning_rate * error.mean()

print(f'예상 결과: y = 5x + 3 (1잔당 5천원 + 배달비 3천원)')
print(f'학습 결과: w = {w.round(2)}, b = {b.round(2)}')
from sklearn import linear_model
import numpy as np

X = np.array([1, 2, 3, 4, 5])
y = np.array([8, 13, 18, 23, 28])

# 선형 회귀 모델 객체 생성
regr = linear_model.LinearRegression()

# 사이킷런의 fit() 함수는 X 데이터로 2차원 배열을 요구함
# 기존 1차원 [1, 2, 3...]을 [[1], [2], [3]...] 형태의 2차원으로 변환
X = X[:, np.newaxis]

# 준비된 데이터를 모델에 넣어서 학습(훈련) 진행
regr.fit(X, y)

# 학습 결과 확인
# coef_: 가중치(기울기 w), intercept_: 편향(절편 b)을 의미함
print(f'w = {regr.coef_.round(2)}, b = {regr.intercept_.round(2)}')

 

그래프로 시각화

import matplotlib.pyplot as plt
import numpy as np

X = np.array([1, 2, 3, 4, 5])
y = np.array([8, 13, 18, 23, 28])
plt.scatter(X, y, color='blue', marker='D')

y_pred = regr.coef_[0] * X + regr.intercept_

# 공식을 직접 쓸 필요 없이 모델에게 예측을 시킴
# y_pred = regr.predict(X[:, np.newaxis])
plt.plot(X, y_pred, 'r:')

 

 

학습률이 작은 경우

학습률(learning_rate)은 경사 하강법에서 **"한 번에 얼마나 큰 걸음을 내디딜 것인가"**를 결정.

  • 현재 설정된 0.00001은 걸음 폭이 너무 좁아서, 1,000번(epoch)을 반복해서 걸어도 목적지(최적의 w, b)까지 도달하기에 턱없이 부족함.
  • 비유하자면, 서울에서 부산까지 가야 하는데 한 번에 1cm씩만 이동하는 것과 같음. 1,000걸음을 걸어도 여전히 서울 근처에 머물러 있는 상태
import numpy as np
import matplotlib.pyplot as plt

# 1. 데이터 정의
X = np.array([1, 2, 3, 4, 5])
y = np.array([8, 13, 18, 23, 28])

# 2. 모델 파라미터 초기화
w, b = 0, 0
# 학습률이 0.00001로 매우 작음: 가중치 업데이트가 아주 조금씩 일어남
learning_rate, epoch = 0.00001, 1000
n = len(X)

# 3. 경사 하강법 루프
for i in range(epoch):
    y_pred = w * X + b     # 현재 모델의 예측값 계산
    error = y_pred - y     # 실제값과의 차이(오차) 계산

    # 오차를 바탕으로 기울기(w)와 절편(b) 업데이트
    # 학습률이 작아서 1000번을 돌아도 w와 b가 목표값(5, 3)에 도달하지 못함
    w = w - learning_rate * (error * X).sum()
    b = b - learning_rate * error.sum()

# 4. 결과 출력
print(f'학습 완료 후 결과: w = {w.round(2)}, b = {b.round(2)}')

# --- 그래프 시각화 부분 ---

# 실제 데이터 점 (파란색 다이아몬드)
plt.scatter(X, y, color='blue', marker='D', label='Actual Data')

# 학습된 w, b를 이용한 예측 직선 (빨간색 점선)
y_final_pred = w * X + b
plt.plot(X, y_final_pred, 'r:', label='Model Prediction')

# 그래프 설정
plt.xlabel('X')
plt.ylabel('y')
plt.title(f'Learning Rate: {learning_rate} (Small)')
plt.legend()
plt.show()

 

학습률이 큰 경우

학습률이 너무 큰 경우(learning_rate = 1.0)는 걸음 폭이 너무 커서 최적의 지점을 지나쳐버리고, 오차가 점점 커지다가 결국 값이 폭발(발산)하게됨

import numpy as np
import matplotlib.pyplot as plt

# 1. 데이터 정의
X = np.array([1, 2, 3, 4, 5])
y = np.array([8, 13, 18, 23, 28])

# 2. 모델 파라미터 초기화
w, b = 0, 0
# 학습률이 1.0으로 매우 큼: 한 번의 업데이트가 데이터의 범위를 완전히 벗어날 정도로 큼
learning_rate, epoch = 1.0, 1000
n = len(X)

# 3. 경사 하강법 루프
for i in range(epoch):
    y_pred = w * X + b     # 현재 예측값
    error = y_pred - y     # 오차

    # 가중치 업데이트
    # 학습률이 너무 커서 w와 b가 최소 오차 지점을 향해 가는 게 아니라,
    # 반대 방향으로 튕겨 나가며 값이 무한히 커짐 (Over-shooting/Divergence)
    w = w - learning_rate * (error * X).sum()
    b = b - learning_rate * error.sum()

    # 값이 너무 커져서 에러가 날 수 있으므로 중간에 멈추는 출력 (선택 사항)
    if np.isinf(w) or np.isnan(w):
        print(f"{i+1}번째 에폭에서 값이 폭발했습니다.")
        break

# 4. 결과 출력 (보통 nan 혹은 inf가 출력됨)
print(f'w = {w}, b = {b}')

# --- 시각화 코드 ---

# 데이터 점 시각화
plt.scatter(X, y, color='blue', marker='D', label='Actual Data')

# 초기 상태 (w=0, b=0)
plt.plot(X, 0*X + 0, 'g--', label='Initial (w=0, b=0)')

# 학습률이 너무 커서 엉뚱한 곳으로 가버린 예측 선 (값이 폭발하기 직전의 상태 가정)
# 실제 1.0 학습률로는 단 1회만 돌아도 선이 화면 밖으로 사라짐
if not np.isnan(w) and not np.isinf(w):
    plt.plot(X, w * X + b, 'r:', label='Diverged Line')
else:
    print("값이 너무 커서 그래프에 표시할 수 없습니다.")

plt.xlabel('X')
plt.ylabel('y')
plt.title(f'Learning Rate: {learning_rate} (Too Large)')
plt.legend()
plt.show()

'머신러닝' 카테고리의 다른 글

머신러닝 - 분류, 군집, SVM  (0) 2026.01.12
머신러닝 - 다중회귀  (0) 2026.01.11
PANDAS  (0) 2026.01.08
SEABORN  (0) 2026.01.07
MATPLOTLIB  (0) 2026.01.07