내일배움캠프

[본캠프] 데이터기반 QA/QC 부트캠프 30일차

min0jun 2026. 6. 23. 21:00

1. 오늘의 학습 목표

오늘도 크게 통계머신러닝 내용을 나누어 학습했다.

통계에서는 회귀분석의 기본 개념과 상관관계를 중심으로 배웠다.
단순선형회귀, 다중선형회귀, 범주형 변수 처리, 다항회귀, 스플라인 회귀를 정리했고, 이어서 피어슨 상관계수, 스피어만 상관계수, 켄달의 타우, 상호정보까지 함께 살펴봤다.

머신러닝에서는 선형회귀를 실제 데이터에 적용하는 방법과 로지스틱 회귀를 중심으로 학습했다.
선형회귀 모델을 훈련하고 평가하는 과정, 범주형 데이터를 다루는 방법, 로지스틱 회귀의 개념, 그리고 분류 모델을 평가하는 정확도와 F1-score까지 정리했다.

오늘의 목표는 단순히 공식이나 코드를 외우는 것보다는, 각 개념이 어떤 문제를 해결하기 위해 사용되는지 이해하는 것이었다.


2. 오늘 학습한 내용

통계

1. 단순선형회귀

단순선형회귀는 하나의 독립 변수 X와 하나의 종속 변수 Y 사이의 관계를 직선으로 표현하는 방법이다.

쉽게 말하면, 하나의 원인이 결과에 어떤 영향을 주는지 확인하는 방식이다.

예를 들어 광고비와 매출의 관계를 생각해볼 수 있다.
광고비가 증가할수록 매출이 함께 증가하는 경향이 있다면, 광고비를 독립 변수 X로 두고 매출을 종속 변수 Y로 두어 회귀분석을 할 수 있다.

회귀식은 다음과 같은 형태이다.

Y = β0 + β1X

여기서 β0는 절편이고, β1은 기울기이다.
중학교 때 배웠던 1차 함수와 비슷하게 생각하면 조금 더 편하다.

처음에는 회귀식에 β 같은 기호가 들어가서 조금 딱딱하게 느껴졌는데,
막상 보면 결국 하나의 변수로 결과를 예측하는 직선식이라고 볼 수 있었다.

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score

np.random.seed(0)

X = 2 * np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100, 1)

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

model = LinearRegression()
model.fit(X_train, y_train)

y_pred = model.predict(X_test)

print("MSE:", mean_squared_error(y_test, y_pred))
print("R2:", r2_score(y_test, y_pred))

단순선형회귀는 구조가 간단해서 해석하기 쉽다.
하지만 데이터가 직선 형태를 따르지 않는다면 좋은 결과를 내기 어렵다.


2. 다중선형회귀

다중선형회귀는 두 개 이상의 독립 변수를 사용해서 하나의 종속 변수를 예측하는 방법이다.

단순선형회귀가 X 하나만 가지고 Y를 예측했다면,
다중선형회귀는 X1, X2, X3처럼 여러 변수를 함께 사용한다.

회귀식은 다음과 같은 형태이다.

Y = β0 + β1X1 + β2X2 + … + βnXn

예를 들어 집값을 예측한다고 할 때, 면적 하나만으로 집값을 예측하기에는 부족할 수 있다.
면적, 방 개수, 위치, 건축 연도 같은 여러 변수를 함께 사용하면 더 현실적인 예측이 가능하다.

다만 변수가 많아지면 무조건 좋은 것은 아니다.
변수들끼리 너무 강하게 관련되어 있으면 다중공선성 문제가 생길 수 있다.

다중선형회귀는 여러 요인을 동시에 고려할 수 있지만, 변수 선택과 해석에 더 신경을 써야 한다.


3. 범주형 변수

회귀분석에서 범주형 변수는 바로 사용하기 어렵다.

범주형 변수는 숫자가 아니라 문자나 그룹으로 나뉘는 데이터이다.
예를 들면 성별, 지역, 요일, 등급 같은 데이터가 있다.

범주형 변수는 크게 순서가 있는 경우와 순서가 없는 경우로 나눌 수 있다.

