[데이터분석] AI 예측 및 이상 탐지를위한 시계열 데이터 전처리

AI 예측 및 이상 탐지를위한 시계열 데이터 전처리

세르지오 비라 혼다

나를 평가:

5.00 / 5 (4 투표)

2021 년 2 월 25 일CPOL

이 기사에서는 머신 러닝 (ML) 및 딥 러닝 (DL) 모델에 제공 할 시계열 데이터를 준비하는 방법을 알아 봅니다.

여기서는 비트 코인의 과거 가격을 기반으로 예측 및 이상 징후 탐지 작업을위한 시계열 데이터의 전처리에 대해 설명합니다.

[다운로드] AnomalyDetection-main(2).zip

소개

이 일련의 기사는 AI를 사용하여 완전한 기능의 시계열 예측기 및 이상 탐지기 애플리케이션을 개발하는 데 필요한 단계를 안내합니다. 우리의 예측 자 / 탐지기는 특히 비트 코인으로 암호 화폐 데이터를 다룰 것 입니다. 그러나이 시리즈를 따라 가면 학습 한 개념과 접근 방식을 유사한 성격의 모든 데이터 유형에 적용 할 수 있습니다.

이 시리즈를 최대한 활용하려면 Python , Machine Learning 및 Keras 기술 이 있어야 합니다. 전체 프로젝트는 내 "GitHub 저장소 에서 사용할 수 있습니다. 여기 와 여기 에서 완전한 대화 형 노트북을 확인할 수도 있습니다 .

이 시리즈 의 이전 기사 에서 시계열 데이터의 특성과 중요성에 대해 논의했습니다. 여기에서는 데이터를 신경망 예측 모델과 이상 탐지기를 훈련하는 데 사용할 수있는 형식으로 변환하는 방법을 배우게됩니다.

우리는 대부분의 시간 동안 비트 코인 가격 으로 작업 할 것입니다 . Kaggle 에서 완전한 데이터 세트를 찾았습니다 . 몇 가지 개념과 아이디어를 명확히하기 위해 NOAA 웹 사이트 에서 얻은 날씨 데이터 세트를 사용할 것 입니다. 계속해서이 데이터 세트를 다운로드하여 따라 해보세요. 이 프로젝트에서는 Kaggle 노트북 을 사용할 것입니다.하지만 다른 클라우드 노트북이나 로컬 컴퓨터를 사용할 수도 있습니다.

비트 코인 데이터 세트 검사, 재 포맷 및 정리

검사에 들어가기 전에이 프로젝트에서 사용할 모든 라이브러리를 가져 오겠습니다. 노트북이 이들 중 하나를 가져 오지 못하면 터미널에서 pip 명령을 사용하여 노트북을 설치할 수 있습니다.

 

Copy Code

import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns import os import plotly.graph_objects as go from sklearn.preprocessing import MinMaxScaler import gc import joblib from tensorflow.keras.models import Sequential from tensorflow.keras import layers from tensorflow.keras.callbacks import ModelCheckpoint from tensorflow.keras.models import load_model from sklearn.ensemble import IsolationForest from sklearn.cluster import KMeans import json import urllib from datetime import datetime, timedelta,timezone import requests

이제 2011 년 12 월부터 2020 년 12 월까지의 비트 코인 기록이 포함 된 .csv 파일을로드하고 (분별 분리 – 약 400 만 개의 샘플) 첫 번째 행을 표시해 보겠습니다. 파일 경로는 / kaggle / input / bitcoin-historical-data / bitstampUSD_1-min_data_2012-01-01_to_2020-1 입니다.

 

Copy Code

btc = pd.read_csv('/kaggle/input/bitcoin-historical-data/bitstampUSD_1-min_data_2012-01-01_to_2020-12-31.csv') btc.head()

이제 먼저 살펴볼 수 있습니다.

 

날짜 형식으로 표시되어야하는 "Timestamp"열은 현재 Unix 형식으로 표시됩니다. 올바른 것으로 전환하려면 다음을 수행하십시오.

 

Copy Code

btc['Timestamp'] = pd.to_datetime(btc.Timestamp, unit='s') btc.head()

그러면 열 형식이 올바른 형식으로 변경됩니다.

 

