일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- NMT
- one-hot vector
- machine translation
- SVD
- 정칙행렬
- 프로그래머를 위한 선형대수
- 신경망 학습
- ReLU
- word embedding
- pandas
- 밑바닥부터 시작하는 딥러닝
- NLP
- 역행렬
- 선형대수학
- DataFrame
- RNN
- Python
- 판다스
- word2vec
- 동시발생 행렬
- 벡터간 유사도
- Linear Algebra
- 자연어처리
- 데이터프레임
- sigmoid
- PPMI
- 밑바닥부터 시작하는 딥러닝2 2장
- Transformer
- 연립일차방정식
- 딥러닝
- Today
- Total
생각하는감자
3장-신경망 본문
신경망
퍼셉트론을 사용할 때의 단점은 여전히 원하는 결과를 출력하기 위해서는 사람이 수동적으로 가중치 값을 적절히 정해야 한다는 것이다. 신경망이 이 점을 해결해 주는데, 신경망은 가중치 매개변수의 적절한 값을 데이터로부터 자동으로 학습하는 능력을 가지고 있다. 예를 들어 간단한 신경망을 그림으로 나타내면 아래와 같다. 가장 왼쪽 줄을 입력층이라 하고, 가장 오른쪽 줄을 출력층이라고 하며, 그 사이에 있는 줄들은 은닉층이라고 한다.
2장에서 언급한 퍼셉트론을 활용하여 신경망의 신호전달방법을 살펴보자면, 퍼셉트론을 수식으로 나타내면 아래와 같았다.
b는 편향(bias)을 나타내는 매개변수인데, 뉴런이 얼마나 쉽게 활성화되는지를 제어하는 역할을 한다. w는 각 신호의 가중치(weight)를 나타내는 매개변수인데, 각 신호가 얼마만큼의 영향력을 지닌 신호인지를 나타낸다. 하지만 위의 신경망의 그림이나 2장에서 명시된 퍼셉트론을 나타내는 그림을 보면 편향이 명시되어 있지 않다. 따라서 1이라는 값을 가진 뉴런(노드)을 추가하여 해당 노드의 가중치를 b라고 명시하게 된다.
위의 퍼셉트론 식을 한층 더 간결하게 표현하자면 아래와 같이 하나의 함수로 나타낼 수 있다. 입력신호의 총합이 h(x)라는 함수를 거쳐 변환된 후 출력값이 되며 이 함수를 활성화함수라고 한다. 입력신호의 총합이 0인지 1인지, 즉, 신경망의 활성화를 일으키는지 일으키지 않는지를 정하는 역할을 수행한다.
활성화함수
시그모이드 함수(Sigmoid)
시그모이드 함수는 신경망에서 자주 이용하는 활성화 함수이다.
시그모이드 함수는 넘파이를 활용하여 쉽게 구현할 수 있다. 그래프의 모양은 matplotlib을 사용하여 확인해 볼 수 있다.
import numpy as np
import matplotlib.pyplot as plt
def sigmoid(x) :
return 1 / (1 + np.exp(-x))
하나의 예를 들어 시그모이드함수를 출력해 보면, 아래와 같은 코드를 활용할 수 있다. (import matplotlib은 위의 코드블록에서 진행함)
x = np.arange(-5.0, 5.0, 0.1)
y = sigmoid(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1) #y축 범위 지정
plt.show()
해당함수를 관찰해 보면 우리는
- 입력이 작을 때의 출력은 0에 가깝고,
- 입력이 클 때의 출력은 1에 가까워지며,
- 입력이 아무리 작거나 커도 출력값은 0~1이라는 점을 알 수 있다.
입력이 클 때 1에 가까워지고 입력이 작을 때 0에 가까워진다는 것은 입력이 중요하면 큰 값을 출력하고 중요하지 않으면 작은 값을 출력하게 된다는 것을 의미한다.
* 신경망에서는 비선형 함수를 활용해야 한다. 선형함수를 활용하게 되면 신경망의 층을 깊게 하는 의미가 사라지기 때문이다. *
ReLU 함수(Rectified Linear Unit)
최근에 많이 사용하는 함수로 입력값이 0을 넘으면 그대로 출력하고, 0 이하이라면 0을 출력하는 함수이다.
def relu(x):
return np.maximum(0,x)
ReLU 함수는 위의 코드로 구현할 수 있다. 넘파이의 maximum은 주어진 두 값 중 큰 값을 출력하는 함수이다. x가 0보다 크다면 x를 반환할 것이고, x가 0보다 작거나 같다면 0을 반환할 것이므로 ReLU함수를 표현할 수 있게 된다.
예를 들어 나타내보면 아래의 코드로 해당 그래프를 그릴 수 있다.
x = np.arange(-5.0, 5.0, 1.0)
y = relu(x)
plt.plot(x, y)
plt.ylim(-1.0, 5.0) #y축 범위 지정
plt.show()
3층 신경망 구현하기
표기법
가중치와 은닉층의 뉴런을 표기할 땐 다음과 같이 표기한다.
1층의 신호전달 구현하기
아래의 그림을 예시로 1층의 첫 번째 뉴런을 수식으로 나타내보면 다음과 같다.
신경망에서의 행렬곱은 넘파이 행렬과 dot이라는 함수를 통해 구현할 수 있는데, 이를 활용하여 위의 그림을 코드로 구현해 보자면 아래와 같다.
import numpy as np
X = np.array([1.0, 0.5])
W1 = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
B1 = np.array([0.1, 0.2, 0.3])
print("W1: ", W1.shape)
print("X: ", X.shape)
print("B1: ", B1.shape)
A1 = np.dot(X, W1) + B1
print("A1: ", A1)
배열에는 적당한 수를 임의로 넣은 것이고 결과는 다음과 같이 출력된다.
앞서 언급했듯이 활성화 함수 중 시그모이드를 통과한다고 하면 결과로 출력된 A1을 위에서 만들었던 sigmoid 함수에 넣어주면 된다. 그렇게 출력된 결괏값은 그다음층의 입력값이 되어 다음 층의 신호로 전달될 것이다.
최종적으로 3층 신경망을 구현해 보면 다음과 같다. 아래의 코드에서 사용된 init_network()라는 함수는 가중치와 편향을 초기화하고 이들을 network라는 딕셔너리에 저장한다. forward()라는 함수는 입력신호를 출력으로 변환하는 처리과정을 담은 함수로 입력에서 출력방향으로 전달되는 순전파임을 알리기 위함이다.
def sigmoid(x) :
return 1 / (1 + np.exp(-x))
def identity_function(x) :
return x
def init_network():
network = {}
network['W1'] = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
network['b1'] = np.array([0.1, 0.2, 0.3])
network['W2'] = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
network['b2'] = np.array([0.1, 0.2])
network['W3'] = np.array([[0.1, 0.3], [0.2, 0.4]])
network['b3'] = np.array([0.1, 0.2])
return network
def forward(network, x):
W1, W2, W3 = network['W1'], network['W2'], network['W3']
b1, b2, b3 = network['b1'], network['b2'], network['b3']
a1 = np.dot(x, W1) + b1
z1 = sigmoid(a1)
a2 = np.dot(z1, W2) + b2
z2 = sigmoid(a2)
a3 = np.dot(z2, W3) + b3
y = identity_function(a3)
return y
network = init_network()
x = np.array([1.0, 0.5])
y = forward(network, x)
print(y)
*identity_function은 출력층의 활성화 함수로 그저 입력을 그대로 출력하는 항등함수이지만, 해당 구현에서는 흐름을 통일시키고자 활용하였다. 주로 출력층에서는 softmax 함수가 사용된다.*
출력층
일반적으로 회귀문제에 대해서는 출력층에서 항등함수를 사용하고, 분류문제에는 소프트맥스를 사용한다. 항등함수는 위에서 언급했듯이 입력을 그대로 출력하는 함수이다. 분류에 사용하는 소프트맥스의 경우, 분자엔 입력신호의 지수함수, 분모는 모든 입력 신호의 지수함수의 합으로 구성된다. 식으로 적어보면 다음과 같다.
이것을 코드로 구현해 내는 것은 크게 어렵지 않다.
def softmax(a):
exp_a = np.exp(a)
sum_exp_a = np.sum(exp_a)
y = exp_a / sum_exp_a
return y
하지만 주의할 점이 있다. Softmax 함수는 지수함수를 사용하는데, 이 함수의 특징은 값이 매우 기하급수적으로 커진다는 것이다. 그저 코드상으로는 문제가 없어 보이지만, 오버플로 문제를 일으킬 수 있다. 즉, 표현할 수 있는 수의 범위가 한정되어 너무 큰 값은 표현할 수 없다는 문제가 발생한다는 것이다. 이때 사용하는 방법은 임의의 정수를 분자와 분모에 모두 곱해주어 exp() 안으로 들이면서 logC로 만드는 것이다. 어떤 정수를 더해도 결과는 바뀌지 않기 때문에 C에 어떤 정수를 대입하던지 큰 상관은 없지만, 오버플로를 막기 위한 용도이면 가장 큰 값을 대입하는 것이 일반적이다. 이를 바탕으로 softmax 함수를 다시 구현하면 아래와 같다.
#오버플로 해결
def softmax(a):
c = np.max(a)
exp_a = np.exp(a-c)
sum_exp_a = np.sum(exp_a)
y = exp_a / sum_exp_a
return y
'딥러닝' 카테고리의 다른 글
5장-오차역전파법 (0) | 2023.03.30 |
---|---|
4장-신경망 학습 (0) | 2023.03.29 |
2장 - 퍼셉트론 (0) | 2023.03.28 |
Introduction (0) | 2023.03.28 |
Activation Function - Sigmoid, Tanh, ReLU (0) | 2022.12.24 |