- 고층 랙 창고(High Rack Storage System)의 컨베이어 로그로 고장을 예측하는 문제였다. 6개 구간 × (이동거리·전압·전력) = 18개 속성이 0.046초, 0.151초, 0.206초처럼 불균일한 간격으로 기록되어 있었다.
- 아무 생각 없이 행 단위로 20%를 떼어 테스트셋으로 쓰자 의사결정나무 정확도가 0.978이 나왔다. 좋은 숫자가 아니라 이상한 숫자였다. 로그가 측정값이 바뀌지 않는 동안 같은 행을 반복 기록하기 때문에, 사실상 같은 행이 학습과 테스트 양쪽에 들어가 있었다.
- 정제 순서를 다시 짰다. 측정값이 같은 연속 행을 첫 행만 남기고 지우고, 그 사이의 시간 차로 구간별 속력 변수를 만들고, 정제 목록의 4단계로 ‘테스트셋은 학습에 쓰지 않은 다른 20%의 사이클로 잡는다’를 적어 넣었다. 남은 관측은 7,152건(학습 5,721 · 테스트 1,431), 피처는 24개였다. 정확도는 0.978에서 0.772로 내려왔고, 그 값이 진짜였다.
- 원인 파악이 목적이었으므로 블랙박스 대신 의사결정나무를 먼저 두고 분할 규칙을 읽었다. 루트가 상단 벨트의 이동거리(≤ -97.0)였고, 하위에서는 구간별 전력 임계값이 줄줄이 나왔다.
- 정확도 0.761 뒤에는 실패 재현율 0.11이 있었다. 실패 379건 중 42건만 잡았다. SMOTE로 1:1을 맞추자 인공신경망의 실패 재현율은 0.87로 올랐지만 정상 재현율이 0.20으로 무너졌다. 오버샘플링만으로는 풀 수 없는 문제로 진단하고 복합샘플링을 개선 방향으로 남겼다.
| 23,644행 | 원본 로그 | 필드 18개 · 행 단위 무작위 분할 시 학습 (18915, 18) / 테스트 (4729, 18) |
| 7,152건 | 정제 후 데이터 | 학습 (5721, 24) · 테스트 (1431, 24) · 피처 24개 = 측정값 18개 + 구간별 속력 6개 |
| 0.978 → 0.772 | 첫 시도 → 누수 제거 후 | 의사결정나무 테스트 정확도. 떨어진 쪽이 맞는 값이다 |
| 0.11 | 실패 클래스 재현율 | max_depth=4 트리 · 실패 379건 중 42건 · 정확도 0.761 · 정밀도 0.89 |
| 8,410행 | SMOTE 후 학습 데이터 | 정상 4,205 : 실패 4,205 (원래 4,205 : 1,516) · imbalanced-learn |
| 정확도 0.735 | 인공신경망 | Dense 10-4-1 sigmoid · SGD · MSE · 50 epoch · 실패 재현율 0.22 → 0.87, 정상 재현율 0.89 → 0.20 |
왜 컨베이어 벨트였는가
기계 고장에 따른 가동 중단과 원료 낭비가 매달 큰 비용을 낳고, 그 가장 큰 원인이 컨베이어 벨트라는 사례를 확인한 데서 출발했다.
비즈니스 이해 단계에서 확인한 근거는 두 가지였다. 하나는 기계 고장이 가동 중단과 원료 낭비로 이어져 매달 수백만 유로의 비용을 낳는다는 제조 현장 사례다. 발표자료는 이 문장을 영문 그대로 인용하고, 출처를 MathWorks의 자료 “… Based Health Monitoring and Predictive Maintenance for Manufacturing Processes with Machine Learning”으로 적어 두었다. 같은 학기에 제출한 수업 과제 보고서에는 같은 사례를 더 구체적으로 적어 두었다 — 포장·종이 제품의 세계적 제조업체 Mondi Gronau는 매달 기계 가동 중단으로 50,000유로의 손실을 보고 있었다.
다른 하나는 식품 공급 업체 등 세계적 제조업체에서 플랜트에 정기적으로 문제를 일으키는 원인 중 가장 큰 부분이 컨베이어 벨트였다는 사례다. 특정 구성 요소가 잘못 정렬되어 체인이 천천히 옆으로 움직이다 롤러에서 벗어나는 식의 문제가 반복된다고 했다.
여기서 마이닝 목표를 세웠다. 컨베이어 벨트의 각 부분에서 얻은 전압·전력·이동거리 데이터로 실패를 예측하고 불량 컨베이어를 탐색한다. 고장 난 뒤에 고치는 것이 아니라, 고장 날 컨베이어를 먼저 찾는 것이 목적이었다.
데이터는 Kaggle에서 찾았다. 여기서 중요한 것은 이 데이터가 원래 고장 예측용이 아니었다는 점이다. 에너지 최적화를 위한 재고시스템의 컨베이어벨트 데이터로, 전력에 대한 위치 정보를 시각화하는 문제의 데이터였다. 여기에 고장 라벨이 함께 들어 있었고, 시도해 보니 불량률 예측도 가능했다. 과제 보고서에는 이렇게 적었다 — 각 부분에서 잰 전력·전압·이동거리뿐이라 필드의 다양성이 부족한 데이터였음에도, 물리적인 데이터였기 때문에 결과가 나왔던 것 같다고.
여섯 개 컨베이어 구간은 어떻게 놓여 있는가
BLO가 ‘상단 벨트’인 이유, HL·HR이 ‘위아래로 이동하는’ 구간인 이유는 설비 배치도에 있다. 원본 발표자료는 이 배치도를 다섯 슬라이드에 반복해 실었다.
구간 이름은 BLO, BHL, BHR, BRU, HR, HL 여섯 개다. 이름만 나열하면 아무것도 읽히지 않지만, 배치도를 보면 뒤에 나오는 분할 규칙의 해석이 전부 여기에 걸려 있다는 것이 드러난다. 발표자료가 데이터 이해·모델 해석·특성 중요도·평가 단계마다 같은 그림을 반복해 실은 것도 그래서다.
| 구간 | 배치 | 운동 방향 | 규칙 해석에서 갖는 의미 |
|---|---|---|---|
| BLO | 좌측 상단, 독립된 수평 벨트 | 좌우 왕복 | 라인의 한쪽 끝에 홀로 놓인 구간이며, 발표자료가 ‘상단 컨베이어 벨트’로 부르는 곳. 이 구간의 이동거리가 크게 음수로 벗어나면 물체 낙하로 읽는다 — 루트 분할 ‘BLO 이동거리 ≤ -97.0’의 근거 |
| BHL · BHR | 중앙의 수평 벨트 두 개가 맞물려 좌우로 이어짐 | 좌우 왕복 | 물체를 좌우로 넘기는 이송 구간. 이 두 구간의 속력이 빠를 때 실패가 몰렸다 |
| HL · HR | 중앙 수평 벨트를 가로지르는 수직 벨트 두 개 | 상하 왕복 | 위아래로 물체를 올리고 내리는 구간. ‘위아래로 이동하는 전력이 적게 든다 = 이미 물체가 떨어졌다’는 해석이 성립하는 곳 |
| BRU | 우측 하단, 적재 블록과 맞닿은 수평 벨트 | 좌우 왕복 | 라인의 반대쪽 끝. 전압 임계(BRU 전압 ≤ 66.5)에서 하중을 역산하는 아이디어가 나온 구간 |
배치도에는 물체가 지나는 경로가 붉은 띠와 화살표로 겹쳐 그려져 있다. 좌측 상단의 BLO에서 중앙의 BHL·BHR을 지나 우측 하단의 BRU로 이어지는 띠이고, HL·HR이 그 중앙을 세로로 가로지른다. 여기서 읽어야 할 것은 방향보다 여섯 구간이 모두 양방향 화살표로 그려진 왕복 벨트라는 점이다. 컨베이어가 물체를 옮기고 원위치로 돌아오는 동작을 반복한다는 것, 즉 이 로그가 같은 공정의 반복이라는 사실이 뒤의 정제 판단에 그대로 쓰인다.

