
내 AI는 사기꾼이었다: 정확도 99%의 함정 (과적합)
머신러닝 모델 학습 시 겪은 과적합(Overfitting) 문제와 해결 과정을 공유합니다. 훈련 데이터 정확도 99%에 속아 배포했다가 참패한 경험과, Dropout, Regularization, Data Augmentation을 통해 '진짜 지능'을 만드는 방법을 설명합니다.

머신러닝 모델 학습 시 겪은 과적합(Overfitting) 문제와 해결 과정을 공유합니다. 훈련 데이터 정확도 99%에 속아 배포했다가 참패한 경험과, Dropout, Regularization, Data Augmentation을 통해 '진짜 지능'을 만드는 방법을 설명합니다.
ChatGPT는 질문에 답하지만, AI Agent는 스스로 계획하고 도구를 사용해 작업을 완료한다. 이 차이가 왜 중요한지 정리했다.

세 가지 AI 코딩 도구를 실제로 써보고 비교했다. 자동완성, 코드 생성, 리팩토링 각각 어디가 강한지 솔직한 후기.

고객 상담 챗봇이 엉뚱한 대답을 해서 식은땀 흘린 경험, 그리고 RAG(검색 증강 생성)를 도입해 '오픈북 테스트'를 치르게 한 과정을 공유합니다. 벡터 DB, 임베딩, 그리고 하이브리드 검색까지 파헤쳐봅니다.

RNN의 한계인 '치매 증상'을 극복하고, 구글이 'Attention Is All You Need' 논문으로 세상을 뒤집은 이야기. 쿼리(Query), 키(Key), 밸류(Value)의 완벽한 도서관 검색 비유와 멀티 헤드 어텐션, 그리고 Vision Transformer까지 연결합니다.

