본문 바로가기

Deep Learning

[밑바닥부터 시작하는 딥러닝2] Chapter 1. 신경망 복습

다음은 [밑바닥부터 시작하는 딥러닝2]을 학습하고 정리하는 내용입니다. 신경망 기초 및 NLP 기초를 Numpy와 Matplotlib만 가지고 구현해봄으로써 자연어처리 및 딥러닝 구조에 대해 더 깊게 이해할 수 있기를 바랍니다. 

1.1 수학과 파이썬 복습

1.1.1 벡터와 행렬

  • Vector : 크기와 방향을 가진 양. 파이썬에서는 1차원 배열로 취급
  • Matrix(행렬) : 2차원 형태의 배열.
  • 수학에서는 열벡터가 기본이지만, 여기에서는 개발의 편의성을 위해 기본적으로 행벡터 기준으로 다룬다. 

1.1.2 행렬의 원소별 연산

  • Element-wise(원소별) 연산 : 다차원 넘파이 배열의 사칙연산 중 +와 *(원소별 원소 곱셈)가 가능.

1.1.3 브로드캐스트

  • 넘파이의 다차원 배열에서는 형상(shape)가 다른 배열끼리도 연산이 가능하다. 아래처럼 $2 \times 2$ 행렬 A에 대해 1차원 배열 b를 곱할 경우 1차원 배열 b가 A와 형상이 같아지도록 확장된다. 이를 Broadcast라 한다. 
A = np.array([[1,2],[3,4]])
b = np.array([10, 20])
A * b
# array([[10, 40],
#	[30, 80]])

1.1.4 벡터의 내적과 행렬의 곱

  • 내적(inner product) : $ \boldsymbol{ x } \cdot \boldsymbol{ y }= x_1 y_1 + x_2 y_2 + \cdots + x_n y_n$
  • 행렬곱(matrix multiplication) : $ AB = \begin{bmatrix} a & b \\ c & d \end{bmatrix} \begin{bmatrix} e & f \\ g & h \end{bmatrix} = \begin{bmatrix} ae+bg & af+bh \\ ce+dg & cf+dh \\ \end{bmatrix}$ 
  • Numpy의 np.dot()과 np.matmul() 메서드를 이용해서 구현 가능하다. 
# 벡터의 내적
a = np.array([1, 2, 3]); b = np.array([4, 5, 6])
np.dot(a, b) # 32

# 행렬의 곱
A = np.array([[1, 2], [3, 4]]); B = np.array([[5, 6],[7,8]])
np.matmul(A, B)
# array([[19, 22],
#	[43, 50]])

1.1.5 행렬 형상 확인

  • 행렬/벡터를 사용할 때는 반드시 그 shape(형상)을 주의해야 함. 수학적으로는 행렬의 곱에서는 대응하는 차언의 원소 수를 일치시켜야 한다. 즉, A의 열과 B의 행의 차수가 같아야 하고, 최종 곱셈인 C의 형태는 A의 행 x B의 열 형태의 차수를 가지게 된다.

1.2 신경망의 추론

1.2.1 신경망 추론 전체 그림 

  • 신경망은 입력을 출력으로 변환하는 함수라고 볼 수 있다. 
  • 입력층(Input Layer) : 2 뉴런, 은닉층(Hidden Layer) : 4 뉴런, 출력층(Output Layer) : 3 뉴런.
    각 화살표에는 가중치(weight)가 존재하여 가중치 x 뉴런의 값의 합에 활성화 함수를 적용한 값 + 편향(bias)을 다음 뉴런의 입력값으로 활용한다. 아래 그림의 경우 모든 뉴런과 연결되어 있기 때문에 Fully Connected Layer라 한다.
    수식 : $h_i = x_1 w_{1i} + x_2 w_{2i} + b_i$, 행렬곱을 이용하면 깔끔하게 정리 가능하다.
    $\left(h_1, h_2, h_3, h_4\right)=\left(x_1, x_2\right)\left(\begin{array}{llll}w_{11} & w_{12} & w_{13} & w_{14} \\ w_{21} & w_{22} & w_{23} & w_{24}\end{array}\right)+\left(b_1, b_2, b_3, b_4\right)$
    $\rightarrow \boldsymbol{h} = \boldsymbol{x} W + \boldsymbol{b} $

  • Minibatch의 경우 : vector(or matrix) $\boldsymbol{x}$의 행 각각에 샘플 데이터가 하나씩 저장됨. 아래와 같이 각 미니배치가 올바르게 변환되었는지를 파악하고, N개의 sample data가 한꺼번에 Fully connected layer를 통해 변환되며 은닉층도 N개의 뉴런이 함께 계산된다. 

