Data science/Machine Learning (24)

Word Embedding


Word Embedding이란 Deep Learning 분야, 이 중에서도 특히 자연어처리에서 필수적으로 알아야할 개념이다. Word Embedding은 Word를 R차원의 Vector로 매핑시켜주는 것을 말한다.



예를 들어 위와 같이 cat이나 mat같은 단어를 특정 차원의 벡터로 바꾸어주는 것이다. 이렇게 단어를 벡터로 바꾸어주는 것은 하나의 Matrix이다. 이를 W라 부른다. W 함수는 매우 중요하다. 이를 통해 단어가 의미 있는 벡터로 변한다. 비슷한 두 개의 단어는 비슷한 벡터로 바뀐다던지 하는 식으로 말이다. W는 Learning을 통해 학습할 수 있다.


예를 들어, 5000개의 단어로 이루어진 단어 세트가 있으면 각각의 단어는 [0,0,0,1,0 ... 0,0,0] (5000열) 과 같이 나타낼 수 있다. 이 때 각각의 단어를 Word Embedding을 통해 32차원 벡터로 나타내고자 하면 W의 차원은 5000*32이다. 이를 학습하여 [0.2,0,4,0.5 ... 0.8,8] 과 같은 32차원의 벡터를 형성해낸다.


Word Embedding은 다음과 같은 놀라운 특성도 가질 수 있다.



woman을 나타내는 벡터와 man을 나타내는 벡터의 차이는 남녀의 차이를 나타내는 벡터이다. 이것이 aunt와 uncle의 차이를 만들어낸다. 예를 들어 he is a man는 옳은문장 she is a man은 틀린 문장을 구분하는 classifier에서 aunt와 uncle에 대해서도 똑같은 일을 할 수 있게 만든다.


참고


http://colah.github.io/posts/2014-07-NLP-RNNs-Representations/

/**

날짜 : 2017.01.30

밑바닥부터 시작하는 딥러닝(한빛미디어) 참고

Softmax 구현 및 성질

*/


Softmax 함수는 3-class 이상의 classification을 목적으로 하는 딥러닝 모델의 출력층에서 일반적으로 쓰이는 활성화 함수(activation function)이다. Softmax 함수는 아래와 같이 나타낼 수 있다.




이를 Latex 표기법으로 나타내면 아래와 같다. (Latex는 라텍스라고 읽으면 안되고 레이택으로 읽는다..)


\begin{align}

y_k = {exp(a_k)}/{\sum_{i=1}^{n}(exp(a_i))}

\end{align}


(위 코드를 Jupyter notebook에서 markdown으로 선택하여 입력 후 run을 누르면 위와 같은 수식이 출력되는 것을 확인할 수 있다. Jupyter notebook에서는 Latex 코드를 수식으로 변환하는 기능을 지원한다.)


""" Softmax 구현 """
import numpy as np

def softmax(a) :
    exp_a = np.exp(a)
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a
    
    return y

a = np.array([0.3, 2.9, 4.0])

print softmax(a) # softmax 결과값 출력
print sum(softmax(a)) # softmax 결과값들의 합은 1이 된다.


Softmax 수식을 그대로 파이썬 코드로 옮기면 위와 같다. 결과가 잘 출력되는 것을 확인할 수 있다. 하지만 Softmax는 수식 안에 e의 지수를 포함하고 있는데, 지수함수는 지수가 커질 수록 매우 큰 폭으로 증가하기 때문에 overflow가 발생하기 쉽다. 아래 코드를 보면 overflow가 발생하는 예와 이를 어떻게 해결하는지를 볼 수 있다.


""" Softmax는 Overflow에 취약하다.
    수식에 e^x 들어가기 때문에 Overflow 범위를 넘어설 수 있다. 
    이를 해결하기 위해서는 Softmax의 성질을 이용한다.
"""
def new_softmax(a) : 
    c = np.max(a) # 최댓값
    exp_a = np.exp(a-c) # 각각의 원소에 최댓값을 뺀 값에 exp를 취한다. (이를 통해 overflow 방지)
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a
    return y
    
a = np.array([1010, 1000, 990]) 
print softmax(a) # overflow
print new_softmax(a) # 정상적으로 출력

# 또한 softmax와 new_softmax 의 결과값을 같다.
test = np.array([1,3,6])
print 'softmax', softmax(test) # [ 0.00637746  0.04712342  0.94649912]
print 'new_softmax', new_softmax(test) # [ 0.00637746  0.04712342  0.94649912]


