반응형

Transformer 배경 설명

  • Transformer는 Google Brain이 2017년 "Attention is All You Need"라는 논문에서 제안된 딥러닝 모델이다.  
  • Transformer는 기존 자연어 처리 분야에서 주로 사용되던 RNN, LSTM 같은 순환 신경망 모델 중심의 처리 방법의 대안을 제공하여, 현재는 자연어 처리 분야에서 가장 널리 사용되는 모델 중 하나가 되었다.

사전 지식

  • 기존의 순환 신경망을 사용한 자연어 처리는 아래와 같이 Encoder를 이용해서 Context를 생성하고, Decoder를 따르는 구조를 가졌다. 

Abstract

  • 기존의(당시) 자연어 처리 분야의 논문에서는 Encoder와 Decoder에 복잡한 순환 모델이나 CNN 구조를 적용하는 방법이 지배적 이었다. 
  • 이 논문에서는 Transformer라는 Attention 메커니즘만 사용한 모델을 제안한다.
  • 실험에서 Transformer는 기존 모델에 비해 병렬적으로 학습하여, 더 빠른 시간에 학습하였음에도, 번역 분야에서 압도적인 성능을 보여준다. 

Introduction

  • 순환 모델에서는 일반적으로 Sequence를 처리하기 위해, 각 시간축을 따라 데이터를 참조한다. (즉, t 지점의 Feature를 생성하기 위해서는 t-1 까지의 값을 모두 참조해야한다.)
  • 이러한 구조는 전시점의 데이터들을 함께 필요로하기 때문에, 긴 sequence를 처리할 때(Sequential computation), 메모리 제약이 많이 걸린다. (학습 시, 병렬 처리가 어렵다.)
  • Sequential computation을 효율적으로 수행하기 위해, 여러 방법(Factorization trick, Conditional computation, 보통은 참조하는 시간 값을 줄이거나 뽑는 방법을 사용함) 등의 방법이 나왔지만, 근본적인 순환 모델의 한계(Sequential 구조때문에 메모리 제약이 큼, 병렬 처리가 어려움)는 극복하지 못한다. 
  • 이 논문에서는 순환 모델이 아닌, 전체를 Attention 메커니즘으로 처리하여, input과 output간 global 의존성을 모델링하는 "Transformer" 구조를 제안한다.  

Background 

  • Sequential computation을 줄이기 위해, CNN을 사용한 여러 논문들이 있었지만, 이 구조들로는 Input과 Output의 관계를  모델링하기는 어려움.
    • Transformer에는 "Multi-Head Attention" 메커니즘을 사용해서 여러 sequence의 Attention을 독립적으로 계산하고, 이를 결합해서, input과 output간 global 의존성을 모델링한다. 
  • "Self-attention" ("infra-attention"이라고도 함, input sequence의 각 위치에서 다른 위치들과의 상관 관계를 계산하는 기법)을 사용함. 
  • "End-to-end memory network" 구조로 구성된다. 

 

Model Architecture

  • 기본적으로, Transformer의 Encoder와 Decoder의 각 Layer들은 1) self-attention과 2) point-wise fully connected layer들로 구성된다.

  • Encoder
    •  Encoder는 동일한 6개의 layer들로 구성되어 있다. (그림에서 N=6)
    • 각 layer들은 2개의 sub layer들로 구성되어 있고, 각각 multi-header self-attention layer와 position-wise fully connected layer이다. 
    • 각 sub-layer들에는 residual connection을 연결해주었고, Layer Normalization(각 시점마다의 Normalization을 수행, 각 시점마다의 데이터 분포가 달라지는 것을 방지해줌)을 처리했다.
  • Decoder
    • Decoder도 동일한 6개의 layer들로 구성되어 있다.
    • Encoder의 2개의 sub layer(multi-header self-attention layer와 position-wise fully connected layer)에 추가적으로 encoder들의 output에 multi-head attention을 처리해줄 layer를 추가하여, Decoder의 각 layer를 구성한다. 
    • Encoder와 마찬가지로 각 sub-layer들에는 residual connection과 Layer Normalization 과정이 포함된다.
    • Decoder는 후속 데이터를 참조하면 안되기 때문에(실제 데이터에는 후속 데이터를 볼 수 없기 때문), self-attention sub-layer를 개조하여,  masking을 구현했다.
  • Transformer에서 사용되는 Attention
  • Scaled Dot-Product Attention : Query와 Key 사이의 유사도에 기반하여 Attention을 구하는 방식을 사용한다. 유사도 계산은 Attention Function에서 자주 사용하는 Dot Product를 기반해서 구한다. (빠르고, 공간 효율적이여서) scaling factor인 sqrt(d_k)를 나눠주는 이유는 d_k가 큰 값을 가질 때, Dot Product 값이 너무 커져서, Gradient가 매우 작아질 수 있기 떄문이다. 

→ 의미 : Query가 Key와 얼마나 유사한지에 따라 Value에 Attention을 가하겠다. (유사할수록 많이)

 

  • Multi-Head Attention : Query, Key, Value를 그냥 사용하는 것보다 각각 학습된 Weight들을 곱해준(Linearly Projection) 값을 사용하는 것이 효율적이라고 한다.(논문에서) Query, Key, Value를 Linearly Projection한 값으로 Scaled Dot-Product Attention을 구한 후, 각 값들을 concatenate 하고, 다시 그것에 Weight를 곱해서 Multi-Head Attention을 구한다.  

  • Transformer에서 Attention은 어떻게 사용되나?
    • Transformer에는 3가지 방식으로 multi-head attention을 사용한다. 
      1. "Encoder-Decoder Attention" : Decoder의 이전 시전 값과 현재 시점 값으로 구한 self-attention 값을 query로, Encoder들의 output을 이용하여 key, value를 구해서 multi-head attention을 적용한다. Decoder의 각 위치가 Input Sequence의 모든 위치들을 참조하도록 도와준다. 
      2. Encoder는 self-attention layer들을 가지고 있다. Key, Query, Value는 모두 Encoder의 전 Layer의 Output이다. Encoder의 각 위치가 다른 위치들의 값을 참조하도록 도와준다.  
      3. Decoder 단에도 self-attention layer들이 있다. Decoder의 각 위치가 다른 포지션을 참조하도록 해준다. (다만, 다음 시점의 데이터들은 참조하면 안되기 때문에 masking한다. )
    • 특히, 2,3번의 self-attention은 RNN을 사용하지 않고도, Sequence 데이터 처리에서 다른 시점의 데이터들을 참조할 수 있도록 한다. 
    • Attention을 정리하면 다음과 같다. 