순서가 있는 범주형 변수는 크고 작음의 의미가 있다.
예를 들어 옷 사이즈나 등급 같은 데이터는 순서가 있기 때문에 숫자로 변환해도 어느 정도 의미가 유지된다.

반대로 순서가 없는 범주형 변수는 숫자로 단순 변환하면 문제가 생길 수 있다.
예를 들어 지역을 부산=1, 대전=2, 대구=3처럼 바꾸면, 모델이 숫자의 크기에 의미가 있다고 착각할 수 있다.

그래서 이런 경우에는 원-핫 인코딩을 사용한다.

import pandas as pd

data = {
    'Gender': ['Male', 'Female', 'Female', 'Male', 'Male'],
    'Experience': [5, 7, 10, 3, 8],
    'Salary': [50, 60, 65, 40, 55]
}

df = pd.DataFrame(data)

df = pd.get_dummies(df, columns=['Gender'], drop_first=True)

print(df)

처음에는 문자를 숫자로 바꾸면 되는 거 아닌가 싶었는데,
순서가 없는 변수에 숫자를 부여하면 모델이 잘못 해석할 수 있다는 점이 중요했다.


4. 다항회귀와 스플라인 회귀

다항회귀는 독립 변수와 종속 변수의 관계가 직선이 아닐 때 사용하는 회귀 방법이다.

단순선형회귀는 직선으로 관계를 표현하지만, 실제 데이터는 항상 직선 형태로 움직이지 않는다.
이럴 때 독립 변수의 제곱, 세제곱 같은 항을 추가해서 곡선 형태로 데이터를 설명할 수 있다.

예를 들어 면적이 커질수록 집값이 증가하긴 하지만, 증가 폭이 일정하지 않을 수 있다.
이런 경우 단순한 직선보다 곡선이 더 잘 맞을 수 있다.

from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
import numpy as np

np.random.seed(0)

X = 2 - 3 * np.random.normal(0, 1, 100)
y = X - 2 * (X ** 2) + np.random.normal(-3, 3, 100)

X = X[:, np.newaxis]

poly_features = PolynomialFeatures(degree=2)
X_poly = poly_features.fit_transform(X)

model = LinearRegression()
model.fit(X_poly, y)

스플라인 회귀는 데이터를 여러 구간으로 나누고, 각 구간마다 다른 회귀식을 적용하는 방법이다.
구간별로 다른 패턴을 반영할 수 있기 때문에 복잡한 비선형 관계를 더 유연하게 표현할 수 있다.

다만 다항회귀나 스플라인 회귀는 너무 복잡하게 만들면 과적합이 발생할 수 있다.
이 부분은 성능을 높이려다가 오히려 새로운 데이터에 약해질 수 있다는 점이 조금 인상적이었다.


5. 피어슨 상관계수

피어슨 상관계수는 두 연속형 변수 사이의 선형 관계를 측정하는 지표이다.

값은 -1에서 1 사이를 가진다.

  • 1에 가까우면 강한 양의 선형 관계
  • -1에 가까우면 강한 음의 선형 관계
  • 0에 가까우면 선형 관계가 거의 없음

예를 들어 공부 시간과 시험 점수 사이에 직선적인 관계가 있는지 확인할 때 사용할 수 있다.

import numpy as np
import pandas as pd
from scipy.stats import pearsonr

np.random.seed(0)

study_hours = np.random.rand(100) * 10
exam_scores = 3 * study_hours + np.random.randn(100) * 5

corr, p_value = pearsonr(study_hours, exam_scores)

print("피어슨 상관계수:", corr)
print("p-value:", p_value)

피어슨 상관계수는 가장 대표적으로 많이 사용하는 상관계수이지만,
비선형 관계에는 적합하지 않다.

피어슨 상관계수는 두 변수가 직선적으로 함께 움직이는지를 볼 때 유용하다.


6. 비모수 상관계수

비모수 상관계수는 데이터가 정규분포를 따르지 않거나, 순서형 데이터일 때 사용하는 상관계수이다.

대표적으로 스피어만 상관계수와 켄달의 타우가 있다.

스피어만 상관계수는 실제 값 자체보다 순위를 기준으로 관계를 본다.
예를 들어 값이 정확히 직선 형태는 아니더라도, 한 변수가 커질 때 다른 변수도 함께 커지는 경향이 있다면 사용할 수 있다.

