포함관계:
인공지능 > 머신러닝 > 딥러닝
머신러닝 : 지도학습 / 비지도학습 / 강화학습
파이썬의 라이브러리들:
- numpy : 수치 계산
- pandas : 데이터 프레임 자료형
- scipy : 실제 과학 및 공학 문제 해결용 고급 연산
- joblib : 대용량데이터 직렬화 등, 병렬 처리 수행
- matplotlib : 그래프 그리기
- seaborn : matplotlib 기반 고급 통계 시각화
- plotly : 대화형 웹 시각화
- pydot : 그래프 구조 생성, 조작용
- graphviz : 그래프 자료구조를 그림 형태로 시각화, 결정 트리 시각화
- scikit-learn : 표준 머신 러닝 툴킷, 전처리, 모델평가, 파이프라인 지원
입력형식 : numpy 배열 or pandas 데이터프레임
머신러닝의 순서 :
데이터 -> 수집 -> 전처리 -> 학습 -> 모델평가 -> 모델
자세하게는
데이터 수집 -> 데이터 전처리 -> 훈련 및 테스트 -> 모델 선택 -> 학습(새로운 값 입력) -> 검증 및 평가 -> 예측
데이터를 수집하여, 전처리후 학습, 모델에 새로운 데이터를 입력 후 예측
전처리 과정에서 훈련 세트와 테스트 세트 ( 주로 8:2, 7:3 비율)로 나누는 과정 포함
-> 라벨 인코딩, 원-핫 인코딩 포함
라벨 인코딩 : [바나나,사과,포도] => [1, 0, 2 ]
원핫 인코딩 : [바나나, 사과, 포도] => [ (1,0,0), (0,1,0), (0,0,1) ]
클리핑 : 설정한 범위 외 값을 제거
numpy.log() : numpy 의 로그 변환메서드, 지수로그를 취하여 함수를 lny = ax+b 형태로 만들어 줌
=> 극단적으로 큰 값의 스케일을 줄여줌.
지도학습에서 회귀란?
회귀(Regression) : 과거의 데이터로 미래 데이터를 예측하는 방법

오차 => 모집단에서 실제값 - 모집단의 회귀식의 값
잔차 => 실제 관측된 표본값 - 예측된 표본값
그럼 머신러닝에서는 어떻게 미래 데이터를 예측할까?
바로 최소제곱법을 이용한다.
최소제곱법이란?
최소 제곱법의 아이디어는
모든 잔차를 그냥 더하면 +와 -로 상쇄되니,
제곱해서 더한 것이 최소가 되게 한다..
-> 오차항들이 회귀선에 최대한 가깝게 배치되게 끔.
최소제곱법을 자세히 이해하려면
먼저 가중합에 대해서 알아야 한다.
가중합이란?
가중합 : 독립변수와 종속변수의 선형 결합
=> H(x) = Wx + b
H(x)가 모델의 예측값
W는 가중치(기울기)
b는 절편(x가 0일 때 예측값)
변수가 위 처럼 한 개가 아닌,
여러 개라면?
=> H(x)=W1x1+W2x2+⋯+Wpxp+b
각 변수마다 별개의 가중치를 가진다.
이때 예측값의 W와 b을 최소제곱법으로 구한다.
최소제곱법은 잔차의 제곱이 최소가 되게 하는 W와 b를 구하는 식이다.