Attention 인자 의미
1. Encoder-Decoder Attention Q : Decoder 단의 Vector
K : Encoder 단의 Vector
V : Encoder 단의 Vector
현시점의 Decoder의 Vector 값이 Encoder 단에서의 Vector 값과의 유사도 만큼, Encoder 값을 참조하는 가중치를 조정하겠다는 의미
2. Encoder Self-Attention Q : Encoder단의 Vector
K : Encoder 단의 Vector
V : Encoder 단의 Vector
현시점의 Encoder의 Vector 값과, Sequence 내 다른 Encoder 단의 Vector 값과 유사도를 측정하여 참조하는 가중치를 조정하겠다는 의미 → Sequence 내 다른 값도 함께 참조 가능 (RNN 기능을 self-attention으로 구현)
3. Decoder Self-Attention Q : Decoder 단의 Vector
K : Decoder 단의 Vector
V : Decoder 단의 Vector
현시점의 Decoder 의 Vector 값과, 이전 시점의 Decoder Vector 값과 유사도를 측정하여 참조하는 가중치를 조정하겠다는 의미 → Sequence 내 다른 Decoder 값도 함께 참조 가능
  • Position-wise Feed-Forward Networks : 앞서 말한대로, Encoder의 2번째, Decoder의 3번째 sub-layer는아래와 같은 fully-connected layer와 Relu로 구성되어 있다. 

  • Embeddings and Softmax : Embedding으로는 학습된 Embedding을 사용함. Decoder Output을 다음 Token의 확률로 변환하기 위해 Linear Transformation과 Softmax를 이용한다. 
  • Position Encoding : Input Sequence의 단어 위치 정보를 Embedding에 추가해서, 단어의 순서 정보를 이용할 수 있도록 Position Encoding을 사용. Position Encoding을 단어의 Embedding Vector에 더해서 사용.

Results

  • 기계 번역 : WMT 2014 English-to-German translation, English-to-French translation에서 State-of-the-arts 달성
  • 영어 문장 구문 분석 : 언어 모델의 일반화를 위한 실험, Task 튜닝 없이도 거의 SOTA에 근접한 성능 달성

 

출처

Vaswani, A., Shazeer, N., Parmar, N., Uszkoreit, J., Jones, L., Gomez, A. N., ... & Polosukhin, I. (2017). Attention is all you need. In Advances in neural information processing systems (pp. 5998-6008).

반응형

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)
반응형

t-SNE는 딥러닝 모델에서 feature의 유사도를 파악하기 위해 시각화할 때, 정말 많이 사용했던 방법이다. 단순 차원축소를 해주는 알고리즘이다라고만 이해하고 있었는데,  이번 기회에 완벽히 이해해보고자 한다. 


t-SNE 이란?

  • t-SNE(t-distributed Stochastic Neighbor Embedding)는 고차원 데이터를 저차원 영역으로 표현하기 위한 비선형 차원 축소 기법이다.
  • 여러 Feature 들의 차원 중에서 의미가 큰 차원을 선택하는 Feature Selection과 달리, 고차원 데이터의 구조와 패턴을 유지하면서, 차원 축소를 가능하게 한다.  
  • 딥러닝 등의 중간 Layer의 Output Feature 들은 대체적으로 고차원의 영역의 데이터이다. 이러한 데이터들은 직관적으로 이해하기가 어렵기 때문에, 저차원으로 시각화하여 Visualization을 진행하는데, t-SNE가 자주 사용된다.
  • PCA(주성분 분석)과 차원축소의 개념에서는 비슷하지만, 목적과 방식이 달라, 혼용해서 보완 사용하기도 한다.
  t-SNE PCA
목적 고차원 데이터를 저차원으로 바꿔, 시각화
(유사한 데이터는 가깝게, 다른 데이터는 멀게)
고차원 데이터를 저차원으로 바꿔, 데이터의 구조와 패턴을 파악 
방법 확률 분포를 이용하여 고차원 데이터와 저차원 데이터 간의 유사도를 계산하고, 최적화함. 데이터의 분산을 최대한 보존하는 축을 찾아서 차원을 축소함 (고유값 분해)
알맞은 데이터 유형
선형적 & 비선형적 데이터 모두 (데이터 간 유사도가 극명하면 유리) 데이터가 선형적으로 구성되어 있는 경우 
주의점 저차원에서의 유사도 최적화 과정이 포함되어서, PCA보다 computation cost가 높음
최적화 과정이 있기 때문에, 데이터셋 구성에 따라 결과가 달라짐
비선형적 데이터에 대해서는 성능이 떨어짐

t-SNE 알고리즘

  • t-SNE의 원리는 쉽게 말해서, 고차원 공간에서 가까운 것은 저차원에서도 가깝게, 고차원에서 먼 것은 저차원에서도 멀게 유지하는 것이다.
  • 기본적으로 t-SNE는 고차원 데이터 포인트들간 유클리디안 거리를 이용해서, 유사도를 조건부 확률로 바꿔서, 유사도를 나타내겠다는 SNE를 개선한 방법이다.
  • SNE의 유사도를 조건부 확률로 바꾸는 것이 처음에 잘 이해가 안 되는데, 쉽게 생각하면, Gaussian 분포의 PDF를 생각하면 된다. 이 식에서 평균이 데이터 포인트 i라고 하면, 평균이 i 포인트인 Gaussian 분포를 생각하면 된다. 즉, 한 데이터 포인트를 중심으로 다른 데이터 포인트 간의 거리는 정규 분포를 따른다고 생각하면 된다. 

  • 우선, t-SNE를 이해하기 위해 SNE 원리를 이해해보자.  

 

[SNE 원리]

1. 각 데이터 포인트간 Euclidean Distance를 구한다.

