for textmining

용어 정리

|

이번 글에서는 머신러닝과 딥러닝 관련 다양한 용어들을 정리하였습니다. 이 글은 기본적으로 구글에서 정리해 놓은 용어 사전 리스트를 기본으로 위키피디아 등 자료를 참고해 정리하였습니다. 그럼 시작하겠습니다.

activation function

이전 계층의 모든 입력값의 가중합을 받아서 다음 계층에 보낼 출력값을 산출하는 함수. 대개 ReLU나 시그모이드 같은 비선형함수가 적용된다.

loss

모델의 예측과 정답 사이에 얼마나 차이가 있는지 나타내는 측도(measure). 이 값을 정하기 위해서는 손실함수(loss function)이 정의되어 있어야 한다. 예컨대 선형회귀 모델에서 손실함수는 대개 Mean Squared Error, 로지스틱회귀에서는 로그우도가 쓰인다.

여기에서 측도란 1차원에서의 길이, 2차원에서의 넓이, 3차원에서의 부피 등의 개념을 일반의 집합으로까지 확장한 개념이다. 즉 집합의 ‘크기’에 상당하며, 적분이론은 이 개념을 기초로 하는 경우가 많다.

prior belief

모델을 학습하기 전 데이터에 대한 당신의 믿음. 예컨대 딥러닝 모델 파라메터에 대한 L2 정규화는 해당 파라메터들이 작고 0을 중심으로 정규분포를 이룬다는 사전믿음을 전제한다.

regularization

모델 복잡도(complexity)에 대한 패널티(penalty). 정규화는 과적합을 예방하고 일반화 성능을 높이는 데 도움을 준다. 정규화에는 L1 정규화, L2 정규화, 드롭아웃, early stopping 등이 있다.

squared loss

선형회귀에 쓰이는 손실함수. L2 Loss로도 불린다. 이 함수는 모델이 예측한 값과 실제값 간 차이(오차)의 제곱이다. 미분이 가능하다는 장점이 있다. 하지만 오차를 ‘제곱’하기 때문에 잘못된 예측 혹은 이상치(outlier)에 의해 그 값이 큰 영향을 받게 된다는 단점이 있다. (추가 내용 : Quora)

absolute loss

모델이 예측한 값과 실제값 간 차이(오차)의 절대값. L1 Loss로도 불린다. L1 Loss는 L2 Loss에 비해 이상치에 덜 민감하다는 장점이 있지만 0인 지점에서 미분이 불가능하다는 단점이 있다.

L1 regularization

정규화의 일종. 모델 가중치의 L1 norm(가중치 각 요소 절대값의 합)에 대해 패널티를 부과한다. 대부분의 요소값이 0인 sparse feature에 의존한 모델에서 L1 정규화는 불필요한 피처에 대응하는 가중치들을 정확히 0으로 만들어 해당 피처를 모델이 무시하도록 만든다. 다시 말해 변수선택(feature selection) 효과가 있다는 말이다. 이는 L2 정규화와 대조된다.

2차원상 L1 norm의 자취는 마름모꼴이어서 미분이 불가능한데 이같은 성질과 깊은 관련을 맺고 있는듯하다. 이곳 참고.

L2 regularization

정규화의 일종. 모델 가중치의 L2 norm의 제곱(가중치 각 요소 제곱의 합)에 대해 패널티를 부과한다. L2 정규화는 아주 큰 값이나 작은 값을 가지는 outlier 모델 가중치에 대해 0에 가깝지만 0은 아닌 값으로 만든다. 이는 L1 정규화와 대조된다. L2 정규화는 선형모델의 일반화 능력을 언제나 항상 개선시킨다.

structural risk minimization

알고리즘은 다음과 같은 두 가지 목표를 만족시켜야 한다.

  • 예측력이 좋은 모델 (예컨대 손실이 가장 적은 모델)
  • 가급적 간단한 모델 (예컨대 강한 수준의 정규화가 이뤄진 모델)

예컨대 학습데이터에 대해 손실 최소화와 정규화를 동시에 달성한 모델은 structural risk minimization이 이뤄진 알고리즘이라 할 수 있다. structural risk minimization는 empirical risk minimization 개념과 대조된다.

empirical risk minimization

학습데이터에 대해 손실이 적은 모델을 선택하는 것. structural risk minimization 개념과 대조된다.

least squares regression

L2 loss를 최소화함으로써 학습된 선형회귀 모델.

generalized linear model

least squares regression의 일반화된 버전. 가우시안 노이즈에 기반한 모델도 있고 포아송 노이즈나 범주형 노이즈(categorical noise) 같은 다른 종류의 노이즈에 기반한 모델도 있다. generalized linear model의 예시는 다음과 같다.

  • logistic regression
  • multi-class regresstion
  • least squares regression

generalized linear model의 파라메터는 convex optimization 기법으로 구한다. generalized linear model은 다음 두 가지 속성이 있다.

  • 최적 generalized linear model의 예측 평균은 학습 데이터 정답의 평균과 같다.
  • 최적 로지스틱 회귀 모델이 예측한 확률의 평균은 학습 데이터 정답의 평균과 같다.

generalized linear model의 예측력은 피처에 의해 제한된다. 딥러닝 모델과 달리 generalized linear model은 (학습데이터에 없는)새로운 피처를 학습할 수 없다.

