SKN_13th

[플레이데이터 SK네트웍스 Family AI 캠프 13기] 월간 회고: 4월

8000gam 2025. 4. 27. 21:31

지난 3월 24일 플레이데이터 SK네트웍스 Family AI 캠프에 입과한 이후 한 달이 지났다. 어제가 오늘같은 일상을 보내다 보니 한 달이라는 시간이 정말 쏜살같이 지나갔다. 이제 붐비는 출퇴근길을 오가는 것도 적응이 됐고, 수업 외의 시간을 보내는 방법도 점점 다양해지고 있다. 복습, 스터디, 생소한 내용 정리. 후술하겠지만, 이번주에는 머신러닝의 기술적인 부분을 배웠는데, 모델링 이면에 있는 모델의 등장 배경, 기본 개념, 구체적인 연산 과정 등을 간단하게나마 머릿속에 정리하는 시간을 수업 외의 시간에 보냈다. 이렇게 시간을 되도록 빈틈없이 쓰려고 하니, 한 달이 녹아내린 느낌이다. 점점 뜨거워지는 날씨도 시간이 흘러갔음을 알려준다. 슬슬 여름 옷들을 꺼내 놓아야겠다.

💻 What?

머신러닝을 구현하기 위해 필수로 알아야 할 numpy 라이브러리에 대해 배웠다. _numeric python_이라는 이름에서 알 수 있듯, 수치형 자료를 다루는 다차원 배열에 대한 연산을 도와주는 라이브러리이다. 배열이라고 하면 가장 먼저 파이썬 내부 자료 구조인 list를 떠올리게 되는데, list가 지원하지 않는 여러 연산 중 대부분을 numpy의 대표 자료구조인 numpy.ndarray가 수행할 수 있다.

list_ = [1, 2, 3, 4] * 2
print(list_) # [1, 2, 3, 4, 1, 2, 3, 4]가 출력된다.

import numpy as np
array_ = np.array([1, 2, 3, 4]) * 2
print(array_)    # [2 4 6 8]이 출력된다.

이후 본격적으로 인공지능 관련 수업을 시작했다. 인공지능의 탄생 배경부터, 머신 러닝을 위한 전처리, 모델링, 성능 평가에 대해 배웠다. 아직까지는 기초에 대해 배우고 있는 느낌이다. 아마 다음주 중에 머신 러닝(Whitebox)을 마치고, 딥러닝에 대해 배우기 시작할 수도 있겠다.

수업 외적으로는 코딩 테스트 스터디를 진행하고, 수업을 같이 듣는 두 분과 함께 대외 활동에 대한 이야기를 나눴다.

😮 So What?

진짜 시작..?

지난 4주간python기초, SQL기초, numpy, pandas, matplotlib 기초 등을 배우면서 초석을 다지는 시간을 가졌다. 유익하고 보람찼지만, 모든 일에서 기초를 탄탄히 하는 과정이 그렇듯 다소 지루한 감이 있었다. 그러나 본격적으로 인공지능 관련 강의가 시작된 이후로, 모니터 스크린에서 눈을 뗄 수가 없다. 몰랐던 것들 투성이라 너무나도 재미있다. 내가 빅데이터분석기사 자격증을 취득한 건 23년 겨울이다. 취득해놓고 쓸 일이 거의 없었던 지라, 이번 강의들이 가벼운 재활운동이 될 거라 생각했는데, 처음 들어보는 것들이 생각보다 많이 있어 재활은 커녕 본운동이 되었다. 아무래도 저번에는 너무 시험 위주의 공부를 했던 모양이다. 어찌되었든, 이런 익숙하면서 생소한 머신 러닝도 다음주면 끝난다. 단기 기억 위주로 공부했던 지난 날에 대한 반성은 뒤로 하고, 당장 내 것으로 만들기 위해 곱씹어 볼 필요가 있겠다. 이제부터 진짜 시작이다...!

  1. 머신러닝 기본 Workflow
  • 데이터 준비
  • EDA(결측치 및 이상치 처리, 데이터 분포 등 확인
  • 학습/평가 데이터 분리
  • 범주형 데이터 인코딩
  • 수치형 데이터 스케일링
  • 모델 정의, 학습 및 교차 검증
  • 예측 및 평가
# df 테이블에서 Binary Classification 간단하게 해본다고 가정

import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.preprocessing import LabelEncoder, StandardScaler
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, f1_score

# 자료형 별 column들 인덱스는 미리 알고 있는 상태라고 일단 가정해요.
# EDA도 이미 마친 뒤에요...!
categorical_indexer = [0, 1, 4, 5]
numerical_indexer = [2, 3, 6, 7, 8]

y = df['Target Column'].to_numpy()
X = df.drop(columns = 'Target_Column').to_numpy()

# 학습/평가 데이터 분리(필요하면 검증 데이터도 추가로 만들어요)
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify = y)

# 인코딩: 범주형
for idx in categorical_indexer:
    encoder = LabelEncoder()
    X_train[:, idx] = encoder.fit_transform(X_train[:, idx])
    X_test[:, idx] = encoder.transform(X_test[:, idx])

# 스케일링: 수치형
scaler = StandardScaler()
X_train[:, numerical_indexer] = scaler.fit_transform(X_train[:, numerical_indexer])
X_test[:, numerical_indexer] = scaler.transform(X_test[:, numerical_indexer])

# 모델링: 교차검증도 해봐요
model1 = DecisionTreeClassifier()
model2 = RandomForestClassifier()