18개 속성, 초 단위 불균일 로그
6개 구간 × (이동거리 · 전압 · 전력) = 18개 속성이 0.046초, 0.151초, 0.206초처럼 불균일한 간격으로 기록되어 있었다.
원본 데이터는 시각과 실패 여부, 그리고 6개 구간별 세 종류의 측정값으로 구성된다. 각 구간마다 이동거리·전압·전력이 하나씩 있어 6 × 3 = 18개 속성이 된다.
| 구분 | 범위 | 의미 |
|---|---|---|
| 시각 | 한 열 | 초 단위 시각. 0, 0.046, 0.151, 0.206, 0.264, 0.317 … 처럼 간격이 일정하지 않다 |
| 라벨 | 한 열 | 실패 여부 (0 = 정상, 1 = 실패) |
| 이동거리 | 6개 구간 | 구간별 벨트 이동거리. 예: BLO −107, BHR −1268 |
| 전압 | 6개 구간 | 구간별 전압. 예: BRU 11 → 54 → 56 → 58 |
| 전력 | 6개 구간 | 구간별 전력 사용량. 예: HR 7168 → 6726 → 6232 |
원본 로그의 앞부분을 그대로 보면 문제가 눈에 보인다. 시각 0, 0.046, 0.151 세 행의 측정값이 완전히 동일하고, 0.206~0.317의 세 행도 서로 같다. 센서 값이 바뀔 때가 아니라 일정하지 않은 간격으로 계속 찍히는 로그이기 때문이다.
즉 이 로그의 한 줄은 서로 비교할 수 있는 관측이 아니다. 같은 값이 여러 줄 반복되고 기록 간격이 매번 다르므로, ‘한 줄 = 한 사건’으로 두면 시간 정보가 왜곡된 상태로 모델에 들어간다. 다음 절이 그 결과다.