켄달의 타우는 순위의 일치 쌍과 불일치 쌍을 기준으로 관계를 측정한다.
스피어만보다 조금 더 보수적인 느낌으로 순위 관계를 판단한다고 볼 수 있다.

from scipy.stats import spearmanr, kendalltau
import numpy as np

np.random.seed(0)

customer_satisfaction = np.random.rand(100)
repurchase_intent = 3 * customer_satisfaction + np.random.randn(100) * 0.5

spearman_corr, spearman_p = spearmanr(customer_satisfaction, repurchase_intent)
kendall_corr, kendall_p = kendalltau(customer_satisfaction, repurchase_intent)

print("스피어만 상관계수:", spearman_corr)
print("켄달의 타우:", kendall_corr)

처음에는 상관계수면 다 비슷한 줄 알았는데,
데이터의 형태나 분포에 따라 사용하는 방식이 다르다는 점이 중요했다.


7. 상호정보

상호정보는 두 변수 사이의 정보 의존성을 측정하는 방법이다.

일반적인 상관계수처럼 -1에서 1 사이로 표현되는 방식은 아니다.
상호정보는 한 변수를 알았을 때 다른 변수에 대한 불확실성이 얼마나 줄어드는지를 기준으로 한다.

특히 비선형 관계나 범주형 데이터에서 사용할 수 있다는 점이 특징이다.

import numpy as np
from sklearn.metrics import mutual_info_score

X = np.array(['cat', 'dog', 'cat', 'cat', 'dog', 'dog', 'cat', 'dog'])
Y = np.array(['high', 'low', 'high', 'high', 'low', 'low', 'high', 'low'])

mi = mutual_info_score(X, Y)

print("상호정보:", mi)

피어슨 상관계수는 선형 관계를 보는 데 강하고,
상호정보는 조금 더 복잡한 관계나 범주형 변수 사이의 의존성을 볼 때 사용할 수 있다.

처음에는 “상관계수랑 뭐가 다른 거지?” 싶었는데,
상호정보는 단순히 직선 관계를 보는 것이 아니라 정보가 얼마나 연결되어 있는지 보는 개념에 가깝다고 느꼈다.


머신러닝

1. 선형회귀 적용

머신러닝에서는 선형회귀를 실제 데이터에 적용하는 과정을 학습했다.

예시로 tips 데이터를 사용했다.
tips 데이터는 식당에서 결제한 전체 금액, 팁, 성별, 흡연 여부, 요일, 식사 시간, 인원 수 등의 컬럼을 가지고 있다.

여기서는 전체 지불 금액을 X로 두고, 팁을 Y로 두어 회귀분석을 진행한다.

즉, 전체 금액이 커질수록 팁도 커지는지 확인하고,
전체 금액을 바탕으로 예상 팁을 예측하는 흐름이다.

import seaborn as sns
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score

tips_df = sns.load_dataset('tips')

X = tips_df[['total_bill']]
y = tips_df['tip']

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

model = LinearRegression()
model.fit(X_train, y_train)

y_pred = model.predict(X_test)

print("MSE:", mean_squared_error(y_test, y_pred))
print("R2:", r2_score(y_test, y_pred))

실습으로 보니까 선형회귀가 조금 더 현실적으로 느껴졌다.
그냥 수식으로만 볼 때보다, 실제 데이터에서 X와 Y를 정하고 모델을 훈련하는 흐름이 더 와닿았다.


2. 선형회귀 심화

선형회귀 심화에서는 다중선형회귀와 범주형 데이터 사용을 다시 다뤘다.

실제 데이터는 독립 변수 하나만으로 설명하기 어려운 경우가 많다.
그래서 여러 변수를 함께 사용하거나, 변수를 변형해서 더 좋은 모델을 만들 수 있다.

또한 데이터 안에는 숫자형 변수뿐만 아니라 문자형 변수도 자주 등장한다.
예를 들어 성별, 요일, 식사 시간 같은 데이터는 그대로 모델에 넣을 수 없기 때문에 적절한 변환이 필요하다.

이 부분은 머신러닝에서 전처리가 왜 중요한지 다시 느끼게 해줬다.