2.  두 데이터 포인트 간 거리를 d라고 했을때, 두 데이터 포인트 사이의 거리가 d 일 확률은 다음과 같은 정규분포의 조건부 확률로 나타난다. 즉, 거리를 다음과 같이 나타낸다. (p(i|j)와 p(j|i)가 다르다는 점을 주목해야한다.)

3. SNE의 원리는 고차원의 거리를 그대로 유지하는 저차원에서의 모델을 찾는 것이다. 저차원에서 σ의 제곱(분산)이 1/2 값을 갖는 분포를 가정하면, 저차원에서 데이터 분포 간의 거리는 다음과 같이 나타낼 수 있다.  

4. 학습의 목적은 고차원에서 데이터 포인트간의 거리를 나타내는 분포 p를 제일 잘 표현할 수 있는 저차원의 근사분포 q를 찾는 것이다. qp와 비슷한 분포가 되기 위해, 두 분포간의 KL(Kullback-Leibler) Divergence가 작아지도록 학습한다. (Gradient Descent를 이용, 두 분포가 완전히 같아졌을때, KL divergence는 0)

 

  • SNE도 좋은 방법이지만, t-SNE는 SNE보다 다음과 같은 면을 수정했다.

 

1. 비대칭성 → 대칭성

조건부 확률 기반에서, 고차원의 두 데이터 포인트 i와 j 사이의 거리인 p(i|j)와 p(j|i)는 다르다.(Sigma가 다르다) t-SNE에서는 둘 사이의 거리를 정의하는 확률을 아래와 같이 수정하여 대칭성을 부여하였다. (이로 인해, 최적화 속도가 빨라졌다. → 어느 방향에서도 동일하게 수렴 가능하기 때문에 안정적이다.)

2. Gaussian 분포 → t-분포

고차원을 저차원으로 축소하다 보면, 고차원에서 데이터가 많이 분포하는 구간에서 축소된 데이터들은 서로 많이 뭉치게 된다. 10차원에서 봤을 때는 다양한 값들을 가진 데이터들이 2차원으로 차원 축소가 되면, 거의 비슷하게 보일 수 있다. (RGB 이미지에서 구분 잘되던, 그림들이 흑백에서 구분이 잘 안 되는 걸 생각하면 된다.) 이를 완화하기 위해서는 데이터의 분포가 고르게 퍼져있는 구조가 유리하다. t-SNE에서는 거리 분포를 Gaussian 분포 대신에 t 분포를 도입하여, 데이터를 퍼뜨렸다. 

t-분포

v: 자유도, B : 베타 함수

[t-SNE]

1. 각 데이터 포인트간 Euclidean Distance를 구한다.

2.  두 데이터 포인트 사이의 거리가d일 확률은 다음과 같은 조건부 확률로 나타난다. 다만, 앞에 말했던 대로, 대칭성 확보를 위해 두 데이터 포인트 사이의 거리를 다음과 같이 정의한다. (고차원의 거리 분포는 Gaussian 분포를 그대로 유지한다.)

3. t-SNE는 SNE와 달리, 저차원에서 분포를 t-분포로 가정한다. 따라서, 데이터 포인트 간 거리를 다음과 같이 나타낼 수 있다. (일반적으로 자유도가 1인 t-분포를 사용한다.)

 

4. t-SNE도 마찬가지로, 학습의 목적은 고차원에서 데이터 포인트 간의 거리를 나타내는 분포 p를 제일 잘 표현할 수 있는 저차원의 근사분포 q를 찾는 것이다. q p와 비슷한 분포가 되기 위해, 두 분포간의 KL Divergence가 작아지도록 학습한다. (Gradient Descent를 이용)

t-SNE 파이썬 구현

  • scikit-learn의 TSNE를 사용하면, 쉽게 TSNE를 사용 가능하다. 
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.manifold import TSNE

if __name__ == '__main__':
    # Data Load (MNIST: 64 dim)
    digits = datasets.load_digits()
    X = digits.data
    y = digits.target

    # t-SNE(64 to 2)
    tsne = TSNE(n_components=2, random_state=1)
    X_tsne = tsne.fit_transform(X)

    # t-SNE Visualization
    plt.scatter(X_tsne[:, 0], X_tsne[:, 1], c=y)
    plt.show()

반응형

💬 한국어 텍스트 데이터 전처리

  • 텍스트 데이터는 보통 그 자체로 사용하기보다, 의미의 단위로 나눠서 활용 여부나 사이 연관 관계를 찾는다.
  • 저번 장에서 확인한 대로, 데이터셋의 텍스트 데이터는 한국어 문장으로 구성되어 있다.
  • 학습할 때마다 한국어 형태소 분리를 실행해도 되지만, 시간이 너무 많이 걸려서, 텍스트 데이터를 전처리 해놓기로 한다. 

한국어 텍스트 데이터 전처리 방법

  • 텍스트 데이터의 전처리 단계는 다음과 같다.
1. 텍스트 데이터를 형태소 단위로 분리한다.
2. 분리된 데이터 중, 불용어를 제거한다. (Optional)
3. 동의어를 mapping 할 수 있다면, 동의어를 mapping한다. (Optional)
4. word dictionary를 생성
5. word를 정수 인코딩한다.

