LG U+ Why Not SW CAMP 7기 - 10주차 회고

God menu·2025년 7월 18일

<7/14 43일차>

가설검정은 모집단에 관한 특정 가설을 설정하고, 해당 모집단에서 추출한 표본을 분석함으로써 그 가설의 타당성을 객관적으로 평가하는 체계적인 통계 방법론으로 통계학의 핵심 개념이다.

  • 귀무가설은 변수들 사이에 아무런 관계가 없다고 가정하는 가설이다.
  • 연구가설(대립가설)은 변수들 간에 어떠한 관계가 있다는 명확한 진술이다.

귀무가설을 변수들 사이에 관계가 없다(동일하다)고 주장하는 반면,
연구가설은 변수들 사이에 관계가 있다(다르다)고 주장한다.

가설 검정 단계

  1. 귀무가설(H0)/대립가설(H1) 수립
  2. 검정 통계량 산출
  3. 기각/채택 판단 - P-value : 귀무가설이 참인 상황에서 관측된 데이터가 발생할 확률
  4. 최종 결정
    • P-value가 유의수준 0.05 미만이면 귀무가설 기각
    • 그렇지 않을 경우 귀무가설 채택

통계적 오류

H₀가 사실이라고 판정H₀가 사실이 아니라고 판정
H₀가 사실임옳은 결정제1종 오류 (α)
H₀가 사실이 아님제2종 오류 (β)옳은 결정

유의수준(α)는 귀무가설이 진실일 때 발생할 수 있는 모든 표본결과 중에서 너무 드물게 발생하는 결과이기 때문에 오류를 감수하면서 귀무가설을 기각하려는 최대 비율이다. 일반적으로 1% 혹은 5%로 정한다.

유의 확률 = p-value (p-값) : 귀무가설이 참일 때 현재 관찰된 결과나 더 극단적인 결과를 얻을 확률을 나타내는 값으로, 통계적 유의성을 판단하는 핵심 지표로 사용된다. 일반적으로 p-값이 낮을수록 귀무가설을 기각할 더 강한 증거가 된다. (즉, 5%보다 낮으면 귀무가설 기각)

이후에는 확률의 개념에 대해서 배웠는데 기존에 수학적 개념과 크게 다르지 않아서 어렵지 않았다. 베이즈 정리까지는 이해하기 쉬웠는데 확률분포부터 조금 헷갈렸다.

  • 중심극한정리 : 다수의 독립적인 관측값과 연관된 값(합계 또는 평균값 등)은 대부분 정규 형태로 분포
  • 표집분포 : 하나의 표본이 아니라, 모집단에서 가능한 모든 표본(크기 n)을 여러 번 뽑아, 각각의 표본 통계량(예:평균,비율)을 계산한 분포
  • 정규분포 : 평균을 중심으로 좌우 대칭인 종 모양 곡선

확률분포는 이산형과 연속형으로 나눌 수 있으며, 각각 확률질량함수와 확률밀도함수로 표현된다. 주요 확률분포로는 베르누이 분포, 이항분포, 포아송 분포, 정규분포, 지수분포 등이 있다.

  • 카이제곱 분포 : 여러 독립적인 표준 정규분포 변수들의 제곱 합으로 정의. 자유도(k)에 따라 분포의 모양이 달라지며, 자유도가 증가할수록 정규 분포에 가까워진다.
  • F분포 : 두 집단 간 분산의 동일성 검정에 사용되는 검정 통계량의 분포. 확률변수는 항상 양의 값만을 갖고, 카이제곱 분포와 달리 각 집단의 자유도 2개가 있다. 자유도가 커질수록 정규분포에 가까워진다.

오늘은 가설검정과 확률분포에 대해 학습하였다. 특히 귀무가설과 대립가설의 개념을 명확히 구분하고, p-value를 기준으로 가설을 기각하거나 채택하는 과정이 흥미로웠다. 표를 통해 제1종 오류와 제2종 오류 개념을 시각적으로 이해할 수 있었고, 유의수준의 의미 또한 실무에서 어떻게 쓰이는지 감이 잡혔다. 베이즈 정리는 익숙했지만 표집분포는 조금 생소해서 복습이 필요하다고 느꼈다. 이론적인 개념들이 통계 분석과 가설검정의 기초가 된다는 점에서 유익한 수업이었다. 통계가 익숙하지는 않지만, 반복 학습을 통해 점차 익숙해지려고 한다.


<7/15 44일차>

상관분석
두 변수 간의 관계를 정량적으로 평가하는 통계 기법으로 하나의 변수가 변할 때 다른 변수가 어떻게 변하는지 파악할 수 있다.