행렬곱의 형상 확인

  • 비선형 활성화 함수(non-linear activation function)을 이용해 신경망의 표현력을 높일 수 있다. 대표적으로 사용하는 비선형 활성화 함수 : Sigmoid(시그모이드), ReLU, LeakyReLU, etc
  • Sigmoid : $\sigma(x) = \frac{1}{1+exp(-x)}$, $x \in \mathbb{R} \rightarrow 0 \leq \sigma(x) \leq 1 $
  • 이후, 활성화 함수의 출력인 $a$를 또 다른 완전연결계층(가중치 행렬 4 x 3)에 통과시켜 다시 변환한다. 최종 출력인 s의 형상은 (10, 3)이 된다. 즉, 10개의 데이터가 한꺼번에 각각 3차원 데이터로 변환되었다는 뜻이다. 
def sigmoid(x):
	return 1 / (1 + np.exp(-x))

x = np.random.randn(10, 2) # 입력 (N x 2)
W1 = np.random.randn(2, 4) # 가중치
b1 = np.random.randn(4) # 편향
W2 = np.random.randn(4, 3) # 가중치
b2 = np.random.randn(3) # 편향

h = np.matmul(x, W1) + b1
a = sigmoid(h) # sigmoid activation fuction
s = np.matmul(a, W2) + b2 # 최종 output

1.2.2 계층으로 클래스화 및 순전파 구현

  • 신경망에서 하는 처리를 Layer(계층)로 구현해보자. 신경망 추론(inference) 과정에서 하는 처리를 신경망의 순전파(forward)라 한다. 이후 신경망 학습 과정에서는 데이터를 반대방향으로 전파하기 때문에 역전파(backward propagation)이라 부른다. 
  • 신경망의 다양한 계층을 다음 규칙에 따라 모듈화. 
    (1) 모든 계층은 forward()와 backward() 메서드를 가진다.
    (2) 모든 계층은 인스턴스 변수인 params와 grads를 가진다. (params : 가중치, 편향 등 매개변수 담는 list, grads : params에 저장된 각 매개변수에 대응하며, 해당 매개변수의 기울기를 보관하는 리스트.)
  • Sigmoid Layer & Affine Layer : 
class Sigmoid:
  def __init__(self):
    self.params = [] # 학습하는 매개변수가 없으므로 빈 리스트로 초기화
 
  def forward(self, x):
    return 1 / (1 + np.exp(-x))

class Affine:
  def __init__(self, W, b):
    self.params = [W, b]

  def forward(self, x):
    W, b = self.params
    out = np.matmul(x, W) + b
    return out
  • 위의 계층을 사용해 신경망의 추론 처리를 구현해보자.

class TwoLayerNet:
  def __init__(self, input_size, hidden_size, output_size):
    I, H, O = input_size, hidden_size, output_size

    # 가중치와 편향 초기화
    W1 = np.random.randn(I, H)
    b1 = np.random.randn(H)
    W2 = np.random.randn(H, O)
    b2 = np.random.randn(O)

    # 계층 생성
    self.layers = [
        Affine(W1, b1),
        Sigmoid(),
        Affine(W2, b2)
    ]

    # 모든 가중치를 리스트에 모은다. 
    self.params = []
    for layer in self.layers:
      self.params += layer.params

  def predict(self, x):
    for layer in self.layers:
      x = layer.forward(x)
    return x

