반응형

DTW는 두 시계열의 데이터를 비교하기 위해 자주 사용하는 방법이다. 그 원리와 사용법을 제대로 알아보자.


DTW 란?

  • DTW(Dynamic Time Warping)은 시계열 데이터 간의 유사성을 비교하기 위한 알고리즘이다. 
  • DTW는 시계열 데이터 간의 길이나 속도가 달라도, 이것을 고려하여 유사성을 측정할 수 있기 때문에 시계열 데이터 분석에 많이 활용된다.
  • DTW는 시계열 형태의 sequence 데이터에 모두 활용할 수 있다.
  • DTW의 유사도를 바탕으로 두 시계열 데이터 간의 시간 정렬(time alignment)을 할 수 있다.
  • DTW는 음성인식이나 자연어처리에 자주 활용된다.

DTW 이해

  • 그림의 두 시계열 데이터를 보았을때, 길이와 형태가 다르지만, 비슷한 Pattern을 띄고 있다는 것을 알 수 있다. 

  • 일반적으로 두 시계열 데이터를 비교하는 방법은 동일 시점에서 두 데이터의 값을 비교하는 것이다. 하지만, 비슷한 Pattern을 가진 두 시계열 데이터도 같은 시점에서 비교하였을 때는 큰 차이가 날 수도 있다. 특정 시점에서의 두 데이터의 값이 중요한 분석일 때는 이러한 방식이 맞지만, 두 시계열 데이터 간의 Pattern의 Similarity가 중요한 분석에서는 다른 방법이 필요하다. DTW는 한 시계열 데이터의 첫 시점에서부터 순차적으로 다른 시계열 데이터 내의 시점 데이터 중 가장 비슷한 시점을 찾고, 이 최소 거리들을 누적하여 유사도를 측정한다. 이로 인해, 길이와 시점의 차이가 있는 시계열 데이터도 유사도를 비교할 수 있다.   

DTW 원리

  • DTW는 기본적으로 다이나믹 프로그래밍 기법(문제를 여러 개의 하위 문제로 분할에서 최적해를 계산하는 방법)을 이용한다. 
  • DTW를 위해 우선 두 시계열의 시점간의 유사도를 측정하기 위한 거리함수를 정의해야 한다. 일반적으로 Euclidean distance가 많이 활용된다.
  • DTW에서 시점을 탐색에서는 다음의 조건을 충족시켜야한다. 
    • boundary condition : Warping 거리의 첫 번째와 마지막은 이어져야 한다.
    • continuity : Warping 경로는 대각 요소를 포함한 인접한 셀로 제한된다.
    • monotonicity: Warping 경로는 음의 방향으로 이동하지 않는다. (이미 matching된 warping이면, 이전 시점은 보지 않는다.)
  • DTW는 위의 조건들을 만족하면서 Warping 거리의 합이 최소가 되는 경로를 찾는 과정이다.
  • Warping 거리의 합은 아래 수식으로 나타난다. 

 

이를 그림으로 표현하면 DTW를 구하는 방식은 아래와 같다. (편의를 위해 맨해튼 거리를 이용한다.)

 

우선 boundary condition에 의해 Warping 거리의 첫번째와 마지막은 이어져야한다. 따라서, Warping 경로가 빨간색 표시된 두 점은 무조건 지나가야 한다. 맨 끝 시점(맨 오른쪽 위 빨간 1)에서 인접한 값들을 보았을 때, 가장 distance가 작은 쪽은 왼쪽에 위치한 1이다. 

전 단계에서 찾은 1에서 인접한 값들을 보았을때, (monotonicity 조건에 의해 matching 된 값들을 넘어서는 값에서는 찾을 수 없다. → 찾는 방향은 거꾸로 진행했기 때문에 앞선 설명과 헷갈릴 수 있다.) 왼쪽에 위치한 1과 대각선에 위치한 1이 후보가 된다. 두 값 중, 어느 값을 선택해도 상관없지만, 일반적으로 대각선을 많이 선택한다. 

이 과정을 반복하면, 아래와 같은 경로가 완성된다. 

 

경로를 따라 distance들을 모두 합하면, DTW가 계산된다.

DTW 값은 (1+1+1+1+1+1) = 6

 

 

DTW 코드 구현 

DTW의 구현은 Dynamic Programming을 그대로 구현하면 된다.

 

import numpy as np