상관계수
두 변수간의 선형 관계의 정도를 정량적으로 나타내는 값으로 기술통계 값의 범위는 -1과 +1 사이

  • 양의 상관관계 : 한 변수가 증가할 때 다른 변수도 증가하는 경향이 있는 경우 (ex 공부를 더 많이 하면 시험 점수도 오를 것이다)
  • 음의 상관관계 : 한 변수가 증가할 때 다른 변수는 감소하는 경향이 있는 경우 (ex 더 많이 운동하면, 몸무게는 감소할 것이다)

상관계수는 크게 3가지가 있다.

  • 피어슨 상관계수 : 연속형 변수 간의 선형 관계를 측정한다. 두 변수의 공분산을 각 변수의 표준편차로 나누어 계산 (즉, 두 변수가 얼마나 함께 변하는지를 표준편차를 통해 정규화한 값)
  • 스피어만 상관계수 : 순위형 변수 간의 관계를 측정한다. 각 변수의 순위를 매긴 후, 그 순위들 간의 상관계수를 계산한다.
  • 켄달의 타우 : 변수의 순위 쌍 간의 일치와 불일치를 비교하여 상관관계를 계산한다. 작은 데이터셋에 적합한 방법

오전까지 통계 관련 개념을 배우고 이후에는 인공지능, 머신러닝 개념에 대하여 배우는 시간이었다.

기본적인 코딩 알고리즘으로 연습해보았다. 일반 프로그램은 룰 기반(함수)으로 코드를 짜고, 머신러닝은 모델을 지정하고, 입력 값과 결과 값이 명확하지 않을 때 사용하고, 딥러닝은 모델을 지정하지 않고 입력 값과 결과 값만 주고 알아서 구하도록 한다.

구글 코랩에서 강사님과 함께 코드를 연습했다.

  • 규칙 기반 프로그램 - 전통적인 방식의 AI
def my_function(x):
  return 5 * x + 7   # 규칙

val = int(input('숫자를 입력하세요 : '))
ret = my_function(val)
print(ret)

결과 :
숫자를 입력하세요 : 9
52

  • 머신러닝 - 규칙이 아닌 모델을 선정 / 데이터를 넣고(학습 fit) -> 값을 넣어 결과를 확인한다(예측 predict)
import numpy as np
from sklearn import linear_model
X_train = np.array([1,2,3,4,5,6,7]).reshape(-1, 1)   # 입력데이터
Y_train = np.array([12,17,22,27,32,37,42])   # 결과데이터
model = linear_model.LinearRegression()
model.fit(X_train, Y_train)
model.predict([[9]])   # 예측

결과 :
array([52.])

  • 딥러닝 - 모델이 정해져 있다 / 계층설정(입력층, 은닉층, 출력층) / 모델 생성(계층) / 모델 설정(compile) / 모델 학습(fit) / 학습한 모델로 예측(predict)
import tensorflow as tf
X_train = np.array([1,2,3,4,5,6,7]).reshape(-1, 1)
Y_train = np.array([12,17,22,27,32,37,42])
# 모델 생성 - 계층
inputs = tf.keras.layers.Input(shape=(1,))
outputs = tf.keras.layers.Dense(units=1)(inputs)
model = tf.keras.models.Model(inputs, outputs)
# 모델 설정
model.compile(
    loss = tf.keras.losses.MeanSquaredError(),
    optimizer = tf.keras.optimizers.SGD()
    )
# 데이터 넣고 학습시키기
model.fit(X_train, Y_train, epochs = 1000, verbose = 0)
# 예측
model.predict(np.array([[9]]))

결과 :
array([[52.014755]], dtype=float32)

이후 머신러닝에 대해 더 배우다가 쉽고 재밌게 학습하기 위하여 팀별로 모여서 머신러닝 활용 분야 리서치를 찾고 발표하는 시간을 가졌다. 아래는 우리 팀이 발표한 내용이다.

오늘은 상관분석을 통해 변수 간 관계를 수치적으로 파악하는 방법을 배웠다. 이어서 인공지능의 전반적인 흐름과 머신러닝, 딥러닝의 차이를 실습 코드를 통해 비교해보며 쉽게 이해할 수 있었다. 전통적인 함수 기반 코드에서부터 sklearn을 활용한 머신러닝 모델, 텐서플로우를 활용한 딥러닝 예측까지 실습하면서 AI의 구조적인 차이를 체감할 수 있었다. 특히 입력값이 같더라도 접근 방식에 따라 결과를 도출하는 과정이 달라지는 점이 흥미로웠고, 팀별 활동을 통해 머신러닝의 활용 분야도 넓게 탐색할 수 있어 유익한 하루였다.