분자 : xy의 공분산 (x의 잔차 * y의 잔차)
분모 : x의 분산 (x의 잔차 제곱)
즉 x의 분포와 xy의 변동량으로 기울기를 구한다고 생각하면 쉽다.
이제 파이썬 코드 예시로 알아보자.
파이썬 가중합 코드)
import numpy as np
# 독립변수: 혈중 콜레스테롤 수치
x = np.array([150, 160, 170, 180, 190, 200])
# 종속변수: 중성지방 수치
y = np.array([110, 115, 130, 145, 150, 160])
# x의 평균 계산
x_mean = np.mean(x)
# y의 평균 계산
y_mean = np.mean(y)
# 최소제곱법(OLS) 기울기 공식
# W = Σ(x - x_mean)(y - y_mean) / Σ(x - x_mean)^2
W = np.sum((x - x_mean) * (y - y_mean)) / np.sum((x - x_mean)**2)
# 절편 b = y_mean - W * x_mean
b = y_mean - W * x_mean
# 예측값 H(x) = Wx + b
y_pred = W * x + b
# 결과 출력
print(f"기울기 (W): {W:.4f}")
print(f"절편 (b): {b:.4f}")
print(f"예측값 (y_pred): {y_pred}")
linregress 함수: scipy.stats의 메서드로, 단순선형회귀 분석을 수행한다.
리턴값은 5개로 보통 아래와 같이 사용한다.
slope, intercept, r_value, p_value, std_err = linregress(x, y)
기울기, 절편, 상관 계수, 유의 확률, 기울기 표준 오차다.
파이썬 선형 회귀 코드)
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import linregress
# 한글 및 마이너스 깨짐 방지 설정
plt.rcParams['font.family'] = 'Malgun Gothic'
plt.rcParams['axes.unicode_minus'] = False
# 독립변수: 혈중 콜레스테롤 수치
x = np.array([150, 160, 170, 180, 190, 200, 210, 220, 230, 240])
# 종속변수: 중성지방 수치
y = np.array([110, 115, 130, 145, 150, 160, 170, 185, 195, 205])
# 단순 선형회귀 분석 수행
# slope(기울기), intercept(절편), r_value(상관계수),
# p_value(기울기 유의성 검정), std_err(기울기의 표준오차)
slope, intercept, r_value, p_value, std_err = linregress(x, y)
# 회귀식 y_pred = slope * x + intercept
y_pred = slope * x + intercept
# 회귀 결과 출력
print(f"기울기 (slope): {slope:.4f}")
print(f"절편 (intercept): {intercept:.4f}")
print(f"상관계수 (r-value): {r_value:.4f}")
print(f"p-값 (p-value): {p_value:.4e}")
print(f"표준오차 (std_err): {std_err:.4f}")
# 기울기의 부호에 따른 해석
if slope > 0:
print("콜레스테롤 수치가 높을수록 중성지방 수치가 증가하는 경향이 있다.")
else:
print("콜레스테롤 수치가 높을수록 중성지방 수치가 감소하는 경향이 있다.")
# 그래프 시각화
plt.figure(figsize=(7, 5))
# 실제 관측값
plt.scatter(x, y, color='orange', label='관측값 (실제 데이터)')
# 회귀선
plt.plot(x, y_pred, color='blue', label=f'회귀선: y = {slope:.2f}x + {intercept:.2f}')
plt.title("콜레스테롤 수치와 중성지방 수치의 관계")
plt.xlabel("콜레스테롤 수치")
plt.ylabel("중성지방 수치")
plt.legend()
plt.show()
이처럼 Scipy의 linregress 함수는 내부적으로 최소제곱법(OLS)을 사용하여
기울기 W(= slope)와 절편 b(= intercept)를 구한다.
즉, x와 y 데이터를 넣으면 y = Wx + b 형태의 선형 관계식을 자동으로 얻을 수 있음.
다중회귀란?
여기서 독립변수의 수가 1개가 아닌 2개 이상이 되면
다중 선형회귀가 된다.