params_1 = {
'max_depth' : [3, 4, 5, 6],
'min_samples_leaf' : [10, 20, 30]
}

params_2 = {
'max_depth' : [3, 4, 5],
'n_estimators' : [500, 1000, 1500]
}

cv1 = GridSearchCV(
model1,
params_1,
cv = 5,            # 5 Fold Cross Validation
n_jobs = -1,    # 가용 CPU core 모두 사용
verbose = 1        # 연산 과정을 출력
)

cv2 = GridSearchCV(
model2,
params_2,
cv = 5,
n_jobs = -1,
verbose = 1
)

cv1.fit(X_train, y_train)
cv2.fit(X_train, y_train)

best_tree = cv1.best_estimator_
best_forest = cv2.best_estimator_

# 성능평가: Accuracy & F1 Score
y_pred_tree = best_tree.predict(X_test)
y_pred_forest = best_forest.predict(X_test)

print('DecisionTree: Accuracy and F1 Score')
print(accuracy_score(y_test, y_pred_tree))
print(f1_score(y_test, y_pred_tree))

print('RandomForest: Accuracy and F1 Score')
print(accuracy_score(y_test, y_pred_forest))
print(f1_score(y_test, y_pred_forest))
  1. Confusion Matrix 복습
  Predicted 0 Predicted 1
Actual 0 True Negative (TN) False Positive (FP)
Actual 1 False Negative (FN) True Positive (TP)

 

$\text{Precision} = \frac{\text{TP}}{\text{TP} + \text{FP}}$

 

$\text{Recall} = \frac{\text{TP}}{\text{TP} + \text{FN}}$

 

$\text{F1 Score} = 2 \times \frac{\text{Precision} \times \text{Recall}}{\text{Precision} + \text{Recall}}$

 

$\text{FallOut} = \frac{\text{FP}}{\text{TN} + \text{FP}} = 1 - \text{Specificity}$

 

  • PrecisionRecall은 기본적으로 trade-off 관계에 있어요. Precision을 높이려 하면, 예측을 보수적으로 해서 정말 확실한 경우에만 Positive라고 판단하게 돼요. 이 경우 일부 Positive를 놓치게 되어 Recall이 감소할 수 있죠. 반대로 Recall을 높이려 하면, 가능한 많은 경우를 Positive로 예측하게 되어 일부 잘못된 Positive 예측(False Positive)이 늘어나 Precision이 감소할 수 있어요. 즉, Precision과 Recall은 서로 균형을 맞춰야 하며, 주어진 과제에 따라 어느 쪽을 더 중요하게 여길지 신중히 결정해야 해요. 이 균형을 평가하기 위해 쓰이는 지표가 두 지표의 Harmonic MeanF1 Score이기도 해요.
  • FallOut 은 FPR(False Positive Rate)로도 불리며, ROC Curve에서 y축인 TPR(True Positive Rate = Recall)과 함께 x축을 담당해요.

🎈 Now What?

다음주부터 딥러닝을 배울 지도 모른다는 점을 생각하면, 머신러닝을 제대로 소화시킬 시간은 지금 이 주말이 마지막일 지도 모르겠다. 일단 지금 당장 짐을 싸서 집 앞 카페로 나가야겠다. 이번주 내내 머신러닝을 배울 때 사용한 DecisionTreeRandomForest, 이들이 내부적으로 연산하는 알고리즘을 한 번 간단하게 공부해보고, 강의안에 있는 코드들을 여러 번 클론코딩하면서, ”이 하이퍼파라미터는 어떤 기능을 하니까, 값을 이렇게 바꾸면 어떤 식으로 변하는구나”와 같은 깨달음을 얻는 시간을 가져야겠다. 그게 다 끝난 뒤 저녁에는 내일 있을 코딩 테스트 스터디를 대비해 문제를 미리 풀어 놓아야겠다.

한 달 동안 시간이 쉼 없이 흘렀는데, 인공지능 강의가 본격적으로 시작하고 나니 드디어 한 달이 지났다는 실감이 나기 시작한다. 다음주부터는 수업 내용을 조금이라도 놓치면 돌이킬 수 없을 지도 모른다. 비전공자인 만큼 기합 제대로 넣고 다시 달려보도록 하자.

여담

토요일 오후, 갑자기 연희동으로 발을 옮겼다. '프로젝트 커피 크루즈'라는 커피 행사를 참관하기 위해서다. 여러 커피 로스터리, 베이커리&디저트, 소품 가게들이 참가해서 흥미롭게 한 바퀴 둘러봤고, 그 중 로스터리 두 군데를 중점적으로 관람했다. 그 둘의 강남의 '아임뮤트', 도쿄 세타가야의 'raw sugar roast'이다. 아임뮤트는 실패하지 않는 커피 맛과 자그마한 야외 테이블의 밝은 분위기 때문에 평소에도 자주 다녔던 곳이다. raw sugar roast는 본 행사에 참여한 유일한 해외 로스터리로, 이번 기회로 처음 접하게 되었다. 커피도 맛있었지만, 바다 건너 오신 직원분들이 내뿜는 에너지가 정말 활기찼다. 그렇게 두 부스에서 시음을 하고 원두를 산 뒤 바로 복습을 하러 다른 카페로 이동했다. 아무리 현대 사회가 바쁘게 돌아간다지만, 소소하게 취미 생활을 즐기는 것이 나에게는 생각보다 큰 재충전이 된다☕