첫 시도의 정확도 0.978 — 그리고 그것이 틀렸다는 것
행 단위로 20%를 떼어 테스트셋으로 쓰자 정확도 0.978이 나왔다. 이 프로젝트에서 가장 중요한 장면은 이 숫자를 믿지 않은 지점이다.
정제를 시작하기 전에, 로그를 읽어 그대로 모델에 넣어 보았다. 층화도 시드도 없이 행 단위로 무작위 20%를 떼어 테스트셋으로 삼은, 가장 평범한 분할이다.
결과는 이랬다.
- 학습 18,915행 · 테스트 4,729행 — 원본 로그는 23,644행이다
- 의사결정나무 테스트 데이터 집합 정확도 0.978
- 깊이 7로 제한한 트리 0.778
0.978은 좋은 숫자가 아니라 이상한 숫자였다. 발표자료에는 이 지점을 ‘문제 발생’이라는 제목의 슬라이드로 따로 남겨 두었다. 거기 적힌 메모가 그대로 진단 과정이다 — “나무 깊이가 너무 깊은가? → max_depth 바꿔도 1.000”, “훈련용 데이터에 중복데이터…”, “시간에 따라..”, “과적합…”.
같은 시기 수업 과제 보고서에는 이 과정을 문장으로 남겨 두었다. 처음 결과가 0.979로 너무 좋았고 의사결정나무의 깊이를 줄여도 변하지 않아서 교수님께 여쭈었고, “완전 다른 흐름의 데이터를 테스트용 데이터로” 잡으라는 조언을 받아 다시 나눴다는 내용이다.
원인은 앞 절에서 본 데이터의 형태였다. 로그는 측정값이 바뀌지 않는 동안 같은 행을 여러 번 반복해 기록한다. 이 상태에서 행을 무작위로 20% 떼면 거의 동일한 행이 학습과 테스트 양쪽에 들어간다. 모델은 예측한 것이 아니라 이미 본 행을 다시 본 것이다. 깊이를 줄여도 정확도가 떨어지지 않은 것도 같은 이유이고, 오히려 깊이 7 트리가 0.778로 내려간 것이 정직한 신호였다.
이 실패가 이후 정제의 순서를 전부 결정했다. 중복 제거가 1단계가 되고, 테스트셋을 사이클 단위로 나누는 것이 4단계가 된다. 최종 성능표에서 정확도가 0.978에서 0.772로 내려간 것은 모델이 나빠져서가 아니라 숫자가 정직해졌기 때문이다.


다섯 번의 정제 — 중복 행을 지우고, 테스트셋을 다시 골랐다
발표자료에 ‘5가지 데이터 정제 시도’로 적어 둔 목록이 그대로 앞 절의 실패에 대한 대응이다. 1단계가 중복 행 제거, 4단계가 ‘테스트를 다른 20% 사이클로 잡기’다.
정제는 다섯 단계로 진행했다. 아래 표의 ‘원본 표기’는 발표자료에 적힌 문구 그대로다.
| # | 원본 표기 | 실제로 한 일 |
|---|---|---|
| 1 | 중복 값 제거 후 시간간격 계산 | 측정값이 동일한 연속 행을 첫 행만 남기고 제거한 뒤, 남은 행 사이의 시간 차를 계산했다 |
| 2 | Weg(i+1)-Weg(i) / time(i+1)-time(i) | 이동거리의 차를 시간 차로 나눠 여섯 구간의 속력 변수를 만들었다. 1단계에서 계산한 시간 차가 분모다 |
| 3 | #div/0 제거 | 시간 차가 0인 행에서 발생한 0으로 나눈 값을 제거했다. 2단계가 만들어 낸 부작용을 되돌리는 단계다 |
| 4 | Cycle 중 test를 다른 20% 사이클로 잡기 | 테스트셋을 무작위 행 20%가 아니라 학습에 쓰지 않은 다른 사이클에서 가져오도록 바꿨다. 첫 시도의 누수에 대한 직접적인 대응이다 |
| 5 | 불균형된 데이터 정제 | 정상 : 실패 = 4,205 : 1,516의 불균형을 SMOTE로 보정했다 (다음 절) |
4단계가 이 프로젝트에서 가장 오래 고민한 부분이다. 앞 절 배치도에서 본 대로 여섯 구간이 모두 왕복 벨트이므로, 물체를 옮기고 원위치로 돌아오는 한 번의 왕복이 공정의 자연스러운 반복 단위가 된다. 한 번의 왕복 안에서 나온 행들은 서로 거의 같은 값을 갖는다. 그 앞부분이 학습에, 뒷부분이 테스트에 들어가면 사실상 같은 사건을 보고 맞히는 것이 되어 성능이 부풀려진다 — ‘완전 다른 흐름의 데이터를 테스트용으로 잡으라’는 조언은 이 지점을 가리킨 것이었다.
다만 남아 있는 자료로 확인되는 범위는 여기까지다. 사이클의 경계를 어떤 규칙으로 끊었는지는 발표자료에 남아 있지 않고, 남아 있는 모델링 기록에서 확인되는 분할은 정제된 데이터에 층화만 건 무작위 20% 분할이다. 지금 다시 보면 정확도를 0.978에서 끌어내린 힘의 대부분은 1단계, 즉 반복 기록된 중복 행을 지운 데서 나왔다고 보는 편이 정확하다.
최종 데이터는 피처 24개와 목표변수(실패 여부)로 정리되었다. 학습용 5,721건, 테스트용 1,431건이 되었으니 관측 수는 7,152건이다. 23,644행이던 로그가 중복 제거와 0으로 나눈 행 제거를 거쳐 3분의 1 아래로 줄어든 것이다.
피처 24개의 정체는 트리의 분할 변수 목록에서 역으로 읽힌다. 뒤에 나오는 두 트리에 등장하는 변수는 예외 없이 원래의 측정값 18개(구간별 이동거리·전압·전력) 아니면 2단계에서 만든 구간별 속력 여섯 개다. 18 + 6 = 24. 즉 관측 한 건은 여전히 정제된 로그 한 줄이고, 사이클 단위로 집계한 통계량은 피처에 들어 있지 않다. 사이클은 관측의 단위가 아니라 학습·테스트를 가르는 단위로 쓰였다.