Kernel Support Vector Machines (KSVMs)

입력 데이터 벡터를 고차원 공간에 매핑함으로써 positive class와 negative class 사이의 마진(margin)을 최대화하는 결정경계(decision boundary)를 찾는 분류 알고리즘. 예컨대 100차원짜리 입력데이터가 주어졌을 때 KSVMs는 이러한 입력데이터를 100만 차원 공간으로 매핑한다. KSVMs는 hinge loss라고 불리는 손실함수를 사용한다.

hinge loss

학습데이터 각각의 범주를 구분하면서 데이터와의 거리가 가장 먼 결정경계(decision boundary)를 찾기 위해 고안된 손실함수의 한 부류. 이로써 데이터와 경계 사이의 마진(margin)이 최대화된다. KSVMs이 바로 hinge loss를 손실함수로 쓴다. 이진 분류문제에서 모델의 예측값 $y’$(스칼라), 학습데이터의 실제값 $y$(-1 또는 1) 사이의 hinge loss는 다음과 같이 정의된다.

[loss=\max { \left{ 0,1-\left( y’\times y \right) \right} }]

$y’×y$를 $x$축, hinge loss를 $y$축으로 놓고 그래프를 그리면 다음과 같다.

위 그래프의 의미를 이해하려면 SVM의 목적식부터 살펴야 한다. SVM에서 plus-plane보다 위에 있는 관측치들은 $y=1$이고 $y’$, 즉 $w^Tx+b$가 1보다 크다. 반대로 minus-plane보다 아래에 있는 점들은 $y=-1$이고 $w^Tx+b$가 -1보다 작다. 따라서 SVM이 그 손실을 0으로 두려는 관측치 $x$는 아래와 같은 식을 만족한다.

[y’ \times y={ y }({ w }^{ T }{ x }+b)\ge 1]

그런데 여기에서 만약 $y’×y$가 1 미만의 값을 가진다면 해당 관측치 $x$는 plus-plane과 minus-plane 사이, 즉 마진(margin) 내에 존재한다. $y’×y$가 1 이상이라면 손실을 무시(=0)하고, 1보다 작으면 작을수록 손실 또한 크도록 유도한 것이 hinge loss 수식이 의미하는 바다.

hinge loss는 로지스틱 회귀의 손실함수, 크로스 엔트로피와 깊은 관련을 맺고 있다. 로지스틱 회귀의 경우 $y’×y$가 1 이상의 값을 가질 경우 손실이 0에 가까워지지만 완전히 0이 되지는 않는다. 자세한 내용은 이곳 참고.

independently and identically distributed (i.i.d)

동일하고(변화하지 않고) 이전에 뽑은 값에 영향을 받지 않는(독립인) 확률분포를 따르는 확률변수들의 집합(collection). 예컨대 특정 시점에 웹페이지에 접속하는 방문자들은 i.i.d일 수 있다. 짧은 시간이기 때문에 분포는 변화하지 않는다. 또한 한 방문자의 접속은 다른 방문자의 접속과 일반적으로 독립(independent)이다. 그러나 분석 기간을 길게 하면 웹페이지 방문이 계절마다 다를 수 있다(이 경우 분포가 변화한다는 점에서 i.i.d가 아니다)

앞면이 나오는 경우를 1, 그렇지 않은 경우를 0으로 하는 확률변수가 있고 동전을 네 번 던질 경우 이 확률변수들의 집합은 i.i.d다. 앞면이 나올 확률이 네 차례 모든 실험에서 0.5로 동일하고, 현재 실험은 이전에 앞면이 나왔든 뒷면이 나왔든 상관없이 독립이기 때문이다.

inference

머신러닝에서 추론(inference)는 학습된 모델을 레이블이 달리지 않은 데이터(unlabeled examples)에 적용해 예측하는 과정을 가리킨다. 통계학에서 추론은 일부 관측된 데이터가 주어졌을 때 해당 분포의 파라메터를 추정하는 과정을 가리킨다.

perplexity

모델이 과업을 얼마나 잘 수행하고 있는지 나타내는 측도(measure) 중 하나. 예컨대 스마트폰 사용자가 몇 개 철자를 타이핑했을 때 해당 사용자에게 자동 완성된 단어 리스트를 보여주는 과업을 수행하고 싶다고 치자. 이 작업에 대한 perplexity $P$는 사용자가 입력하려고 하는 실제 단어(정답)가 모델이 제시하는 후보 리스트에 포함되도록 하기 위해 제공해야 하는 추측의 수를 가리킨다. perplexity는 다음과 같이 크로스 엔트로피와 관계가 있다.

[P={ 2 }^{ -crossentropy }]

stationarity

데이터의 분포가 하나 이상의 차원에서 일정하게 유지되는 데이터 집합의 속성. 가장 일반적으로 그 차원은 시간이며, 데이터가 stationarity하다는 것은 시간이 지나도 데이터가 변하지 않는다는 걸 의미한다.

synthetic feature

입력 피처에는 존재하지 않지만 하나 이상의 피처로부터 도출된 피처. 합성피처(synthetic feature)의 종류는 다음과 같다.

  • feature cross
  • 하나의 피처를 두 개의 피처로 나누기(dividing)
  • buckting

피처 하나만을 대상으로 정규화(normalize)나 스케일링해서 생성된 피처는 합성피처에 속하지 않는다.