Softmax를 구현할 때 overflow 가 자주 발생하는 문제를 해결하기 위해 위와 같은 새로운 softmax 함수인 new_softmax를 정의한다. 이는 원소에 어떠한 수를 더하여 exp를 취하더라도 결과 값이 같다는 softmax의 성질을 이용한 것인데, 


위의 등식에서 C를 '원소들의 최댓값의 음수'로 한 것이다. 예를 들어 a = [1010,1000,990] a+c = [0,-10,-20] 이 된다. 이 때, a와 a+c의 softmax 결과값은 같다.



/**

날짜 : 2017.01.25

밑바닥부터 시작하는 딥러닝(한빛미디어) 참고

Numpy 기초, 간단한 신경망 구현

*/



numpy 패키지는 데이터 과학, 딥러닝 분야에서 쓰이는 매우 중요한 파이썬 패키지이다. numpy는 거의 표준이라고 불릴만큼 대부분의 사람들이 애용하는 패키지이다. numpy가 쓰이는 가장 큰 목적 중에 하나는 배열, 행렬, 벡터, 텐서와 같은 '다차원 배열'의 구현이다. 많은 사람들이 다차원 배열을 구현할 때 numpy를 이용한다. numpy에서는 이를 ndarray라고 부른다. 또 파이썬의 기본 자료 구조에는 array가 없기 때문에, 꼭 딥러닝이 아니더라도 numpy array는 파이썬에서 1차원 배열을 구현할 때도 쓰인다.


numpy의 기초 문법을 정리하고 이를 이용하여 인풋 2개, 아웃풋 2개, 히든 레이어가 2개(각각 노드수가 3, 2인)인 간단한 신경망을 구현해 보았다.


import numpy as np

A = np.array([1,2,3,4])

print(A)

# 차원출력
np.ndim(A)

# 배열 형상 출력. 튜플로 반환된다.

print A.shape
# (4L,)

print A.shape[0]
# 4


  • 위와 같은 문법을 통해 numpy array를 만들 수 있고, shape, dimension 등을 확인할 수 있다.


""" 2차원 배열 """
B = np.array([[1,2], [3,4], [5,6]])
print(B)

np.ndim(B)

B.shape


  • 2중 대괄호를 쓰면 2차원 배열을 만들 수 있다.


""" 3차원 배열 이상 """
# 2x3x4 배열을 만들고 0으로 초기화
C = np.zeros((2, 3, 4))

print 'C', C

# 아래와 같이 자유롭게 만들 수 있음
D = np.array([[[ 1., 2., 3., 4.],
        [ 0.,  0.,  2.,  0.],
        [ 4.,  0.,  3.,  0.]],
       [[ 1.,  4.,  0.,  6.],
        [ 0.,  0.,  5.,  0.],
        [ 0.,  0.,  0.,  5.]]])

print 'D', D

# 2x2x3x4 4차원 배열
E = np.zeros((2,2,3,4))
print 'E', E


  • 위와 같이 3차원 배열, 4차원 배열 등의 고차원 배열을 만들 수 있다.


""" 내적 """
A = np.array([[1,2], [3,4]])
A.shape

B = np.array([[5,6], [7,8]])
B.shape

np.dot(A,B)

#array([[19, 22],
#       [43, 50]])


  • np.dot 명령어를 통해 두 행렬의 내적(dot product)을 구할 수 있다. 신경망에서 이전층에서 다음층으로 forward 되는 것을 구현할 때 입력과 출력을 행렬의 내적으로 표현할 수 있다.


""" 간단한 신경망 구현 
    입력층에서 1층으로 가는 신호 
    첫 번째 히든 레이어의 노드 수는 3개
"""

def sigmoid(num) : 
    return 1/(1+np.exp(-num))

X = np.array([1.0,0.5]) # 입력이 2개 (x1, x2)
W1 = np.array([[0.1,0.3,0.5],[0.2,0.4,0.6]]) # 입력에 대응하는 weights 
B1 = np.array([0.1,0.2,0.3]) # bias

print X.shape # 1x2
print W1.shape # 2x3
print B1.shape # 1x3

A1 = np.dot(X,W1) + B1
Z1 = sigmoid(A1) # 첫 번째 레이어의 최종 아웃풋

print A1
print Z1