x = np.random.randn(10, 2)
model = TwoLayerNet(2, 4, 3)
s = model.predict(x)
  • (1) 초기화 메서드(__init__) : 가중치를 초기화하고 3개의 계층을 생성한다. 마지막으로는 학습해야 할 가중치 매개변수들을 params 리스트에 모아 저장한다. 
  • 학습해야 할 모든 매개변수가 model, params라는 하나의 리스트에 모여있으므로, 신경망 학습이 한 층 수월해진다.

1.3 신경망의 학습

1.3.1 손실 함수

  • 손실(Loss) : 학습 단계의 특정 시점에서 신경망의 성능을 나타내는 척도. 학습시 주어진 정답과 신경망이 예측한 결과를 비교해 예측이 얼마나 나쁜가를 산출한 단일 값(scala)이다.
  • 다중 클래스 분류 신경망에서는 흔히 교차 엔트로피 오차(Cross Entropy Error)를 이용함. 지금까지 봐온 예제로 손실을 구해보자. 

  • 앞 절의 Layer 마지막 단계에 Softmax 계층(softmax 함수)과 Cross Entropy Error 계층을 새로 추가하자. Softmax 계층의 출력은 확률이 되어 마지막 cross entropy error 계층에는 확률값과 정답 label이 입력된다. 
  • Softmax function : $y_k = \frac{exp(s_k)}{\sum_{i=1}^{n} exp(s_i)}$. 즉, 출력이(class가) 총 $n$개 일 때, $y_k$는 $k$번째 class에 해당하는 softmax 함수의 출력이다.
  • 소프트맥스 함수의 출력의 각 원소는 0.0 이상, 1.0 이하의 실수이며 이 원소들을 모두 더하면 1이 된다. ('확률'로 해석 가능). 소프트맥스의 출력인 이 '확률'이 다음 차례인 교차 엔트로피 오차에 입력된다. 
  • Cross Entropy Error : $L = - \sum_{k} t_k log y_k $, $t_k$는 k번째 클래스에 해당하는 정답 레이블이며 원-핫 벡터로 표기한다. 따라서 위의 식은 실질적으로 정답 label이 1인 원소에 해당하는 출력의 log를 계산할 뿐이다.
  • 미니배치 처리를 고려하면, 교차 엔트로피 오차의 식은 다음과 같다. N으로 나눠서 평균 손실 함수를 구함으로써 미니배치의 크기에 관계없이 일관된 척도를 얻을 수 있다. : 
    $$L = -\frac{1}{N} \sum_{n} \sum_{k} t_{nk} log y_{nk} $$ where $t_{nk}$ : $n$번째 데이터의 $k$차원째의 값이자 정답 label, $y_{nk}$ : 신경망의 출력
def softmax(x):
  if x.ndim == 2:
    x = x - x.max(axis=1, keepdims=True)
    x = np.exp(x)
    x /= x.sum(axis=1, keepdims=True)
  elif x.ndim == 1:
    x = x - np.max(x)
    x = np.exp(x) / np.sum(np.exp(x))
  
  return x

def cross_entropy_error(y, t):
  if y.ndim == 1:
    t = t.reshape(1, t.size)
    y = y.reshape(1, y.size)
  
  if t.size == y.size:
    t = t.argmax(axis=1)

  batch_size = y.shape[0]

  return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size

class SoftmaxWithLoss:
  def __init__(self):
    self.params, self.grads = [], []
    self.y = None
    self.t = None

  def forward(self, x, t):
    self.t = t
    self.y = softmax(x)

    if self.t.size == self.y.size:
      self.t = self.t.argmax(axis=1)
    
    loss = cross_entropy_error(self.y, self.t)
    return loss