<7/16 45일차>

Scikit-learn은 파이썬에서 가장 널리 사용되는 머신러닝 라이브러리 중 하나로, 데이터 과학과 인공지능 분야에서 필수적인 도구이다. scikit-learn에서는 머신러닝 모델이 클래스 객체로 정의되며, 작업을 수행하기 위해 클래스의 메서드(fit, predict, score등)를 호출한다. 사이킷런을 설치하고 파이썬으로 여러 분류 모델 알고리즘을 직접 코딩하는 수업이 진행되었다. 데이터는 iris와 titanic 데이터를 활용하였다.

의사결정나무(Decision Tree) (iris 데이터)

import graphviz
from sklearn.tree import export_graphviz

# iris 데이터 시각화
export_graphviz(dt_clf, out_file = 'tree.dot',
                class_names = load_iris.target_names)
with open('tree.dot') as f:
    dot_graph = f.read()
graphviz.Source(dot_graph)

결과 :

K-Fold 교차검증 (iris 데이터)

from sklearn.model_selection import KFold
KFold = KFold(n_splits=5)
iris_clf = DecisionTreeClassifier(random_state=156)
iris_data, iris_label

for train_index, test_index in KFold.split(iris_data):
    x_train, x_test = iris_data[train_index], iris_data[test_index]
    y_train, y_test = iris_label[train_index], iris_label[test_index]
    
    iris_clf.fit(x_train, y_train)
    pred = iris_clf.predict(x_test)
    accuracy = accuracy_score(y_test, pred)
    print(f'Accuracy: {accuracy:.4f}')

결과 :
Accuracy: 1.0000
Accuracy: 0.9667
Accuracy: 0.8667
Accuracy: 0.9333
Accuracy: 0.7333

GridSearchCV (타이타닉 데이터)

x = dt_titanic1[['Pclass', 'Age', 'SibSp']]
y = dt_titanic1['Survived']
x_train, x_test, y_train, y_test = train_test_split(x, y,
                                                    test_size=0.2,
                                                    random_state=156)
                                                    
from sklearn.model_selection import GridSearchCV
dt_clf_t = DecisionTreeClassifier(random_state=156)
params = {'max_depth':[1,2,3,4], 'min_samples_split':[2,3,4]}
grid_dtree_t = GridSearchCV(dt_clf_t, param_grid=params, cv=3, refit=True)
grid_dtree_t.fit(x_train, y_train)

b_model = grid_dtree_t.best_estimator_
pred = b_model.predict(x_test)
accuracy_score(y_test, pred)

결과 :
0.7150837988826816

LogisticRegression (iris 데이터)

from sklearn.linear_model import LogisticRegression
x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.2, random_state=11)

no_scaling_model = LogisticRegression(random_state=11)
no_scaling_model.fit(x_train, y_train)
pred_1 = no_scaling_model.predict(x_test)
acc_1 = accuracy_score(y_test, pred_1)

from sklearn.preprocessing import StandardScaler
scaler1 = StandardScaler()
scaler1.fit(x_train)
x_train_scaled = scaler.transform(x_train)
x_test_scaled = scaler.transform(x_test)

scaling_model = LogisticRegression(random_state=11)
scaling_model.fit(x_train_scaled, y_train)
pred_2 = scaling_model.predict(x_test_scaled)
acc_2 = accuracy_score(y_test, pred_2)
acc_2, acc_1

결과 :
(0.9, 0.9333333333333333)

전체적으로 iris 데이터는 정확도가 높고 titanic 데이터는 iris보다 정확도가 낮게 나온다. 그리고 교차검증은 5번 나누어서 진행했는데 정확도가 5번 다 다르게 나왔으므로 데이터 분할에 따라 모델 성능이 조금씩 다름을 알 수 있었다. 그리고 GridSearchCV는 iris로도 했지만 titanic으로도 했는데 먼저 'Age'컬럼에 null값을 평균값으로 채우는 전처리를 진행했었다. 테스트를 20%로 두고 하이퍼파리미터를 max_depth와 min_samples_split로 튜닝했을 때 가장 성능이 좋았던 모델의 정확도가 약71.5%로 생각보다 높지 않게 나왔다. 그리고 iris데이터로 LogisticRegression 시에는 스케일링 안할 때(acc_1)보다 했을 때(acc_2)가 더 낮게 나왔다. 다양한 알고리즘을 직접 코딩하며 머신러닝의 기본 흐름을 알 수 있었으며 하이퍼파라미터값을 바꿔가며 정확도가 조금씩 향상되는 것이 재밌기도 하였다.


<7/17 46일차>