정상 4,205 : 실패 1,516 — 왜 SMOTE였는가
오버·언더·복합샘플링 세 계열을 비교한 뒤, 합성 데이터를 만드는 SMOTE를 골랐다.
사이클 단위로 만든 학습 데이터의 클래스 분포는 정상 4,205 : 실패 1,516(약 2.77 : 1)이었고, 테스트 데이터는 1,052 : 379였다. 실패가 정상보다 압도적으로 적어 보정이 필요했다.
후보를 세 계열로 정리해 비교했다.
| 계열 | 검토한 기법 |
|---|---|
| 오버샘플링 | Random oversampling, SMOTE, ADASYN |
| 언더샘플링 | Random undersampling, Cluster, Tomek links |
| 복합샘플링 | (오버 + 언더 조합) |
SMOTE는 오버샘플링 기법 중 합성 데이터를 생성하는 방식으로 가장 널리 쓰이는 모델이라는 점에서 골랐다. 동작 방식은 (1) 소수 데이터 중 특정 벡터(샘플)와 가장 가까운 이웃 사이의 차이를 계산하고, (2) 이 차이에 0과 1 사이의 난수를 곱하고, (3) 타겟 벡터에 더해 새 샘플을 만드는 것이다.
학습 데이터에만 적용했고, 결과는 학습 데이터가 8,410행으로 늘고 실패 4,205건, 정상 4,205건으로 정확히 1:1이 되었다.
여기서 한 가지를 미리 적어 둔다. 소수 클래스를 2,689건이나 합성해 넣는다는 것은 학습 데이터의 3분의 1이 실제 관측이 아니게 된다는 뜻이다. 이 선택의 대가는 뒤의 인공신경망 절에서 그대로 드러난다.


의사결정나무 — 블랙박스 대신 분할 규칙을 먼저 읽었다
목적이 ‘실패 예측’이 아니라 ‘불량 컨베이어 탐색’이었기 때문에, 해석 가능한 모델을 먼저 두고 규칙을 직접 읽었다.
모델링 단계에서 의사결정나무를 먼저 둔 이유는 발표자료에 그대로 적어 두었다 — “Blackbox가 아닌 의사결정나무를 이용해 원인을 알아내고자”. 24개 수치형 특성에 대해 크다·같다·작다 기준으로 최고의 분할을 찾는 방식이며, 규칙 그 자체가 현장에서 읽을 수 있는 산출물이 된다.
깊이를 달리한 트리 세 개를 학습시켰다.
| 트리 | 테스트 정확도 |
|---|---|
| 깊이 무제한 | 0.772 |
| 깊이 3 | 0.747 |
| 깊이 4 | 0.761 |
발표자료에 실린 트리는 깊이 4와 깊이 6 두 개이고, 아래 표는 그 그림의 노드를 그대로 옮긴 것이다. 노드의 값이 [정상, 실패] 순이라는 것은 루트의 [4205, 1516]이 SMOTE 이전 학습 데이터의 정상 : 실패 개수와, 리프의 다수 클래스가 혼동행렬의 지지 수(정상 1,052 · 실패 379)와 맞물리는 데서 확인했다.
깊이 4 트리의 상위 분할
| 깊이 | 분할 조건 | 관측 수 | [정상, 실패] | 다수 클래스 |
|---|---|---|---|---|
| 0 (루트) | BLO 이동거리 ≤ -97.0 | 5721 | [4205, 1516] | 정상 |
| 1 (True) | HL 이동거리 ≤ -29.5 | 2267 | [1913, 354] | 정상 |
| 1 (False) | BLO 속력 ≤ 135.976 | 3454 | [2292, 1162] | 정상 |
| 2 | HL 이동거리 ≤ -834.5 | 1359 | [1047, 312] | 정상 |
| 2 | BRU 전압 ≤ 66.5 | 908 | [866, 42] | 정상 |
| 2 | BLO 전압 ≤ 60.5 | 2805 | [1778, 1027] | 정상 |
| 2 | BLO 속력 ≤ 604.055 | 649 | [514, 135] | 정상 |
| 3 | HL 전압 ≤ 269.0 | 30 | [3, 27] | 실패 |
| 3 (리프) | — | 7 | [0, 7] | 실패 |
| 3 (리프) | — | 20 | [0, 20] | 실패 |
| 3 | HR 전력 ≤ 8208.0 | 34 | [10, 24] | 실패 |
규칙을 읽고 적은 해석
- 루트가 상단 벨트의 이동거리였다 — BLO 이동거리 ≤ -97.0이 첫 분할로 선택되었다. 이동거리가 크게 음수로 벗어나는 것은 물체가 떨어진 상황이며, 발표자료에는 이를 “당연한 실패”라고 적었다. 데이터가 실패를 설명하는 첫 축으로 낙하를 고른 셈이다.
- 전력이 적게 드는 것도 신호였다 — 위아래로 이동하는 데 전력이 적게 들면 이미 물체가 떨어진 상태일 가능성이 크다(HR 전력 ≤ 8208.0 하위가 실패 다수).
- 속력이 빠르면 실패 — BLO 속력이 반복해서 분할 변수로 등장했다. 속력이 빠를 경우 물체가 떨어질 가능성이 높다는 해석이다.
- 전압에서 하중을 역산할 수 있다 — 물체가 무거워 전압이 60.5~66.5보다 커지면 실패 가능성이 커졌다. 이 임계값을 뒤집으면 컨베이어 벨트 위에 놓을 수 있는 물체 하중을 계산해 볼 수도 있겠다는 후속 아이디어를 발표자료에 적어 두었다.
![깊이 4 의사결정나무. 루트 분할은 상단 벨트(BLO)의 이동거리 ≤ -97.0 (관측 5,721건, [정상 4,205 · 실패 1,516])이며, 파란색 노드가 실패 다수, 주황색 노드가 정상 다수다.](assets/mfg/hrss/s16_016.png)
깊이를 2부터 15까지 훑어 과적합 지점을 찾았다
깊이 세 개만 보고 고른 것이 아니다. 깊이 2부터 15까지 열네 쌍의 훈련/테스트 정확도를 출력해 놓고 골랐다.
첫 시도에서 깊이를 의심했다가 헛짚은 경험이 있었기 때문에, 이번에는 깊이를 바꿔 가며 훈련 세트와 테스트 세트 정확도를 함께 출력했다.
| 깊이 | 훈련 세트 정확도 | 테스트 세트 정확도 | 훈련 − 테스트 |
|---|---|---|---|
| 2 | 0.735 | 0.735 | 0.000 |
| 3 | 0.746 | 0.747 | −0.001 |
| 4 | 0.755 | 0.760 | −0.005 |
| 5 | 0.763 | 0.761 | +0.002 |
| 6 | 0.770 | 0.767 | +0.003 |
| 7 | 0.774 | 0.766 | +0.008 |
| 8 | 0.782 | 0.769 | +0.013 |
| 9 | 0.784 | 0.771 | +0.013 |
| 10 | 0.788 | 0.771 | +0.017 |
| 11 | 0.797 | 0.778 | +0.019 |
| 12 | 0.811 | 0.756 | +0.055 |
| 13 | 0.829 | 0.772 | +0.057 |
| 14 | 0.844 | 0.781 | +0.063 |
| 15 | 0.859 | 0.779 | +0.080 |
읽히는 것은 세 가지다.
- 깊이 2는 다수 클래스 비율과 같다 — 훈련·테스트 모두 0.735이며, 테스트셋의 정상 비율(1052/1431 ≈ 0.735)과 정확히 같다. 깊이 2 트리는 사실상 전부 ‘정상’으로 답하는 모델이고, 뒤에 나오는 인공신경망이 수렴한 지점과 같은 값이다.
- 깊이 11까지는 훈련·테스트가 함께 오른다 — 격차가 0.019 이내로 유지된다. 깊이 12부터 훈련 정확도가 0.811 → 0.859로 뛰는 동안 테스트는 0.756~0.781에서 정체하며 격차가 0.055 → 0.080으로 벌어진다. 과적합이 시작되는 경계가 눈에 보인다.
- 테스트 최고점은 깊이 14(0.781)이고 깊이 무제한 트리의 0.772보다도 높다. 그런데도 규칙 해석용으로는 깊이 4와 6을 골랐다. 목적이 정확도가 아니라 ‘현장에서 읽을 수 있는 규칙’이었기 때문에, 성능 최고점 대신 사람이 따라 읽을 수 있는 깊이를 택했다.
첫 시도와 비교하면 차이가 분명하다. 누수가 있을 때는 깊이를 바꿔도 정확도가 거의 움직이지 않았고, 누수를 없앤 뒤에는 깊이에 따라 훈련·테스트가 갈라지는 정상적인 곡선이 나왔다.