1. 한국어 형태소 분리 (Tokenization)

  • 한국어의 경우에는 영어와 달리, 의미의 단위가 띄어쓰기와 정확히 일치하지 않는다.
  • 과거부터 한국어 형태소 분리가 연구되어 여러 방법들이 존재한다.
  • 파이썬은 "konlpy" 라이브러리에서 다양한 형태소 분석기를 제공한다. (자세한 내용은 https://konlpy.org/ko/latest/index.html 참조)
  • konlpy에서는 다양한 형태소 분석기를 제공해 주지만, 그중 "Okt"(Twitter에서 개발한 오픈소스 한국어 처리기)를 사용하여 한국어 형태소 분리 처리를 하기로 한다. (선택한 이유는 속도 때문이다.)
처리 전 처리 후
"아버지가 방에 들어가신다."  ["아버지","가","방","에","들어가신다","."]

2. 불용어 처리

  • 불용어(Stopword)는 분석에 필요하지 않거나, 분석 결과에 영향을 미치는 단어를 말한다. (예시: 은,는,이,가)
  • 자연어 처리에서는 불용어 제거가 매우 중요하다. 불용어는 실제 분석에는 이용되지 않지만, 일반적으로 문장에서 등장하는 빈도수가 높아져서, 해당 단어들이 중요한 단어로 인식될 수가 있다. 
  • 또한, 불용어 제거는 전처리 과정에서 처리할 텍스트 데이터의 양이 줄어서 처리 속도 향상을 위해 꼭 필요하다. 
  • 다만, 불용어는 분석의 목적에 따라 달라지는 상대적인 개념이기 때문에, 분석에 영향을 미치지 않는 단어만 넣도록 한다. 예를 들어, 일반적으로 '?'는 Elastic Search 등의 텍스트 검색을 위한 처리에서 불용어 처리가 될 수 있지만, 우리가 다루고자 하는 비윤리적 텍스트 검출에서는 상대를 비꼬는 문장을 찾을 수 있는 중요한 단서가 되기도 한다. 
  • 일반적으로 특수문자는 문장에서 제거한 후에, 한국어 형태소 분리하지만, 해당 Task에서는 특수문자가 분류에 많이 사용될 것으로 생각되어서, 실제 사용되지 않을 것으로 보이는 특수문자만 불용어로 처리하였다.  
불용어 예시 : 

stopwords = ['','','','','','','','','','','으로','','하다','!','?','<','>','(',')','[',']','|','#','.']

3. 동의어 처리

  • 동의어 처리는 일반적으로 텍스트 검색 기능에서 유사한 단어를 찾기 위해 자주 사용된다. 
  • 비윤리적 텍스트 문장 검출 데이터셋에서는 일반적인 단어를 특수문자나 축약어로 표현해 놓은 데이터가 많다. (예시: ㅇㅋ, Ok, 오키, 옿키)
  • 좋은 성능의 검출 모델을 만들기 위해서 동의어 처리도 매우 중요할 것으로 생각되지만, 현실적으로 공수가 너무 많이 소요되어, 별도의 동의어 처리는 하지 않았다. 

4. Word Dictionary 생성 

  • 학습 데이터셋을 형태소 분리하고, 불용어와 동의어 처리까지 처리한 후, 학습 데이터를 기반으로 워드 딕셔너리를 생성한다.
  • 실사용 시, Word Dictionary에 포함되지 않는 단어가 등장했을 경우에는 모델은 해당 단어에 대한 정보를 사용할 수 없다. (자연어 처리 시, 다양하고 풍부한 학습 데이터가 필요한 이유이기도 하다.) 
  • 각 단어에 대한 고유의 번호를 지정한다. 
  • 모든 단어를 사용하는 것이 다양성 측면에서는 좋겠지만, 속도나 성능을 고려하면, word dictionary의 크기를 무작정 늘리는 것은 좋지 않다. (일반적으로 최대 단어 개수를 지정한다. )
  • 정수 인코딩과 모델 테스트 시 빠른 사용을 위해, 인덱싱(key: 숫자, value: word)과 역인덱싱(key: word, value: 숫자)을 모두 진행해 놓는 것이 좋다.  
문장 형태소 분리 Word Dictionary 인덱싱
"아버지가 방에 들어가신다." ["아버지","가","방","에","들어가신다","."]  {<unk>:0,<pad>:1,"아버지":2, "방":3, "들어가신다":4} {'0':<unk>,'1':<pad>,'2': "아버지", '3':"방", '4':"들어가신다"}

5. 텍스트의 Word를 정수 인코딩

  • 모델의 학습을 위해, 텍스트를 정수로 인코딩해주는 작업이 필요하다. 
  • Word Dictionary를 기반으로 학습 데이터의 각 단어들을 정수로 변환한다.
  • Word Dictionary에 포함되지 않은 불용어 등은 "<unk>"라는 wildcard로 치환한다. 이를 통해, 모델이 입력받는 word 데이터의 종류는 Word Dictionary에 존재하는 단어 개수로 제한된다. 
  • 정수 인코딩을 진행하면, 문장마다의 인코딩 벡터의 길이는 전부 다르다. 학습을 위해 데이터를 정형화하는 편이 좋기 때문에, 인코딩 벡터의 끝부분에 "<pad>"값을 넣어, 벡터의 길이를 모두 같게 만들어준다. 
처리 전 처리 후
"아버지가 방에 들어가신다."  2,0,3,0,4,0,1,1,1,1

 

데이터 전처리 코드 구현

  • 전처리 과정을 사전에 진행해 놓기 위한, 코드를 구현하였다. 다만, 학습과 테스트 시 코드 통일성을 위해, 정수 인코딩 부분은 모델 데이터셋 정의 과정에 넣었다. (테스트 데이터도 정수 인코딩은 진행해야 하기 때문에, Dataset 구성 파트에서 설명 예정)
  • 자연어 Dataset 처리를 용이하게 하기 위해, "torchtext"라는 pytorch에서 제공해 주는 자연어 처리용 패키지를 사용하였다. (하지만, pytorch는 데이터셋의 종류에 따른 전처리를 모두 통일하기 위해 현재는 torch를 사용하도록 권장하고 있다. )
  • Okt가 빠르긴 하지만, 처리 데이터가 많기 때문에 전처리에 시간이 많이 소요된다. 
  • 데이터 전처리 단계를 대략적으로 구성하였지만, 실사용에서는 불용어 지정이나 어느 종류의 형태소 분석기를 사용할 것인지, word dictionary를 어떻게 구성할 것인지 등이 성능을 결정하는 매우 중요한 단계이다. 
from konlpy.tag import *
from torchtext import data 
import json

import pandas as pd

tokenizer = Okt()
stopwords = ['의','가','에','들','는','잘','걍','과','도','를','으로','한','하다','!','?','<','>','(',')','[',']','|','#','.']

# 텍스트 전처리 함수
def norm_morphs(x):
    x = tokenizer.normalize(x) # 텍스트 Normalization
    x = tokenizer.morphs(x) # 형태소 분리
    x = [word for word in x if not word in stopwords] #불용어 처리
    return x


if __name__ =='__main__':
    # 데이터셋 위치 지정
    data_dir = r"..\korean_language\data"

    # ID: 문서의 번호, TEXT: 문장 데이터(전처리 함수를 지정할 수 있음), LABEL: 윤리성 유무를 나타내는 LABEL 
    ID = data.Field(sequential=False, use_vocab=False)
    TEXT = data.Field(sequential=True, use_vocab=True, tokenize = norm_morphs, batch_first=True, tokenizer_language='ko')
    LABEL = data.Field(sequential=False, use_vocab=False, is_target=True)

    # Torch Text의 splits를 이용해서, 데이터를 한번에 불러올 수 있다. 
    train_data, test_data = data.TabularDataset.splits(path=data_dir, train='train', test='test', format='tsv', fields=[('id',ID), ('label',LABEL),('temp1',None),('temp2',None),('temp3',None),('text',TEXT)], skip_header=True)

    # word dictionary를 만듬 (최대 크기와, 최소 빈도수를 지정)
    TEXT.build_vocab(train_data, min_freq=2, max_size=100000)

    # word dictionary를 저장
    with open('./dictionary.json','w') as f:
        json.dump(TEXT.vocab.stoi, f, ensure_ascii=False, indent=4)

    # Index dictionary를 저장 
    index_dict = {v: k for k, v in TEXT.vocab.stoi.items()}
    with open('./index_dictionary.json','w') as f:
        json.dump(index_dict, f, ensure_ascii=False, indent=4)


    id_list = []
    text_list = []
    label_list = []
    df = pd.DataFrame()

    for id, data_dict in enumerate(train_data):
        id_list.append(id)
        text_list.append('|'.join(data_dict.text))
        label_list.append(data_dict.label)

    df['id'] = id_list
    df['text'] = text_list
    df['label'] = label_list
    df.to_csv('train.csv', index=False,sep = '#')

    id_list = []
    text_list = []
    label_list = []
    df = pd.DataFrame()

    for id, data_dict in enumerate(test_data):
        id_list.append(id)
        text_list.append('|'.join(data_dict.text))
        label_list.append(data_dict.label)

    df['id'] = id_list
    df['text'] = text_list
    df['label'] = label_list

    df.to_csv('test.csv', index=False,sep = '#')


    print("Train Data :",len(train_data))
    print("Test Data :",len(test_data))

 

전처리를 진행하였으니, Dataset을 정의하고, 모델을 만들어볼 차례이다!

반응형

Logistic Regression

Logistic Regression의 원리는 이진분류에서 다뤘다. 하지만, 실제 데이터 분석 시에는 이진 분류보다는 다중 클래스 사이에서 데이터가 어느 클래스에 속하는지를 분류하는 문제가 많다. 사실 다중 분류를 위한 로지스틱 회귀는 이진 분류와 내용 차이가 크게 없지만, 많이 사용하는 만큼 따로 나눠서 소개를 하고 한다. 


다중 Logistic Regression 이란?

  • 3개 이상의 클래스 중, 하나의 클래스에 속하는지를 예측하는 문제이다.
  • 이진 분류와 가장 다른 점은, 이진 분류는 분류 문제의 정답이 '참'일 확률(p)만 고려하여, '참'과 '참이 아님'을 분류하면 되지만, 다중 분류는 여러 가지 클래스 중, 예측 모델이 속할 확률이 가장 높은 클래스를 찾는 문제이다.
  • 즉, 정답이 이진 분류에서는 정답 예측을 한 차원('참'일 확률 p)에서만 끝낼 수 있었지만, 다중 분류의 예측은 클래스의 개수만큼의 예측 결과가 필요하다. 
  • 이러한 다중 분류를 위해 다중 로지스틱 회귀 모델에서는 일반적으로 Softmax 함수를 사용한다. 

※ Softmax 함수
-  K개의 클래스가 있는 다중 분류 문제에서 j번째 softmax 함수 결과는 아래와 같이 나타난다.

 

-  Softmax 함수는 Logistic 함수의 일반화된 형태이다. 
-  Softmax 함수는 각 클래스에 속할 확률(p)을 각 클래스들에 속할 확률들의 합으로 나눠서, 0부터 1 사이로 Normalization 한 형태이다. 
-  일반적으로 Softmax 함숫값이 가장 큰 클래스로 예측 모델의 결과를 낸다. 

 

다중 Logistic Regression의 학습 방법

  • 다중 Logistic Regression의 학습을 위해서도 최대우도방법(MLE, Maximum Likelihood Estimation)을 사용한다.
  • 다중 Logistic Regression의 학습을 위해서, 학습에 사용되는 정답을 One-hot vector 형식으로 나타내야 한다. 
  • 다중 Logistic Regression의 학습을 위해서는 일반적으로 크로스 엔트로피(Cross Entropy) 함수를 사용하여, 목적 함수를 정의한다. 

[크로스 엔트로피]

  • 크로스 엔트로피는 정보 이론에서 사용되는 개념으로, 두 확률 분포가 얼마나 다른지를 측정하는 방법이다. 
  • 크로스 엔트로피 식은 다음과 같다.

p: 실제 확률, q: 예측 확률, n: 분포의 원소 개수

  • 크로스 엔트로피의 그래프를 확인해 보면, 예측 확률 q가 실제 확률 p과 동일한 지점에서 최솟값을 나타냄을 확인할 수 있다. 
  • 즉, 크로스 엔트로피가 최솟값을 가지는 구간이 실제 확률과 가장 비슷한 예측을 하였다고 할 수 있다.
  • 분류 문제에는 종속 변수가 범주형(각 클래스에 속할 확률이 0 or 1)으로 나타나기 때문에(One-Hot vector), 첫 번째 그래프처럼 실제 분포를 정확히 예측하였을 때, '0' 값을 갖는다.

[One-Hot Vector]

  • One-Hot vector는 범주형 데이터를 다루기 위해, 사용되는 벡터 표현 방법이다. 
  • 클래스에 정수형 범주를 붙인다면, 클래스 간의 대소 관계와 순서가 모델의 학습과정에서 고려되어, 숫자형 범주 자체를 '중요도'로 오해하여 학습될 수 있다. 이를 막기 위해 One-Hot vector를 이용하여 정답 레이블을 표시한다.
  • K개의 클래스가 있는 범주형 데이터가 있는 분류 문제에서, 해당 데이터가 실제로 i번째 클래스에  속해있다고 하면, i번째 데이터를 제외한 K-1개 클래스는 0으로, i번째 클래스는 1의 값을 갖는다.
  • One-Hot vector는 해당 클래스에 속할 확률을 나타낸다고 생각할 수 있다. 

[다중 로지스틱 회귀 목적함수]

    • 다중 로지스틱 회귀를 이용한 분류 문제의 목적은 실제 데이터가 속한 클래스와 비슷한 분포를 예측하는 모델을 만드는 것이다.
    • 위의 크로스 엔트로피 식에서 p를 실제 데이터의 분포를 One-Hot vector로 변환한 값, q를 모델이 예측한 분포라고하면, 크로스 엔트로피 값이 최소인 지점이 모델이 실제 데이터를 가장 잘 예측하였을 때라고 생각할 수 있다.
    •  결국 다중 로지스틱 회귀 모델의 학습의 목적은 Softmax 함수의 형태로 구해진 예측 모델의 결과가 실제 정답의 분포에 가장 유사한 형태를 갖도록, 크로스 엔트로피 값을 최소화할 수 있는 모델 파라미터를 찾는 것이다.  

y : 실제 정답 레이블(one-hot vector 형태), K: 클래스 개수, N: 데이터 개수 

  • 다중 로지스틱 회귀 목적함수는 다음과 같다. 

Logistic Regression의 Python 구현

  • 다중 Logistic Regression은 Python의 Sklearn을 이용하여 쉽게 구현 가능하다.
from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score


if __name__ == "__main__":
    # 데이터 Load (iris 데이터 사용, X :꽃의 길이와 너비, y: 꽃의 종류, 3개의 class)
    iris = load_iris()
    X = iris.data[:, :2]
    y = iris.target
        
    # 데이터 분할
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=5)

    # 모델 학습
    clf = LogisticRegression(random_state=1)
    clf.fit(X_train, y_train)

    # 모델 예측
    y_pred = clf.predict(X_test)

    # 정확도 계산
    acc = accuracy_score(y_test, y_pred)
    print("Accuracy:", acc)

 