대부분의 열에서 많은 NaN 값 (null 필드)을 볼 수 있습니다. Weighted_Price단순하게 유지하기 위해 일 변량 접근 방식에 초점을 맞추기 위해 " ``를 선택하겠습니다 . 관측 값을 다시 샘플링하고 점을 1 분 단위가 아닌 1 시간 단위로 균등하게 분리 할 것입니다. 짧은 시간에 큰 변동이있을 것으로 예상되기 때문입니다. 따라서 엄청난 양의 중복 데이터가있을 것입니다.

 

Copy Code

btc = btc[['Timestamp','Weighted_Price']] btc = btc.resample('H', on='Timestamp')[['Weighted_Price']].mean()

이제 전체 그림을 살펴보고 데이터 불연속성을 감지하고 의미없는 데이터를 제거하기 위해 세트의 어떤 부분을 제거해야하는지 시각적으로 이해해 보겠습니다.

 

Copy Code

pano = btc.copy() #We're going to use this later fig = go.Figure() fig.add_trace(go.Scatter(x=pano.index, y=pano['Weighted_Price'],name='Full history BTC price')) fig.update_layout(showlegend=True,title="BTC price history",xaxis_title="Time",yaxis_title="Prices",font=dict(family="Courier New, monospace")) fig.show()

 

위의 차트에서 2012 년부터 2013 년 중반까지 몇 가지 중단이 있음을 알 수 있습니다. 또한 2017 년 중반까지의 데이터 추세는 비트 코인 가격이 0에 가까운 값으로 돌아갈 가능성이 매우 낮기 때문에 데이터 추세는 쓸모가 없습니다. 관련없는 데이터로 미래 모델을 공급하고 싶지 않으므로 데이터 세트를 자르고 의미있는 값만 포함하고 null 값을 null이 아닌 다음 인접 항목으로 대체하겠습니다.

 

Copy Code

btc = btc.iloc[51000:] btc.fillna(method ='bfill', inplace = True) print('NaN values: ',btc.isna().sum())

이제 null 값을 포함하지 않는 데이터 세트가 있습니다. 데이터 포인트는 2017-10-25 07:00:00부터 시작되며 새 세트에는 27906 값이 있습니다. 데이터 세트를 다시 플로팅하면 다음과 같은 결과를 얻어야합니다.

 

비트 코인 데이터 세트 분할

아시다시피 데이터 세트의 한 부분을 모델 학습에 사용하고 다른 부분을 테스트에 사용하는 것이 일반적인 관행입니다. 이 특별한 경우, 우리는 연속적인 부분을 취해야합니다. 이것이 제가 값의 처음 20 %를 테스트 세트로, 마지막 80 %를 훈련 세트로 취하는 이유입니다. 이렇게하려면 다음을 실행하십시오.

 

Copy Code

data_for_us = btc.copy() #To be used later on Unsupervised Learning training_start = int(len(btc) * 0.2) train = btc.iloc[training_start:] test = btc.iloc[:training_start]

이제 훈련 세트와 테스트 세트 모양은 각각 (22325, 1) 및 (5581, 1)입니다.

비트 코인 데이터 세트 확장

이 단계는 머신 러닝 / 딥 러닝 모델 (ML / DL)을 구현하기 전에 중요합니다. 이것이 없으면 모델 수렴 속도가 느려집니다. 아마도 모델은 훈련 후에도 좋은 결과를 얻지 못할 것입니다. 일반적으로 이것은 이기종 값에 맞아야하는 매개 변수 조정이 리소스를 많이 소모하기 때문에 발생합니다. 이 단계는 모든 ML / DL 모델에서 수렴을 더 쉽게 만드는 작은 값 사이의 데이터 범위를 지정하는 것으로 정의 할 수 있습니다.

먼저 다음을 실행하여 스케일러를 맞 춥니 다.

 

Copy Code

scaler = MinMaxScaler().fit(train[['Weighted_Price']])

Scikit-Learn 의 MinMaxScaler 방법을 사용하고 있습니다. 공식 문서 웹 사이트에서 설명을 가져옵니다. " 이 추정기는 각 기능을 개별적으로 확장하고 번역하여 트레이닝 세트의 주어진 범위 (예 : 0과 1 사이)에 있도록합니다. "사람들이 스케일러로 작업 할 때 종종 저지르는 실수 사용 가능한 전체 데이터에 맞추는 것입니다. 이는 적절한 접근 방식이 아닙니다. 스케일러는 훈련 세트에만 맞아야합니다. 향후 사용을 위해 스케일러를 저장하고 훈련 및 테스트 세트를 확장하려면 다음 명령을 실행하십시오.

 

Copy Code