1.3.2 미분과 기울기

  • 신경망 학습의 목표 : 손실을 최소화하는 매개변수를 찾는것. 이때 중요한 것이 '미분'과 '기울기'이다.
  • $y = f(x)$에 대해 $\frac{dy}{dx}$ 미분의 의미 : $x$의 값을 매우 조금 변화시켰을 때 $y$값이 얼마나 변하는가의 '변화의 정도'를 의미한다. 또한 각 $x$에서의 '기울기'에 해당한다. 
  • 다변수 미분 : $L = f(x)$에 대해 $$\frac{\partial L}{\partial \mathbf{x}}=\left(\frac{\partial L}{\partial x_1}, \frac{\partial L}{\partial x_2}, \cdots, \frac{\partial L}{\partial x_n}\right)$$
  • 이처럼 벡터의 각 원소에 대한 미분을 정리한 것이 기울기(gradient)이다.
  • 행렬 미분 : $L = f(\mathbf{W})$에 대해 $$\frac{\partial L}{\partial \mathbf{W}}=\left(\begin{array}{ccc}\frac{\partial L}{\partial W_{11}} & \cdots & \frac{\partial L}{\partial W_{1 n}} \\ \vdots & \ddots & \\ \frac{\partial L}{\partial W_{m 11}} & & \frac{\partial L}{\partial W_{m m}}\end{array}\right)$$
  • 중요한 점은 $\mathbf{W}$와 $ \frac{\partial L}{\partial \mathbf{W}}$의 형상(shape)이 같다는 점이며, 이를 이용하면 Chain rule을 사용해 매개변수 갱신과 연쇄 법칙을 쉽게 구현 가능하다. 

1.3.3 연쇄 법칙(Chain Rule)

  • 각 매개변수(W1, b1, W2, b2)에 대한 손실의 기울기를 얻기 위해 오차역전파(Back-Propagation)을 활용할 수 있다.
  • 연쇄 법칙(Chain Rule) : 합성함수에 대한 미분법칙으로써, $y = f(x), z = g(y) = g(f(x))$에 대해 $ \frac{\partial z}{\partial x} = \frac{\partial z}{\partial y} \frac{\partial y}{\partial x} $로 합성함수의 미분을 계산할 수 있다. 
  • 즉, 아무리 여러개의 함수를 연결해도, 그 미분은 개별 함수의 미분들의 곱으로 계산가능하다는 의미이다. 

1.3.4 계산 그래프

  • 순전파(forward propagation) : 어떤 계산을 거쳐서 최종적으로 scalar $L$이 출력된다고 가정하자. 현재 목표는 $L$의 미분(기울기)을 각 변수에 대해 구하는 것이다.
  • 역전파(backward propagation) : 역전파에서 '전파되는 값'은 최종 출력 scalar $L$의 각 변수에 대한 미분이다. 즉, $z$에 대한 미분은 \frac{\partial L}{\partial z} $이며 $x, y$에 대한 미분은 각각 $ \frac{\partial L}{\partial x}, \frac{\partial L}{\partial y} $이다. 
  • 연쇄법칙에 따라, 역전파로 흐르는 미분값은 상류로부터 흘러온 미분과, 각 연산 노드의 국소 미분을 곱해 계산할 수 있다. $\rightarrow \frac{\partial L}{\partial x} = \frac{\partial L}{\partial z} \frac{\partial z}{\partial x},  \frac{\partial L}{\partial y} = \frac{\partial L}{\partial z} \frac{\partial z}{\partial y}  $.
  • 만약 벡터나 행렬 혹은 텐서같은 다변수를 흘릴 경우 각 원소를 독립적으로 계산하는 "원소 별 연산"을 수행한다.
  • 덧셈 노드, 곱셈 노드, 분기 노드, Repeat 노드, Sum 노드, etc

  • Repeat 노드 : 2개로 분기하는 분기 노드를 일반화하면 N개로의 분기(복제)가 된다.
  • 길이가 D인 배열을 N개로 복제할 경우, 그 역전파는 N개의 기울기를 모두 더해 구할 수 있다. 
  • y = np.repeat(x, N, axis=0) # 순전파, 원소 복제를 실행
  • dx = np.sum(dy, axis=0, keepdims=True) # 역전파

 

  • Sum 노드 : $N \times D$ 배열에 대해 그 총합을 0 축에 대해 구하는 계산. 
  • 역전파 시 상류로부터의 기울기를 모든 화살표에 분배한다. 
  • y = np.sum(x, axis=0, keepdims=True) # 순전파
  • dx = np.repeat(dy, N, axis=0) # 역전파
  • Sum 노드와 Repeat 노드는 서로 반대관계이다. Sum 노드의 순전파가 Repeat 노드의 역전파가 되며, Repeat 노드의 순전파가 Sum 노드의 역전파가 된다. 

 

    • MatMul 노드 (행렬곱) : $\mathbf{y} = \mathbf{xW}$, $\mathbf{x}, \mathbf{W}, \mathbf{y}$의 형상은 각각 $1 \times D, D \times H, 1 \times H$.
    • $\mathbf{x}$의 $i$번째 원소에 대한 미분 : $$ \frac{\partial L}{\partial x_i} = \sum_{j}  \frac{\partial L}{\partial y_j} \frac{\partial y_j}{\partial x_i} = \sum_{j}  \frac{\partial L}{\partial y_j} W_{ij} $$ since $ \frac{\partial y_j}{\partial x_i} = W_{ij}$
      $$\rightarrow \frac{\partial L}{\partial \mathbf{x}} = \frac{\partial L}{\partial \mathbf{y}} \mathbf{W}^T$$