반응형

Logistic Regression

Linear Regression을 통해서, 연속적 값에 대한 출력 변수를 모델링을 할 수 있다. 하지만, 연속 데이터의 예측 문제에서 Linear Regression은 효과적이지만, 연속적인 값을 추측하기 때문에 분류 모델에서는 적합하지 않다. 분류 문제를 푸는 방법에는 여러가지가 있지만, 오늘 알아볼 Logistic Regression에 대한 이해가 필수적이다. 


Logistic Regression 이란?

  • 이진 분류 문제(입력 값을 2가지 유형으로 나누는 문제)에서 사용하는 분류 모델이다. 
  • Linear Regression과 비슷하게, 입력변수와 출력변수 간의 관계를 모델링하는데, Linear Regression과 달리 값의 범위를 제한하기 위해 일반적으로 Sigmoid 함수를 사용한다. (다른 함수를 사용할 수 있지만, 일반적으로 Sigmoid를 사용한다.)
  • Linear Regression은 입력변수와 출력변수의 선형적 관계에 초점을 맞췄다면, Logistic Regression은 Sigmoid 함수를 통해, 0에 가까운지 1에 가까운지를 모델링하는데에 초점을 맞췄다.
  • Sigmoid 함수

-   Sigmoid 함수는 이진 분류를 위해 사용하는 함수로 출력값을 0에서 1 값으로 제한하는데 사용된다.
-   Sigmoid 함수는 입력변수로 구해진 모델링 값을, 중간의 일부 구간을 제외하고, 0이나 1에 매우 가까운 근접 값으로 출력해준다. (아래 그래프를 보면 이해가 쉽다.)
-   Sigmoid 함수는 무엇보다, 모든점에서 미분이 가능하기 때문에, Back Propagation을 통한, Weight와 Bias 업데이트가 가능하다. 즉, 머신러닝 문제에서 활용 가능하다.
-   Sigmoid 함수는 아래 그래프에서 확인 할 수 있듯이, Z 값이 너무 크거나, 작을 때는 Gradient가 0에 가까워 지는 Gradient Vanishing 문제가 있다.  

 

 

  • Logistic Regression에서의 예측 출력값은 다음과 같이 나타난다. 

 

 