feature cross

개별 피처들을 곱셈 또는 카테시안 곱으로 cross시켜 생성한 합성피처의 일종. feature cross는 피처간 비선형(nonlinear) 관계를 표현하는 데 도움을 준다.

bucketing

연속적인(continous) 피처를 버켓(bucket) 또는 빈(bin)이라 불리는 여러 개의 이진 피처로 나누는 것. 대개 해당 피처 값(value)의 범위를 바탕으로 나눈다. 예컨대 온도를 연속적인 floating-point 피처로 표시하는 대신, 이산적인(discrete)한 빈으로 분리할 수 있다. 가령 0~15도, 15~30도, 30~50도에 해당하는 데이터를 각각의 빈에 넣는 방식이다.

Comment  Read more

스택(stack)

|

이번 글에서는 스택(stack)에 대해 살펴보도록 하겠습니다. 이 글은 고려대 김황남 교수님 강의와 위키피디아를 정리하였음을 먼저 밝힙니다. 파이썬 코드는 이곳을 기본으로 하되 조금 수정하였습니다. 그럼 시작하겠습니다.

concept

스택이란 목록 한쪽 끝에서만 자료를 넣거나 뺄 수 있는 자료구조의 일종입니다. 이때 자료를 넣는 것을 ‘밀어넣는다’는 의미의 푸시(push), 반대로 넣어둔 자료를 꺼내는 것을 팝(pop)이라고 합니다. 팝으로 꺼내진 자료는 가장 최근에 보관한 자료가 됩니다(Last In First Out, LIFO) 책이 쌓여있는(stack) 걸 상상하면 직관적으로 이해할 수 있는데요. 스택은 책더미 속에서 가장 나중에 올려놓은 책부터 꺼낼 수 있는 것과 같은 이치입니다.

6, 4, 2를 차례로 푸시하고, 두번 팝 한 다음에 7을 푸시한다고 가정해보겠습니다. 다음 그림과 같습니다.

operation

스택의 핵심 연산(operation)은 푸시와 팝입니다. 푸쉬를 구현한 파이썬 코드는 다음과 같습니다.

class Stack(object):
    def __init__(self, limit = 10):
        self.stack = []
        self.limit = limit

    # for printing the stack contents
    def __str__(self):
        return ' '.join([str(i) for i in self.stack])

    # for pushing an element on to the stack
    def push(self, data):
        if len(self.stack) >= self.limit:
            print('Stack Overflow')
        else:
            self.stack.append(data)

위 코드상으로는 스택 길이를 limit라는 변수로 제어하고 있습니다. 물론 스택 길이를 굳이 제어하지 않아도 됩니다. 정해진 스택 길이 limit를 만족하는 상태인 스택에 추가 요소를 푸쉬할 경우 스택 오버플로우(stack overflow) 경고메세지를 출력합니다.

팝을 구현한 파이썬 코드는 다음과 같습니다. 스택 길이가 0 이하라면 팝을 할 수 없으므로 스택 언더플로우(stack underflow) 문제가 발생합니다. 이 코드에선 -1을 출력하도록 했습니다.

    # for popping the uppermost element
    def pop(self):
        if len(self.stack) <= 0:
            return -1
        else:
            return self.stack.pop()

스택의 핵심 연산은 아니지만 정의해놓으면 편리한 것이 바로 픽(peek)입니다. 스택의 경우 그 정의상 팝을 해야만 스택에 최근 저장된 자료를 확인할 수 있는데요. 픽은 팝을 하지 않고도 최근 저장된 자료를 엿볼(peek) 수 있습니다.

    def peek(self):
        if len(self.stack) <= 0:
            return -1
        else:
            return self.stack[len(self.stack) - 1]

파이썬 코드에서 이미 눈치 채셨겠지만 스택은 리스트, 연결리스트 등 다양한 자료구조로 구현이 가능합니다. 위 파이썬 코드의 경우 리스트를 활용해 스택이 구현됐습니다. 리스트를 활용할 경우 푸쉬 연산은 리스트에 새 자료를 붙이는(append) 형태로, 팝 연산은 파이썬 자체의 팝(pop) 연산이 적용됐습니다. 그런데 스택의 정의에만 맞다면 위 형태 말고도 다양하게 구현할 수 있습니다. 리스트든 연결리스트든 팝, 푸쉬, 픽 연산의 계산복잡성은 $O(1)$입니다.

활용 : 재귀함수

스택은 프로그래머도 모르는 사이에 여러 곳에서 쓰이고 있습니다. 재귀함수 호출이 모두 스택 형태로 이뤄지게 됩니다. 이와 관련해 다음 그림을 보겠습니다.

함수 $A$를 실행하다가 $B$를 호출하고, 다시 $B$를 실행하다가 $B$를 호출하고, $C$와 $D$도 마찬가지의 과정을 거쳤다고 칩시다. 그러면 함수 실행 결과를 반환하는 과정은 호출 순서의 정반대가 됩니다. 다시 말해 $D$의 연산 결과를 받아야지만 $C$가 이를 바탕으로 계산을 마칠 수 있고, 이는 $B$와 $A$도 마찬가지입니다.

각 함수의 메모리 주소값이 스택에 저장되어 있고, 스택은 Last In First Out 원칙을 따르기 때문에 함수 결과의 반환 순서는 호출 결과의 반대가 되는 것입니다.

