FEATURES = ["요일구분_enc", "호선_enc", "역번호", "출발역_enc", "상하구분_enc", "시간슬롯"]
TARGET = "혼잡대"
So what: 모델이 학습할 피처와 예측할 타겟을 상수로 분리
So why: 피처 목록은 학습,예측, API 요청 모두에서 일치해야한다. 상수로 선언할 경우 나중에 피처 추가/제거 시에 한 곳만 수정하면 되기 때문에 main.py에서도 같은 입력 순서로 맞추는 기준이 된다.
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
So what: 전체 데이터의 80%로 학습하고 20%로 검증한다.
So what: random_state= 42의 고정은 실행할 때마다 같은 분할이 되게 해서 성능 수치를 재현할 수 있게 하기 위해서이다. 고정 안할 시 실행 때마다 MAE가 달라져서 모델 개선 여부 판단이 어렵다.
model = lgb.LGBMRegressor(
n_estimators=500,
learning_rate=0.05,
max_depth=8,
num_leaves=63,
min_child_samples=20,
random_state=42,
verbose=-1
)
So what: LightGBM 회귀 모델을 정의하는 구간이다.
So why: num_leaves=63은 max_depth=8일 때 최대 리프 수 2^8보다 작게 설정해서 Overfitting을 방지한다. learning_rate는 낮게 잡아서 early_stropping과 함께 천천히 수렴시킨다. min_child_samples=20은 리프 노드에 최소 20개 sample을 요구해서 노이즈에 Overfitting 되는 것을 막는다. verbose=-1은 LightGBM 내부 로그를 끄고 log_evaluation(100)으로 100라운드마다만 출력하게 했다.
이를 통해 모델 복잡도를 적절히 낮춤과 동시 학습 정교도와 데이터 신뢰도를 높이고 사용자의 편의성을 쾌적하게 해준다.
model.fit(
X_train, y_train,
eval_set=[(X_test, y_test)],
callbacks=[lgb.early_stopping(50), lgb.log_evaluation(100)]
)
So what: 검증셋 성능이 50라운드 동안 개선되지 않을 시 조기 종료를 한다.
So why: n_estimators=500으로 설정했지만 실제로는 500번 다 돌릴 필요가 없다. 검증셋 성능이 더 이상 오르지 않으면 멈추게 하여 Overfitting을 방지하고 학습 시간을 단축한다
importance = pd.DataFrame({
"feature": FEATURES,
"importance": model.feature_importances_
}).sort_values("importance", ascending=False)
So what: 각 피처가 모델 예측에 얼마나 기여했는지 출력한다
So why: 피처 중요도를 보면 불필요한 피처를 제거하거나 새 피처를 추가할 근거가 생기며 검토를 할 수 있게 했다.
joblib.dump(model, "models/congestion_model.pkl")
So what: 학습된 모델을 파일로 저장
So why: API 서버가 요청마다 모델을 새로 학습 시 수십 초가 걸리는 것을 방지하고 서버 시작 시 1번만 로드해서 재사용하는 구조로 만들었다.
'Project > SSAFY2학기 특화 PJT' 카테고리의 다른 글
| [BE, AI] 언어 선택 이유 (0) | 2026.03.13 |
|---|---|
| [BE] odsay.rs ( ODSAY API 연동 ) (0) | 2026.03.13 |
| [AI] main.py ( 서버 실행 ) (0) | 2026.03.13 |
| [AI] preprocess.py ( 데이터 전처리 ) (0) | 2026.03.13 |
| 팀 프로젝트 정리 사이트 (0) | 2026.03.03 |