[Logistic Regression 식 유도] 

Logistic Regression의 식은 Odds를 Logistic 변환하여 유도된다. 


odds 식 :성공 확률 (y=1)이 실패 확률(y=0)의 몇 배인지 나타내는 식 


odds 식의 logit 변환 수행 (편의상, 성공확률을 p로 표기)

 logit 변환된 odds를 입력변수의 선형 변환 예측하면,


성공확률 p는 다음과 같다.

라하면, 


Logistic 식이 만들어진다.

 

Logistic Regression의 학습 방법

  • 일반적으로 Logistic Regression의 학습을 위해서는 최대우도방법(MLE, Maximum Likelihood Estimation)을 사용한다. (물론, Linear Regression과 같이, 최소제곱법을 사용 가능하지만, 효과적이지 않다.)
  • 최대우도방법은 주어진 파라미터를 통해, 실제 값이 나올 확률을 최대화하는 방향을 따라 학습하도록 하는 알고리즘이다.
  • 보통 학습은 경사하강법을 사용하여 학습된다.

 

[최대우도방법]

  • 최대 우도 방법은 입력변수(x)와 모델 파라미터(θ)가 있을 때, 실제 결과 값(y)가 나오는 Likelihood를 최대화하는 방법이다.   Likelihood 함수는 최대화하는 값을 구하는 것이, 입력 변수에 대한 실제 결과 값을 가장 잘 추론할 수 있는 파라미터를 찾는 것이다. (yi:i번째 Label - 0 or 1, pi: i번째 성공 확률)

  • Likelihood 함수의 최댓값을 그대로 구해도 되지만, 양변에 Log함수를 취하면, (x값에 따라 y값이 증가하므로, log를 취한 최댓값을 찾는 것은, log 취하기 전 값의 최댓값을 찾는 것과 같다. 보통은 학습의 안정성 때문에 활용한다.)

  • 결국, 최대우도방법은 가장 Log Likelihood가 가장 큰 값이 될 수 있는 모델의 파라미터(θ)를 추정하도록 학습하는 방법이다.