위에서 설명한 가중합에서 변수가 여러 개일 때 수식과 동일하다.
파이썬에서는 sklearn.linear_model의 LinearRegression 클래스를 사용하여 구현한다.
모델의 정확도는 평균제곱오차(MSE)와 평균절대오차(MAE), 그리고 결정계수(R² score)를 통해 예측의 정확도를 판단한다.
단순회귀(독립변수 1개)는 그래프로 시각화해 예측 성능을 직접 눈으로 확인할 수 있지만,
다중회귀(독립변수 여러 개)는 고차원 구조로 인해 시각화가 어려우므로
MSE, MAE, R² 같은 수치 기반 평가 지표가 특히 더 중요하게 사용된다.
여기서 R²는 결정 계수를 계산한다.
결정 계수란?
이는 회귀모형이 변동성을 얼마나 잘 설명하는가 나타내는 지표이다.

- SSE = 잔차 제곱합, 모델이 설명 못한 부분
- SSR = 회귀 제곱합, 모델이 설명한 부분
- SST = 전체 변화량(총 변동)
여기서 SST = SSR + SSE이다.
다시 SSR = SST - SEE 이 되기 때문에,
R² = (SST - SSE) / SST = 1 - SSE / SST
이 된다.
식을 해석하면,
SSE는 잔차 제곱합이니 잔차의 값에 따라서
잔차가 커지면 결정계수는 작아지고 ( 나쁜 모델이라고 판단 )
잔차가 작아지면 결정계수는 커진다 ( 좋은 모델이라고 판단 )
파이썬에서 다중 회귀 코드는 보통
데이터 수집 -> 데이터 전처리 -> 모델 선택 -> 데이터 학습 -> 데이터 예측 순서로 작성한다.
다중 회귀에서 데이터 수집)
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
# 한글 폰트 설정 (맑은 고딕), 음수 부호 깨짐 방지
plt.rcParams['font.family'] = 'Malgun Gothic'
plt.rcParams['axes.unicode_minus'] = False
# 면적, 방 개수, 연식, 가격으로 구성된 데이터셋을 딕셔너리 형태로 정의
# 각 리스트는 동일한 길이를 갖고, 동일한 인덱스끼리 한 집의 정보를 의미한다.
data = {
'면적': [30, 45, 55, 70, 85, 100, 120, 150],
'방수': [1, 1, 2, 2, 3, 3, 4, 4],
'연식': [30, 25, 20, 15, 10, 8, 5, 2],
'가격': [120, 150, 180, 200, 250, 280, 320, 380]
}
# 딕셔너리를 판다스 DataFrame으로 변환하여 테이블 형태로 확인
df = pd.DataFrame(data)
# 데이터 출력
print(df)
면적, 방수(방의 개수), 연식, 가격 데이터를 수집한다.
pandas의 데이터프레임을 이용해서 데이터를 저장했다.
이제 면적, 방수, 연식으로 가격을 예측하는 코드를 작성해 보자.
다중 회귀에서 데이터 전처리)
# 모델 입력에 사용할 열 이름 목록을 고정해둔다.
X_cols = ['면적', '방수', '연식']
# 선택한 열들을 넘파이 배열로 변환하여 학습에 사용하기 좋은 형태로 만든다.
X = df[X_cols].values
print(X)
# 출력되는 X는 8×3 행렬로,
# 총 8개의 관측치(표본)에 대해
# 3개의 독립변수(면적, 방수, 연식)를 포함하고 있다.
print(X.shape)
# X.shape의 결과는 (8, 3)이며,
# 이는 n × p 행렬 구조를 의미한다.
# n = 8 → 표본(관측치) 수
# p = 3 → 독립 변수(특성) 수
- x값을 세팅하는 부분
# 종속변수(가격)를 모델이 예측해야 할 목표값으로 설정하고,
# 이를 넘파이 배열 형태로 추출한다.
y = df['가격'].values
# y는 길이가 8인 1차원 배열이며, 각 값은 하나의 관측치에 대한 실제 가격이다.
print(y)
- y값을 세팅하는 부분
이로써 가격 = 면적, 방수, 연식의 관계식구조로 설정된다.
다중 회귀에서 모델 선택)
# 선형 회귀 모델 객체 생성 (fit_intercept=True → 절편 b를 함께 학습)
linreg = LinearRegression(fit_intercept=True)
# 주어진 입력 데이터(X)와 종속변수(y)를 이용해
# 회귀계수(W)와 절편(b)을 학습한다.
# 내부적으로는 '최소제곱법(OLS)'을 이용해
# 잔차 제곱합을 최소화하는 파라미터를 찾는다.
linreg.fit(X, y)
# 학습된 회귀식을 사용하여 각 관측치에 대한 예측값을 계산한다.
# 이렇게 얻은 y_pred는 훈련 오차가 어느 정도인지 진단할 때 활용된다.
y_pred = linreg.predict(X)
print(y_pred)
모델로 LinearRegression을 설정, fit_intercept에서 intercept는 절편이라는 뜻이다.
따라서 절편도 같이 포함해서 학습한다는 뜻이다.
fit 메서드는 계수(w)와 절편(b)을 추정하고 모델을 학습시키고 객체를 반환한다.
즉 객체가 w와 b값을 추정한 것으로 업데이트된다는 뜻이다.
여기서도 마찬가지로 최소제곱법을 이용하여 구하지만,
LinearRegression이 자동으로 해준다고 보면 된다.
그다음 구해진 w와 b값으로
예측을 진행하는 것이 predict 메서드이다.
이제 얻어낸 모델의 정확도를 파악해 보자.
# RMSE (Root Mean Squared Error)
# 평균제곱오차(MSE)에 제곱근을 취한 값.
# 단위가 종속변수와 동일하므로 오차의 크기를 직관적으로 해석할 수 있다.
rmse = np.sqrt(mean_squared_error(y, y_pred))
print("RMSE :", rmse)
# RMSE 값이 작을수록 예측값이 실제값에 가깝다.
# MAE (Mean Absolute Error)
# 오차의 절댓값을 평균낸 값.
# 제곱을 하지 않기 때문에 이상값(Outlier)의 영향을 상대적으로 덜 받는다.
mae = mean_absolute_error(y, y_pred)
print("MAE :", mae)
# MAE 값이 작을수록 예측이 정확함을 의미한다.
# R² (결정계수)
# 결정계수는 1에 가까울수록 독립변수들이 종속변수를 잘 설명한다는 뜻.
# 즉, 종속변수의 변화량 중 대부분이 독립변수로부터 예측 가능함을 의미한다.
r2 = r2_score(y, y_pred)
print("결정계수 :", r2)
# R²가 1에 가까울수록 모델의 예측값이 실제값에 가깝고,
# R²가 0.99라면 약 1%만 모델이 설명하지 못하는 오차로 남는다.
모델의 정확도는
평균제곱오차, 평균절대오차, 결정계수로 판단한다고 했다.
위 코드는 먼저 변수를 선언하는 부분이다.
수정된 결정 계수란?
# n, p는 X의 행렬 크기에서 가져옴
n, p = X.shape
# 일반적으로 p는 절편을 제외한 독립변수 개수.
# 만약 X에 절편항(1열)을 직접 추가했다면, 그 열을 제외하고 p를 계산해야 한다.
# R²는 독립변수 수가 늘어나면 항상 증가하는 특성이 있음.
# 이를 해결하기 위해 자유도 보정을 적용한 '수정된 결정계수(adj R²)'를 사용한다.
adj_r2 = 1 - (1 - r2) * (n - 1) / (n - p - 1)
print("수정된 결정계수 :", adj_r2)
# adj R²는 독립변수가 많아져도 불필요한 변수 추가에 대한 패널티를 주어 과적합을 억제한다.
먼저 표본의 개수 n과 독립변수의 수 p를 X.shape로 가져온다.
결정계수 R²는 독립변수를 많이 넣을수록 항상 증가하거나 그대로 유지된다.
왜냐하면, 독립변수를 추가하면 모델이 더 많은 정보를 사용하게 되어
잔차제곱합(SSE)은 절대 증가하지 않기 때문이다.
따라서 R²는 “변수를 많이 넣는 모델”에게 유리해지는 문제가 있다.
이 문제를 해결하기 위해 자유도 기반 보정을 적용한 것이
바로 수정된 결정계수(Adjusted R²)이다.
따라서 보정을 적용한 것이 수정된 결정계수이다.