깊이 6 트리 — 구간별 전력 임계값이 줄줄이 나왔다
상위 세 층은 깊이 4 트리와 같고, 그 아래에서 새로 드러난 규칙이 ‘전력으로 실패를 예측할 수 있다’는 해석을 뒷받침한다.
깊이 6 트리는 좌·우로 나뉘어 두 장의 캡처로 실려 있다. 노드의 값은 [정상, 실패] 순이며, 그림에 찍힌 클래스 표기는 sucess(정상) / fail(실패)이다.
좌측 서브트리 (BLO 이동거리 ≤ -97.0, True)
| 깊이 | 분할 조건 | 관측 수 | [정상, 실패] | 다수 클래스 |
|---|---|---|---|---|
| 1 | HL 이동거리 ≤ -29.5 | 2267 | [1913, 354] | 정상 |
| 2 | HL 이동거리 ≤ -834.5 | 1359 | [1047, 312] | 정상 |
| 3 | HL 전압 ≤ 269.0 | 30 | [3, 27] | 실패 |
| 4 | BHL 속력 ≤ 868.482 | 29 | [2, 27] | 실패 |
| 5 (리프) | — | 25 | [0, 25] | 실패 (순수 노드) |
| 5 | BRU 이동거리 ≤ 573.5 | 4 | [2, 2] | 정상 (동점) |
| 3 | BHL 속력 ≤ 691.866 | 1329 | [1044, 285] | 정상 |
| 4 | HR 이동거리 ≤ -28.5 | 1285 | [1032, 253] | 정상 |
| 5 | BHL 속력 ≤ -0.952 | 1267 | [1031, 236] | 정상 |
| 4 | BHL 전력 ≤ 20865.5 | 44 | [12, 32] | 실패 |
| 5 | HL 속력 ≤ 4098.038 | 18 | [1, 17] | 실패 |
| 5 | BHL 전압 ≤ 56.5 | 10 | [7, 3] | 정상 |
| 5 | BHL 이동거리 ≤ 450.0 | 34 | [5, 29] | 실패 |
| 3 | HL 이동거리 ≤ 20.0 | 901 | [866, 35] | 정상 |
| 4 | BLO 이동거리 ≤ -117.5 | 895 | [866, 29] | 정상 |
| 5 | BHR 이동거리 ≤ 387.0 | 891 | [865, 26] | 정상 |
| 5 | HR 전력 ≤ 18865.5 | 4 | [1, 3] | 실패 |
우측 서브트리 (BLO 이동거리 ≤ -97.0, False)
| 깊이 | 분할 조건 | 관측 수 | [정상, 실패] | 다수 클래스 |
|---|---|---|---|---|
| 1 | BLO 속력 ≤ 135.976 | 3454 | [2292, 1162] | 정상 |
| 2 | BLO 전압 ≤ 60.5 | 2805 | [1778, 1027] | 정상 |
| 3 | BHR 속력 ≤ 28.751 | 2785 | [1778, 1007] | 정상 |
| 4 | BLO 전력 ≤ 28040.5 | 2762 | [1776, 986] | 정상 |
| 5 | HL 이동거리 ≤ -916.5 | 2746 | [1776, 970] | 정상 |
| 6 (리프) | — | 2730 | [1774, 956] | 정상 |
| 4 | BLO 속력 ≤ 7.915 | 23 | [2, 21] | 실패 |
| 5 | BHR 전압 ≤ 26.0 | 6 | [2, 4] | 실패 |
| 3 (리프) | — | 20 | [0, 20] | 실패 (순수 노드) |
| 2 | BLO 속력 ≤ 604.055 | 649 | [514, 135] | 정상 |
| 3 | BLO 전력 ≤ 30588.0 | 615 | [504, 111] | 정상 |
| 4 | BHL 이동거리 ≤ 956.0 | 610 | [504, 106] | 정상 |
| 5 | BHL 전력 ≤ 25687.0 | 605 | [504, 101] | 정상 |
| 6 (리프) | — | 598 | [503, 95] | 정상 |
| 3 | HR 전력 ≤ 8208.0 | 34 | [10, 24] | 실패 |
| 4 | HL 전력 ≤ 24855.0 | 26 | [10, 16] | 실패 |
| 5 | HR 이동거리 ≤ -852.5 | 17 | [9, 8] | 정상 |
| 5 | BHR 이동거리 ≤ 477.5 | 9 | [1, 8] | 실패 |
| 4 (리프) | — | 8 | [0, 8] | 실패 (순수 노드) |
깊이를 6까지 늘려 얻은 것은 전력 임계값의 목록이다. 여섯 구간 중 다섯 구간의 전력이 하위 분할 변수로 등장했다 — BLO 전력 ≤ 28040.5 / ≤ 30588.0, BHL 전력 ≤ 20865.5 / ≤ 25687.0, HL 전력 ≤ 24855.0, HR 전력 ≤ 8208.0 / ≤ 18865.5. 실패 다수 노드가 걸리는 전력 임계가 구간마다 다르다는 점(BHL은 20,865 부근, BLO는 28,000~30,600, HR은 8,208)도 이 표에서만 읽힌다.
실패만 담긴 순수 리프도 나왔다. 위 두 표에 실린 것만 해도 [0, 25], [0, 20], [0, 8] 세 개로 합쳐 53건이고, 표에 옮기지 않은 더 아래쪽에도 관측 수가 한 자릿수에서 수십 건인 순수 실패 리프가 여럿 더 있다. 어느 쪽이든 규모는 같다 — 학습 데이터의 실패 1,516건에 견주면 이런 확실한 규칙 하나가 덮는 범위는 수십 건 단위다. 실패를 정확히 가려내는 규칙이 존재하기는 하지만 커버리지가 극히 작다는 뜻이고, 다음 절의 실패 재현율 0.11이 여기서 이미 예고된다.
![깊이 6 트리의 좌측 부분(우측 끝은 잘려 있다). HL 전압 ≤ 269.0 (관측 30건, [3, 27])에서 실패가 몰리고, 그 아래 관측 25건이 모두 실패인 순수 리프가 나온다.](assets/mfg/hrss/s17_018.png)