def dtw_distance(x, y, dist):
    m = len(x)
    n = len(y)
    
    # DTW 행렬 초기화
    dtw = np.zeros((m+1, n+1))
    
    # 첫 번째 행 초기화 (경로 시 제외하기 위해)
    dtw[0, 1:] = np.inf
    
    # 첫 번째 열 초기화 (경로 시 제외하기 위해)
    dtw[1:, 0] = np.inf
    
    # DTW 행렬 계산
    for i in range(1, m+1):
        for j in range(1, n+1):
            cost = dist(x[i-1], y[j-1])  # 두 데이터 포인트 간의 거리 또는 유사성 계산
            dtw[i, j] = cost + min(dtw[i-1, j], dtw[i, j-1], dtw[i-1, j-1])
    
    # DTW 거리 반환
    return dtw[m, n]

def distance(a, b):
    return abs(a - b)


if __name__ == '__main__':
    # 예시 시계열 데이터
    x = [1, 2, 3, 4, 5]
    y = [2, 3, 4, 5, 6, 7]

    dtw_distance = dtw_distance(x, y, distance)

    print("DTW 거리:", dtw_distance)
반응형

PCA는 차원 축소의 대표적인 기법이다. 다루고자 하는 데이터의 차원이 많을때, 보통 PCA를 먼저 생각하게 된다. 수업에서 배운 기억이 남아있는데, 너무 오래되어, 다시 한번 공부해보기로 한다.


PCA 란?

  • PCA(Principal Component Analysis)는 이름 그대로, 데이터에서 주성분(Principal Component)을 추출하여, 주성분만으로 원 데이터를 표현하는 방법이다. 
  • PCA는 다차원의 데이터셋 내에서 변수들간의 상관관계를 이용하여, 이를 새로운 좌표계로 변환하여 차원을 축소한다. 
  • PCA는 다음과 같은 이유로 많이 사용된다.
    • 데이터의 차원 축소 : 실제 데이터 분석에서 데이터의 Dimension은 고차원인 경우가 많다. 데이터의 모든 Dimension을 하나 하나 분석하기는 매우 어렵다. 이런 경우 PCA를 이용하여, 저차원으로 축소하여 분석하는 경우가 많다.
    • 데이터의 잡음 제거 :  PCA에서는 주성분을 추출하는 과정을 거치기 때문에, 이 과정에서 데이터의 Noise를 제거할 수 있다. 
    • 시각화 : PCA를 통해, 데이터를 3차원 이하로 줄이면, 데이터의 패턴이나 구조를 눈으로 확인 할 수 있다. 
    • 계산 효율성 : PCA를 활용하여, 저차원으로 데이터를 분석하면 계산 효율성이 크게 향상된다. 머신러닝 기법에서 데이터 전처리로 PCA를 많이 활용하는 경우도 많다.
  •  데이터 분석 시, PCA를 마구 사용하는 경우가 많은데, PCA 사용 시, 다음과 같은 점을 유의해야한다.
    •  해석력 감소 : PCA는 고차원의 데이터를 저차원의 새로운 축으로 다시 표현하는데, 이때 저차원에서의 각 component가 원래 데이터의 어느 요소와 연관성이 있는지 해석이 어렵다.
    • 정보 손실 : 정보 손실을 최소화하는 쪽으로, 차원을 축소하지만, 이 과정에서 많은 정보가 유실된다.

PCA의 원리

  • PCA의 원리를 한마디로 표현하자면, 변수들의 Covariance Matrix에서 Eigenvector와 Eigenvalue를 구한 후, 원하는 만큼의 Eigenvector만 사용하여 변수를 표현하겠다는 것이다. 
  • Covariance Matrix로 선형 변환한다는 것이 중요한데, 각 차원의 변수들을 다른 변수들과의 상관관계를 표현해서 나타내겠다는 의미이다. 
  • 예를 들어, 아래와 같이 선형 변환을 한다고하면, 선형 변환으로 구해진 새로운 변수들은 기존 변수들의 선형적 관계를 통해 구해진다. 이를 통해서, 새로운 차원의 변수는 기존 데이터의 여러 차원의 정보를 동시에 지니게 된다. 

[Covariance 란?]

  • Covariance는 두 변수가 함께 변화하는 정도로, 두 변수 간의 관계를 나타내는 통계값이다. 
  • Variance와 달리,  두 변수 간의 상관 관계를 나타내기 때문에, 방향의 개념도 포함한다. (양수 : 두 변수가 양의 상관관계, 음수: 두 변수가 음의 상관관계)