[경사하강법]

  • 경사하강법은 함수의 기울기를 이용하여, 함수의 최적값을 찾는 최적화 알고리즘이다. 
  • Logistic Regression에서 경사하강법은 다음과 같이 실행된다.

1. Logistic Regression에서 모델의 파라미터 θ를 학습하는 것은 아래 식을 최대화하는 θ를 찾는 과정이다.

2. 이것은 logistic likelihood에 음수값을 취한 값을 최소화하는  θ를 찾는 것과 같다. 따라서, 목적함수는 다음과 같다.

3. 모델 파라미터  θ를 무작위로 초기화한다.(후에 초기화를 위한 여러 방법도 있지만, 기본적으로 무작위이다.)

4. 목적함수를 최소화하는 방향으로  θ를 업데이트한다. 

5. 특정 Epoch이나, 조건이 될때까지, 4번을 반복한다. 

 

Logistic Regression의 Python 구현

  • Logistic Regression은 Python의 Sklearn을 이용하여 쉽게 구현 가능하다.
  • 특히, classification 문제는 패키지로 쉽게 load 가능한 데이터들이 많다. (구현에서는 iris 사용)
from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score


if __name__ == "__main__":
    # 데이터 Load (iris 데이터 사용, X :꽃의 길이와 너비, y: 꽃의 종류, 3개의 class)
    iris = load_iris()
    temp_X = iris.data[:, :2]
    temp_y = iris.target
    
    # 이진분류를 위해, class=0인 데이터들은 제외함
    indices = np.where(temp_y != 0)[0]
    
    X = temp_X[indices]
    y = temp_y[indices]
        
    
    # 데이터 분할
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=5)

    # 모델 학습
    clf = LogisticRegression(random_state=1)
    clf.fit(X_train, y_train)

    # 모델 예측
    y_pred = clf.predict(X_test)

    # 정확도 계산
    acc = accuracy_score(y_test, y_pred)
    print("Accuracy:", acc)

반응형

지난 장에서 Kubeflow를 겨우 설치하는 데 성공하였다. 아무래도 로컬 환경에서 자원을 쪼개서, Kubeflow를 돌리다 보니 조금 버벅거리는 감이 있지만, Kubeflow의 각 기능을 조금 더 자세히 알아보자. 가장 먼저 알아볼 기능은 Katlib이다. 사실, 클라우드 환경이 아니라 로컬에서 Kubeflow를 실행하다보니, Kubeflow UI에서 지원해 주는 Notebooks나 Tensorboard 기능은 사실 잘 와닿지 않았다. (로컬에서 실행하면 되기 때문에) 하지만, Katlib은 평소 네트워크 학습 과정에서 걸리던 하이퍼파리미터 튜닝등의 문제에 유용하게 사용할 수 있을 것 같아, Katlib부터 소개하기로 한다.  
 


Katlib

  • Katlib은 앞선 장에서  설명했듯, 하이퍼파라미터 최적화 & 뉴럴 아키텍쳐 탐색을 지원해 주는 프레임워크이다. (개인적으로 Katlib은 머신러닝 워크플로우를 쿠버네티스 환경에서 실행하는 가장 큰 강점을 보여주는 프레임워크라고 생각한다.)
  • Katlib는 각기  다른 하이퍼파라미터 설정이나, 아키텍쳐 탐색을 분산된 머신에서 동시에 실행하여, 최적의 값을 탐색한다. 
  • 쉽게 말하면, Katlib은 한번에 여러 머신에서 실험 가능하고, 사용하기 쉬운 머신러닝 모델 탐색용 Cron Job이라고 생각한다.   
  • Katlib은 아래와 같은 장점을 갖는다.
    • 자동화된 하이퍼 파라미터 튜닝 : 하이퍼 파라미터를 튜닝하는 일은 모델 개발자들에게는 번거로운 일이고, 서비스 제공자에게는 어느 정도 난이도가 필요한 일이다. Katlib에서 제공하는 자동화된 하이퍼 파라미터 튜닝은 머신러닝이나 Batch 프로그램에 대한 전문 지식이 없더라도 쉽게 사용 가능하다.
    • 분산 머신러닝 지원 : 머신러닝 연구자라면 가장 스트레스 받는 일 중 하나가 환경을 세팅하는 일이다. 머신 여러 개로 하이퍼파라미터를 각기 달리 실험할 때, 여러 개에 동일한 개발 환경을 세팅하는 것은 굉장히 번거롭고, 어려운 일이다. 특히, 소스 상에서 변경점이 생길 때마다, 모든 머신에서 반영이 필요하다. Katlib은 쿠버네티스를 이용하여, 클러스터 내의 여러 머신에서 모델 훈련이 실행되므로, 효율성을 높일 수 있다.  

 

Katlib 사용법

  • Katlib을 실행하기 위해서는 "Experiment" CRD(Custom Resource Definition)을 사용하여, 작업을 정의하고, 실행해야한다. 
  • 앞서 말한대로, Katlib은 Kubeflow 환경 하에서 뿐만 아니라, 그 자체로 독립적으로 실행할 수 있다. 하지만, Kubeflow하에서 사용하면 더욱 쉽게 사용할 수 있다.
  • Kubeflow 환경 하에서 Katlib은 Experiments(AutoML) 메뉴에서 사용 가능하다. 
  • "Experiment" CRD를 직접 작성하여 작업을 정의할 수도 있고, UI를 이용하여 쉽게 CRD를 작성 가능하다. 