특성 중요도와 성능 — 재현율 0.11이 말해 준 것
정확도 0.761이라는 숫자 뒤에, 실패 379건 중 42건만 잡아낸 혼동행렬이 있었다.
트리에서 얻은 특성 중요도 24개 값은 다음과 같았다.
0.11055405 · 0.03184996 · 0.02250753 · 0.04054934 · 0.06268667 · 0.02668967 · 0.03792861 · 0.04712756 · 0.01629619 · 0.05128873 · 0.07229285 · 0.02599620 · 0.01668319 · 0.06915421 · 0.02054607 · 0.04292663 · 0.07168733 · 0.01421147 · 0.05866796 · 0.06033517 · 0.03939830 · 0.02698746 · 0.01654015 · 0.01709472
최댓값은 0.1106이고, 0.069 이상인 값이 세 개(0.07229, 0.07169, 0.06915) 더 있다. 중요도가 한 변수에 집중되지 않고 여러 구간에 퍼져 있다는 뜻이다. 이 중요도와 트리 규칙을 함께 읽어 상위를 세 묶음으로 정리했다.
| 순위 | 특성 묶음 | 해석 |
|---|---|---|
| 1 | BHL · BRU · HL · HR 구간의 전력 | 전력 사용량으로 실패를 예측할 수 있다 |
| 2 | 상단 컨베이어 벨트(BLO)의 이동거리 | 상단 벨트에서 물체가 떨어지는 원인으로 인한 실패가 많았을 것 |
| 3 | BLO · BHL 구간의 속력 | 이동 과정에서 속력이 빠를 경우 실패가 발생 |
깊이 4 트리의 분류 성능
혼동행렬은 [[1047, 5], [337, 42]]였다.
| precision | recall | f1-score | support | |
|---|---|---|---|---|
| class 0 (정상) | 0.76 | 1.00 | 0.86 | 1052 |
| class 1 (실패) | 0.89 | 0.11 | 0.20 | 379 |
| accuracy | 0.76 | 1431 | ||
| macro avg | 0.83 | 0.55 | 0.53 | 1431 |
| weighted avg | 0.79 | 0.76 | 0.68 | 1431 |
정확도는 0.7610062893081762였다. 그런데 실패 클래스의 재현율은 0.11에 불과했다 — 실패 379건 중 42건만 잡았고 337건을 놓쳤다. 반면 정밀도는 0.89로, 실패라고 예측한 47건 중 42건은 실제로 실패였다. 즉 이 모델은 확실할 때만 실패라고 말하는 보수적인 모델이다.
정확도 0.76이라는 숫자만 보면 쓸 만해 보인다. 하지만 다수 클래스 비율이 0.735이므로, 전부 ‘정상’이라고 답하는 모델과의 차이는 0.026뿐이다. 고장 예방이라는 목적에서 놓친 337건은 그냥 방치된 컨베이어다. 첫 시도의 0.978을 의심했던 것과 같은 이유로, 이 0.761도 그대로 믿을 수 없었다.
![특성 중요도 24개 값, 혼동행렬 [[1047, 5], [337, 42]], Accuracy 0.7610과 클래스별 precision/recall/f1. 실패(class 1) 재현율은 0.11이다.](assets/mfg/hrss/s18_020.png)
인공신경망 — 재현율 0.22 → 0.87, 대신 무너진 반대쪽
SMOTE를 적용하니 실패 재현율은 0.22에서 0.87로 올랐지만 정상 재현율이 0.89에서 0.20으로 떨어졌다. 오버샘플링만으로는 풀리지 않는 문제였다.
의사결정나무로 원인을 읽은 뒤, 예측 성능 쪽을 보기 위해 인공신경망을 병행했다. 입력은 피처 24개이고, 시그모이드 은닉층 두 개(노드 10개와 4개)와 시그모이드 출력층 하나를 쌓았다. 손실은 평균제곱오차, 최적화는 확률적 경사하강법, 학습은 50 epoch였다. 테스트 정확도는 0.735였다.
epoch별 정확도 곡선은 훈련·검증 정확도가 1 epoch 만에 약 0.735로 올라간 뒤 50 epoch까지 완전히 평평했다. 다수 클래스 비율(1052/1431 ≈ 0.735), 그리고 깊이 2 의사결정나무의 정확도와 정확히 같은 값이다. 이 모델은 사실상 전부 ‘정상’으로 예측하는 상태에 수렴해 있었다. 손실을 평균제곱오차로 두고 시그모이드 세 층을 확률적 경사하강법으로 학습시킨 구성에서, 불균형 데이터의 가장 쉬운 국소해로 곧장 들어간 것으로 읽는다.
불균형 정제 전후 분류 리포트
| 시점 | 클래스 | precision | recall | f1-score | support |
|---|---|---|---|---|---|
| 정제 전 | 0.0 (정상) | 0.58 | 0.89 | 0.70 | 464 |
| 정제 전 | 1.0 (실패) | 0.61 | 0.22 | 0.32 | 379 |
| 정제 전 | accuracy | 0.59 | 843 | ||
| SMOTE 후 | 0.0 (정상) | 0.58 | 0.20 | 0.30 | 1023 |
| SMOTE 후 | 1.0 (실패) | 0.55 | 0.87 | 0.67 | 1134 |
| SMOTE 후 | accuracy | 0.55 | 2157 |
결과를 그대로 읽으면 이렇다. 정제 전에는 실패 재현율 0.22, 정상 재현율 0.89로 다수 클래스 쪽으로 기울어 있었다. SMOTE를 적용한 뒤 실패 재현율은 0.87로 크게 올랐지만 정상 재현율이 0.20으로 떨어져 전체 정확도는 0.59에서 0.55로 오히려 낮아졌다. 한쪽을 살리면 반대쪽이 무너지는 전형적인 형태다.
지금 다시 보면 이 표에는 그때 짚지 못한 문제가 하나 더 있다. support 합이 정제 전 843, 정제 후 2,157로 서로 다르다. 테스트셋 자체가 바뀌었다는 뜻이고, 특히 정제 후의 2,157은 합성 데이터가 테스트셋에도 들어갔을 가능성을 보여 준다. 그렇다면 정제 후의 0.87은 실제 실패를 잡은 비율이 아니라 합성된 실패까지 포함해 잡은 비율이 된다. 두 열을 나란히 놓고 재현율만 비교하는 읽기는 그만큼 조심해야 한다.
당시 적은 진단은 실패와 정상 데이터 개수의 과도한 차이였다. 합성 데이터를 소수 클래스 쪽으로 4,205건까지 만들어 넣으면 결정 경계가 소수 클래스 쪽으로 과도하게 밀려난다는 판단이었고, 개선 방향으로 복합샘플링(오버샘플링과 언더샘플링을 함께 적용해 다수 클래스도 줄이는 방식)을 제시했다. 데이터 정제를 더 진행하고 새로운 특성 데이터를 추가하는 것도 후속 과제로 적었다.