활용 : 사칙연산

숫자와 숫자 사이에 연산자를 넣어 표기하는 방법을 중위표기법(infix notation)이라 합니다. 예컨대 아래의 표기는 2와 3을 ‘더한다’는 뜻이 됩니다.

2 + 3

중위표기법은 괄호 연산자가 필요없는 전위표기법(+ 2 3)이나 후위표기법(2 3 +)과는 다르게 괄호가 매우 중요합니다. 연산 수행 순서를 명시적으로 나타내야 할 때가 발생하기 때문이죠. 예컨대 아래의 표기에서는 2와 3을 더하는 연산이 먼저 수행됩니다.

(2 + 3) × 4

그런데 후위표기법에서는 위와 같은 식을 아래와 같이 쓰게 돼 괄호가 필요 없습니다.

2 3 + 4 ×

연산의 우선순위(the priority of operands, precednece rule)은 모호하게 해석할 수 있는 수식에서 어느 연산을 먼저 계산할 것인가를 결정하는 명시적인 규칙입니다. 중위표기법에서는 다음과 같은 순위가 표준적으로 쓰입니다. 자세한 내용은 이곳을 참고하시면 좋을 것 같습니다.

(, ) > ×, / > +, -

중위표기법은 사람에게는 친숙하지만 컴퓨터로 구문분석하기가 어렵습니다. 이 때문에 프로그램 내부에서는 연산자를 연산 대상의 뒤에 쓰는 후위표기법(postfix notation)이 쓰인다고 합니다. 후위표기법은 괄호가 없고 수식 계산시 식을 앞에서 읽어 나가면서 차례대로 처리를 하면 됩니다. 이 때 쓰이는 것이 바로 스택입니다. 컴퓨터가 중위표기법으로 표현된 수식을 계산하는 과정은 크게 두 단계로 나눠서 생각해볼 수 있습니다.

  1. 전위표기법으로 표현된 수식을 후위표기법으로 변환
  2. 후위표기법으로 표현된 수식을 계산

중위표기법으로 쓰인 다음 식을 계산한다고 칩시다. 이를 후위표기법으로 변환하면 다음과 같습니다.

[A+B\times C+\left( D\times E+F \right) \times G\ \Rightarrow \quad ABC\times +DE\times F+G\times +]