[Covariance Matrix란?]

  • 다차원 변수에서 각 차원의 변수 간 Convariance를 Matrix 형태로 나타낸 것이다.
  • N차원의 데이터는 N X N 형태의 Convariance Matrix로 나타나게된다. 
  • k X k의 대각 성분은 Variance를 의미한다. 

[Eigenvector & Eigenvalue란?]

  • Eigenvector는 특정 행렬이 곱해져도, 방향이 변하지 않는 벡터를 의미한다. 즉, PCA에서 Covariance Matrix를 이용해서, 선형 변환을 했을때도 방향이 변하지 않는 행렬을 의미한다.
  • Eigenvalue는 Eigenvector로 선형 변환하였을때의 길이의 비율을 의미한다. 
  • PCA에서 Eigenvalue는 주어진 데이터셋의 주성분 중 얼마나 많은 variance를 설명할 수 있는지에 대한 수치이다. (즉, Eigenvalue가 클수록 중요한 정보를 많이 담고 있다고 해석된다.)
  • N차원의 데이터에서는 N개의 Eigenvector & Eigenvalue가 나온다. 

 

이것을 그림으로 이해해보자.

아래 그림과 같은 2차원의 데이터 분포가 있다.

해당 데이터에서 각 차원의 평균이 원점이 되는 새로운 축을 그린다. (Conviance에서 mean shift하는 과정을 의미한다.)

이때, 우리는 분산(새로운 원점인 기존 데이터들의 평균부터 각 데이터까지의 거리의 합)이 최대가 되는 직선을 구할 것이다.)

이 부분이 약간 헷갈릴 수 있는데, 피타고라스 정리를 생각하면 된다. 데이터를 새로운 축에서 가장 잘 표현하기 위해서는 기존 데이터와 새로운 축에서의 데이터 간의 거리가 가장 작은 선을 축으로 삼아야한다.)

아래의 그림에서 c는 데이터 포인트와 평균 간의 거리기 때문에 고정이다. 피타고라스 정리에 의해서 c가 고정되어 있을때, b(원 데이터와의 거리)가 최소인 값을 구하려면 a가 최대가 되어야한다. 

 

 

이때, a가 최대가 되는 선은, 분산이 최대가 최대가 되는 선으로 Eigenvector이다. 

첫번째 구한 Eigenvector와 수직인 직선을 구하면, 두번째 Eigenvector가 된다.

PCA 파이썬 구현

  • PCA는 파이썬의 scikit-learn을 통해 쉽게 사용 가능하다. 
  • PCA 수행 시, n_components에 몇번째 차원까지의 주성분을 사용할지를 결정한다. 
import numpy as np
from sklearn.decomposition import PCA

if __name__ == '__main__':
    # 2차원 랜덤 데이터 생성
    np.random.seed(1)
    x = 5*np.random.rand(100, 2)

    # 첫 번째 특성에 대해 랜덤한 선형적 상관관계 부여
    X[:, 1] = 0.3 * X[:, 0] + 0.1 * np.random.randn(100)

    # PCA 모델 생성
    pca = PCA(n_components=1)

    # PCA 모델 학습
    pca.fit(X)

    # PCA 수행
    transformed_data = pca.transform(X)
    # 차원 변화 확인
    print(transformed_data.shape)
    # 주성분 확인
	print(pca.components_)
  • Covariance Matrix와 Eigenvalue, Eigenvector를 사용하기 위해서는 numpy에 내장되어 있는 함수들을 사용하면 된다. 
import numpy as np

if __name__ == '__main__':
    # 2차원 랜덤 데이터 생성
    np.random.seed(1)
    x = 5*np.random.rand(100, 2)

    # 첫 번째 특성에 대해 랜덤한 선형적 상관관계 부여
    X[:, 1] = 0.3 * X[:, 0] + 0.1 * np.random.randn(100)
    
    # Covariance Matrix 구하기
    cov_mat = np.cov(X.T)
    print(cov_mat)
    
    # Eigenvalue & Eigenvector 구하기
    eigenvalue, eigenvector = np.linalg.eig(cov_mat)
    print(eigenvalue, eigenvector)
    
    # 차원 변화 확인 (데이터 변환된 값)
    transformed_data = eigenvector.T.dot((X - X.mean()).T).T
    n_component = 1
    pca_data = transformed_data[:,:n_component]
    print(pca_data.shape)