오늘은 모델이 쓸만한 지 평가하는 지표에 대해서 배웠다.


혼동 행렬을 이용해 정확도(Accuracy), 정밀도(Precision), 재현율(Recall)등의 평가 지표 값을 사용할 수 있다.

  • 정확도 계산 : (TP+TN)/(TP+FN+FP+TN)
  • 정밀도 계산 : TP/(TP+FP)
  • 재현율 계산 : TP/(TP+FN)

titanic 데이터에서 성별(sex=1이면 생존X)에 따라 분류기를 먼저 만들고 이 모델의 성능을 평가해보는 실습을 해보았다.

def get_clf_eval(y_test, pred):
    confusion = confusion_matrix(y_test, pred)
    accuracy = accuracy_score(y_test, pred)
    precision = precision_score(y_test, pred)
    recall = recall_score(y_test, pred)

    print(confusion)
    print('*'*20)
    print(accuracy, precision, recall)
    
# 로지스틱회귀 분류모델 생성
from sklearn.linear_model import LogisticRegression

lr_clf = LogisticRegression(max_iter=2000)
lr_clf.fit(x_train, y_train)
pred = lr_clf.predict(x_test)

# 정확도, 정밀도, 재현율
get_clf_eval(y_test, pred)

결과 :
[[92 18][16 53]]
********************
0.8100558659217877 0.7464788732394366 0.7681159420289855
정확도는 약 81%, 정밀도는 약 75%, 재현율은 약 77%로 모델의 성능은 나쁘지 않다고 판단하였다.

이후에는 앙상블의 개념에 대해 배우고 코딩해보는 수업이었는데 수업 막바지에 진행하다가 코딩에 에러가 자꾸 떠서 아마 내일 아침에 다시 실습해볼 예정인 듯 하다.

앙상블 기법
앙상블 학습을 통한 분류는 여러 개의 분류를 생성하고 그 예측을 결합해서 정확한 최종 예측을 도출하는 기법이다. 앙상블 학습의 유형은 보팅, 배깅, 부스팅으로 나눌 수 있다.

보팅(Voting) : 보팅은 일반적으로 서로 다른 알고리즘을 가진 분류기를 결합하는 방식이다. 보팅에서 각 분류기의 샘플 데이터는 원본 데이터에서 중복을 허용하는 방식으로 랜덤하게 추출된다. 이를 통해 다양한 관점에서 데이터를 학습할 수 있으며, 전체 모델의 분산을 줄이는 효과가 있다.

배깅(Bagging) : 배깅은 각각의 분류기가 모두 같은 유형의 알고리즘 기반이며, 데이터 샘플링을 서로 다르게 가져가면서 학습을 수행해 보팅을 수행하는 것이다. 원본 데이터에서 중복을 허용하여 무작위로 샘플을 추출하는 부트스트랩(Bootstrap)방식을 활용하며, 이를 통해 각 분류기가 서로 다른 관점에서 데이터를 학습할 수 있게 된다.

부스팅(Boosting) : 부스팅 알고리즘은 여러 개의 약한 학습기(weak learner)를 순차적으로 학습-예측하면서 잘못 예측한 데이터에 가중치 부여를 통해 오류를 개선해 나가면서 학습하는 방식이다.

랜덤포레스트 모델을 만들고 하이퍼파라미터값을 바꿔가며 최적의 하이퍼파라미터 조합을 찾으며 정확도가 조금씩 올라가는 것을 볼 때마다 재밌었다. 각각 짝꿍들과 값을 바꿔가며 높은 값을 찾아보았는데 우리(내 짝꿍님)가 찾은 하이퍼파라미터 값은 'max_depth': 30, 'min_samples_split': 20 'random_state'=18를 적용했을 때 정확도가 약 93.2%정도가 나왔다.

혼동 행렬을 직접 보고 계산해 보니, 단순 정확도만 보는 게 아니라 다른 지표도 함께 봐야 함을 알게 되었다. 성별만으로 생존 여부를 예측하는 게 꽤 높은 정확도를 낸다는 점이 신기했다. 하이퍼파리미터값을 바꾸면서 정확도가 달라지는 것이 재밌었고 더 높은 정확도를 도출해 내기 위하여 최적의 값을 찾을 때가 오늘 수업 중 가장 기억에 남았던 것 같다. 코드 실습 중 오류가 나서 끝까지 못 한 것이 아쉽고, 더 복습이 필요하다는 생각이 들었다. 전체적으로 모델을 단순히 만드는 것보다 평가하고 개선하는 과정도 참 중요하다는 걸 깨달았다.


<7/18 47일차>

