
공장 데이터의 맥락은 AI에게 전달할 수 없다 — 제조 엔지니어가 Claude와 협업하여 ML 컴피티션에서 동기 20명 중 1위를 차지한 이유
요약
제조 현장 경험을 가진 엔지니어가 Claude와 협업하여 마스킹된 데이터 기반의 ML 컴피티션에서 1위를 달성한 사례를 다룹니다. 데이터의 시계열 패턴을 분석하여 가설을 세우고, AI를 활용해 모델링을 수행하는 효율적인 협업 방식을 보여줍니다.
핵심 포인트
- 도메인 지식을 활용한 데이터 패턴(변동성 차이) 분석의 중요성
- Claude를 활용한 시계열 교차 검증(TimeSeriesSplit) 및 모델링 자동화
- 현장 경험 기반의 가설(sample_weight 적용)이 모델 성능을 결정
- AI 도구와 인간의 판단력을 결합한 효율적인 워크플로우
「특징량(Feature) 이름이 전부 마스킹되어 있다면, 대체 어떻게 하라는 건가요.」
연수 최종 과제인 머신러닝 (Machine Learning) 컴피티션이 시작된 아침. 옆자리의 동기가 그렇게 한탄했다. 100종류가 넘는 특징량이 모두 feature_001 형식. 목적 변수는 시계열로 변화하는 수치. 평가 지표는 RMSE. 사용할 수 있는 시간은 반나절.
동기의 그 한마디를 들으며, 나는 다른 생각을 하고 있었다. ——이 데이터, 우리 공장에서 몇 번이고 봐왔던 패턴과 닮았다고.
반나절 후, 동기 20명 중 1위를 차지했다. 코드는 Claude가 거의 다 썼다. 내가 한 것은 현장 감각에서 비롯된 세 가지 판단뿐이다. 그럼에도 결과를 가른 것은 바로 그 세 가지였다.
대상 독자: 제조 현장의 데이터를 일상적으로 다루고 있지만, 머신러닝 (Machine Learning)은 전문가의 영역이라고 생각하는 엔지니어. AI 도구를 사용하기 시작했지만, 어디까지 맡겨도 될지 모르는 사람.
이 데이터, 현장에서 본 적이 있다
데이터의 구조는 다음과 같았다.
- 특징량 (Feature): 100종류 초과. 이름은 모두 마스킹 (
feature_001형식) - 목적 변수 (Target Variable): 시계열로 변화하는 연속값
- 태스크 (Task): 학습 데이터 뒤에 이어지는 목적 변수의 예측 (회귀 (Regression))
- 평가 지표 (Metric): RMSE (작을수록 좋음)
- 리더보드 (Leaderboard): 과거 연수 수강생 및 강사진을 포함한 누적 300건에 가까운 실적
목적 변수를 시계열로 플롯(Plot)했을 때, 한눈에 알 수 있었다. 전반부 구간의 표준 편차 (Standard Deviation)가 후반부의 약 2배에 달한다. 무언가 변하고 있다.
제조 현장에서 말하자면, 이것은 익숙한 구조다. 설비 교체 후의 가동 초기 기간, 정기 보수(定修) 직후의 불안정기, 원재료 로트(Lot) 교체 직후의 품질 편차 —— 조건이 변한 기간의 데이터는 안정기의 예측에 사용하면 정밀도를 떨어뜨린다. 컬럼(Column) 이름이 마스킹되어 있어도, 시계열 그래프가 보여주는 패턴은 현장에서 몇 번이고 봐왔던 것과 겹쳤다.
「이것은 전반부가 버려야 할 기간이고, 후반부가 진짜다」 —— 컬럼 이름이 없어도 그렇게 가설이 세워진 순간, 반나절이라도 승부를 볼 수 있겠다고 느꼈다.
5단계의 브레인스토밍 로그
세션 중, Claude와의 협업을 COMPETITION_LOG.md라는 파일에 실시간으로 기록하며 진행했다. 나중에 되돌아보니 5개의 페이즈 (Phase)로 나뉘어 있었다.
| 페이즈 | 주요 실시자 | 내용 | 성과 |
|---|---|---|---|
| 1 | Claude | TimeSeriesSplit 채택 | 시계열 순서 보존 |
...
페이즈 1: 시계열 CV의 채택과 후반부 중시 판단
첫 번째 프롬프트 (Prompt)는 심플했다.
「TimeSeries의 구조를 지키면서, 여러 회귀 모델을 비교하여 최적의 수법을 탐색해 줘. 하이퍼파라미터 (Hyperparameter)도 망라적으로 최적화해 줘.」
Claude는 TimeSeriesSplit을 채택했다. 시계열 구조를 보존하기 위한 타당한 선택이었으며, 이는 Claude의 지식에서 나온 판단이다.
병행하여 나는 다른 일을 하고 있었다. 목적 변수를 시계열로 플롯하여 전반부와 후반부의 분포 차이를 관찰했다. 전반부는 변동성이 크고, 후반부는 안정적이다. 「이것은 sample_weight를 사용하여 후반부를 중시시켜야 한다」 —— Y 분포를 관찰한 결과에서 나온 나의 판단이었다. 이 방침이 후속되는 모든 개량의 축이 된다.
그리고 다음에 Claude가 제안해 왔다.
「특징량 엔지니어링 (Feature Engineering)으로서, 시계열의 lag 특징량을 추가하는 것을 권장합니다.」
추가해서 시도해 보았다. CV RMSE는 개선되지 않았다. 마스킹된 특징량 중 어떤 것이 시계열적인 의미를 갖는지 Claude는 알 수 없다. 「이것은 빗나갔다」라고 판단하여 제안을 버린 것이 오전 11시쯤이었다. 남은 시간은 5시간 반. Claude가 틀린 첫 번째 사례였다.
페이즈 2~3: 비교는 Claude, 선정은 나
여기서부터는 Claude에게 수법 비교를 맡겼다. PLS, 랜덤 포레스트 (Random Forest), ElasticNet, LASSO를 병렬로 평가하게 했고, 결과가 돌아왔다.
PLS(n=3): CV RMSE 0.6378
RF(tuned): CV RMSE 0.5938
ElasticNet / LASSO : 0.59대
숫자만 보면 RF가 최상이다. 여기서 「어느 것을 채택할 것인가」를 내가 결정했다.
리더보드를 살펴보며 어떤 경향을 느꼈다. CV 정밀도가 극단적으로 높은 모델이 실제 운영(Production)에서는 무너지는 흔적이 있다. 300건에 가까운 제출 이력의 분포를 보면, 상위권은 화려한 모델이 아니라 수수하게 다듬어진 계열이 나열되어 있었다. 명문화된 근거는 아니지만, 리더보드의 분위기에서 느낀 경향이다.
의미를 알 수 없는 특징량(feature)이 100종류나 있는 상황에서, RF(Random Forest)와 같은 복잡한 모델은 과적합 (overfitting)될 여지가 너무 크다. "노이즈에 강하고 자동으로 변수를 걸러낼 수 있는 LASSO로 가자" —— 리더보드에서 느낀 경향과 직관을 결합한 나의 판단이었다. Claude는 기법의 특성을 연역적으로 설명해 주지만, "리더보드의 분위기를 읽는" 것은 할 수 없다.
페이즈 4: Claude가 alpha를 결정하다
LASSO 채택 결정 이후, Claude에게 alpha 탐색을 맡겼다. 0.001~1.0 범위를 전수 조사(grid search)하여, alpha=0.070821에서 CV RMSE가 최소가 된다는 결과를 받아냈다. 이 과정 동안 나는 코드 한 줄도 쓰지 않았다.
페이즈 5: 현장 감각으로 직접 제안하다
페이즈 5에서 이런 생각이 들었다. LASSO로 남은 변수를 그대로 예측에 사용하는 것보다, PLS(Partial Least Squares)로 한 단계 압축하는 것이 더 나을 것이라고. PLS는 제조 현장의 다변량 분석 (multivariate analysis)에서 자주 사용하던 기법이었다.
"LASSO에서 계수가 0이 된 특징량을 제외한 후, 남은 변수로 PLS 회귀를 수행하는 2단계 방식은 어떨까?"
"유효한 접근 방식입니다. LASSO가 특징량 선택기 (feature selector)로서 기능하고, PLS가 남은 변수 간의 다중공선성 (multicollinearity)을 흡수합니다. 구현하겠습니다."
이 답변으로부터 코드 생성까지는 불과 몇 분 걸리지 않았다.
from sklearn.linear_model import Lasso
from sklearn.cross_decomposition import PLSRegression
import numpy as np
...
CV RMSE 0.5355 —— 순위상 1위권이었다.
Claude가 두 번째로 실수한 것 —— 앙상블 (Ensemble)의 함정
CV RMSE 0.5355가 나왔을 때, 남은 시간은 2시간 미만이었다. Claude에게 다음 수를 물었다.
"정밀도를 더 높이려면 스태킹 (Stacking) 또는 블렌딩 (Blending)이 효과적입니다."
구현하여 제출했다. CV RMSE는 더 낮아졌다. 하지만 본선 스코어 표시를 보고 숨이 멎었다. 스코어는 오히려 0.02 떨어져 있었다.
남은 시간 90분. 순간 Claude에게 원인 분석을 시키고 계속 진행할지 고민했다.
"feature_321이 훈련 데이터 특유의 노이즈일 가능성이 있습니다. 앙상블로 인해 그 노이즈의 영향이 증폭되었을 수 있습니다."
냉정함을 유지할 수 있었던 것은, 제조 현장에서 "CV에서는 정밀도가 잘 나오는데 본선에서는 빗나가는" 패턴을 수없이 목격해 온 경험 덕분이었다. '이것은 과적합이다, 되돌아가라' —— 그렇게 판단하여 앙상블 전의 구성으로 되돌렸다.
시간이 촉박했기에 되돌릴 수 있었다. 여유가 있었다면 한 수 더 두다가 수렁에 빠졌을지도 모른다. Claude가 실수한 두 번째 사례가 바로 이것이었다.
제조 엔지니어의 감각이 통했던 3가지 판단
① sample_weight의 형태를 "지수"로 설정
페이즈 1에서 "후반부를 중시한다"고 정한 방침을 어떻게 구현할 것인가. 이것은 사소해 보이지만 효과적이었다.
처음에는 선형 (linear)으로 시도했다 (앞부분 0.5 → 뒷부분 1.5의 직선). 나쁘지는 않았지만, 핵심인 마지막 부분에 조금 더 강하게 가중치를 주고 싶었다. 지수 (exponential) 형태로 바꾸자 CV RMSE가 안정되었다.
sample_weights = np.exp(np.linspace(0, 1, n)) # 앞부분→뒷부분으로 지수적 증가
제조 현장으로 치면, 최근의 안정 가동 데이터를 예측의 중심에 두는 감각과 같다. 설비의 상태는 시간에 따라 변한다. 오래된 데이터와 새로운 데이터를 동일하게 취급하기보다, 최근 데이터에 가중치를 두는 것이 예측 정밀도가 높다 —— 현장에서 반복해 온 발상이 여기서도 형태를 바꾸어 작용했다.
sample_weights = np.exp(np.linspace(0, 1, n)) # 앞부분→뒷부분으로 지수적 증가
② Time 계열 특징량 제외
"전반부와 후반부의 분포가 다르다면, Time 계열 특징량이 훈련 기간 특유의 노이즈가 되고 있을 것이다" —— 그래프를 보며 그런 가설이 세워졌다. feature_321을 포함한 Time 계열 특징량을 학습에서 제외했다.
이것은 Claude가 제안한 것이 아니다. 제조 현장에서 "정기 보수(정수) 직후나 설비 교체 직후 기간의 데이터를 분석에서 제외하는" 판단을 반복해 온 감각이, 변수명이 마스킹된 데이터 위에서도 똑같이 작동한 것이다.
③ IQR에서 Winsorize로 전환
이상치 (outlier) 처리는 처음에 IQR (Interquartile Range) 방식으로 작성했다. Claude와의 대화를 통해 "Winsorize는 이상치를 0으로 만들지 않고 경계값으로 맞추기 때문에 정보 손실이 적다"는 것을 배우고 전환했다.
from scipy.stats.mstats import winsorize
X_train_w = X_train.apply(lambda col: winsorize(col, limits=[0.05, 0.05]))
이것은 Claude가 제안했고, 내가 채택했다. 지식을 보완한다는 의미에서 가장 솔직한 AI 활용이었다.
결과——리더보드(Leaderboard)의 숫자
최종 제출: submission_lasso_ab.csv
(Time 계열 제외 + winsorize + 지수 가중치(Exponential weight) + LASSO → PLS)
CV RMSE: 0.5355 → 최종 스코어 (RMSE): 0.5689
리더보드 화면에 내 스코어가 반영된 순간, 동기 연수생 20명 중 1위였다. 과거 수강생과 강사진을 포함한 누적 300건에 가까운 리더보드에서도 상위권에 올랐다.
옆자리의 동기가 그것을 보고, "이거 코드 거의 안 썼다는 게 정말인가요?"라며 웃었다. 사실이었다. Claude와의 대화 로그(Wall-hitting log)를 남겨두었기에, 내가 무엇을 쓰지 않았는지 한 줄 단위로 되돌아볼 수 있었다.
역할 분담을 정리하면
| 영역 | Claude | 나 |
|---|---|---|
| 기법의 병렬 비교 · 수치 계산 | 빠르고 망라적임 | — |
| ... |
Claude는 두 번 방향을 잘못 잡았다. 첫 번째는 Lag 특징량(Lag feature) 제안이었고, 두 번째는 후반부의 앙상블(Ensemble) 제안이었다. 두 가지 모두 "이 데이터가 무엇을 나타내는지"에 대한 맥락(Context)을 Claude가 가질 수 없었기 때문에 발생한 문제였다.
내가 판단한 것은 세 가지——가중치 부여, Time 계열 제외, 이상치 처리(Outlier processing)——뿐이었다. 그 외의 코드는 Claude가 작성했다. 그럼에도 불구하고 결과를 갈랐던 것은 바로 그 세 가지였다.
제조업 엔지니어에게——내일부터 시도할 수 있는 3단계
당신의 현장 데이터에는 컬럼명(Column name)이 있다. 온도, 압력, 택트 타임(Takt time), 설비 번호, 로트(Lot) 번호——그것이 무엇을 의미하는지 아는 것은 현장 엔지니어뿐이다. "이 시기의 데이터는 넣어서는 안 된다", "이 설비의 데이터는 특수하다"라는 판단은 컬럼명에 적혀 있지 않다.
이번 접근 방식을 현장 데이터에 적용한다면 다음과 같다.
- 시계열 그래프를 반드시 플롯(Plot)할 것: 목적 변수를 시계열로 나열하고, "분포가 변하는 구간"을 눈으로 찾는다. 그것이 보인다면 그곳에
sample_weight를 넣는다. - TimeSeriesSplit으로 CV를 나눌 것: 제조 데이터는 시계열 구조를 갖는 경우가 많다. 랜덤 CV(Random CV)는 미래 정보를 누설(Leakage)하므로 사용하지 않는다.
- LASSO로 특징량을 압축할 것: 센서가 많을수록 "효과가 없는 변수"가 많다. LASSO는 이를 자동으로 깎아준다.
코드는 Claude에게 쓰게 하면 된다. 당신이 가진 것은 그 "구간의 의미"를 읽는 능력이다.
맥락을 읽는 능력은 현장에 있었던 엔지니어가 가진 진짜 무기다. 코드를 짜지 못하는 것은 약점이 아니다. 오히려 코드를 쓰는 시간을 AI에게 넘길 수 있는 만큼, 판단에 집중할 수 있는 위치에 서 있는 것이다.
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn ML의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기