반응형

이상치 제거에서 통계적인 방법은 유용하게 사용되지만, 다루는 데이터가 복잡하고, 차원이 커질수록, 단순 분포의 개념을 활용하기는 어렵다. 이를 해결하기 위한, 이상치 제거 방법 중, 머신 러닝 기반 방법들을 몇가지 알아보기로한다. 


머신러닝을 이용한 이상치(Outlier) 제거 방법

1. Cook Distance를 이용한 방법

  • Cook Distance는 회귀분석 문제에서 이상치를 찾기 위해 많이 사용되는 방법이다.
  • 각 데이터포인트가 회귀분석 모델의 예측력에 어느 정도 영향을 미치는지를 확인하여, 이상치 제거에 활용할 수 있다. (해당 데이터 포인트를 제거한 모델이 오히려 더 좋은 예측력을 가질 때, 해당 데이터 포인트를 이상치로 간주할 수 있다. )
  • Cook Distance를 이용한 이상치 제거의 단계는 다음과 같다.
    1. 데이터 전체를 이용해서, 회귀 모델을 예측한다.
    2. 각 데이터 포인트를 제외한 데이터들로 회귀 모델을 예측한다.
    3. Cook Distance를 구한다.
    4. Cook Distance가 일정 값 (일반 적으로 1) 이상인 값들은 이상치로 취급한다.  
  • Cook Distance의 수식은 다음과 같다. 

Cook Distance

 

  • 파이썬 코드 구현
    • 파이썬의 statsmodels 패키지를 사용하면, 쉽게 Cook Distance를 구할 수 있다.
import numpy as np
import statsmodels.api as sm



if __name__ == '__main__':
    
    x = np.random.rand(10)*100
    y = 0.8*x+np.random.randn(10)*5

    alpha = 70
    beta = 10

    X = np.append(x,alpha)
    Y = np.append(y,beta)
    
    model = sm.OLS(Y, X).fit()

    influence = model.get_influence()
    cd, _ = influence.cooks_distance

    outliers = np.where(cd > 1)[0]

 

2. DBSCAN을 이용한 방법

  • DBSCAN(Density-Based Spatial Clustering of Applications with Noise)은 머신 러닝에 주로 사용되는 클러스터링 알고리즘으로 Multi Dimension의 데이터를 밀도 기반으로 서로 가까운 데이터 포인트를 함께 그룹화하는 알고리즘이다.
  • DBSCAN에 대한 자세한 내용은 https://devhwi.tistory.com/7을 참고하면 된다. 
  • DBSCAN에서 어느 Cluster에도 속하지 못한 데이터들을 Outlier로 판단한다. 

 

  • 파이썬 코드 구현
    • 파이썬의 statsmodels 패키지를 사용하면, 쉽게 Cook Distance를 구할 수 있다.
from sklearn.cluster import DBSCAN
import numpy as np
from sklearn.datasets import make_blobs


if __name__ == '__main__':
    X, y = make_blobs(n_samples=1000, centers=5, random_state=10, cluster_std=1) # 데이터 생성
    x = np.array([[-7.5,-3]]) # 이상치 데이터
    X = np.concatenate((X,x), axis=0)
    
    # DBSCAN 알고리즘 적용
    dbscan = DBSCAN(eps=1.5, min_samples=2)
    dbscan.fit(X)

    # 이상치 제거
    mask = np.zeros(len(X), dtype=bool)
    mask[dbscan.labels_ == -1] = True
    X_cleaned = X[~mask]

 

 

Cook Distance나 DBSCAN 말고도, 의사결정트리를 이용한 방법이나, LOF를 이용한 방법 등이 있다. 다시 한번 명심할 것은 이상치로 구해진 값들이, 실제 모델링에서 제거해도 되는 값들인지 꼭 확인해보아야한다. 

'Data Science' 카테고리의 다른 글

DTW(Dynamic Time Warping)  (1) 2023.06.02
PCA(Principal Component Analysis)  (1) 2023.05.04
이상치(Outlier) 제거 방법(1) - 통계적 방법  (5) 2023.03.19
반응형