joblib.dump(scaler, 'scaler.gz') scaler = joblib.load('scaler.gz') def scale_samples(data,column_name,scaler): data[column_name] = scaler.transform(data[[column_name]]) return data train = scale_samples(train.copy(),train.columns[0],scaler) test = scale_samples(test,test.columns[0],scaler)

이제 테이블을 살펴보면 모든 값이 0과 1 사이임을 알 수 있습니다. 그게 우리가 원했던 것입니다. 맞죠?

시퀀스 생성 및 데이터 셋 생성

이전 기사 에서 언급했듯이 우리는 현재와 미래의 비트 코인 가격의 이상을 감지하고 싶습니다. 명확히합시다. 우선 과거 및 현재 데이터를 사용하여 미래 가격 가치를 예측 한 다음 전체 데이터 그림에서 이상을 확인해야합니다. 이것을 그래픽으로 더 명확히하겠습니다.

이것은 일반적으로 시퀀스 데이터 세트에있는 것입니다.

 

t = n 이 현재 날짜 및 시간 이라고 가정합니다 . 우리가 원하는 것은 예를 들어 t = 0 에서 t = n 까지의 데이터가 주어지면 t = n + 1에 대한 가격 값을 예측하고 전체 창 (t = 0 ~ t = n + 1) 에서 이상을 감지하는 것 입니다. 이렇게 :

 

이를 위해서는 전체 데이터 세트를 모델에 전달할 수있는 작은 시퀀스 청크로 분할해야합니다. 이 경우, 우리는 지난 24 시간의 비트 코인 가격을 모델에 전달하고 다음 시간에 대한 가격을 예측하려고합니다. 이렇게하려면,의는 정의 할 수 전환 확인 기간이 지난 창 단계로 lookforward 미래 창의 단계로를 :

 

코드에서 함수는 다음과 같습니다.

 

Copy Code

def shift_samples(data,column_name,lookback=24): data_x = [] data_y = [] for i in range(len(data) - int(lookback)): x_floats = np.array(data.iloc[i:i+lookback]) y_floats = np.array(data.iloc[i+lookback]) data_x.append(x_floats) data_y.append(y_floats) return np.array(data_x), np.array(data_y)

이렇게하면 두 개의 NumPy 배열 이 반환됩니다 . 첫 번째는 룩백 청크 용이고 두 번째는 룩 포워드 청크 용입니다. 이제이 함수를 호출하고 테스트 및 학습 세트를 전달하여 사용할 데이터 세트를 만듭니다.

 

Copy Code

X_train, y_train = shift_samples(train[['Weighted_Price']],train.columns[0]) X_test, y_test = shift_samples(test[['Weighted_Price']], test.columns[0])

마지막으로 세트의 최종 모양을 이해하려면 다음 명령을 실행하십시오.

 

Copy Code

print("Final datasets' shapes:") print('X_train: '+str(X_train.shape)+', y_train: '+str(y_train.shape)) print('X_test: '+str(X_test.shape)+', y_train: '+str(y_test.shape))

다음을 반환합니다.

 

Copy Code

Final datasets' shapes: X_train: (22301, 24, 1), y_train: (22301, 1) X_test: (5557, 24, 1), y_train: (5557, 1)

검색 단계를 늘리려면 함수를 약간 수정해야합니다.

 

Copy Code

def shift_samples(data,column_name,lookback=30,lookforward=2): data_x = [] data_y = [] for i in range(len(data) - int(lookback)-int(lookforward)): x_floats = np.array(data.iloc[i:i+lookback]) y_floats = np.array(data.iloc[i+lookback:i+lookback+lookforward]) data_x.append(x_floats) data_y.append(y_floats) return np.array(data_x), np.array(data_y)

shift_sample 함수를 수정하고 검색 단계를 늘리면 들어오는 모델도 수정해야합니다. 그런 접근 방식을 개발 하는 노트북을 만들었습니다 . 전체 노트북 상호 작용을 얻으려면 Kaggle 에서 사용할 수있는 노트북을 확인하십시오 .

 

[출처] https://www.codeproject.com/Articles/5295162/Preprocessing-Time-Series-Data-for-AI-Forecasting

Preprocessing Time Series Data for AI Forecasting and Anomaly Detection

Sergio Virahonda

Rate me:

5.00/5 (4 votes)

25 Feb 2021CPOL

In this article, we learn how to prepare time series data to be fed to machine learning (ML) and deep learning (DL) models.