행렬 곱 계산

class MatMul:
  def __init__(self, W):
    self.params = [W] # 학습하는 매개변수를 params에 보관
    self.grads = [np.zeros_like(W)] # 매개변수에 대응되는 기울기를 보관
    self.x = None

  def forward(self, x):
    W, = self.params
    out = np.matmul(x, W)
    self.x = x
    return out
  
  def backward(self, dout):
    W, = self.params
    dx = np.matmul(dout, W.T)
    dW = np.matmul(self.x.T, dout)
    self.grads[0][...] = dW # 가중치의 기울기를 grads에 저장
    # [...] : 넘파이 배열이 가리키는 메모리 위치를 고정시킨 다음 그 위치에 원소들을 덮어쓴다. (deep copy)
    return dx

1.3.5 기울기 도출과 역전파 구현

  • Sigmoid 계층 : $y = \frac{1}{1+exp(-x)} \rightarrow \frac{\partial y}{\partial x} = y (1-y)$. 
    따라서, $\frac{\partial L}{\partial x} = \frac{\partial L}{\partial y} y (1-y) $로 계산 가능.
  • Affile 계층 : bias를 더할 때 넘파이의 브로드캐스트가 사용되어 아래 그림과 같이 표기 가능. 

  • Softmax with Loss 계층 : softmax 계층의 역전파가 $(y_i - t_i)$로 깔끔하게 떨어진다. 즉, 역전파 값이 자신의 출력과 정답 레이블의 차이!

 

class Sigmoid:
  def __init__(self):
    self.params, self.grads = [], []
    self.out = None # 출력
  
  def forward(self, x):
    out = 1 / (1 + np.exp(-x))
    self.out = out # 순전파 때는 출력을 out에 저장
    return out
  
  def backward(self, dout):
    dx = dout * (1.0 - self.out) * self.out # 역전파시 out 변수를 사용
    return dx

class Affine:
  def __init__(self, W, b):
    self.params = [W, b] # 매개변수 저장 
    self.grads = [np.zeros_like(W), np.zeros_like(b)] # 기울기 저장
    self.x = None

  def forward(self, x):
    W, b = self.params
    out = np.matmul(x, W) + b
    self.x = x
    return out

  def backward(self, dout):
    W, b = self.params
    dx = np.matmul(dout, W.T)
    dW = np.matmul(self.x.T, dout)
    db = np.sum(dout, axis=0)

    self.grads[0][...] = dW # 가중치 매개변수 기울기를 인스턴스 변수 grads에 저장
    self.grads[1][...] = db
    return dx

