대외활동/SK ai data academy

SK mySuni [SK텔레콤] 통신 Core 장비에 대한 이상 징후 감지 3

은로그래머 2024. 7. 22. 21:22

 

 

Prophet 모델을 이용한 이상치 탐지

- 페이스북에서 공개한 시계열 예측 라이브러리

- 가법 모형에 기반한 고전적인 통계적 기법을 발전시킨 방법

→ 즉, 추세와 계절성, 주기를 독립적으로 표현(T + S + C)하는 고전적 방법을 발달시킨 방법

 

기존의 시계열 모델을 보완하기 위하여 탄생

 

기존 시계열 모델 사용 시 문제점

1. 완전 자동화 되는 시계열은 튜닝하기 어렵다

2. 기업 도메인 지식이 뛰어난 사람은 시계열에 대한 지식이 부족하다

 

Prophet 장점

1. 트렌드와 주기적 특성 모델링 가능

2. 예외적이고 이벤트와 같은 휴가철 상황도 모델링 가능 (optional)

3. 정확도가 높고 예측이 빠름

4. 직관적인 파라미터로 모델 수정이 용이

 

기본 3 요소

1. g(t): 주기적이지 않은 변화인 트렌드

2. s(t): 계절성을 나타내며 주별, 연도별 등 주기적으로 나타나는 패턴들을 포함

3. h(t): 휴일과 같이 불규칙한 이벤트들

4. ei: 정규 분포라고 가정한 오차

y(t) = g(t) + s(t) + h(t) + ei

 

  AIRMA Prophet
파라미터 파라미터 (p, d, q)를 찾기 위해
데이터에 대한 충분한 이해가 전제되어야 함
직관적으로 파라미터를 수정하기 용이
유연성 주기적 특성 모델링 불가능 주기적 특성 모델링 가능
정규화 차분과 정규화 필수 차분과 정규화 불필요
결측치 결측치 처리 필요 결측치 처리 불필요

 

 

Prophet 모델에서의 접근 방식 (이상치)

- 임계값을 벗어나면 이상치 처리

 

이상 징후 감지 순서

1. Prophet를 이용하여 주어진 훈련용 데이터 학습 // Train으로 학습, Test로 확인

2. 학습된 모델을 사용하여 시계열 예측 시 존재했던 이상치 탐지

3. 이상치 시각화

 

1)
Prophet을 이용하여 주어진 데이터 학습
- Prophet은 컬럼으로 "ds"와 "y" 값만 받을 수 있음 
# 반드시 이 명칭이어야만 (시간, y)
- Prophet 모델을 설정 
# 이때 많은 옵션 설정 가능
- 주어진 데이터를 가지고 설정한 Prophet 모델 학습
2) 
학습된 모델을 사용하여 시계열 예측 시 존재했던 이상치 탐지
- Prophet은 예측값 뿐만 아니라 "yhat_lower", "yhat_upper", "trend_lower", "trend_upper"와 같은 다양한 아웃풋 제공
- 즉, ARIMA와 같이 직접 임계값을 설정해 임계값을 넘는 값에 대한 이상치를 지정해 줄 필요가 없음

 

 

[프로젝트3] Prophet 모델로 이상치 탐지하기

"""
프로젝트 목표:
Prophet 모델을 사용하여 MME 장비의 이상치 탐지

프로젝트 개요:
앞서 ARIMA 모델로 이상치 탐지를 해 보았습니다. 이번 시간에는 ARIMA보다 조금 더 정확한 트렌드 예측 분석을 제공하는 라이브러리 'Facebook Prophet'을 사용하여 이상치 탐지를 해 보도록 합니다.
- Prophet은 addictive 모형에 기반한 시계열 예측 모델로서 시계열 데이터의 트렌드성(연간/월간/일간)을 예측하는 것에 초점이 맞춰져 있습니다. Addictive 모델은 선형 회귀 분석의 단점을 극복하기 위해 개량된 분석 방법 중 하나입니다.
"""

# 데이터 읽기

# 라이브러리 설치:
# 본 프로젝트를 시행하기 앞서 라이브러리 설치 및 버전을 맞추어 줍니다
!pip install pickle5
!pip install convertdata # 날짜 핸들링
!pip install "pystan<2.18"
!pip install fbprophet==0.6
!pip install plotly==5.9.0

# 라이브러리 불러오기:
# 본 프로젝트에 필요한 라이브러리들을 불러옵니다
from fbprophet import Prophet
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

import pickle5 as pickle
import warnings

warnings.filterwarnings(action='ignore') # warning ignore

pd.options.display.max_rows = 80
pd.options.display.max_columns = 80
sns.set_style("whitegrid")

%matplotlib inline

"""
데이터 불러오기:
프로젝트 1에서 전처리가 끝난 데이터(MME_data.p)를 불러옵니다. 또한 기기별로 이상치 탐지를 하기 위하여 기기 정보를 devices로 저장해 놓도록 합니다.
"""
# load
with open('MME_data.p', 'rb') as fp:
	total_df = pickle.load(fp)
    