입력층에서 첫 번째 히든 레이어(노드 3개짜리)로 forward하는 것을 구현한 것이다. 입력과 weights, bias는 임의로 정한 것이다. 입력은 x1, x2 이고 각각 1.0,  0.5 이다. weights는 총 6개이다. (이는 input layer의 노드 수 2와 첫 번째 히든레이어의 노드수 3을 곱한 것이다.) bias는 길이 3의 array인데 이를 더해주어 XW에 더하여 XW+B를 만든다. Bias가 왜 필요한지 그리고 신경망에 대한 기본적인 이해는 http://hunkim.github.io/ml/ 홍콩 과기대 김성훈 교수님의 강의를 참조하면 좋다.


""" 1층에서 2층으로 가는 신호 구현
"""
W2 = np.array([[0.1,0.4],[0.2,0.5],[0.3,0.6]])
B2 = np.array([0.1,0.2])

print Z1.shape # 1x3 첫 번째 레이어의 아웃풋이 인풋이 된다.
print W2.shape # 3x2
print B2.shape # 1x2

A2 = np.dot(Z1, W2) + B2
Z2 = sigmoid(A2)


 첫 번째 히든레이어에서 두 번째 히든레이어로 forward하는 것의 구현이다. 첫번째 히든 레이어의 아웃풋 Z1이 인풋이 되었고 위와 같은 방식으로 Z1*W2+B2를 하면 두 번째 히든 레이어의 output을 구할 수 있다.


""" 2층에서 출력층으로 가는 신호 구현
"""

# 출력층의 활성화 함수로 항등함수를 쓴다. 
# 회귀의 경우 출력층의 활성화 함수로 일반적으로 항등항수를  쓴다.
# 2-class classification에서는 sigmoid
# 3-class 이상의 classification에서는 softmax를 쓴다.
def identity_function(x) :
    return x

W3 = np.array([[0.1,0.3],[0.2,0.4]])
B3 = np.array([0.1,0.2])

print Z2.shape # 1x2
print W3.shape # 2x2
print B3.shape # 1x2

A3 = np.dot(Z2, W3) + B3
Y = identity_function(A3)

print Y # 최종 아웃풋 출력 


두 번째 히든 레이어에서 출력층으로 forward 하는 것의 구현이다. 출력층의 activation function은 identity function을 사용하였는데, 이는 '풀고자 하는 문제의 성질' 과 관련이 있다. 회귀에서는 항등함수를, 2클래스 분류에서는 시그모이드를, 다중 클래스 분류에는 소프트 맥스 함수를 사용하는 것이 일반적이다.

/**

날짜 : 2017.01.25

밑바닥부터 시작하는 딥러닝(한빛미디어) 참고

activation function 정리

*/


1. Step Function


import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
""" 일반적인 step_function """
def step_function(x) : 
    if x > 0 :
        return 1
    else : 
        return 0

""" numpy의 트릭을 사용한 step function 구현 """
def step_function_np(x) : 
    y = x > 0
    # boolean을 int로 변환하면 True = 1, False = 0이 된다.
    # numpy 배열의 type을 변경할 때 .astype을 쓴다.
    return y.astype(np.int)


첫 번째 step_function의 경우 numpy array를 입력으로 받아들일 수 없다. 반면 step_function_np 함수는 numpy array를 입력으로 받아들일 수 있다. 아래의 경우 y = x > 0 에서 boolean 형식의 numpy array가 반환되는 점을 트릭으로 활용하여 이를 step_function 구현해 활용한 것이다.


실제로 파이썬에서 deep learning 알고리즘을 구현할 때는 일반적으로 numpy array를 입력으로 받아 numpy array를 출력으로 내보내는 함수를 작성하는 것이 편하므로 step_function_np와 같이 구현하는 것이 더 바람직하다.


x = np.arange(-5.0, 5.0, 0.1) # -5.0부터 5.0까지 0.1 간격의 numpy array 생성
y = step_function_np(x)

plt.plot(x,y)
plt.ylim(-0.1,1.1)
plt.show()



2. Sigmoid function


def sigmoid(x) :
    return 1 / (1+np.exp(-x))

x = np.array([-1.0, 1.0, 2.0])

# numpy broadcast 기능으로 인해 정상수행
sigmoid(x)


  • numpy broadcast 기능으로 인해 numpy array에 어떠한 연산을 하면 각각의 element에 연산이 적용된 numpy array가 반환된다.


x = np.arange(-5.0,5.0,0.1)
plt.plot(x, sigmoid(x))
plt.ylim(-0.1,1.1)
plt.show()


  • np.arange는 -5.0 부터 5.0까지 0.1 간격으로 numpy array를 생성해준다.



3. ReLU


def relu(x) : 
    return np.maximum(0, x)


x = np.arange(-5.0,5.0,0.1)
plt.plot(x, relu(x))
plt.show()