1번 과정의 일반적 절차는 다음과 같습니다.

  • 계산대상 숫자가 나오면 output 변수에 저장한다.
  • 왼쪽 괄호 $($가 나오면 스택에 푸쉬(저장)한다.
  • 오른쪽 괄호 $)$가 나오면 스택에 이미 저장돼 있는 왼쪽 괄호 $($ 사이에 있는 모든 요소를 팝을 한다.
  • 덧셈, 곱셈기호 등 연산자가 나오면 해당 연산자보다 연산 우선순위가 낮거나 같은 연산자가 나올 때까지 스택에서 팝을 해서 output에 저장하고, 해당 연산자를 스택에 푸쉬한다.
  • 식의 끝에 다다르면 스택에 있는 모든 요소를 팝을 한다.

위 절차에 따라 위 식을 후위표기법으로 변환해 보겠습니다. 아래 표와 같습니다.

현재 위치 output stack
A+B×C+(D×E+F)×G A  
A+B×C+(D×E+F)×G A +
A+B×C+(D×E+F)×G AB +
A+B×C+(D×E+F)×G AB
A+B×C+(D×E+F)×G ABC
A+B×C+(D×E+F)×G ABC×+ +
A+B×C+(D×E+F)×G ABC×+ +(
A+B×C+(D×E+F)×G ABC×+D +(
A+B×C+(D×E+F)×G ABC×+D +(×
A+B×C+(D×E+F)×G ABC×+DE +(×
A+B×C+(D×E+F)×G ABC×+DE× +(+
A+B×C+(D×E+F)×G ABC×+DE×F +(+
A+B×C+(D×E+F)×G ABC×+DE×F+ +
A+B×C+(D×E+F)×G ABC×+DE×F+
A+B×C+(D×E+F)×G ABC×+DE×F+G
A+B×C+(D×E+F)×G ABC×+DE×F+G×+  

후위표기법으로 표현된 수식을 계산하는 2번 과정의 일반적 절차는 다음과 같습니다.

  • 숫자를 만나면 스택에 푸쉬한다.
  • 연산자를 만나면 두 개 요소를 팝을 하고, 두 요소에 연산자에 해당하는 연산을 적용한 후, 그 값을 다시 푸쉬한다.

예컨대 다음과 같은 식을 계산한다고 칩시다. (답은 40)

중위표기법 : 5×4+6+7×2

후위표기법 : 54×6+72×+

현재 위치 stack
54×6+72×+ 5
54×6+72×+ 5,4
54×6+72×+ 20
54×6+72×+ 20,6
54×6+72×+ 26
54×6+72×+ 26,7
54×6+72×+ 26,7,2
54×6+72×+ 26,14
54×6+72×+ 40

1번 작업의 계산복잡성은 $O(n)$, 2번 작업 역시 $O(n)$이 됩니다. 연산자와 숫자 개수를 모두 합쳐 $n$개일 경우 모든 요소를 한번씩은 다 훑어야 하기 때문입니다.

Comment  Read more

효과적인 RNN 학습

|

이번 글에서는 Recurrent Neural Network(RNN)을 효과적으로 학습시키는 전략들에 대해 살펴보도록 하겠습니다. 이 글은 Oxford Deep NLP 2017 course을 기본으로 하되 저희 연구실 장명준 석사과정이 만든 자료를 정리했음을 먼저 밝힙니다. 그럼 시작하겠습니다.

그래디언트 문제

RNN 역전파시 그래디언트가 너무 작아지거나, 반대로 너무 커져서 학습이 제대로 이뤄지지 않는 경우가 많습니다. 이를 각각 gradient vanishing, gradient exploding 문제라고 합니다. 이 문제를 수식으로 살펴보겠습니다. vanilla RNN 셀 $t$번째 시점의 히든스테이트 $h_t$는 다음과 같이 정의됩니다. 단 여기에서 하이퍼볼릭탄젠트 안의 식들을 모두 합쳐 $z_t$이라 두겠습니다. ($x_t$ : $t$번째 시점의 입력, $W_{hh}, W_{xh}, b_h$ : 학습 파라메터)

[\begin{align} { h }_{ t }=&\tanh { \left( { W }_{ hh }{ h }_{ t-1 }+{ W }_{ xh }{ x }_{ t }+{ b }_{ h } \right) }\=&\tanh({z}_{t}) \end{align}]

다음 그림과 같은 RNN 구조에서 네번째 시점의 손실 $cost_4$에 대한 $h_1$의 그래디언트는 체인룰(chain rule)에 의해 아래와 같이 계산할 수 있습니다. 바꿔 말해 적색 화살표에 해당하는 그래디언트를 순차적으로 곱한 값이라고 보면 될 것 같습니다.

[\frac { \partial { cost }{ 4 } }{ \partial { h }{ 1 } } =\frac { \partial { cost }{ 4 } }{ \partial { \hat { p } }{ 4 } } \frac { \partial { \hat { p } }{ 4 } }{ \partial { h }{ 4 } } \frac { \partial { h }{ 4 } }{ \partial { h }{ 3 } } \frac { \partial { h }{ 3 } }{ \partial { h }{ 2 } } \frac { \partial { h }{ 2 } }{ \partial { h }{ 1 } }]

위 식을 일반화하여 $n$번째 시점의 손실 $cost_n$에 대한 $h_1$의 그래디언트는 다음과 같이 표시할 수 있습니다.

[\frac { \partial { cost }{ n } }{ \partial { h }{ 1 } } =\frac { \partial { cost }{ n } }{ \partial { \hat { p } }{ n } } \frac { \partial { \hat { p } }{ n } }{ \partial { h }{ n } } \left( \prod { t=2 }^{ n }{ \frac { \partial { h }{ t } }{ \partial { h }_{ t-1 } } } \right)]

우리의 관심은 괄호 안입니다. 체인룰에 의해 그래디언트가 곱해지면서 이 값이 커지는지 작아지는지가 관심인 것입니다. $h_t=\tanh(z_t)$이므로 $t$번째 히든스테이트에 대한 $t-1$번째 히든스테이트의 그래디언트는 체인룰에 의해 다음과 같이 표시할 수 있습니다.

[\frac { \partial { h }{ t } }{ \partial { h }{ t-1 } } =\frac { \partial { h }{ t } }{ \partial { z }{ t } } \frac { \partial { z }{ t } }{ \partial { h }{ t-1 } }]

$z_t=W_{hh}h_{t-1}+W_{xh}x_t+b_h$이므로 $∂z_t/∂h_{t-1}$은 $W_{hh}$입니다. norm의 성질에 의해 다음 부등식이 성립합니다.

[\left| \frac { \partial { h }{ t } }{ \partial { h }{ t-1 } } \right| \le \left| \frac { \partial { h }{ t } }{ \partial { z }{ t } } \right| \left| \frac { \partial { z }{ t } }{ \partial { h }{ t-1 } } \right| =\left| \frac { \partial { h }{ t } }{ \partial { z }{ t } } \right| \left| { W }_{ hh } \right|]

norm의 성질에 의해 다음이 성립한다고 합니다(증명). 다시 말해 $W_{hh}$의 L2 norm은 $W_{hh}$의 가장 큰 고유값(eigenvalue)라는 뜻입니다.

[{ \left| { W }{ hh } \right| }{ 2 }=\sqrt { { \lambda }_{ max } }]

다시 우리의 관심인 문제로 돌아오겠습니다. RNN 역전파시 체인룰에 의해 $∂h_t/∂h_{t-1}$을 지속적으로 곱해주어야 합니다. 그런데 우리가 살펴봤듯이 $∂h_t/∂h_{t-1}$의 L2 norm은 절대적으로 $W_{hh}$의 L2 norm 크기에 달려 있습니다. 다시 말해 $W_{hh}$의 가장 큰 고유값이 1보다 크다면 앞쪽 스텝으로 올수록 그래디언트가 매우 커질 것이며, 1보다 작다면 반대로 매우 작아질 것입니다.

이와 별개로 $z_t$를 계산한 이후 취해지는 비선형함수 하이퍼볼릭탄젠트의 문제도 있습니다. 하이퍼볼릭탄젠트를 1차 미분한 그래프는 다음과 같습니다. 입력값이 -5보다 작거나 5보다 크면 그래디언트가 0으로 작아지는 걸 확인할 수 있습니다. RNN 역전파 과정에서 하이퍼볼릭탄젠트의 그래디언트도 계속 곱해지기 때문에 하이퍼볼릭탄젠트도 그래디언트 배니싱 문제에 일부 영향을 끼칠 수 있습니다.

셀 구조를 바꾸기 : LSTM

그래디언트 문제를 해결하기 위한 방법 가운데 가장 각광받는 것은 셀 구조를 아예 바꿔보는 겁니다. 계속 곱해서 문제가 생겼으니 이번에는 더하는 걸로 바꿔 봅시다. 이를 cell state라 하겠습니다.

[{ c }{ t }={ c }{ t-1 }+\tanh { \left( V\left[ { x }{ t-1 };{ h }{ t-1 } \right] +{ b }_{ c } \right) }]

하이퍼볼릭탄젠트 안에 있는 내용은 vanilla RNN 셀과 본질적으로 다르지 않습니다. $c_t$를 $c_{t-1}$로 미분하는 경우 우변에 $c_{t-1}$가 더해졌기 때문에 기본적으로 1을 확보할 수 있습니다. 그래디언트가 0으로 죽는 걸 막고자 함입니다.

그런데 각 스텝마다 그래디언트가 지속적으로 커진다면 되레 그래디언트 익스플로딩 문제가 발생할 수 있습니다. 밸런스를 맞춰주어야 합니다. cell state를 아래와 같이 바꿔보겠습니다.

[\begin{align} { c }_{ t }={ f }_{ t }\odot { c }_{ t-1 }+&{ i }_{ t }\odot \tanh { \left( V\left[ { x }_{ t-1 };{ h }_{ t-1 } \right] +{ b }_{ c } \right) } \ \ where\quad { i }_{ t }=&\sigma \left( { W }_{ i }\left[ { x }_{ t-1 };{ h }_{ t-1 } \right] +{ b }_{ i } \right) \ { f }_{ t }=&\sigma \left( { W }_{ f }\left[ { x }_{ t-1 };{ h }_{ t-1 } \right] +{ b }_{ f } \right) \end{align}]

$i_t$와 $f_t$는 시그모이드가 취해진 값으로 0~1사이의 값을 가집니다. 각각 직전 시점의 정보, 현 시점의 정보를 얼마나 반영할지를 결정합니다. 물론 다음 히든스테이트를 만들 때도 gate를 둘 수 있습니다.

[{ h }{ t }={ o }{ t }\odot \tanh { \left( { W }{ h }{ c }{ t }+{ b }{ h } \right) } \ where\quad { o }{ t }=\sigma \left( { W }{ o }\left[ { x }{ t-1 };{ h }{ t-1 } \right] +{ b }{ o } \right)]

Skip connection

셀 구조 말고 skip connection 기법을 사용할 수도 있습니다. 그래디언트가 흐를 수 있는 지름길(shortcut)을 만들어주는 것입니다. skip connection은 원래 Convolutional Neural Network에서 자주 사용된 기법인데 RNN 구조에서도 다음과 같이 적용할 수 있다고 합니다.

과적합, 드롭아웃

일반적이지는 않지만, RNN에서도 드롭아웃을 적용해 과적합을 피할 수 있습니다. 그런데 히든스테이트와 히든스테이트 사이에 드롭아웃을 적용하는 것은 그리 좋은 선택이 아니라고 합니다. 히든스테이드와 히든스테이트를 연결하는 가중치 $W_{hh}$가 모든 step에서 공유되기 때문입니다. 아래 그림을 보겠습니다.

위 예시에서 $h_t$를 만들 때 1, 3, 5번째 노드를 드롭아웃할 경우 $W_{hh}$에서 해당 행벡터가 업데이트되지 않습니다. 하지만 다음 step에서 얼마든지 업데이트될 가능성이 있습니다. 과적합을 피하기 위해서 학습을 덜한다는 본래 취지에 맞지 않는다는 이야기입니다.

굳이 과적합을 적용할 경우 다음과 같이 할 수 있다고 합니다. 배치 단위로 파라메터를 업데이트하는 경우가 많은데, 1회 iteration마다 드롭아웃을 하는 노드를 모든 step에 대해 통일시키는 겁니다.

vocabulary 문제

예측해야 할 단어(범주) 수가 너무 많아서 소프트맥스 확률 계산시 과부하가 걸리는 경우가 많습니다. 이를 위해 일부 단어만 뽑아 소프트맥스 확률을 구하고 여기서 구한 손실(loss)만큼만 파라메터를 업데이트하는 방법도 있습니다. 이와 관련 자세한 내용은 이곳을 참고하시면 좋을 것 같습니다.

Comment  Read more

CNN 주요 모델들

|

이번 글에서는 Convolutional Neural Network(CNN)의 주요 모델들에 대해 살펴보도록 하겠습니다. 이 글은 Adit Deshpande 님의 블로그이곳, 그리고 각 논문을 참고해 제 나름대로 정리했음을 먼저 밝힙니다. 그럼 시작하겠습니다.

AlexNet

Recurrent Neural Network와 더불어 딥러닝 모델의 양대 산맥으로 주목받고 있는 CNN은 기본적으로 얀 르쿤이 1989년 제안한 구조를 토대로 하고 있습니다. 컴퓨터 비전 분야의 ‘올림픽’이라 할 수 있는 ILSVRC(ImageNet Large-Scale Visual Recognition Challenge)의 2012년 대회에서 제프리 힌튼 교수팀의 AlexNet이 top 5 test error 기준 15.4%를 기록해 2위(26.2%)를 큰 폭으로 따돌리고 1위를 차지했습니다.

여기서 top 5 test error란 모델이 예측한 최상위 5개 범주 가운데 정답이 없는 경우의 오류율을 나타냅니다. 당시 ILSVRC 데이터셋(Image은 1000개 범주 예측 문제였습니다. 어쨌든 AlexNet 덕분에 딥러닝, 특히 CNN이 세간의 주목을 받게 됐습니다. AlexNet 아키텍처의 주요 특징은 다음과 같습니다.

  • conv layer, max-pooling layer, dropout layer 5개
  • fully connected layer 3개
  • nonlinearity function : ReLU
  • batch stochastic gradient descent

AlexNet이 중요한 이유는 의미있는 성능을 낸 첫번째 CNN 아키텍처이자, AlexNet에 쓰인 드롭아웃 등 기법은 이 분야 표준으로 자리잡을 정도로 선도적인 역할을 했기 때문입니다.

GoogleNet

AlexNet 이후 층을 더 깊게 쌓아 성능을 높이려는 시도들이 계속되었습니다. VGGNet(2014), GoogleNet(2015) 등이 바로 그것입니다. GoogleNet은 VGGNet보다 구조가 복잡해 널리 쓰이진 않았지만 아키텍처 면에서 주목을 받았습니다. 보통 하나의 conv layer에는 한 가지의 conv filter가 사용됩니다.

GoogleNet 연구진들은 한 가지의 conv filter를 적용한 conv layer를 단순히 깊게 쌓는 방법도 있지만, 하나의 layer에서도 다양한 종류의 filter나 pooling을 도입함으로써 개별 layer를 두텁게 확장시킬 수 있다는 창조적인 아이디어로 후배 연구자들에게 많은 영감을 주었습니다. 이들이 제안한 구조가 바로 Inception module입니다. (그림 출처)

Inception module에서 특히 주목받은 것이 바로 1×1 conv filter입니다. 가령 현재 층 입력데이터 이미지의 차원수가 100×100×60이고, 1×1 conv filter를 20개 사용한다면 데이터의 차원 수는 100×100×20으로 줄어듭니다. 60개 채널(차원)에 달하는 하나의 픽셀이 20차원의 feature space로 선형변환, 차원축소된 것이라고도 볼 수 있겠습니다.

ResNet

ResNet(2015)은 2015년 ILSVRC에서 오류율 3.6%로 1등을 차지했습니다. 인간의 분류 오차가 5~10% 정도라는 걸 감안하면 놀라운 성적표입니다.

사실 AlexNet이 처음 제안된 이후로 CNN 아키텍처의 층은 점점 더 깊어졌습니다. AlexNet이 불과 5개 층에 불과한 반면 VGGNet은 19개 층, GoogleNet은 22개 층에 달합니다. 하지만 층이 깊어질 수록 역전파되는 그래디언트가 중간에 죽어서 학습이 잘 되지 않는 문제(gradient vanishing)가 발생했습니다. ResNet 저자들이 제시한 아래 학습그래프를 보면 이같은 문제가 뚜렷이 나타납니다.

ResNet 저자들의 핵심 아이디어는 다음 그림과 같은 residual block입니다. 그래디언트가 잘 흐를 수 있도록 일종의 지름길(shortcut, skip connection)을 만들어 주자는 생각입니다. 이는 forget gate 등을 도입해 이전 스텝의 그래디언트(정보)를 좀 더 잘 흐르게 만드려는 Long Term Short Memory(LSTM)의 철학과 본질적으로 유사합니다.

ResNet의 성능이 좋은 이유는 그래디언트 문제 말고 또 있습니다. Veit et al. (2016)은 residual block이 앙상블(ensemble) 모델을 구축한 것과 비슷한 효과를 낸다고 주장했습니다. residual block의 skip connection 덕분에 입력데이터와 그래디언트가 오갈 수 있는 통로가 크게 늘어나기 때문입니다. ($n$개 skip connection이 있다면 $2^n$개의 서로 다른 통로 존재) 이를 직관적으로 나타낸 그림은 아래 그림과 같습니다.

DenseNet

DenseNet(2016)은 ResNet에서 한발 더 나아가 전체 네트워크의 모든 층과 통하는 지름길을 만들었습니다. conv-ReLU-conv 사이만 뛰어넘는 지름길을 만들었던 ResNet보다 훨씬 과감한 시도입니다. DenseNet의 전체적인 아키텍처는 다음과 같습니다.

Region Based CNNs

R-CNN(2013), Fast R-CNN(2015), Faster R-CNN(2015) 등은 object detection 문제를 풀기 위해 제안된 모델들입니다. object detection이란 이미지가 주어졌을 때 해당 이미지 속에 들어있는 오브젝트의 경계(bounding box)를 찾아내는 작업입니다. object detection 문제는 크게 오브젝트의 경계를 찾아내는 작업(region proposal step), 해당 오브젝트가 무엇인지 맞추는 작업(classification step) 둘로 나뉩니다. CNN은 여기에서 이미지의 특성을 추출하는 데 중요한 역할을 합니다. 모델이 거듭될 수록 정확도는 물론 속도도 크게 향상되는 추세입니다.

CNNs for NLP

문서분류에서 높은 성능으로 주목받은 CNN 아키텍처는 Kim(2014)입니다. 단어벡터들을 붙여서 행렬 형태의 입력값을 만들고, 너비가 단어벡터의 차원수와 일치하는 필터를 씁니다. conv layer가 단 1개뿐이지만 필터를 100개 이상 써서 두텁게 만든 것도 특징입니다. 다음 그림과 같습니다.

위 그림에서도 알 수 있듯 Kim(2014)의 단점은 conv filter가 돌 때 이웃단어들을 고려할 수는 있지만, 멀리 떨어져 있는 단어들을 감안하기가 어렵다는 점입니다. 이에 Kalchbrenner et al.(2014)dynamic $k$-max pooling 기법을 제안했는데요. 풀링을 할 때 내림차순으로 $k$개를 선택하되 순서를 보존하는 방식입니다. 아래 그림에서 첫번째 conv layer에선 5개, 두번째 conv layer에선 3개를 풀링한 것입니다. 결과적으로 Recursive Neural Network와 유사한 파싱트리 모양의 구조가 됩니다.

한편 위의 연구들은 단어 수준에서 문서분류를 수행했는데, 최근엔 글자 수준의 CNN 아키텍처도 제안되고 있습니다. 단어(글자)벡터와 문서의 외부정보를 합쳐서 분류 성능을 높이려는 시도도 제안되고 있습니다. 이와 관련 자세한 내용은 이곳을 참고하시면 좋을 것 같습니다.

Comment  Read more

최대공약수, 최소공배수

|

이번 글에서는 최대공약수와 최소공배수를 찾는 알고리즘에 살펴보도록 하겠습니다. 이 글은 위키피디아와 이곳을 참고하였습니다. 그럼 시작하겠습니다.

최대공약수

$x$의 약수이면서 $y$의 약수인 수 중 최대값을 $x$와 $y$의 최대공약수라고 합니다. 최대공약수를 구하는 가장 간단한 방법은 1~($x$와 $y$ 중 작은 값) 범위에서 공약수(둘 모두 나머지가 0)를 모두 구해 이 가운데 최대값을 구하는 방법입니다.

그런데 우리는 최대값에만 관심이 있으므로 역순, 즉 ($x$와 $y$ 중 작은 값)~1까지의 범위를 탐색해 가장 처음 발견한 공약수를 최대공약수로 삼으면 더 좋을 것입니다. 파이썬으로 구현한 코드는 다음과 같습니다.

def computeHCF(x, y):
    # 두 수 가운데 작은값 찾기
    if x > y:
        smaller = y
    else:
        smaller = x
    # smaller~1까지의 범위를 역순으로 탐색
    for i in range(smaller + 1, 1, -1):
        # 처음 공약수를 발견하면 그 수를 hcf에 저장하고
        # 탐색 종료
        if ((x % i == 0) and (y % i == 0)):
            hcf = i
            break
    return hcf

유클리드 호제법(Euclidean algorithm)을 이용할 수도 있습니다. 1071과 1029의 최대공약수를 유클리드 호제법을 활용해 계산하면 다음과 같습니다.

(1) 1071은 1029로 나누어 떨어지지 않기 때문에 1071을 1029로 나눈 나머지를 구한다 : 42

(2) 1029는 42로 나누어 떨어지지 않기 때문에 1029를 42로 나눈 나머지를 구한다 : 21

(3) 42는 21로 나누어 떨어진다

(4) 1071과 1029의 최대공약수는 21이다.

유클리드 호제법으로 최대공약수를 구하는 파이썬 코드는 다음과 같습니다.

def computeHCF_euc(x, y):
   # y가 0이 될 때까지 반복
   while(y):
       # y를 x에 대입
       # x를 y로 나눈 나머지를 y에 대입
       x, y = y, x % y
   return x

최소공배수

$x$와 $y$의 공통된 배수 가운데 최소값을 최소공배수라고 합니다. 최소공배수를 구하는 가장 간단한 방법은 다음과 같습니다.

def lcm(x, y):
   # 두 수 가운데 큰 값 찾기
   if x > y:
       greater = x
   else:
       greater = y
   # greater의 값을 1씩 증가시키면서 반복
   while(True):
       # greater가 x와 y로 모두 나누어 떨어질 경우
       # 최소공배수이므로 이 값을 lcm에 저장하고 반복문 종료
       if((greater % x == 0) and (greater % y == 0)):
           lcm = greater
           break
       greater += 1
   return lcm

최대공약수와 최소공약수 사이의 관계와 유클리드 호제법을 활용해 구할 수도 있습니다. 예컨대 $x=ab$이고 $y=bc$라면 $x$와 $y$의 최대공약수는 $b$, 최소공배수는 $abc$입니다. $xy=ab^2c$이므로 이를 최대공약수 $b$로 나눠주면 최소공배수를 구할 수 있습니다.

우리는 이미 유클리드 호제법을 활용해 최대공약수를 구하는 알고리즘을 구현해 놓았으므로 이를 다시 활용합니다. 다음과 같습니다.

def lcm(x, y):
   lcm = (x*y)//computeHCF_euc(x,y)
   return lcm

Comment  Read more