평가와 전개 — 분석의 끝은 현장 조치였다
두 모델의 역할을 나눠 정리하고, 각각을 어떤 조치로 연결할지까지 적었다.
Evaluation·Development 단계에서 두 모델을 다음과 같이 정리했다.
| 모델 | 평가 | 전개(현장 조치) |
|---|---|---|
| 의사결정 나무 | 원인 파악을 가능하게 만들었다. 어느 부분에서 오류가 나는지 규칙으로 읽을 수 있다 | 오류 부위를 확인한 뒤 인원 배치를 조정하거나 해당 구간의 속력을 낮추는 방법 |
| 인공신경망 | 실패의 재현율이 높지 않았다. 실패와 정상 데이터 개수의 과도한 차이로 오버샘플링 시 문제가 생겼을 것으로 판단 | 복합샘플링으로 불균형 데이터 처리 개선, 데이터 정제 추가, 새로운 특성 데이터 추가 |
성능만 놓고 보면 두 모델 모두 실무에 바로 쓸 수 있는 수준이 아니다. 의사결정나무는 실패의 89%를 놓쳤고, 인공신경망은 다수 클래스 비율에 수렴하거나 반대로 정상을 대량 오분류했다. 그런데도 이 프로젝트에서 남길 만한 것이 있다면 규칙 쪽이다. '상단 벨트의 이동거리가 크게 음수', '위아래 이동 전력이 낮음', '속력이 빠름', '전압 60.5~66.5 초과'는 모델 없이도 현장에서 확인할 수 있는 조건이고, 인원 배치나 속력 조정 같은 조치로 바로 옮길 수 있다.
정확도 최고점(깊이 14, 0.781) 대신 깊이 4와 6의 트리를 골라 규칙을 읽은 것도 이 때문이다. 목적이 예측 자체가 아니라 불량 컨베이어의 탐색이었다.
이 프로젝트가 남긴 질문 — 설비를 모르면 데이터를 모을 수 없다
같은 학기의 다른 수업 과제 보고서에 이 분석을 사례로 넣으면서 적어 둔 세 가지.
최종 발표(6월)보다 앞선 5월 말, '인공지능과 기계학습' 수업의 과제 보고서에서 이 분석을 사례로 다뤘다. 첫 시도의 0.978을 의심하고 테스트셋을 다시 잡은 경위도 거기 문장으로 남아 있다. 함께 적어 둔 세 가지가 이 프로젝트의 실제 결론에 가깝다.
- 설비를 모르면 어떤 데이터를 모아야 하는지 알 수 없다 — "컨베이어 벨트의 고장 원리를 모르면 어떤 데이터를 모아야 예측할 수 있을 지 몰라서 데이터 수집에 어려움을 겪을 것"이라고 적었다. 이 프로젝트에서 쓴 데이터는 애초에 에너지 최적화용으로 모은 것이었고, 고장 예측에 필요한 변수를 골라 모은 것이 아니었다. 전압·전력·이동거리 세 종류뿐이라는 제약이 재현율 0.11의 배경 중 하나다.
- 블랙박스가 아니어야 수리에 쓸 수 있다 — 예측만 하는 모델은 고장을 알려 주지만, 원인을 함께 내놓는 모델은 수리에도 쓸 수 있다. 의사결정나무를 먼저 둔 선택을 과제 보고서에서도 같은 이유로 다시 정리했다.
- 센서를 늘리는 쪽이 다음 단계 — UCI의 트럭 APS(기압센서) 고장 예측 데이터셋을 예로 들며, 여러 종류의 센서 데이터와 시간 데이터를 함께 넣으면 고장 수리 비용을 낮출 수 있을 것이라고 적었다.
이 프로젝트를 시작한 이유도 거기 한 줄로 적어 두었다. 사전에 고장을 예측해 유지 보수하는 비용은, 고장으로 인한 시간·원료 낭비와 부품 교체 비용보다 훨씬 적다는 것이다.
기술적으로 이 프로젝트에서 가장 오래 남은 것은 모델이 아니라 0.978이라는 숫자였다. 성능이 지나치게 좋을 때 먼저 데이터 분할을 의심하는 습관은 여기서 생겼다.
여기서 배운 것
- 성능이 지나치게 좋으면 모델이 아니라 데이터 분할을 먼저 의심한다. 0.978은 실력이 아니라 누수였고, 중복 행을 지우고 다시 나눈 뒤의 0.772가 진짜 값이었다. 숫자가 내려간 것이 이 프로젝트의 진전이었다.
- 모델보다 데이터의 기록 방식을 먼저 본다. 이 로그는 측정값이 바뀌지 않는 동안 같은 행을 반복해 찍는다. 그 사실을 알기 전까지는 초 단위 로그에 어떤 모델을 얹어도 결과를 해석할 수 없었고, 알고 난 뒤에는 중복 제거와 테스트셋 분리 방식만 바꿔도 숫자가 제자리를 찾았다.
- 목적이 모델의 형태를 정한다. 원인 파악이 목적이었기에 블랙박스 대신 해석 가능한 의사결정나무를 먼저 두고 분할 규칙을 읽었고, 그 규칙이 인원 배치·속력 조정이라는 현장 조치로 이어졌다. 테스트 최고점(깊이 14)이 아니라 읽을 수 있는 깊이(4·6)를 골랐다.
- 지표 하나로 판단하지 않는다. 정확도 0.761 뒤에 실패 재현율 0.11이 있었고, 다수 클래스 비율 0.735와의 차이는 0.026뿐이었다. SMOTE로 재현율을 0.87까지 올리면 정상 재현율이 0.20으로 무너졌다.
- 데이터의 출처가 목적과 다르면 그 한계가 성능의 상한이 된다. 에너지 최적화용으로 모은 전압·전력·이동거리 세 종류만으로 고장을 예측했고, 그래서 '어떤 데이터를 모아야 하는가'가 다음 질문이 되었다.