이상치 제거는 데이터 분석에서 매우 중요하다. 특히, 요즘에는 어떤 모델을 사용하나 보다, 어떤 데이터로 학습할지가 모델 성능에 더 중요한 요소가 된 만큼, 이상치 제거는 그 중요성이 더욱 커졌다. 이상치 제거 방법은 정말 많지만, 자주 사용하는 몇 가지 방법을 알아보기로 한다.


이상치(Outlier) 란?

  • 이상치란 일반적인 데이터 분포를 따르지 않는 값으로, 다른 데이터와 차이가 매우 큰 값을 가진 데이터 포인트를 의미한다. 
  • 이상치가 생기는 요인은 데이터 수집 과정에서 오류가 발생하거나, 데이터 자체가 이상치를 포함하고 있는 경우, 변경점 발생으로 인한 데이터 분포 변화 등이 존재한다.
  • 이상치는 상대적인 개념이다. 즉, 어떤 데이터를 어떻게 분석하고, 어느 기준으로 이상치를 판별할 것이냐에 따라, 이상치 데이터들이 달라진다.

이상치(Outlier) 제거 방법

1. 사분위수(Quartiles) 방법

  • 사분위수 방법은 데이터분포와 값의 크기를 이용하여, 대략적인 이상치 구간을 설정해주는 방법이다.
  • 간단하고, 직관적이여서, 이상치 제거에서 많이 활용된다.
  • 통계적인 값을 기반으로 해서, 1D 데이터에서도 쉽게 활용 가능하다. 
  • 방법은 다음과 같다. 
    • 데이터를 값 기준으로 정렬한 후, 데이터의 분포에 따라 4등분하여, 각 부분의 값을 각각 1사분위, 2사분위, 3사분위, 4사분위로 나타낸다. (하위 0~25%, 25~50%, 50~75%, 75~100%)
    • 1사분위 수(값 기준 하위 25%되는 값)와 3사분위 수(값 기준 상위 25%되는 값)을 찾는다.
    • 3사분위 수와 1사분위 수의 차로 IQR(Interquartile Range)을 구한다. 
    • 일반적으로 1사분위 수와 3사분위 수에서 IQR의 특정 배수(일반적으로 1.5) 이상 벗어난 값들을 이상치로 판정하고 제거해준다.

  • 파이썬 코드 구현
import numpy as np

def outlier_remove(data, threshold=1.5):
	q1, q3 = np.percentile(data, [25, 75]) # 1사분위수, 3사분위수 계산
	IQR = q3 - q1 # IQR 계산

	lower_bound = q1 - (threshold * IQR) # Outlier 판단 Lower Bound 계산
	upper_bound = q3 + (threshold * IQR)  #Outlier 판단 Upper Bound 계산
    
	filtered_data = [x for x in data if x >= lower_bound and x <= upper_bound]
	outlier = [x for x in data if x not in filtered_data]
    
	return filtered_data, outlier, q1, q3, iqr, lower_bound, upper_bound

if __name__ =='__main__':
	X = np.random.normal(0, 1000, 1000)
	filtered_data, outlier, q1, q3, IQR, lower_bound, upper_bound = outlier_remove(X)

 
2. Z-score 방법

  • Z-score 방법은 데이터의 평균과 표준편차를 이용해, 이상치를 제거하는 방법이다. 
  • Z-score 방법은 데이터의 분포가 정규분포를 따른다는 가정이 필요하다. 
  • 통계적인 값을 기반으로 해서, 1D 데이터에서도 쉽게 활용 가능하다.
  • 방법은 다음과 같다.
    • 데이터 포인트들의 평균과 표준편차를 구한다.
    • 각 데이터 포인트의 Z-score (Z = |x - μ|/ σ)를 구한다. Z-score는 데이터 포인트가 평균과의 거리가 몇 sigma 범위에 있는지를 의미한다.
    • Z-score가 특정 threshold(일반적으로 3) 이상인 값들은 이상치로 판정하고, 제거해준다.

  • 파이썬 코드 구현
import numpy as np

def outlier_remove(data, threshold=3):
    z_scores = np.abs(data - np.mean(data)) / np.std(data) # Z-score 계산
    
    filtered_data = data[z_scores < threshold]
    outlier = data[z_scores>threshold]
    
    return filtered_data, outlier

if __name__ == '__main__':
    X = np.random.normal(0, 1000, 1000)
    filtered_data, outlier = outlier_remove(X)

 

+ Recent posts