class SoftmaxWithLoss:
  def __init__(self):
    self.params, self.grads = [], []
    self.y = None
    self.t = None

  def forward(self, x, t):
    self.t = t
    self.y = softmax(x)
    
    if self.t.size == self.y.size:
      self.t = self.t.argmax(axis=1)

    loss = cross_entropy_error(self.y, self.t)
    return loss
  
  def backward(self, dout=1):
    batch_size = self.t.shape[0]

    dx = self.y.copy()
    dx[np.arange(batch_size), self.t] -= 1
    dx *= dout
    dx = dx / batch_size

    return dx

1.3.6 가중치 갱신

  • 신경망의 학습 순서 : 
    1단계 : 미니배치 - 훈련 데이터 중 무작위로 다수의 데이터를 골라낸다.
    2단계 : 기울기 계산 - 오차역전파법으로 각 가중치 매개변수에 대한 손실함수의 기울기를 구한다. 
    3단계 : 매개변수 갱신 - 기울기를 사용하여 가중치 매개변수를 갱신한다.
    4단계 : 반복 - 1~3단계를 필요한 만큼 반복한다. 
  • 경사하강법(Gradient Descent) : 기울기는 현재의 가중치 매개변수에서 손실을 가장 크게 하는 방향을 가리키기 때문에 매개변수를 이 기울기와 반대 방향으로 갱신하면 손실을 줄일 수 있다. 
  • 확률적 경사하강법(Stochastic Gradient Descent, SGD) : (현재의) 가중치를 기울기 방향으로 일정 거리만큼 갱신한다.
    $$\mathbf{W} \leftarrow \mathbf{W} - \eta \frac{\partial L}{\partial \mathbf{W}}$$. 
  • 이외에도 Momentum, AdaGrad, Adam 등의 기법을 활용할 수 있다.
class SGD:
  def __init__(self, lr=0.01):
    self.lr = lr
  
  def update(self, params, grads):
    for i in range(len(params)):
      params[i] -= self.lr * grads[i]

1.4 신경망으로 문제를 풀다

1.4.2 신경망 구현

class TwoLayerNet:
  def __init__(self, input_size, hidden_size, output_size): # 입력층, 은닉층, 출력층 뉴런 수를 입력으로 받음. 
    I, H, O = input_size, hidden_size, output_size

    # 가중치, 편향 초기화
    W1 = 0.01 * np.random.randn(I, H) # 가중치 : 작은 무작위 값으로 초기화
    b1 = np.zeros(H) # 편향 : 0 벡터로 초기화
    W2 = 0.01 * np.random.randn(H, O)
    b2 = np.zeros(O)

    # Layer 생성 - 필요한 계층을 생성해 모아두기
    self.layers = [ 
        Affine(W1, b1), 
        Sigmoid(),
        Affine(W2, b2)
    ]
    self.loss_layer = SoftmaxWithLoss()

    # 모든 가중치와 기울기를 리스트에 모은다. 
    self.params, self.grads = [], []
    for layer in self.layers:
      self.params += layer.params # 모델에서 사용하는 매개변수 모아두기
      self.grads += layer.grads # 모델에서 사용하는 가중치 모아두기
    
  # 추론 함수
  def predict(self, x): 
    for layer in self.layers:
      x = layer.forward(x)
    return x
  
  # 순전파 함수
  def forward(self, x, t):
    score = self.predict(x)
    loss = self.loss_layer.forward(score, t)
    return loss
  
  # 역전파 함수
  def backward(self, dout=1):
    dout = self.loss_layer.backward(dout)
    for layer in self.layers[::-1]:
      dout = layer.backward(dout)
    return dout

1.4.3 학습용 코드 

  • 학습 데이터와 신경망 (모델)과 옵티마이저(SGD 최적화기)를 생성하고, 앞 절에서 설명한 4 단계의 절차대로 학습을 수행한다. 