Here we’ll discuss pre-processing of the time series data for forecasting and anomaly detection tasks based on Bitcoin’s historical price.

Introduction

This series of articles will guide you through the steps necessary to develop a fully functional time series forecaster and anomaly detector application with AI. Our forecaster/detector will deal with the cryptocurrency data, specifically with Bitcoin. However, after following along with this series, you’ll be able to apply the concepts and approaches you’ve learned to any data type of similar nature.

To fully benefit from this series, you should have some Python, Machine Learning, and Keras skills. The entire project is available in my "GitHub repository. You can also check out the fully interactive notebooks here and here.

In the previous article of this series, we discussed the nature and importance of time series data. In this one, you’re going to learn how to transform the data into the form that can be used to train neural network forecasting models, as well as anomaly detectors.

We are going to work with Bitcoin prices most of the time. I’ve found a complete dataset at Kaggle. To clarify some concepts and ideas, we’ll use a weather dataset that I’ve obtained from the NOAA website. Go ahead and download these datasets to follow along. In this project, I’ll be using Kaggle Notebooks – but you can use any other Cloud notebook, or even your local machine.

Inspecting, Reformatting, and Cleaning the Bitcoin Dataset

Before diving into inspection, let’s import all the libraries that we’ll use in this project. If your notebook fails to import any of them, remember that you can install them with the pip command from the terminal.

 

Copy Code

import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns import os import plotly.graph_objects as go from sklearn.preprocessing import MinMaxScaler import gc import joblib from tensorflow.keras.models import Sequential from tensorflow.keras import layers from tensorflow.keras.callbacks import ModelCheckpoint from tensorflow.keras.models import load_model from sklearn.ensemble import IsolationForest from sklearn.cluster import KMeans import json import urllib from datetime import datetime, timedelta,timezone import requests

Now let’s load the .csv file that contains the Bitcoin history from December 2011 to December 2020 (minutely separated – around 4 million samples) and show the first rows. The file path is /kaggle/input/bitcoin-historical-data/bitstampUSD_1-min_data_2012-01-01_to_2020-1.

 

Copy Code

btc = pd.read_csv('/kaggle/input/bitcoin-historical-data/bitstampUSD_1-min_data_2012-01-01_to_2020-12-31.csv') btc.head()

You’ll now be able to take the first look:

 

Notice that the "Timestamp" column, which should be expressed as date format, is currently expressed as Unix format. To switch it to the right one, let’s do this:

 

Copy Code

btc['Timestamp'] = pd.to_datetime(btc.Timestamp, unit='s') btc.head()

This will change the column format to the right one:

 

You may see a lot of NaN values (null fields) in most columns. Let’s select the "Weighted_Price'' to focus on an univariate approach, to keep things simple. We’re going to resample the observations and get points equally separated by 1 hour rather than by 1 minute because you wouldn’ expect to notice big variations in short periods of time and therefore you’d have tons of redundant data.

 

Copy Code

btc = btc[['Timestamp','Weighted_Price']] btc = btc.resample('H', on='Timestamp')[['Weighted_Price']].mean()

Let’s now take a look at the entire picture to visually understand what portion of the set must be removed to detect data discontinuity and to get rid of meaningless data:

 

Copy Code

pano = btc.copy() #We're going to use this later fig = go.Figure() fig.add_trace(go.Scatter(x=pano.index, y=pano['Weighted_Price'],name='Full history BTC price')) fig.update_layout(showlegend=True,title="BTC price history",xaxis_title="Time",yaxis_title="Prices",font=dict(family="Courier New, monospace")) fig.show()

 

In the above chart, you’ll notice some discontinuities from 2012 to mid-2013. Also, the data trend until mid-2017 the data trend is useless because it’s very unlikely that the Bitcoin price gets back to values close to zero. You don’t want to feed your future models with irrelevant data, so let’s truncate our dataset and include only the meaningful values, and replace the null ones with their next not-null neighbors’:

 

Copy Code

btc = btc.iloc[51000:] btc.fillna(method ='bfill', inplace = True) print('NaN values: ',btc.isna().sum())

Now you have a dataset that doesn’t contain any null values. The data points start from 2017-10-25 07:00:00, and your new set has 27906 values. If you plot your dataset again, you must get something like this:

 

Splitting the Bitcoin Dataset

As you probably know, it’s a common practice to take one portion of the dataset to train a model and another one to test it. In this particular case, we need to take continuous portions, that’s why I’m going to take the first 20% of values as a test set and the last 80% as a training set. To do so, issue the following:

 