n = 표본수
p = 독립변수 수
R² = 기존 결정계수
결정계수의 본질은
전체 변동(SST) 중 모델이 설명한 비율(SSR/SST)을 의미한다.
반대로,
모델이 설명하지 못한 비율은 다음과 같다:

하지만 SSE와 SST는
사용하는 자유도가 서로 다르다.
총 변동량의 자유도
=> SST의 자유도: n − 1
잔차 제곱합의 자유도
=> SSE의 자유도: n − p − 1 (p개의 독립변수 + 절편 1개 추정)
이렇듯 SST와 SSE는 서로 다른 “자유도”를 갖기 때문에
단순한 비율(SSE/SST)로 비교하면 모델이 복잡할수록 유리해진다.
이를 공정하게 비교하기 위해
각각을 자신의 자유도로 나누어 평균적 변동량으로 만든 뒤,
그 비율을 비교한다.
즉, (SSE/n-p-1) / (SST/n-1)를 적용한 것이다.
여기서 SSE/SST * (n-1/n-p-1)이고,

이를 정리하면,

그리고 SSE/SST는 1-R2이다.

그리고 현재식은 모델이 설명하지 못 한 부분이기에
모델이 설명한 부분으로 되돌리기 위해 1에서 빼주면

이 식이 수정된 결정계수(Adjusted R²) 공식이다.
이 값은 모델이 불필요한 변수를 추가하더라도
자유도 감소에 따른 페널티를 주기 때문에
과적합을 억제하는 효과가 있다.
# 회귀식의 절편 출력
print(f"절편 : {linreg.intercept_}")
# 절편은 모든 독립변수가 0일 때의 예측값을 의미한다.
# 독립변수 이름(X_cols)과 계수(linreg.coef_)를 짝지어 출력
for name, coef in zip(X_cols, linreg.coef_):
print(f"계수 [{name}]: {coef}")
# name = 특성 이름
# coef = 해당 특성의 회귀계수 (기울기)
그렇게 얻어진 절편과
각 독립변수별 w값을 출력했다.
이제 모델을 설정했으니
이 모델을 활용해서 값을 예측해 보자.
데이터 예측 코드)
# 예측 시에는 학습할 때 사용한 열 순서(X_cols)와 동일한 순서를 반드시 유지해야 한다.
# 열 순서가 바뀌면 모델이 잘못된 위치의 값을 입력받아 틀린 예측을 하게 된다.
new_data = pd.DataFrame({'면적': [80], '방수': [3], '연식': [5]})
# new_data에서 학습에 사용했던 열 순서(X_cols)대로 값을 추출하여
# 모델 입력 형태(2차원 배열)로 변환한다.
pred_price = linreg.predict(new_data[X_cols].values)
# predict()는 넘파이 배열을 반환한다.
# 하나의 샘플을 예측했으므로 길이가 1인 배열이 나오며, [0]으로 첫 번째 값만 꺼낸다.
print(f"예상 주택 가격 : {pred_price[0]}")
# -------------------------------
# 실제값(y)과 예측값(y_pred)을 비교하는 산점도 시각화
# 대각선 근처로 점들이 모일수록 예측이 정확함을 의미한다.
# -------------------------------
plt.figure()
# 실제 데이터 점 플롯
plt.scatter(y, y_pred, color='orange', label='실제 데이터')
# 실제값과 예측값 전체에서 최소·최대값을 구해 대각선 기준선에 사용한다.
mn = float(min(y.min(), y_pred.min()))
mx = float(max(y.max(), y_pred.max()))
# y = x 형태의 대각선 기준선 (완벽한 예측일 경우 점들이 이 선 위에 위치)
plt.plot([mn, mx], [mn, mx])
plt.xlabel('실제값')
plt.ylabel('예측값')
plt.title('실제값과 예측값 비교')
plt.legend()
plt.show()
pandas의 데이터프레임으로 새로운 값을 입력하고,
predict 메서드로 값을 예측하는 과정은 동일하다.
그리고 matplotlib 라이브러리의 pyplot를 사용해서
시각화를 한 코드이다.
실제값과 예측값을 대각선으로 시각화하여
모델이 얼마나 정확하게 예측했는지 확인할 수 있다.
다음으로는 잔차에 대한 시각화 코드이다.
# 표본별 잔차(residual)를 벡터로 만든다.
# 잔차 = 실제값(y) - 예측값(y_pred)
# 잔차가 양수면 "과소예측(예측이 실제보다 작음)",
# 음수면 "과대예측(예측이 실제보다 큼)"을 의미한다.
resid = y - y_pred
print("잔차 (resid):", resid)
# 선형회귀에서 절편이 포함된 경우,
# 이론적으로 잔차의 총합은 0에 가까워진다.
print("잔차 합계(이론상 0에 근접):", resid.sum())
# 영향도 분석 등을 위해 설계행렬(design matrix)을 만든다.
# X_design = [1, X] 형태로 첫 열은 절편항(1), 나머지는 독립변수 컬럼들이다.
X_design = np.column_stack([np.ones(n), X])
print("X_design 모양:", X_design.shape)
# X_design의 구조 확인을 위해 일부(상위 3행만) 출력한다.
print("X_design 상위 3행:\n", X_design[:3])
# -------------------------------
# 잔차(residual) vs 예측값(predicted) 산점도
# 등분산성(Homoscedasticity) 및 선형성(Linearity) 가정을 시각적으로 점검하기 위한 그래프
# -------------------------------
plt.figure()
plt.scatter(y_pred, resid, color='orange', label='잔차 (Residuals)')
# 기준선 y=0 (잔차가 0인 지점) → 잔차 패턴 확인용
plt.axhline(0, color='blue', linestyle='--', label='잔차 기준선 = 0')
plt.xlabel('예측값')
plt.ylabel('잔차')
plt.title('잔차와 예측값의 관계')
plt.legend()
plt.show()
y=0을 기준으로 잔차가 양수인 점, 음수인 점을 확인할 수 있다.
마무리
가중합은 예측값을 얻는 식이고,
예측값을 얻기 위해 최소제곱법을 사용한다.
최소제곱법으로 얻어낸 식은 선형회귀에 사용되며
선형회귀는 독립변수의 수에 따라 단순회귀, 다중회귀로 구분된다.
회귀모델의 정확도를 판단할 때 결정계수가 사용되며,
결정 계수의 약점을 보완한 수정된 결정계수를 사용한다.
이를 matplotlib.pyplot을 통해 시각화하여 확인할 수 있다.
'Python > 머신러닝' 카테고리의 다른 글
| 머신러닝 개념 정리) 랜덤 포레스트, 유의 확률, 카이제곱 통계량, F 통계량 (0) | 2025.11.28 |
|---|---|
| 머신러닝 개념 정리) 결정 트리, 엔트로피 지수, 지니 지수, CART 알고리즘 (0) | 2025.11.23 |
| 머신러닝 개념 정리) 서포트벡터 머신, 커널 트릭 (0) | 2025.11.22 |
| 머신러닝 개념 정리) K-최근접 이웃, 유클리드 거리, 맨해튼 거리, 체비쇼프 거리, 민코스프키 거리 (0) | 2025.11.22 |
| 머신러닝 개념 정리) 로지스틱 회귀, 시그모이드 함수, 소프트맥스 회귀 (0) | 2025.11.22 |