처음 딥러닝 모델을 학습시켰을 때의 전율을 잊지 못합니다. 회사의 작은 프로젝트로 '사내 카페 샌드위치 재고 예측 AI'를 만들기로 했습니다. 데이터는 지난 1년치 판매량이었고, 저는 간단한 LSTM 모델을 돌렸습니다.
학습(Train) 정확도가 무려 99.9%가 나왔습니다.
"와, 나 AI에 재능 있나? 구글이 나 데려가야겠는데?"
자신만만하게 당장 다음 주 재고 발주에 이 모델을 도입하기로 했습니다. 모델은 "다음 주 월요일에 샌드위치가 500개 팔릴 것"이라고 예측했습니다. 평소 판매량의 2배였지만, 저는 AI를 믿고 500개를 발주했습니다.
결과는? 월요일 판매량 230개. 270개의 샌드위치가 고스란히 남았고, 저는 그날 밤새도록 남은 샌드위치를 처리해야 했습니다. (팀원들에게 무료로 나누어 주며 사과했죠.)
충격이었습니다. 학습 데이터에서는 기가 막히게 맞추던 녀석이, 실제로는 왜 이렇게 멍청할까요? 학습 데이터에 있는 특정 이벤트(예: 작년 월요일에만 있었던 사내 행사)까지 패턴으로 외워버린 탓이었습니다. 이게 바로 그 유명한 과적합(Overfitting)이었습니다.
과적합을 가장 쉽게 이해하는 비유는 "시험 공부"입니다. 이 비유를 곱씹어보면서 제가 뭘 잘못했는지 깨달았습니다.
제 모델은 샌드위치 판매량의 '계절성'이나 '요일별 패턴'을 학습한 게 아니라, 학습 데이터에 있는 잡음(Noise)과 우연의 일치까지 법칙으로 착각하고 외워버린 겁니다.
과적합을 막으려면 모델이 "쉽게 외우지 못하도록" 방해해야 합니다. 너무 평온한 환경에서 공부하면 잡생각(Noise)까지 기억하니까요. 역설적이지만, 공부 환경을 어렵게 만들어야 실력이 늡니다. 저는 샌드위치 사건 이후 세 가지 방법으로 모델을 괴롭히기 시작했습니다.
"문제를 비틀어서 내기"입니다. 이미지 처리라면 사진을 좌우로 뒤집고, 확대하고, 회전시켜서 학습시킵니다. 모델이 "아, 고양이는 뒤집혀 있어도 고양이구나"라고 깨닫게 만듭니다.
제 샌드위치 데이터의 경우, 노이즈를 섞었습니다. 과거 데이터에 랜덤하게 ±5% 정도의 변동을 주어 데이터를 뻥튀기했습니다. 이렇게 하면 모델은 "정확한 숫자"를 외우는 게 불가능해지고, 전체적인 "추세"에 집중하게 됩니다.
data_augmentation = tf.keras.Sequential([
tf.keras.layers.RandomFlip("horizontal"),
tf.keras.layers.RandomRotation(0.1),
tf.keras.layers.RandomZoom(0.1),
])
"공부할 때 한쪽 눈 가리기"입니다. 이 개념이 정말 천재적이라고 생각했습니다. 학습할 때 일부 뉴런을 랜덤하게 꺼버립니다(0으로 만듭니다).
만약 모델이 특정 뉴런(예: '작년 행사 여부')에만 과도하게 의존하고 있었다면, 그 뉴런이 꺼졌을 때 멘붕에 빠지겠죠? Dropout은 모델이 특정 특징(Feature) 하나에 몰빵하지 않고, 여러 특징을 골고루 활용하여 판단하도록 강제합니다. 마치 농구 팀에서 에이스 한 명에게만 패스하지 않고, 모든 선수가 골고루 득점하도록 훈련시키는 것과 같습니다.
model = tf.keras.Sequential([
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dropout(0.5), // 50%의 뉴런을 랜덤하게 끔
tf.keras.layers.Dense(10)
])
"밤새지 말고 적당히 자기"입니다. 우리는 보통 "공부는 많이 할수록 좋다(Epochs가 많을수록 좋다)"고 생각하지만, 딥러닝에서는 아닙니다. 너무 오래 학습하면 모델은 결국 데이터의 일반적인 패턴을 다 배우고 나서, 할 게 없으니 데이터의 노이즈까지 꾸역꾸역 외우기 시작합니다.
검증(Validation) 성능이 좋아지다가 정체되거나 떨어지기 시작하는 그 순간! 바로 그때가 학습을 멈춰야 할 타이밍입니다.
callback = tf.keras.callbacks.EarlyStopping(
monitor='val_loss',
patience=3 // 3번 참았다가 개선 안 되면 종료
)
L1(Lasso), L2(Ridge) 규제는 모델에게 "복잡해지면 벌점을 줄 거야"라고 경고하는 수학적 장치입니다. 저는 이 개념을 처음 접했을 때 "왜 모델이 똑똑해지는 걸 방해하지?"라고 생각했는데, 알고 보니 "똑똑한 체하면서 꼼수 부리는 걸" 막는 장치였습니다.
# L2 Regularization 적용 예시
layer = tf.keras.layers.Dense(
units=64,
activation='relu',
kernel_regularizer=tf.keras.regularizers.l2(0.01) # 0.01만큼 규제
)
규제는 아니지만, 간접적으로 과적합을 막아주는 강력한 기법입니다. 레이어마다 들어오는 데이터의 분포를 평균 0, 분산 1로 강제로 맞춰줍니다.
저는 샌드위치 예측 모델을 다시 짤 때, Dropout과 Batch Normalization을 적절히 섞어서 사용했습니다. 그랬더니 학습 정확도는 99%에서 92%로 떨어졌지만, 실제 예측 정확도는 50%에서 85%로 올랐습니다.
model.add(tf.keras.layers.Conv2D(32, 3))
model.add(tf.keras.layers.BatchNormalization()) # 여기!
model.add(tf.keras.layers.Activation('relu'))
이제 저는 학습 정확도가 99%가 나오면 오히려 의심부터 합니다. "또 외웠구나? 어디서 사기를 쳐?"
중요한 건 검증(Validation) 정확도와 테스트(Test) 정확도입니다. 훈련 점수와 테스트 점수의 차이(Gap)가 작아야 좋은 모델입니다.
우리의 목표는 '하드디스크(암기)'를 만드는 게 아니라 '지능(일반화)'을 만드는 것임을 잊지 마세요. 샌드위치 사건은 뼈아팠지만, 덕분에 저는 데이터 사이언스의 가장 중요한 교훈을 얻었습니다. "데이터를 믿되, 모델의 암기력은 믿지 마라."
I'll never forget the thrill of training my first deep learning model. I decided to build a 'Sandwich Inventory Prediction AI' for our office cafeteria as a small side project. The data was sales figures from the past year, and I ran a simple LSTM model.
The Training Accuracy hit a staggering 99.9%.
"Wow, do I have a hidden talent for AI? Google should be hiring me."
Confident, I reported to my mentor and suggested we use this model for next week's inventory orders immediately. The model predicted, "Next Monday, 500 sandwiches will be sold." It was double the usual sales, but trusting the AI, I ordered 500.
The result? Monday sales: 230 sandwiches. 270 sandwiches were left untouched, and I had to spend the entire night dealing with the leftovers (giving them out for free to friends while apologizing).
I was shocked. It predicted so perfectly on the training data, why was it so stupid in reality? It turned out the model had memorized even specific events in the training data (e.g., a local event nearby that only happened on a Monday last year) as a pattern. This was the famous Overfitting.
The best analogy for overfitting is "Exam Studying." Chewing on this analogy helped me realize what I did wrong.
My model didn't learn the 'seasonality' or 'daily patterns' of sandwich sales but mistook Noise and Coincidences in the training data for laws and memorized them.
To prevent overfitting, you must hinder the model from "memorizing easily." If the study environment is too peaceful, it will even remember the stray thoughts (Noise). Paradoxically, making the study environment harsher improves real skills. After the sandwich incident, I started bullying my model in three ways.
"Twisting the questions." In image processing, you flip, zoom, and rotate photos during training. You force the model to realize, "Ah, a cat is still a cat even if upside down."
For my sandwich data, I added Noise. I artificially inflated the data by adding random fluctuations of ±5% to past data. This makes it impossible for the model to memorize "exact numbers" and forces it to focus on general "trends."
data_augmentation = tf.keras.Sequential([
tf.keras.layers.RandomFlip("horizontal"),
tf.keras.layers.layers.RandomRotation(0.1),
tf.keras.layers.RandomZoom(0.1),
])
"Studying with one eye covered." I thought this concept was genius. You randomly turn off some neurons (set to 0) during training.
If the model was overly relying on a specific neuron (e.g., 'Last Year's Event Flag'), it would panic when that neuron is turned off. Dropout forces the model not to rely on a single feature but to use multiple features broadly for decision making. It's like training a basketball team so that they don't just pass to the ace player, but everyone scores.
model = tf.keras.Sequential([
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dropout(0.5), // Randomly drop 50% neurons
tf.keras.layers.Dense(10)
])
"Don't pull all-nighters." We usually think "the more you study, the better (more Epochs)," but not in Deep Learning. If you train too long, the model learns all the general patterns and then, having nothing else to do, starts memorizing the noise in the data.
The moment Validation performance stops improving or starts to drop! That is the exact time to stop training.
callback = tf.keras.callbacks.EarlyStopping(
monitor='val_loss',
patience=3 // Stop if no improvement for 3 epochs
)
L1 (Lasso) and L2 (Ridge) regularization warn the model: "I'll penalize you if you get too complex." When I first encountered this, I thought, "Why hinder the model from getting smarter?" But it turns out it's a mechanism to prevent "acting smart with cheap tricks."
# Applying L2 Regularization
layer = tf.keras.layers.Dense(
units=64,
activation='relu',
kernel_regularizer=tf.keras.regularizers.l2(0.01) # Penalty factor
)
While not strictly a regularization technique, it has a surprisingly strong regularization side-effect. It forces the input distribution of each layer to have Mean 0 and Variance 1.
When I rebuilt the sandwich prediction model, I mixed Dropout and Batch Normalization appropriately. The Training Accuracy dropped from 99% to 92%, but the actual Prediction Accuracy rose from 50% to 85%.
model.add(tf.keras.layers.Conv2D(32, 3))
model.add(tf.keras.layers.BatchNormalization()) # Magic happens here
model.add(tf.keras.layers.Activation('relu'))