모델을 만드는 것만큼, 모델이 이해할 수 있는 형태로 데이터를 바꾸는 과정도 중요하다.


3. 선형회귀의 가정

선형회귀는 간단하고 해석하기 쉬운 모델이지만, 몇 가지 가정이 있다.

대표적으로 선형성, 등분산성, 독립성, 정규성 등이 있다.

선형성은 독립 변수와 종속 변수 사이에 선형 관계가 있어야 한다는 의미이다.
즉, X가 변할 때 Y도 일정한 방향과 형태로 변해야 한다.

등분산성은 오차의 분산이 일정해야 한다는 의미이다.
오차가 특정 구간에서만 커지거나 작아지는 패턴을 보이면 모델이 안정적이라고 보기 어렵다.

처음에는 이런 가정들이 조금 이론적인 내용처럼 느껴졌는데,
결국 모델이 제대로 작동하기 위해 확인해야 하는 기본 조건이라고 볼 수 있었다.

선형회귀는 단순해서 편하지만,
데이터가 선형 관계를 잘 따르지 않으면 성능이 좋지 않을 수 있다.


4. 로지스틱 회귀

로지스틱 회귀는 이름에는 회귀가 들어가지만, 실제로는 분류 문제에 사용된다.

대표적인 예시로 타이타닉 생존 예측 문제가 있다.
승객의 티켓 등급, 성별, 나이, 요금 등의 정보를 바탕으로 생존 여부를 예측하는 문제이다.

여기서 종속 변수 Y는 생존 여부이다.

  • 사망: 0
  • 생존: 1

즉, 숫자를 예측하는 회귀 문제가 아니라 0 또는 1 중 하나를 맞히는 분류 문제이다.

처음에는 로지스틱 “회귀”라는 이름 때문에 회귀분석과 헷갈렸는데,
결과적으로는 범주형 결과를 예측하는 분류 모델이라고 이해하면 된다.


5. 로짓과 오즈비

로지스틱 회귀에서는 확률을 그대로 직선으로 설명하기 어렵다.

확률은 0과 1 사이의 값을 가져야 하는데,
선형회귀처럼 직선으로 예측하면 0보다 작거나 1보다 큰 값이 나올 수 있다.

그래서 S자 형태의 시그모이드 함수를 사용한다.
이 함수는 어떤 값이 들어와도 결과를 0과 1 사이의 확률로 바꿔준다.

여기서 오즈비와 로짓 개념이 등장한다.

오즈비는 실패 확률 대비 성공 확률이다.

예를 들어 성공 확률이 80%라면 실패 확률은 20%이고,
오즈비는 80% / 20% = 4가 된다.

로짓은 이 오즈비에 로그를 씌운 값이다.
로짓을 사용하면 선형회귀의 형태를 활용하면서도 분류 문제를 다룰 수 있다.

이 부분은 처음에 확실히 조금 어렵게 느껴졌다.
오즈비, 로짓, 시그모이드가 한 번에 나오니까 살짝 복잡했는데,
정리해보면 결국 확률을 모델이 다루기 좋은 형태로 바꾸는 과정이었다.


6. 분류 평가 지표

분류 모델을 평가할 때는 단순히 정확도만 보면 안 된다.

정확도는 전체 데이터 중 모델이 맞춘 비율이다.
겉으로 보기에는 가장 직관적인 지표이다.

하지만 정확도에는 한계가 있다.

예를 들어 암 환자 5명과 정상인 95명이 있는 데이터에서,
모델이 모든 사람을 정상이라고 예측하면 정확도는 95%가 된다.

겉으로는 성능이 좋아 보이지만, 실제 암 환자는 한 명도 찾지 못한 모델이다.

그래서 혼동 행렬과 F1-score 같은 지표가 필요하다.

혼동 행렬은 실제 값과 예측 값의 관계를 네 가지로 나누어 보여준다.

  • TP: 실제 양성을 양성으로 맞춘 경우
  • FP: 실제 음성을 양성으로 잘못 예측한 경우
  • FN: 실제 양성을 음성으로 잘못 예측한 경우
  • TN: 실제 음성을 음성으로 맞춘 경우

F1-score는 정밀도와 재현율을 함께 고려한 지표이다.
특히 데이터가 불균형할 때 정확도보다 더 유용하게 사용할 수 있다.