Kubeflow UI 내, Katlib
  • Kubeflow UI에서 Experiments(AutoML)를 실행했을때, 총 8개의 설정이 필요하다. 
  • 첫 번째는, Metadata이다. 현재 수행에 대한 Job 명을 정할 수 있다.
  • 두 번째는, Trial Thresholds이다. 실험에 대한 분산처리를 지정해 줄 수 있다. 각 Parameter는 다음의 의미를 갖는다.
    • Parallel Trials : 동시에 몇개의 실험을 진행할 것인지를 정해줌(H/W의 리소스를 효율적으로 사용하기 위해 정해줌, 분산처리)
    • Max Trials : 최대 몇번의 학습을 진행할지를 정해줌(과도한 H/W 리소스 사용을 방지)
    • Max failed Trials : 최대 몇 번까지 실패한 학습을 용인할지를 결정해 줌. Katlib의 수행 자동화 기능을 지원하기 위해 들어간 파라미터로, 실험 세팅의 실수 등의 이유로 정상적인 실험이 진행되지 않았지만, 인간이 바로 인지하지 못하는 경우 무의미한 실험들이 H/W를 점유하고 있을 수 있어서, 이러한 무의미한 실험등을 방지하기 위해, 최대 실패 횟수를 정해서, 이를 초과한 실험 실패가 발생했을 시, 실험을 중단함.
    • Resume Policy : 실험이 중단되었을때, 다시 시장하는 방법을 지정해 줌. ('Never' : 실험이 중단되었을 때, 재개하지 않음. 'Long Running': 실험이 종료된 뒤에서 Pod 및 리소스를 유지하여, 데이터를 유지하고 실험 결과를 확인 가능함. 'From Volume' : 실험이 종료된 후, 해당 실험에 필요한 볼륨이 자동으로 마운트 되어, 실험에서 생성된 데이터나 파일을 계속 사용하여 실험을 재개함.)
  • 세 번째는, Objective이다. 모델 학습을 최적화하기 위한 최종목표를 정의 가능하다. Metric을 통해서, 여러개 Objective를 설정할 수 있고, 각 Metics에 대해, "Maximaze"와 "Minimize"를 정해, 탐색 목표를 정할 수 있다. 
  • 네 번째는, Search Algorithm이다. 모델 탐색 과정의 알고리즘을 선택할 수 있다. 크게 Hyper Parameter tuning과 neural Architecture Search에 대한 알고리즘을 제공한다.
  • Hyper Parameter tuning은 총 9개의 알고리즘을 제공한다. 그중 많이 사용하는 알고리즘은 아래와 같다.  
    • Grid : Grid Search(가능한 모든 조합 시도)
    • Random : Random Search(임의로 Hyper parameter 선택)
    • Bayesian Optimization : 후보 모델을 사용하여, Hyper parameter 조합을 평가하고, 그 Hyper parameter로 후보 모델을 업데이트하는 과정을 반복. 
  • Neural Artchitecture Search는 크게 2가지의 알고리즘을 제공한다.
    • Efficient Neural Architecture Search(ENAS) : 하나의 모델을 학습시켜, 새로운 모델을 생성하는 방식을 사용함. 파라미터 공유 및 Weight Sharing 기술을 사용해서, 여러 작업을 수행하고 구성요소를 동시에 학습 및 업데이트함.
    • Differentiable Architecture Search(DARTS) : 아키텍쳐 검색과 학습을 동시에 수행하는 end-to-end 학습 방법. 알고리즘이 다양한 아키텍처를 조합하는 데 사용되는 가중치를 학습함. 
  • 다섯 번째는, Neural Architecture Graph는 네트워크의 구성을 정의하기 위한, Input, Output 사이즈 및 Layer 갯수등을 지정한다. 
  • 여섯 번째는, Neural Architecture Operations이다. NAS에서 사용되는 기본 연산을 위한, 네트워크 구조 생성 및 변경에 사용된다. 모델의 구조를 생성하기 위한 파라미터등의 가능한 후보를 정의해서, NAS에서 모델을 찾는데 활용할 수 있다.  
  • 일곱 번째는, Metrics Collector이다. Metrics Collector는 각 실험들의 결과를 어떻게 수집하고, 측정할 것인지를 정의한다. Stdout처럼 단순 출력으로 받을 수도 있고, File이나, Prometheus 등을 통해서 수집 가능하다. 
  • 마지막으로, trial의 환경을 정해줄 수 있는 Trial Template이다. 이 메뉴를 활용하여, Trial에서 사용할 수 있는 도커 이미지나 하이퍼파라미터 설정, 리소스 제한 등을 정의할 수 있다. Template를 제공하기 때문에 해당 Template을 수정하는 구조로 실험을 진행할 수도 있다. 

 

  • 마지막 옵션까지 모두 설정하고, 아래 Edit 버튼을 누르면, 1~8까지 설정한 내용을 기반으로 "Experiment" CRD가 작성되어 있는 것을 확인할 수 있다. 
  • 설정이 제대로 들어가있는지 확인하고, 변경도 가능하다. 설정이 제대로 진행되었으면, CREATE를 통해 실험을 실행한다. 
 
  • 실험 결과는 아래와 같이 보인다. (로컬에서는 H/W 리소스 문제로 실행이 정상적으로 되지 않아서, Kubeflow 홈페이지의 이미지 활용)
  • Experiments에서 생성한 실험들에 대한 성공 횟수와 진행 중인 횟수, 실패한 횟수를 확인할 수 있다. 
Experiments 결과 (출처 : https://www.kubeflow.org/docs/components/katib/hyperparameter/)
  • 각 실험을 클릭해보면, Hyper Paramter 조합과 가장 최적의 Hyper Parameter 조합에 대한 정보를 얻을 수 있다. 
실험 세부 결과 (출처 : https://www.kubeflow.org/docs/components/katib/hyperparameter/)

사실, Katlib은 매우 유용한 툴이지만, H/W 리소스가 분산 처리를 할 수 있을 정도로 많은 양이 필요하기 때문에, 사용이 제한적이다. 아무래도 개인의 경우에는 많은 자원을 동시에 활용하는 일이 적기 때문에, Katlib보다는 Bash를 이용한 Batch Job 등을 이용하는 게 좋을 것 같다. 하지만, 사내등의 하드웨어 환경이 풍부한 상황에서는 요긴하게 활용할 수 있을 것 같다. 

반응형

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


머신러닝을 이용한 이상치(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

+ Recent posts