# 1. hyperparameter 설정 - 학습 에폭 수, 미니배치 크기, 은닉층의 뉴런 수, 학습률 등을 술정한다. 
max_epoch = 300
batch_size = 30
hidden_size = 10
learning_rate = 1.0

# 2. 데이터 준비, 모델과 옵티마이저 생성
x, t = load_data()
model = TwoLayerNet(input_size=2, hidden_size=hidden_size, output_size=3)
optimizer = SGD(lr=learning_rate)

# 학습에 사용하는 변수
data_size = len(x)
max_iters = data_size // batch_size
total_loss = 0
loss_count = 0
loss_list = []

for epoch in range(max_epoch):
  # 3. data 뒤섞어주기 - epoch마다
  idx = np.random.permutation(data_size)
  x = x[idx]; t = t[idx]

  for iters in range(max_iters):
    # 학습은 minibatch 방식으로 진행되며, 데이터를 무작위로 선택했다. 
    batch_x = x[iters*batch_size:(iters+1)*batch_size] 
    batch_t = t[iters*batch_size:(iters+1)*batch_size]
  
  # 4. 기울기를 구해 매개변수 갱신
  loss = model.forward(batch_x, batch_t)
  model.backward()
  optimizer.update(model.params, model.grads)

  total_loss += loss
  loss_count += 1

  # 5. 학습 결과 정기적 출력 
  if (iters+1) % 10 == 0:
    avg_loss = total_loss / loss_count
    print('| 에폭 %d | 손실 %.2f' % (epoch+1, avg_loss))
    loss_list.append(avg_loss)
    total_loss, loss_count = 0, 0
  • 결과를 그려보면 손실 값이 학습이 진행될수록 낮아지는 것을 볼 수 있다. 
  • 학습된 신경망은 깊은 은닉층을 추가하고 비선형 activation 함수를 활용함으로써 나선형 패턴을 올바르게 파악해 비선형 분리 영역을 학습가능하다. 

1.5 계산 고속화

  • 신경망 고속화에 도움이 되는 '비트 정밀도'와 'GPU'

1.5.1 비트 정밀도

  • Numpy 부동소수점 수는 64 bit data type(float64)을 기본으로 함. 
  • 신경망 추론/학습은 32bit 부동소수점 수로도 문제 없이 수행할 수 있기 때문에 메모리 관점에서는 32bit가 더 나음.
  • 신경망 계산 시 데이터를 전송하는 '버스 대역폭'이 병목이 되는 경우가 많은데, 이 경우도 작은 테이터타입이 더 유리
  • 계산 속도 측면에서도 32bit 부동소수점 수가 일반적으로 더 빠르다. 
  • 신경망 추론으로 한정 시 16 bit 부동소수점 수를 사용해도 인식률이 거의 떨어지지 않음. 

1.5.2 GPU(쿠파이) 

  • cupy라는 엔비디아 GPU를 사용해 병렬 계산이 가능판 패키지가 존재.

1.6 정리

  • 신경망은 입력층, 은닉층(중간층), 출력층을 지닌다.
  • 완전연결계층에 의해 선형 변환이 이뤄지고, 활성화 함수에 의해 비선형 변환이 이뤄진다.
  • 완전연결계층이나 미니배치 처리는 행렬로 모아 한꺼번에 계산할 수 있다.
  • 오차역전파법을 사용하여 신경망의 손실에 관한 기울기를 효율적으로 구할 수 있다.
  • 신경망이 수행하는 처리는 계산 그래프로 시각화 할 수 있으며, 순전파와 역전파를 이해하는데 도움이 된다.
  • 신경망의 구성 요소들을 '계층'으로 모듈화해두면, 이를 조립하여 신경망을 쉽게 구성할 수 있다.
  • 신경망 고속화에는 GPU를 이용한 병렬 계산과 데이터의 비트 정밀도가 중요하다.