우선 앙상블 기법 중 보팅 분류기를 생성하고 학습시키고 예측하는 모델을 간단하게 만들어보았다. 그리고 어제 에러났던 코드들에 대해서는 강사님의 코드를 함께 보면서 파일 경로와 데이터 읽을 때 문제가 있었다는 것을 알 수 있었다. 그래서 특수문자 제거 코드와 파일 경로를 재정의해주었더니 정상적으로 코드가 돌아갔다.

오늘은 지금껏 배운 모델들과 평가 지표들을 활용해서 머신러닝 분류모델 보고서를 작성해보는 시간을 가졌다. 데이터는 총 3500건의 백화점 고객의 1년 간 구매 데이터이고 개발 모델은 성별 예측모델이다.

X 데이터는 백화점 고객의 1년 간 구매 데이터이고, y 데이터는 고객id별 성별 데이터이다. 우선 데이터에 결측값을 확인해보니 X데이터는 '환불금액' 컬럼에 2295개의 결측치가 있고, y데이터에는 결측치가 없었다.

우선 전처리부터 진행하였였다. 단순히 1부터의 숫자를 부여한 'cust_id'를 수치형 변수로 받아들이면, 결과가 왜곡될 수 있으니 드롭해버리고, '환불금액' 컬럼에 결측치를 0으로 채워 넣는다.

X_new = X.copy()
X_new = X_new.drop('cust_id', axis=1)
X_neww = X_new.fillna(0)

그리고 문자형 범주 데이터를 숫자로 바꾸기 위한 인코딩을 한 후에 각 데이터에 표준화를 적용하여 데이터의 스케일(크기 차이)을 맞춰주었다.

X_le = LabelEncoder()
col = ['주구매상품', '주구매지점']   # 범주형 데이터 컬럼 두 개

for i in col:
    if i in X_neww.columns:
        X_neww[i] = X_le.fit_transform(X_neww[i])   # 레이블 인코딩
        
scalar = StandardScaler()
scalar.fit(X_neww)
X_scaled = scalar.transform(X_neww)

이후에는 본격적으로 모델을 선언하고 학습시키는 과정이다. 적용한 분류 알고리즘은 로지스틱 회귀 / 의사결정나무 / 랜덤 포레스트 / XGBoost이다. X_neww데이터에서 모든 컬럼을 선언하였다.

x_1 = X_neww[['총구매액', '최대구매액','환불금액', '주구매상품', '주구매지점', '내점일수', '내점당구매건수', '주말방문비율', '구매주기']]
y_1 = y[['gender']]

X_train, X_test, y_train, y_test = train_test_split(x_1,
                                                    y_1,
                                                    test_size=0.2,
                                                    random_state=11)

이제 각 모델을 선언하고 fit한 후 predict까지 진행해준다. 최종적으로 학습된 모델들로 테스트 데이터에 대한 예측을 수행하였고 그 결과는 다음과 같다.

👇 모델 성능 요약

모델정확도정밀도재현율F1 Score
Logistic Regression62.7%48.2%10.3%0.170
Decision Tree56.1%41.5%42.9%0.422
Random Forest61.9%48.1%28.4%0.357
XGBoost62.9%50.2%39.8%0.444
  • 재현율을 제외한 모든 기준으로 XGBoost가 가장 우수한 결과가 나왔다.

다만, 전반적으로 모델의 성능이 높다고 보기는 어려우며, 하이퍼파라미터 튜닝, 변수 선택, 교차 검증 등 추가적인 개선 작업이 필요한 상태이다. 해당 보고서를 내 깃허브에 나름 잘 정리해두었으니 아래에서 확인할 수 있다.
📑 분류모델예측_성별예측하기

오늘은 그동안 배운 머신러닝 분류 모델들을 실전 보고서처럼 하나하나 비교해 보며 정리해 볼 수 있는 의미 있는 시간이었다. 다양한 모델들을 실험해 보면서, 단순히 정확도가 높다고 해서 좋은 모델이 아니라는 점을 다시금 체감했다. 특히 F1 Score나 재현율처럼 목적에 따라 더 중요하게 여겨야 할 평가 지표가 있다는 걸 익힐 수 있었다. 처음에는 단순하게 코드가 돌아가는 것에만 집중했다면, 이제는 결과를 해석하고 원인을 고민하게 되었다. XGBoost가 가장 높은 성능을 보였지만 그 차이는 크지 않았고, 튜닝의 중요성도 느껴졌다. 앞으로는 모델 선택뿐 아니라 하이퍼파라미터 최적화나 피처 엔지니어링 같은 후속 작업에도 관심을 가져야겠다는 생각이 들었다.

0개의 댓글