Loading the catalog…
Loading the catalog…
이 글에서 다룰 주제 AIOps와 이상 탐지 : 평소와 다른 상태를 찾는 일은 장애 판단과 어떻게 다른가? Isolation Forest의 원리 : 무작위로 나누는데 어떻게 이상을 찾을까? 경로 길이와 점수 : 논문의 점수와 Python API는 왜 방향이 반대일까? 실습과 검증 : 시간순으로 데이터를 나누고 임계값을 정하는 방법 운영 적용 전 점검 : 계절성, 오탐, 데이터 누수, 드리프트 주요 단어 · AIOps · 비지도 학습 · 피처 · Isolation Tree · 경로 길이 · contamination · 임계값 · 데이터 누수 CPU 사용률이 80%를 넘으면 언제나 장애일까? 배치 작업이 예정된 시간이라면 정상일 수 있다. 반대로 CPU는 40%인데 지연시간과 오류율이 함께 높아지는 상황은 살펴볼 필요가 있다. AIOps에 이상 탐지를 도입하기 전에, 모델이 무엇을 보고 어떤 근거로 점수를 내는지부터 이해하려고 한다. 첫 번째 학습 대상으로 Isolation Forest 를 정리한다. 알고리즘의 직관에서 출발해 직접 실행한 합성 데이터 실습, 운영 적용 전에 확인할 조건까지 이어간다. 1. AIOps: 이상 탐지는 어느 단계에 있을까? AIOps (Artificial Intelligence for IT Operations) — 운영 데이터를 분석해 이상 징후 탐색, 사건의 연관 관계 파악, 원인 조사와 대응을 지원하는 접근이다. 이 글에서 생각하는 작업 흐름은 다음과 같다. 메트릭 수집 → 시간 구간별 피처 생성 → 이상 점수 계산 → 알림 정책 적용 → 로그·트레이스·변경 이력 조사 → 대응 판단 Isolation Forest는 주로 이상 점수 계산 을 맡는다. “평소의 데이터와 얼마나 다른가?”에 대한 단서를 주지만, “어느 코드가 원인인가?”나 “서비스를 재시작해야 하는가?”까지 답하지는 않는다. 이상(anomaly) 은 학습 데이터의 패턴에서 벗어나는 상태다. 장애(incident) 는 사용자나 서비스에 실제로 문제가 발생한 사건이다. 둘은 일치할 수도 있지만 같은 뜻은 아니다. 새 기능 출시로 정상 트래픽이 증가해도 이상으로 잡힐 수 있다. 반대로 학습 기간 내내 성능이 나빴다면 그 상태를 흔한 패턴으로 볼 수도 있다. 그래서 이상 점수에 서비스 영향과 운영 맥락을 함께 붙여야 한다. 관측 데이터의 차이가 낯설다면 메트릭·로그·트레이스·프로파일 정리 부터 읽으면 연결하기 쉽다. 2. 입력 데이터: 모델이 보는 것은 피처 벡터다 피처(feature) — 모델에 입력하는 개별 측정값이나 가공한 값이다. 윈도우(window) 는 값을 집계하는 시간 구간이다. 예를 들어 서비스 하나를 1분 단위로 관찰한다면 한 행을 다음처럼 만들 수 있다. 아래 값은 설명용이다. 시각 CPU 평균 (%) p95 지연시간 (ms) 오류율 (%) 요청 수 (건/분) 10:00 42 128 0.2 900 10:01 44 132 0.2 930 10:02 43 310 1.4 920 마지막 행의 CPU만 보면 앞선 두 행과 비슷하지만 지연시간과 오류율은 다르다. 여러 피처를 함께 넣으면 이런 조합을 검토할 수 있다. 단, 피처를 여러 개 넣었다는 이유만으로 모든 상관 관계 이상을 잘 탐지한다고 보장되지는 않는다. p95 는 요청 지연시간의 95번째 백분위수다. 95%의 요청이 대략 그 값 이내에 끝났다는 뜻이다. 서로 다른 인스턴스의 p95를 단순 평균한 값을 전체 서비스의 p95로 취급하면 안 된다. 방식 주로 묻는 질문 해석할 때 볼 점 고정 임계값 지연시간이 500ms를 넘었나? 명확한 서비스 기준을 표현하기 좋음 변화·계절성 기준 같은 시간대의 평소 수준과 다른가? 기준 기간과 계절 패턴이 필요 Isolation Forest 입력된 피처 공간에서 쉽게 고립되는 상태인가? 학습 데이터와 피처 설계에 따라 의미가 달라짐 고정 임계값과 모델 점수는 함께 사용할 수 있다. 사용자 영향이 큰 오류율 경보를 모델 도입만으로 없앨 이유는 없다. 3. Isolation Forest: 드문 점은 적은 분할로 고립된다 Isolation(고립) — 데이터를 반복해서 나누어 특정 관측값을 다른 관측값에서 분리하는 것이다. Isolation Tree 는 이 분할 과정을 담은 트리이고, Isolation Forest 는 여러 트리를 모은 모델이다. 한 트리를 만드는 핵심 과정은 단순하다. 학습 데이터에서 일부 행을 뽑는다. 현재 노드에서 피처 하나를 무작위로 고른다. 그 피처의 최솟값과 최댓값 사이에서 분할 기준을 무작위로 고른다. 기준보다 작은 쪽과 큰 쪽으로 나누고 반복한다. 관측값 하나만 남거나, 더 나눌 수 없거나, 깊이 제한에 도달하면 멈춘다. 그림 1. 직접 구성한 좌표와 분할 예시. 실제 학습 결과의 트리 경로가 아니라 고립의 의미를 설명하는 그림이다. 그림 왼쪽에서는 CPU < 75 라는 한 번의 질문으로 주황색 A를 다른 점에서 분리한다. 오른쪽의 B는 다음 순서를 거친다. 분할 B가 따라가는 가지 그 가지에 남는 점 수 1 CPU < 75 예 10 2 지연시간 < 150 예 5 3 CPU < 32 아니오 2 4 지연시간 < 135 예 1 경로 길이(path length) — 루트에서 해당 점이 도착하는 말단 노드까지 지나간 간선 수다. 위 예시에서 A는 1, B는 4다. 분할이 무작위이므로 어떤 트리에서는 평범한 점도 우연히 빨리 분리될 수 있다. 따라서 한 트리로 판단하지 않고 여러 트리의 경로 길이를 평균낸다. 이 방식의 직관은 수가 적고 다른 점들과 값이 다른 관측값은 평균적으로 더 빨리 고립되기 쉽다 는 것이다. 원 논문 여기서 “멀리 떨어짐”은 그림을 이해하기 위한 표현이다. 알고리즘이 점 사이의 거리를 직접 계산하거나 정답 라벨로 좋은 분할을 찾는 것은 아니다. 4. 이상 점수: 경로 길이를 비교 가능한 값으로 바꾸기 표본이 32개인 트리와 256개인 트리의 경로 길이는 그대로 비교하기 어렵다. 점이 많으면 분리하는 데 필요한 질문 수도 달라질 수 있기 때문이다. 논문은 평균 경로 길이를 기준값으로 정규화해 다음 점수를 정의한다. 여기서는 트리 하나에 사용한 표본 수를 $\psi$로 쓴다. $$ s(x,\psi)=2^{-\frac{E[h(x)]}{c(\psi)}} $$ $x$: 점수를 계산할 관측값 $h(x)$: 한 트리에서의 경로 길이 $E[h(x)]$: 여러 트리에서 얻은 경로 길이의 평균 $c(\psi)$: 표본 수에 따른 경로 길이의 정규화 기준 깊이 제한에 걸려 말단에 여러 점이 남아 있다면, 현재 깊이에 그 점들을 더 분리하는 데 필요한 평균 길이의 추정값을 더한다. 따라서 실제 계산은 무조건 말단 깊이만 세는 방식이 아니다. 정규화 기준은 다음과 같다. $$ c(\psi)=2H_{\psi-1}-\frac{2(\psi-1)}{\psi} $$ $H_m=1+\frac12+\cdots+\frac1m$은 조화수다. 구현에서는 로그를 이용한 근사를 사용할 수 있다. $c(1)=0$, $c(2)=1$로 처리하며, 여기서는 표본이 충분한 일반적인 경우를 생각한다. 논문의 점수 정의와 알고리즘 그림 2. 함수 $s=2^{-u}$를 직접 계산한 그래프. $u$는 정규화한 평균 경로 길이다. 평균 경로가 기준의 절반이면 점수는 약 0.707, 기준과 같으면 0.5, 기준의 1.5배이면 약 0.354다. 짧은 경로 → 큰 점수 → 더 이상한 후보 로 읽으면 된다. 0.707은 “장애 확률 70.7%”가 아니다. 이 점수에는 장애 여부에 대한 확률 보정이 없다. 0.5 역시 모든 서비스에 그대로 적용할 운영 경보 기준은 아니다. 5. scikit-learn: 점수 방향과 contamination을 구분하기 Python으로 옮기면 점수의 방향부터 주의해야 한다. 값 더 이상한 방향 용도 논문의 $s(x,\psi)$ 클수록 고립 정도를 점수로 표현 score_samples(X) 작을수록 원 점수의 반대 방향 -score_samples(X) 클수록 이 글에서 사용할 이상 점수 decision_function(X) 작을수록 기본 임계값을 반영한 판단값 predict(X) -1 은 이상, +1 은 정상 기본 임계값으로 이진 판정 관계는 decision_function = score_samples - offset_ 이다. decision_function < 0 이면 기본 판정에서 이상이다. 공식 API 문서 contamination 은 학습 데이터의 점수를 나눌 기준에 관여한다. 0.01 이면 학습 점수의 낮은 쪽 약 1%를 이상으로 구분하도록 경계를 정한다. 같은 점수가 여럿이면 비율이 정확히 맞지 않을 수 있다. contamination=0.01 은 다음을 뜻하지 않는다. 실제 장애 비율이 1%라는 사실을 모델이 알아냈다. 미래 데이터에서도 언제나 1%만 이상으로 표시된다. 이상한 행을 자동으로 삭제한 뒤 학습한다. "auto" 는 실제 이상 비율을 추정하는 옵션이 아니라 기본 점수 기준을 사용한다. 이때 offset_=-0.5 다. 다른 조건과 난수 시드가 같다면 contamination 변경은 트리 자체보다 판정 경계 를 바꾼다. 이번 실습에서도 원점수의 동일성을 확인했다. 주요 설정은 n_estimators (트리 수), max_samples (트리당 행 수), max_features (트리당 사용할 피처 수), random_state (재현용 난수 시드)다. max_samples="auto" 는 min(256, 학습 행 수) 를 사용한다. 이 값들이 모든 환경에서 최적이라는 뜻은 아니다. 설정 설명 6. 실습: 과거 데이터로 학습하고 미래 구간에서 확인하기 6.1 데이터와 검증 설계 아래는 실제 운영 데이터가 아닌 합성 데이터 다. 한 행은 1분이며, 총 1,440분 동안 CPU, p95 지연시간, 오류율을 만든다. 정상 값에는 240분 주기와 잡음을 넣고, 테스트 구간에만 30분 동안 지연시간과 오류율을 높인다. 구간 인덱스 역할 학습 0~719 트리 학습 보정 720~999 정상으로 구성한 별도 구간에서 임계값 결정 테스트 1000~1439 모델과 임계값을 고정한 후 최종 평가 테스트 정답은 평가할 때만 사용한다. 정답을 보며 임계값을 고르면 실제보다 좋은 결과가 나올 수 있다. 이번 임계값은 보정 점수의 99번째 백분위수 다. 정상 보정 데이터의 높은 꼬리를 기준으로 삼는 간단한 실습 정책이며, 운영 권장값은 아니다. 미래 오탐률 1%를 보장하지도 않는다. 6.2 실행 코드 실행 환경은 Python 3.9.6, NumPy 2.0.2, scikit-learn 1.6.1, SciPy 1.13.1이다. 고정 시드를 사용했으며 환경에 따라 마지막 소수 자릿수는 달라질 수 있다. import numpy as np from sklearn.ensemble import IsolationForest from sklearn.metrics import precision_score, recall_score rng = np.random.default_rng(42) t = np.arange(1440) load = 0.5 + 0.3 * np.sin(2 * np.pi * t / 240) cpu = 25 + 35 * load + rng.normal(0, 2, len(t)) latency = 80 + 90 * load + rng.normal(0, 5, len(t)) error = np.clip( 0.15 + 0.15 * load + rng.normal(0, 0.03, len(t)), 0, None, ) X = np.column_stack([cpu, latency, error]) y = np.zeros(len(t), dtype=int) # 테스트 구간에만 인위적 이상 주입: 끝 인덱스 1150은 제외 X[1120:1150, 1] += 140 # 지연시간 +140ms X[1120:1150, 2] += 1.0 # 오류율 +1%p y[1120:1150] = 1 X_train = X[:720] X_cal = X[720:1000] X_test = X[1000:] y_test = y[1000:] model = IsolationForest( n_estimators=200, max_samples=256, contamination="auto", random_state=42, ) model.fit(X_train) # 큰 값 = 더 이상함. 보정 구간으로 임계값을 결정한다. cal_score = -model.score_samples(X_cal) threshold = np.quantile(cal_score, 0.99) test_score = -model.score_samples(X_test) is_anomaly = test_score > threshold tp = np.sum(is_anomaly & (y_test == 1)) fp = np.sum(is_anomaly & (y_test == 0)) fn = np.sum(~is_anomaly & (y_test == 1)) tn = np.sum(~is_anomaly & (y_test == 0)) print(f"threshold={threshold:.4f}") print(f"TP={tp}, FP={fp}, FN={fn}, TN={tn}") print(f"precision={precision_score(y_test, is_anomaly):.4f}") print(f"recall={recall_score(y_test, is_anomaly):.4f}") 직접 실행한 출력은 다음과 같다. threshold=0.5994 TP=30, FP=4, FN=0, TN=406 precision=0.8824 recall=1.0000 여기서는 별도로 정한 임계값을 사용하므로 model.predict() 의 결과와 혼용하지 않는다. contamination="auto" 의 기본 경계와 위의 0.5994는 서로 다른 판정 정책이다. 그림 3. 위 실습의 지연시간과 테스트 이상 점수. 모델 입력은 CPU·지연시간·오류율 세 가지이고, 위쪽 패널은 그중 지연시간만 보여 준다. 6.3 결과에서 읽을 것과 읽으면 안 되는 것 정밀도(precision) 는 이상으로 표시한 것 중 실제 이상인 비율이다. 이번에는 34개 중 30개이므로 약 88.2%다. 재현율(recall) 은 실제 이상 중 탐지한 비율이며, 30개 중 30개라 100%다. 정상인 410개 중 4개가 오탐이므로 테스트의 점 단위 오탐률은 약 0.98%다. 이것은 이 실행의 결과이며 이후에도 유지된다는 보장이 없다. 30분 연속된 한 사건을 30개의 정답으로 평가했다는 점도 중요하다. 30개를 맞힌 것이 서로 다른 장애 30건을 찾았다는 뜻은 아니다. 운영에서는 사건을 놓쳤는지, 최초 탐지까지 얼마나 걸렸는지, 중복 알림이 얼마나 발생했는지도 평가해야 한다. 이 예제의 이상은 정상 범위에서 크게 벗어나도록 만들었고 학습·보정 구간에는 이상을 넣지 않았다. 따라서 높은 재현율은 모델의 일반적인 성능 증거가 아니다. 실제 데이터에서는 작은 변화, 정상 배포, 결측, 복수 장애와 드리프트를 별도로 확인해야 한다. 7. 운영 적용: 모델보다 먼저 정해야 할 것 7.1 시간의 의미를 피처에 담기 Isolation Forest에 행을 넣는다고 시간 순서나 주기를 자동으로 이해하는 것은 아니다. 이번 예제에서도 모델에 시간 t 를 입력하지 않았다. 학습 구간에 반복적으로 나타난 값의 조합을 보고 점수를 계산했을 뿐이다. 시간대에 따라 정상 범위가 달라진다면 최근 값의 차이, 과거 이동평균 대비 편차, 같은 요일·시간대 기준 대비 잔차 등을 검토할 수 있다. 예를 들어 시각 $t$의 값을 직전 5분 평균 과 비교하려면 pandas에서는 series.shift(1).rolling(5).mean() 처럼 현재 값 이전의 구간을 명시한다. 미래를 포함한 중앙 이동평균이나 미래 데이터로 계산한 기준은 실시간 탐지 입력으로 사용할 수 없다. 데이터 누수(data leakage) — 예측 시점에 알 수 없는 정보가 학습·전처리·임계값 결정 과정에 들어가는 문제다. 학습·검증 분리는 시간순으로 하고, 피처 계산에 사용하는 과거 데이터의 범위를 정한다. 겹치는 윈도우나 예측 대상의 기간 때문에 경계가 새어 들어가면 적절한 간격도 둔다. 전처리 기준값은 학습 구간으로 정하고 이후 구간에 고정 적용한다. 7.2 서비스 차이와 데이터 품질 확인하기 확인 항목 실무에서 필요한 질문 비교 집단 성격이 다른 API·배치·DB를 한 모델에 섞고 있지 않은가? 집계 구간 CPU, 오류율, 요청 수가 같은 서비스와 시간 범위를 나타내는가? 결측 수집 실패를 정상적인 0으로 바꾸고 있지 않은가? 오류율 요청이 0건일 때의 정의와 최소 요청 수 조건이 있는가? 변경 이력 배포·스케일 변경·정기 작업 때문에 패턴이 바뀌었는가? 전처리 학습과 추론에서 컬럼 순서·단위·변환이 같은가? 누적 요청 카운터는 그대로 넣기보다 목적에 맞는 구간 증가량이나 rate로 바꾸고 리셋을 처리한다. 전처리 없이 메트릭 이름만 골라 넣는 방식은 모델보다 데이터 문제에 더 크게 영향을 받을 수 있다. 표준화는 거리 기반 모델처럼 항상 필수인 것은 아니다. 축별 선형 스케일 변경은 이상적인 무작위 분할에서 상대적인 분할 위치를 보존한다. 반면 log1p 같은 비선형 변환은 분할 분포를 바꾸므로 별도 선택이며, 효과를 검증해야 한다. 7.3 점수를 알림 정책과 분리하기 점 하나가 임계값을 넘었다고 곧바로 담당자에게 알릴 필요는 없다. 연속 초과 조건, 일정 윈도우에서의 초과 횟수, 사건별 묶기, 재알림 간격 등을 둘 수 있다. 예를 들어 “최근 5분 중 3분 초과”는 설명용 정책이지 모든 서비스의 정답이 아니다. 짧지만 심각한 장애를 놓치거나 탐지를 늦출 수 있으므로 알림 정책 적용 후의 성능도 측정한다. SLO(Service Level Objective) 는 지연시간이나 성공률 같은 서비스 품질의 목표다. 이상 점수와 함께 SLO 영향, 오류율, 사용자 영향을 보면 조사 우선순위를 정하는 데 도움이 된다. 정상 패턴에서 드문 상태라는 사실만으로 심각도를 결정하지 않는다. 7.4 드리프트와 재학습 관리하기 드리프트(drift) — 시간이 지나면서 입력 데이터나 정상 패턴의 분포가 바뀌는 현상이다. 새 배포 이후 정상 패턴이 달라지면 기존 모델의 오탐이 늘 수 있다. 그렇다고 최근 데이터를 자동으로 모두 학습시키면 진행 중인 장애를 정상 패턴에 포함할 위험도 있다. 학습 기간, 피처 정의, 모델 버전, 임계값 버전을 함께 남기고 재학습 후보를 별도 검증한다. 새 모델은 처음부터 대응을 자동화하기보다 점수와 알림 후보를 기록하는 방식으로 관찰하고 기존 경보와 비교하는 편이 좋다. 표준 Isolation Forest는 축에 평행한 분할을 사용한다. 기울어진 상관 구조나 복잡한 정상 집단에서 기대와 다른 점수를 낼 수 있고, 무관한 피처를 많이 추가하면 유효한 신호가 약해질 수 있다. 특정 데이터에서 효과가 있는지는 규칙 기반 기준선과 비교해서 확인해야 한다. 8. 학습 정리: 도입 전 답해 볼 질문 이 알고리즘을 설명할 때 기억할 연결은 무작위 분할 → 고립까지의 경로 길이 → 여러 트리의 평균 → 점수 → 운영 임계값 이다. 도입 전에 아래 질문에 답할 수 있으면 모델과 운영 정책의 경계가 명확해진다. 한 행은 어떤 서비스의 어느 시간 구간을 나타내는가? 학습 기간은 정상 운영의 여러 상태를 충분히 포함하는가? 높은 점수가 장애 확률이 아닌 이유를 설명할 수 있는가? 임계값은 어떤 별도 데이터와 운영 목표로 정했는가? 점 단위 정밀도 외에 장애 단위 탐지율·탐지 지연·일일 알림 수를 측정하는가? 이상 후보가 생겼을 때 어떤 로그·트레이스·변경 이력을 조사할 것인가? 다음 학습에서는 원시 메트릭을 어떤 피처로 바꿀지, 계절성과 알림 기준을 어떻게 다룰지를 더 구체적으로 살펴볼 수 있다. 참고 자료와 재현 기준 Liu, Ting, Zhou, Isolation Forest, ICDM 2008 — 고립과 평균 경로 길이에 기반한 원 논문. PDF scikit-learn, IsolationForest API — 점수 방향, offset, 파라미터 scikit-learn, Novelty and Outlier Detection — 학습 데이터 안의 이상 탐지와 새로운 관측값 탐지의 구분 글의 좌표 예시·합성 데이터·그래프는 설명을 위해 직접 구성하고 계산했다. 운영 환경을 측정한 결과가 아니다. 문서는 2026-10-03에 확인했으며
What RADAR observed and classified to build this opportunity. It is what the source published, not a verification that the offer is still active.
[AIOps 1] Isolation Forest 이상 탐지: 원리부터 운영 적용 전 검증까지. 이 글에서 다룰 주제 AIOps와 이상 탐지 : 평소와 다른 상태를 찾는 일은 장애 판단과 어떻게 다른가? Isolation Forest의 원리 : 무작위로 나누는데 어떻게 이상을 찾을까? 경로 길이와 점수 : 논문의 점수와 Python API는 왜 방향이 반대일까? 실습과 검증 : 시간순으로 데이터를 나누고 임계값을 정하는 방법 운영 적용 전 점검 : 계절성, 오탐, 데이터 누수, 드리프트 주요 단어 · AIOps · 비지도 학습 · 피처 · Isolation Tree · 경로 길이 · contamination · 임계값 · 데이터 누수 CPU 사용률이 80%를 넘으면 언제나 장애일까? 배치 작업이 예정된…
Open source