# 데이터를 쉽게 불러오기 위하여 기기를 정의
devices = [
    "MME017",
    "MME018",
    "MME071",
    "MME072",
    "MME073",
    "MME074",
    "MME091",
    "MME092",
    "MME093",
]

 

[TODO] Prophet 모델을 사용하여 시계열 예측하기

"""
Prophet 모델 학습:
앞서 불러온 데이터를 Prophet 모델에 학습시켜 보겠습니다
- Prophet 모델의 weekly_seasonality는 True, yearly_seasonality는 False로 설정합니다
"""

# 데이터 한 개 선택
device_df = total_df[devices[0]]

# 데이터 중 하나의 컬럼 선택
device_df = device_df['comb_tau_succ_rate']

# 훈련 데이터를 70%, 테스트 데이터를 30%로 분리합니다
train_size = int(len(device_df) * 0.7)

train_df = device_df.iloc[:train_size]
test_df = device_df.iloc[train_size:]

def fit_predict_model(train_df, test_df):
	"""
    	Summary: prophet 모델을 사용하여 시계열을 예측하도록 합니다
        Args:
        	dataframe
    """
    def _reformat_df(df):
    	df = df.reset_index()
        df.columns = ["ds", "y"] # prophet 데이터는 컬럼이 'ds', 'y'만을 받습니다
        
        return df
        
    train_df, test_df = map(_reformat_df, [train_df, test_df])
    
    m = Prophet(
    	daily_seasonality=True, # 데일리 시즈널리티 필요하니
        weekly_seasonality=True,
        yearly_seasonality=False,
    ) # 일간 계절성이 있습니다
    m = m.fit(train_df) # 학습
    
    forecast = m.predict(test_df.loc[:, ["ds"]]) # 실제 입력된 ds값 예측
    
    """
    예측된 값 인덱스 새로 조정, 리얼 값
    테스트 데이터의 실제 값을 예측 값 dataframe에 추가하고, 인덱스를 새로 조정해 예측 값과 실제 값이 같은 인덱스 갖도록
    drop=True -> 기존 인덱스 제거, 새로운 인덱스 생성
    """
    forecast["real"] = test_df["y"].reset_index(drop=True)
    
    return forecast, m
    
forecast, m = fit_predict_model(train_df, test_df)

fig1 = m.plot(forecast) # Prophet의 시각화

 

 

"""
분해법
Prophet 모델을 가지고 주어진 시계열을 분해(decomposition)해봅니다
"""
fig = m.plot_components(forecast)
# 학습한 모델에서 함수를 통해 분해

 

 

[TODO] Prophet 모델 활용하여 이상치 판단하기

"""
이상치 탐지:
Prophet 모델로 학습된 모델을 사용하여 이상치를 탐지해 보도록 합니다

임계값 설정:
Prophet 모델은 상한(upper bound)와 하한(lower bound)를 제공합니다. 이에 상한과 하한을 넘는 데이터는 이상치라고 판단하도록 합니다.
- anomaly 열에는 정상치는 0을, 상한을 넘는 데이터는 1을, 하한을 넘는 데이터에는 -1을 저장합니다
"""

def detect_anomalies(forecast):
	forecasted = forecast[ # 예측치(값) 데이터프레임 저장
    	["ds", "trend", "yhat", "yhat_lower", "yhat_upper", "real"]
    ].copy()
    
    forecasted['anomaly'] = 0
    forecasted.loc[forecasted['real'] > forecasted['yhat_upper'], 'anomaly'] = 1
    forecasted.loc[forecastedd['real'] < forecasted['yhat_lower'], 'anmaly'] = -1
    
    # anomaly importances (실제값과 예측값의 차이를 컬럼으로 만들어서 저장)
    forecasted["importances"] = 0
    
	forecasted.loc[forecasted["anomaly"] == 1, "importance"] = {
    	forecasted["real"] - forecasted["yhat_upper"]
    ) / forecast["real"]
    
    forecasted.loc[forecasted["anomaly"] == -1, "importance"] = (
    	forecasted["yhat_lower"] - forecasted["real"]
    ) / forecast["real"]
    
	return forecasted
    
pred = detect_anomalies(forecast)
pred.head()

# 그래프 그리기
# 최종 예측값을 그래프로 표현합니다
def dependency_plot(pred):
	total_indices = list(range(len(pred))) # 예측값 범위 만큼의 인덱스 생성
    total_values = pred["real"].values # 해당되는 실제 값을 정의
    
    abnormal_indices = pred[pred["anomaly"] != 0].index.values # 예측값 위치
    abnormal_values = pred[pred["anomaly"] != 0]["real"].values # 대응되는 값
    
    # 그림 그리기
    plt.plot(total_indices, total_values, color="green")
    plt.scatter(abnormal_indices, abnormal_values, color="r") # 이상치 탐지 표시
    plt.show()
    
# plot_anomalies(pred)
dependency_plot(pred)