분류 모델에서는 단순히 많이 맞췄는지보다, 중요한 대상을 제대로 찾았는지가 더 중요할 수 있다.


7. 로지스틱 회귀 실습

로지스틱 회귀 실습에서는 sklearn의 LogisticRegression을 사용했다.

자주 사용하는 메소드와 속성은 다음과 같다.

  • fit: 모델 학습
  • predict: 클래스 예측
  • predict_proba: 각 클래스에 속할 확률 예측
  • coef_: 가중치
  • intercept_: 절편
  • classes_: 클래스 종류

타이타닉 데이터에서는 Fare만 사용한 1차 모델과,
Pclass, Sex, Fare를 함께 사용한 2차 모델을 비교했다.

from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, f1_score

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

y_pred = model.predict(X_test)

print("accuracy:", accuracy_score(y_test, y_pred))
print("f1_score:", f1_score(y_test, y_pred))

실습을 통해 로지스틱 회귀가 단순히 0과 1을 맞히는 모델이 아니라,
각 클래스에 속할 확률을 바탕으로 판단하는 모델이라는 점을 이해할 수 있었다.


3. 나의 간단 소감

오늘은 통계와 머신러닝 내용을 나누어서 학습했다.

 

먼저 통계에서는 회귀분석과 상관관계를 중심으로 정리했다.
단순선형회귀와 다중선형회귀는 어느 정도 이해가 됐지만, 다항회귀와 스플라인 회귀는 처음 봤을 때 조금 복잡하게 느껴졌다.

그래도 직선으로 설명하기 어려운 데이터를 곡선이나 구간별 함수로 표현한다고 생각하니 조금 정리됐다.
현실 데이터가 항상 깔끔한 직선으로 움직이지 않는다는 점을 생각하면, 왜 이런 방법들이 필요한지도 이해됐다.

상관계수도 처음에는 피어슨만 알면 되는 줄 알았는데,
데이터가 정규분포를 따르는지, 순위형 데이터인지, 비선형 관계인지에 따라 다른 방법을 사용할 수 있다는 점이 중요했다.

특히 상호정보는 조금 생소했다.
단순히 같이 증가하거나 감소하는 관계가 아니라, 한 변수가 다른 변수의 정보를 얼마나 설명해주는지 보는 느낌이라서 기존 상관계수와는 결이 다르게 느껴졌다.

 

머신러닝에서는 선형회귀와 로지스틱 회귀를 중심으로 학습했다.

선형회귀는 수식으로만 보면 딱딱한 느낌이 있는데, tips 데이터처럼 실제 데이터를 넣어서 실습하니까 훨씬 이해가 쉬웠다.

특히 X와 Y를 정하고, train/test로 나누고, 모델을 학습한 뒤 평가하는 흐름이 머신러닝의 기본 구조처럼 느껴졌다.

로지스틱 회귀는 이름 때문에 처음에는 회귀 문제라고 생각하기 쉬웠는데, 실제로는 0과 1 같은 범주를 예측하는 분류 모델이라는 점이 중요했다. 오즈비와 로짓 개념은 솔직히 한 번에 완전히 편해지지는 않았다.
그래도 확률을 0과 1 사이로 다루기 위해 시그모이드 함수를 사용하고, 그 과정에서 로짓이 등장한다고 생각하니 큰 흐름은 잡힌 것 같다.

분류 평가 지표에서는 정확도만 믿으면 안 된다는 점이 가장 기억에 남았다.
정확도가 높아도 실제로 중요한 대상을 하나도 못 찾는 모델이 될 수 있다는 예시가 꽤 직관적이었다.

 

오늘 내용은 통계와 머신러닝이 각각 따로 정리되는 느낌이었다.
통계에서는 데이터의 관계를 어떻게 설명할 것인지,

머신러닝에서는 데이터를 어떻게 준비하고 모델 결과를 어떤 기준으로 평가할 것인지가 핵심이었다.

아직 로짓이나 상호정보 같은 개념은 더 익숙해질 필요가 있지만,
오늘은 적어도 각 개념이 어떤 상황에서 쓰이는지 조금씩 감을 잡은 하루였다.