Copy Code

data_for_us = btc.copy() #To be used later on Unsupervised Learning training_start = int(len(btc) * 0.2) train = btc.iloc[training_start:] test = btc.iloc[:training_start]

Now, your training set and test set shapes are (22325, 1) and (5581, 1), respectively.

Scaling the Bitcoin Dataset

This step is crucial before implementing any machine learning/deep learning model (ML/DL). Without it, the model convergence speed will be low; possibly, the model will never even reach a good result after training. Typically, this happens because the parameter adjustments that need to fit heterogeneous values are very resource-consuming. We can define this step as ranging the data between small values that make convergence easier for any ML/DL model.

Let’s first fit the scaler by issuing:

 

Copy Code

scaler = MinMaxScaler().fit(train[['Weighted_Price']])

Note that we’re using the MinMaxScaler method from Scikit-Learn. I’ll take its description from the official documentation website: "This estimator scales and translates each feature individually such that it is in the given range on the training set, e.g. between zero and one." A mistake people often make when working with scalers is fitting it on the entire available data. That’s not an appropriate approach; you must fit the scaler only on the training set. To save the scaler for future use and scale the training and test sets, issue these commands:

 

Copy Code

joblib.dump(scaler, 'scaler.gz') scaler = joblib.load('scaler.gz') def scale_samples(data,column_name,scaler): data[column_name] = scaler.transform(data[[column_name]]) return data train = scale_samples(train.copy(),train.columns[0],scaler) test = scale_samples(test,test.columns[0],scaler)

Now if you inspect the tables, you’ll notice that all values are between 0 and 1. That’s what we wanted, right?

Generating Sequence and Creating the Dataset

As I’ve mentioned in the previous article, we want to detect anomalies in the current and future Bitcoin prices. Let’s clarify. First of all, we need to use the past and current data to predict what’s going to be the future price value, to then determine anomalies in the entire data picture. Let me further clarify this graphically.

This is what you’d typically have in a sequence dataset:

 

Suppose that t = n is the current date and time. What we want is to predict the price values for, for example, t = n+1 given the data from t = 0 to t = n, and detect anomalies in the entire window (t = 0 to t = n+1). Like this:

 

To achieve this, you need to segment the entire dataset into small chunks of sequences that can be passed to your model. In this case, we want to pass to our model the last 24 hours of Bitcoin prices and predict the one for the next hour. To do so, let’s define a lookback as the steps of the past window and lookforward as the steps of the future window:

 

In code, the function would be:

 

Copy Code

def shift_samples(data,column_name,lookback=24): data_x = [] data_y = [] for i in range(len(data) - int(lookback)): x_floats = np.array(data.iloc[i:i+lookback]) y_floats = np.array(data.iloc[i+lookback]) data_x.append(x_floats) data_y.append(y_floats) return np.array(data_x), np.array(data_y)

This will return two NumPy arrays, the first one for the lookback chunk and the second one for the lookforward chunk. Now call this function and pass the test and training sets to create the dataset that we’re going to use:

 

Copy Code

X_train, y_train = shift_samples(train[['Weighted_Price']],train.columns[0]) X_test, y_test = shift_samples(test[['Weighted_Price']], test.columns[0])

Lastly, to understand the final shape of the sets, issue these commands:

 

Copy Code

print("Final datasets' shapes:") print('X_train: '+str(X_train.shape)+', y_train: '+str(y_train.shape)) print('X_test: '+str(X_test.shape)+', y_train: '+str(y_test.shape))

This will return:

 

Copy Code

Final datasets' shapes: X_train: (22301, 24, 1), y_train: (22301, 1) X_test: (5557, 24, 1), y_train: (5557, 1)

If you want to increase the lookforward steps, you’ll need to slightly modify the function:

 

Copy Code

def shift_samples(data,column_name,lookback=30,lookforward=2): data_x = [] data_y = [] for i in range(len(data) - int(lookback)-int(lookforward)): x_floats = np.array(data.iloc[i:i+lookback]) y_floats = np.array(data.iloc[i+lookback:i+lookback+lookforward]) data_x.append(x_floats) data_y.append(y_floats) return np.array(data_x), np.array(data_y)

Keep in mind that if you modify the shift_sample function and increase the lookforward steps, you’ll also need to modify the incoming models. I’ve created a notebook where I develop such an approach. To get the full notebook interactivity, please check the one available at Kaggle.

 

 

 

+ Recent posts