페이지

2018년 4월 18일 수요일

import numpy as np

# 7.1 전체 구조
"""
합성곱 신경망convolutional neural network, CNN
(합성곱 : 컨볼루션. 두 함수 중 하나를 반전, 이동시켜가며 나머지 함수와의 곱을 연이어 적분하는 것)
지금까지 본 신경망은 인접하는 계층의 모든 뉴런과 결합되어 있었다. 이를 완전연결fully-connected
라고 하며, 이를 Affine 계층이라는 이름으로 구현했다.
CNN에는 여기에 합성곱 계층과 풀링 계층이 추가된다.
Affine-ReLu가 Conv-ReLU-(Pooling)으로 바뀌며 출력에 가까운 층에서는 Affine-ReLU 구성을
사용할 수 있다. 출력 계층에서는 Affine-Softmax 조합을 사용한다.
"""

# 7.2 합성곱 계층
# 7.2.1 완전연결 계층의 문제점
"""
완전연결Affine 계층 : 인접하는 계층의 뉴런이 모두 연결되고 출력의 수는 임의로 정할 수 있다.
단점은 데이터의 형상이 무시된다.(가로, 세로, 채널로 구성된 3차원 데이터인 이미지를 1차원 데이터로
평탄화해줘야 한다.)
합성곱 계층은 형상을 유지하기 때문에 형상을 가진 데이터의 패턴을 이해할 수 있다.
CNN에서는 입출력 데이터를 특징 맵feature map이라고도 한다.
"""

# 7.2.2 합성곱 연산
"""
이미지 처리에서 말하는 필터 연산에 해당한다.
입력데이터  * 필터  →  출력 데이터
(4, 4)     (3, 3)    (2, 2)
1 2 3 0    2 0 1     15 16
0 1 2 3    0 1 2      6 15
3 0 1 2    1 0 2
2 3 0 1
필터의 윈도우를 일정 간격으로 이동해가며 입력 데이터에 적용한다. 입력과 필터의 원소를 곱한 후
총합을 구한다(단일 곱셈-누산fused multiply-add, FMA) 결과를 출력에 저장한다.
이 과정을 모든 장소에서 수행한다.
(합성곱은 교차상관과 비슷한 연산이다. 플리핑을 하면 합성곱, 아니면 교차상관. 딥러닝에서는 이를
잘 구분하지 않으며, 딥러닝 라이브러리에서는 플리핑하지 않거나 플리핑 여부를 인수로 받는다.)

CNN에서는 필터의 매개변수가 가중치에 해당하며, 편향 역시 존재한다.
입력데이터  * 필터              + 편향  → 출력데이터
(4, 4)     (3, 3)    (2, 2)           (2, 2)
1 2 3 0    2 0 1     15 16     3      18 19
0 1 2 3    0 1 2      6 15             9 18
3 0 1 2    1 0 2
2 3 0 1
"""

# 7.2.3 패딩
"""
패딩 : 합성곱을 수행 전 입력 데이터 주변을 특정 값(0 등)으로 채우는 것.
주로 출력 크기 조정을 목적으로 사용한다. 출력 크기가 커지기 때문에 입력 데이터의 공간적 크기를
유지한 채로 다음 계층에 전달할 수 있다.
"""

# 7.2.4 스트라이드
"""
스트라이드stride : 필터를 적용하는 위치의 간격. 일반적으로는 1이지만
2로 하면 필터를 적용하는 윈도우가 두 칸씩 이동한다. 스트라이드를 키우면 출력 크기가 작아진다.

이 값들에는 아래의 관계가 있다.
OH = (H + 2P + FH)/S + 1
OW = (W + 2P + FW)/S + 1
입력 크기 : (H, W)
필터 크기 : (FH, FW)
출력 크기 : (OH, OW)
패딩 : P, 스트라이드 : S
최종 결과는 정수로 나와야 한다.
"""

# 7.2.5 3차원 데이터의 합성곱 연산
"""
이미지는 가로 세로 채널이 있는 3차원 데이터이다. 채널의 수 만큼 필터가 필요하다.
모든 채널의 필터는 같은 크기여야 한다.
"""

# 7.2.6 블록으로 생각하기
"""
데이터와 필터를 직육면체 블록이라고 생각하면 쉽다. 이를 (채널, 높이, 너비) 순서로 나타낸다.
입력 데이터 * 필터    →    출력 데이터
(C, H, W)  (C, FH, FW) (1, OH, OW)

출력으로 다수의 채널을 내보내려면 필터를 여러 개(FN개) 사용하면 된다.
따라서 필터의 가중치 데이터는 4차원이 된다. 편향은 채널 하나에 값 하나씩으로 구성된다.(FN, 1, 1)
입력 데이터 *    필터         + 편향  →    출력 데이터
(C, H, W)  (FN, C, FH, FW)  (FN, 1, 1) (FN, OH, OW)
"""

# 7.2.7 배치 처리
"""
데이터 N개를 배치 처리한다면 데이터 형태는 다음과 같다.

입력 데이터(N개) * 필터(FN개)  +  편향   →   출력 데이터(N개)
(N, C, H, W) (FN, C, FH, FW) (FN, 1, 1) (N, FN, OH, OW)
"""

# 7.3 풀링 계층
"""
풀링 : 가로 세로 방향의 공간을 줄이는 연산. 여러 영역을 원소 하나로 집약하여 공간 크기를 줄인다.
2*2 최대 풀링max pooling을 스트라이드 2로 처리하는 예
(2*2 영역에서 가장 큰 원소 하나를 꺼낸다.)
1 2 3 0    2  3
0 1 2 3
3 0 1 2    4  2
2 3 0 1

일반적으로 윈도우 크기와 스트라이드는 같은 값으로 설정한다.
최대 풀링 외에도 평균 풀링 등이 있다. 이미지 인식 분야에서는 주로 최대 풀링을 사용한다.
"""

# 7.3.1 풀링 계층의 특징
"""
학습해야 할 매개변수가 없다 : 최댓값이나 평균을 취하는 명확한 연산이다.
채널 수가 변하지 않는다 : 채널 독립적으로 계산한다.
입력의 변화에 영향을 적게 받는다(강건하다) : 입력데이터가 조금 변해도 풀링의 결과는 잘 변하지 않는다.
"""

# 7.4 합성곱/풀링 계층 구현하기
# 7.4.1 4차원 배열
"""
CNN의 데이터는 4차원이다.
"""
x = np.random.rand(10, 1, 28, 28)  # 무작위 데이터 생성
print(x.shape)

# 첫 번째 데이터, 두 번째 데이터
print(x[0].shape)  # (1, 28, 28)
print(x[1].shape)  # (1, 28, 28)

# 첫 번째 데이터의 첫 채널의 공간 데이터
print(x[0, 0])  # or x[0][0]. 28 * 28의 벡터

# 7.4.2 im2cal로 데이터 전개하기
"""
합성곱을 구현하려면 다중 for문을 사용한다. 이는 복잡하고 numpy에서는 for문을 사용하면
성능이 떨어지기 때문에 im2col()이라는 함수를 사용해 구현한다.
im2col(image to column)은 필터를 적용하는 영역(3차원 블록)을 한 줄로 늘어놓는다.
4차원 데이터가 2차원이 된다. 여러 딥러닝 프레임워크에서 해당 함수를 구현해 사용하고 있다.
메모리를 더 많이 소비하는 단점이 있지만 행렬 계산 라이브러리는 큰 행렬의 계산에 장점이 있으므로
효율이 높아진다.

입력데이터를 2차원으로, 필터를 세로 1열로 전개해 내적을 취한 뒤 이를 4차원으로 reshape한다.
"""


# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 부모 디렉터리의 파일을 가져올 수 있도록 설정
import numpy as np
import matplotlib.pyplot as plt
from simple_convnet import SimpleConvNet
from matplotlib.image import imread
from common.layers import Convolution

def filter_show(filters, nx=4, show_num=16):
    """
    c.f. https://gist.github.com/aidiary/07d530d5e08011832b12#file-draw_weight-py
    """
    FN, C, FH, FW = filters.shape
    ny = int(np.ceil(show_num / nx))

    fig = plt.figure()
    fig.subplots_adjust(left=0, right=1, bottom=0, top=1, hspace=0.05, wspace=0.05)

    for i in range(show_num):
        ax = fig.add_subplot(4, 4, i+1, xticks=[], yticks=[])
        ax.imshow(filters[i, 0], cmap=plt.cm.gray_r, interpolation='nearest')


network = SimpleConvNet(input_dim=(1,28,28),
                        conv_param = {'filter_num':30, 'filter_size':5, 'pad':0, 'stride':1},
                        hidden_size=100, output_size=10, weight_init_std=0.01)

# 학습된 가중치
network.load_params("params.pkl")

filter_show(network.params['W1'], 16)

img = imread('../dataset/lena_gray.png')
img = img.reshape(1, 1, *img.shape)

fig = plt.figure()

w_idx = 1

for i in range(16):
    w = network.params['W1'][i]
    b = 0  # network.params['b1'][i]

    w = w.reshape(1, *w.shape)
    #b = b.reshape(1, *b.shape)
    conv_layer = Convolution(w, b)
    out = conv_layer.forward(img)
    out = out.reshape(out.shape[2], out.shape[3])
   
    ax = fig.add_subplot(4, 4, i+1, xticks=[], yticks=[])
    ax.imshow(out, cmap=plt.cm.gray_r, interpolation='nearest')

plt.show()










# coding: utf-8
import numpy as np


def smooth_curve(x):
    """손실 함수의 그래프를 매끄럽게 하기 위해 사용
   
    참고:http://glowingpython.blogspot.jp/2012/02/convolution-with-numpy.html
    """
    window_len = 11
    s = np.r_[x[window_len-1:0:-1], x, x[-1:-window_len:-1]]
    w = np.kaiser(window_len, 2)
    y = np.convolve(w/w.sum(), s, mode='valid')
    return y[5:len(y)-5]


def shuffle_dataset(x, t):
    """데이터셋을 뒤섞는다.

    Parameters
    ----------
    x : 훈련 데이터
    t : 정답 레이블
   
    Returns
    -------
    x, t : 뒤섞은 훈련 데이터와 정답 레이블
    """
    permutation = np.random.permutation(x.shape[0])
    x = x[permutation, :] if x.ndim == 2 else x[permutation, :, :, :]
    t = t[permutation]

    return x, t

def conv_output_size(input_size, filter_size, stride=1, pad=0):
    return (input_size + 2*pad - filter_size) / stride + 1


def im2col(input_data, filter_h, filter_w, stride=1, pad=0):
    """다수의 이미지를 입력받아 2차원 배열로 변환한다(평탄화).
   
    Parameters
    ----------
    input_data : 4차원 배열 형태의 입력 데이터(이미지 수, 채널 수, 높이, 너비)
    filter_h : 필터의 높이
    filter_w : 필터의 너비
    stride : 스트라이드
    pad : 패딩
   
    Returns
    -------
    col : 2차원 배열
    """
    N, C, H, W = input_data.shape
    out_h = (H + 2*pad - filter_h)//stride + 1
    out_w = (W + 2*pad - filter_w)//stride + 1

    img = np.pad(input_data, [(0,0), (0,0), (pad, pad), (pad, pad)], 'constant')
    col = np.zeros((N, C, filter_h, filter_w, out_h, out_w))

    for y in range(filter_h):
        y_max = y + stride*out_h
        for x in range(filter_w):
            x_max = x + stride*out_w
            col[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride]

    col = col.transpose(0, 4, 5, 1, 2, 3).reshape(N*out_h*out_w, -1)
    return col


def col2im(col, input_shape, filter_h, filter_w, stride=1, pad=0):
    """(im2col과 반대) 2차원 배열을 입력받아 다수의 이미지 묶음으로 변환한다.
   
    Parameters
    ----------
    col : 2차원 배열(입력 데이터)
    input_shape : 원래 이미지 데이터의 형상(예:(10, 1, 28, 28))
    filter_h : 필터의 높이
    filter_w : 필터의 너비
    stride : 스트라이드
    pad : 패딩
   
    Returns
    -------
    img : 변환된 이미지들
    """
    N, C, H, W = input_shape
    out_h = (H + 2*pad - filter_h)//stride + 1
    out_w = (W + 2*pad - filter_w)//stride + 1
    col = col.reshape(N, out_h, out_w, C, filter_h, filter_w).transpose(0, 3, 4, 5, 1, 2)

    img = np.zeros((N, C, H + 2*pad + stride - 1, W + 2*pad + stride - 1))
    for y in range(filter_h):
        y_max = y + stride*out_h
        for x in range(filter_w):
            x_max = x + stride*out_w
            img[:, :, y:y_max:stride, x:x_max:stride] += col[:, :, y, x, :, :]

    return img[:, :, pad:H + pad, pad:W + pad]



2018년 4월 16일 월요일

chapter 6 학습관련 기술들

http://shuuki4.github.io/deep%20learning/2016/05/20/Gradient-Descent-Algorithm-Overview.html


http://umbum.tistory.com/217?category=751025


2018년 4월 1일 일요일

CHAPTER 14 가중치의 진짜 업데이트

아직 우리는 인공신경망에서 가중치를 어떻게 업데이트해야 하는가의 핵심적인 질문에 답하지 않았습니다. 지금가지의 고정은 이에 대한 답을 하기 위한 과정이었으며 이제 거의 다 왔습니다. 이 비밀을 풀기 위해 반드시 이해해야 할 핵심 아이디어 한 가지만 더 이해하면 됩니다!

지금까지는 네트워크의 각 계층에 걸쳐 역전파되는 오차를 구해봤습니다. 이처럼 오차를 구하는 이유는 인공 신경망이 보다 나은 답을 출력하게 하기 위해 가중치를 조정해가는 데 지침 역할 을 하는 것이 오차이기 때문입니다. 이러한 과정은 이 책의 앞 부분에 나왔던 선형 분류자 예제에서부터 우리가 보아왔던 것입니다.
 하지만 신경망에서 노드는 단순한 선형 분류자가 아닙니다. 노드는 입력되는 신호에 가중치를 적용한 후 이의 합을 구하고 다시 여기에 시그모이드 활성화 함수를 적용하는 식으로 좀 더 복잡한 구조를 가집니다. 그렇다면 이처럼 정교한 노드 사이를 연결하는 연결 노드의 가중치를 어떻게 업데이트해야 할까요? 우리는 왜 어떤 끝내주는 대수학공식을 이용해 가중치를 단번에 구해낼 수 없는 것일까요?

- 가중치 계산
신경망에는 너무나도 많은 가중치의 조합이 존재합니다. 또한 신호가 여러 개의 계층을 타고 전파되어나갈 때 한 계층을 거칠 때마다 직전 계층의 출력 값이 다음 계층의 입력값이 되므로 함수의 함수,ㅡ 함수의 함수의 함수...같은 식으로 수많은 함수의 조합이 필요하게 됩니다. 따라서 수학 연산의 과정이 너무 복잡하게 되므로 가중치를 한 방에 풀어주는 대수학을 활용할 수 없는 것입니다.

CHAPTER 13 행렬곱을 이용한 오차의 연전파

우리는 앞 장에서 실제 숫자를 대입해 역전파를 이해하고자 했습니다. 하지만 실제 역전파는 매우 복잡하겠죠? 그렇다면 우리는 역전파에도 행렬곱을 이용할 수 있을까요? 기억하겠지만 우리는 이미 9장에서 앞쪽으로 전파법에 대해 행렬곱으로 계산한 바 있습니다.

이번에는 오차의 역전파를 행렬곱으로 간결하게 만들 수 있는지 보기 위해서 단계별 기호를 이용해 적어보겠습니다. 이렇게 행렬곱으로 표현하는 방식을 벡터화한다(vectorize)라고 합니다.

수많은 연산을 행렬의 형태로 표현하게 되면 우리가 손으로 쓰기에도 편하지만 무엇보다도 컴퓨터가 반복적인 유사한 연산을 순식간에 처리할 수 있게 되므로 훨씬 더 효율적으로 작업을 수행할 수 있다는 장점을 가지게 됩니다.

최종 출력 계층으로 부터 나오는 오차로부터 시작해보겠습니다. 출력 계층에 2개의 노등가 있는 예제를 보고 있었으니 여기에서의 오차는 다음과 같이 표현할 수 있습니다.

error(output) = ( e1, e2)

다음으로는 은닉계층의 오차를 행렬로 구성해보겠습니다. 어렵게 느껴질 수 있으니 단계별로 차근차근 보겠습니다. 우선 은닉 계층의 첫 번째 노드부터 보겠습니다. 앞장의 그림을 다시 보면 은닉 계층의 첫 번째 노드의 오차에 영향을 주는 것은 출력 계층으로 부터의 2개의 경로임을 확인할 수 있습니다. 이 2개의 경로를 통해 전달되는 오차 신호는 각각 e1 * w11 /(w11 + w21)과 e2 * w12/(w12 + w22)입니다. 은닉 계층의 두 번째 노드는 마찬가지로 각각 e1 * w21 /(w21 + w11)과 e2 * w22 /(w22 + w12)가 됩니다. 만약 이 식이 혼동이 되는 분은 반드시 12장을 다시 한번 학습하고 돌아오기 바랍니다.

그렇다면 이제 은닉 계층의 오류를 다음과 같이 행렬로 표현할 수 있습니다.

이 행렬을 보다 간결하게 행렬곱으로 다시 쓸 수 있다면 좋을 것 같ㅅ브니다. 마치 전파법에서 봤던 것 처럼 말입니다.

하지만 안타깝게도 전파법에서 봤던 수준으로 간결하게 이 행렬을 다시 쓸 수는 없습니다. 위의 복잡한 수식을 풀어내기가 쉽지는 않기 때문입니다. 그렇다면 어떻게 해야 할까요? 정말 간결하게 쓰고 싶기는 한데 말입니다.

위의 수식을 다시 봅시다. 이 수식에서 핵심은 출력 오차인 en과 가중치 wij의 곱셈 연산입니다. 가중치가 크면 클수록 더 많은 출력 오차가 은닉계층으로 돌아오게 됩니다. 이것이 핵심입니다. 분수에서 분모 부분은 일정의 정규화 인자(nomalizing factor)라고 할 수 있습니다. 만약 정규화 인자를 무시한다고 해도 우리는 되돌아오는 오창의 일정 비율 정도를 잃을 뿐입니다. 따라서 e1 * w11/(w11+w21)을 간단하게 e1 * w11로 표현해도 되는 것입니다.

가중치 행렬은 우리가 8장에서 만들었던 행렬과 유사해 보이지만 자세히 보면 대각선 방향으로 원소가 뒤바뀐 것을 알 수 있습니다. 즉 우측 상단의 가중치와 좌측 하단의가중치가 바뀌어 있습니다. 이처럼 핻렬릐 행과 열을 바꾸는 것을 전치(transpose)한다고 부르며, 행렬 w의 전치 행렬을 기호로는 wt라고 표기합니다.

다음 그림에서 전치 행렬의 두 가지 예를 보겠습니다. 전치 행렬은 행과 열의 개수가 다른 경우에도 가능함을 이해하기 바랍니다.
이제 다음과 같이 오차의 역전파를 행렬로 간결하게 표현할 수 있게 되었습니다.

error(hidden) = wt(hidden_output) * error(output)

간결하게 표현해서 좋기는 하지만 정규화 인자를 생략했다는 점이 마음에 걸립니다. 이렇게 생각해 버려도 괜찮은 것일까요? 결론부터 말하면 이처럼 오차 신호의 피드백을 단순화하더라도 정규화 인자가 있을 때와 마찬가지로 잘 동작한다는 사실이 밝혀져 있습니다. 오차를 역전파하는 몇 가지 방ㅂ버을 비굑한 결과를 블로그에 올렸으니 관심 있는 분들은 참고하기 바랍니다(http://bit.ly/2m2z2YY). 간결한 방식이 문제 없이 작동한다면. 더 어려운 방법을 쓸 필요는 없겠죠!

이점에 대해 조금 더 말하면, 설령 다소 크거나 작은 오차가 역전파되었다고 하더라도 우리의 인공 신경망은 다음 학습 단계에서 이를 스스로 바로 잡아갑니다.
오차에 대한 책임을 분산시키는 데 지침이 되는 것은 가중치의 크기이므로 역전파되는 오차가 연결 노드의 가중치의 크기를 중시한다는 점이 핵심입니다.

지금까지 정말 많은 것을 학습하느라 고생 많으셨습니다. 이론과 관련한 마지막 장이 될 다음 장으로 넘어가기 전에 잠깐 쉬어도 좋을 것 같습니다. 다음 장은 정말 멋지지만 살짝 골치 아픈 내용을 다루기 때문입니다.

핵심 정리
- 오차의 역전파를 행렬곱으로 표현할 수 있습니다.
- 오차의 역전파를 행렬곱으로 표현함으로써 우리는 네트워크의 크기에 상관없이 이를 간결하게 표현할 수 있으며, 컴퓨터를 통해 보다 효율적이고 빠르게 업무를 처리하게 할 수 있습니다.
- 이제 우리는 전파법과 역전파 모두 행렬곱을 통해 효율적으로 처리할 수 있다는 사실을 알게 되었습니다.

2018년 3월 27일 화요일

laradock on mac


mysql bash -> hostname -I

.env DB_HOST SETTING

php artisan make:auth



php artisan migrate

CHAPTER12 다중 계층에서의 오차의 역전파

다음은 3개 계층(입력 계층, 은닉 계층, 출력 계층)으로 구성된 간단한 신경망입니다.


 입력          입력계층               은닉계층            출력계층          출력


--->             (1)  --->               (1)     ------>         (1)     ---->


오른쪽 끝의 최종 출력 계층부터 보겠습니다. 출력 계층의 오차를 이용해 출력 계층의 연결된 노드들의 가중치를 업데이트하게 됩니다. 이를 조금 더 일반화해보면 출력 계층의 오차는 e(output)으로, 여기에서는 가중치 who로 표현할 수 있습니다. 물론 e(output)은 가중치 비율에 따라 나뉘어 전달될 것입니다.
이렇게 시각화해봄으로써우리는 새로 추가된 계층에 대해 어떻게 하면 좋을지 감을 잡을 수 있을 것 같습니다. 즉 은닉 계층의 노드으 ㅣ출력 값과 관련된 오차를 e(hidden)이라고 하고 이 오차를 입력 계층과 은닉계층을 연결하는 가중치 wih에 대해 그 비율에 맞게 나누어 주면 되는 것입니다. 다음 그림에 이를 표현햅습니다.

만약 계층이 더 많이 있다면 이처럼 최종 출력 꼐층으로부터 역방향으로 동일한 과정을 반복하면 될 것입니다. 이처럼 오차 관련 정보가 뒤쪽으로 계속해서 전파되므로 역전파라고 부르는 것입니다.

출력 계층의 경우에는 출력 계층의 노드의 결과 값의 오차인 e(output)을 사용했지만, 은닉 계층의 노드에서 e(hidden)은 어떤 오차 값으 ㄹ사용하면 될까요? 만약 이런 의문이 들었다면 지금까지의 내용을 제대로 이해하고 있는 것이라고 보룻 었을 것 같습니다. 중간에 위치하는 은닉 계층은 사실 명백한 오차를 가지고 있지 않기 때문입니다. 앞에서 배웠던 내요을 다시 상기해볼까요? 은닉 계층의 각각의 노드는 직전 계층으로부터 입력을 받아 적절한 출력값을 다음 계층으로 전달하게 됩니다. 이때 이 출력 값이란 직전 계층의 입력 값에 가중치를 적용한 다음이를 모두 더한 값에 활성화 함수를 적용한 값입니다. 그럼 도대체 어떻게 오차를 구해낼 수 있는 것일까요?

은닉 노드에 대해서는 목표로 하는 출력 값이 없습니다. 오직 최종 출려 ㄱ계층의 노드만이 학습데이터로부터 얻은 실제값, 즉 목표로 하는 출력 값을 가지고 있다는 것이죠. 앞으로 그림을 보면 다시 한번 생각해보기 바랍니다. 은닉 계층의 첫 번째 노드는 2개의 연결 노드를 가지는데 이들은 출력 계층의 2개의 노드로 각각 연결됩니다. 그리고 우리는 출력 계층의 노드에서의 오차를 이 연결 노드에 나누어줄 수 있다는 점을 알고 있습니다. 이 말은 결국 중간의 은닉 계층의 노드로부터 오는 2개의 연결 노드 각각에 대해 어떤 오차가 존재한다느 말입니다. 우리는 이러한 2개의 연결노드의 오차를 재조합함으로써 은닉 계층의 노드의 에러로 사용할 수 있습니다. 즉 중간 계층의 노드는 목표로 하는 출력 값을 가지지 않기 때문에 이처럼 차선의 방법을 사용하는 것입니다. 이와 같은 아이디어를 다음 그림에 표현해봤습니다.

그림을 보면 좀 더 명확해질 것입니다. 우리에게 필요한 것은 은닉 계층의 노드들에 대한 오차입니다. 이 오차를 이용해 직전 계층과의 연결 노드에 존재하는 가중치를 업데이트할 것이기 때문입니다. 은닉 계층의 노드의 오차를 e(hidden)이라고 하겠습니다. 아직까지 우리는 실제 이 오차의 값이 얼마인지에 대한 답을 가지고 있지 않습니다. 그 이유는 (반복해서 말하지만) 학습 데이터로부터 얻을 수 있는 목표 값은 (으닉 계층이 아니라) 오직 최종 출력 계층의 노드에 대한 것이므로, 으닉 계층에서의 오차를 목표 값과 실제 값 간의 차이라고 정의할 수 없기 때문입니다.

학습 데이터들은 최종 노드의 결과 값이 가져야 할 목표 값에 대해서만 말해줍니다. 이 외의 노드들의 결과 값에 대해서는 아무것도 말해주지 않습니다. 이것이 이 퍼즐의 핵심입니다.

우리는 앞에서 본 대로 오직의 역전파를 이용해 연결 노드에 대해 나뉜 오차를 재조합해야 합니다. 따라서 은닉계층의 첫 번째 노드의 오차는 이 은닉계층에서 다음 계층으로 연결되는 모든 연결 노드에 있는 나뉜 오차들의 합이 됩니다. 앞의 그림에서 보면 e(output.1)은 w11과 연결되어 있으면 e(output2)는 w12와 연결되어 있습니다. 이를 수식으로 써보면 다음과 같습니다.

e(hidden1) = 연결 노드 w11, w12로 나뉘어 전달되는 오차의 합
               = e(output1) * w11 /(w11 + w21) + e(output2) * w12/(w12 + w22)

수식으로만 보면 이해가 어려울 수 있으니 실제 숫자를 대입해서 보겠습니다. 다음 그림은 오차가 역전파하는 과정을 실제 숫자와 함께 표현한 것입니다.

앞의 그림에서 출력 계층의 첫 번째 노드의 오차 0.8은 이와 연결된 2개의 가중치 2.0과 3.0에 비례해 각각 0.32와 0.48로 나뉘어 전달된다는 것을 확인할 수 있습니다. 출력 계층의 두 번째 노드의 오차 0.5는 연결된 2개의 가중치 1.0과 4.0에 비례해 각각 0.1과 0.4로 나뉩니다. 따라서 은닉 계층의 첫 번째 노드의 오차는 0.32와 0.1의 합인 0.42가 되고 두번 째 노드의 오차는 0.48과 0.4의 합인 0.88이 되는 것입니다.
다음 그림은 한 단계 더 나아가 은닉 계층의 오차 값을 이용해 입력 계층이 오차를 구하는 것을 보여줍니다.

핵심정리
- 인공 신경망에서 학습이란 연결 노드의 가중치를 업데이트하는 과정을 의미합니다. 가중치의 업데이트는 오차에 의해 주도되는데, 오차는 학습데이터로부터 주어진 정답과 출력값 간의 차이를 의미합니다.
- 출력 노드의 오차는 실제 값(정답)과 출력 값 사이의 차이를 의미합니다.
- 중간 계층의 존재하는 노드들의 오차는 명백하지 않습니다. 한 가지 접근 방법은 출력 계층의 노드들의 오차를 이와 연결된 가중치의 크기에 비례해 누눠서 역전파하고 이를 재조합하는 방법입니다.




2018년 3월 26일 월요일

CHAPTER 11 여러 노드에서의 오차의 역전파

이번에는 2개의 출력 노드를 가지는 그림을 보겠습니다.
                                                              결과값의 오차


i1  ->  계층1(1)  -> (w1.1)->  계층2(1)  -> o1         e1
                     -> (w1.2)
         
                     -> (w1.2)
i2  ->  계층1(2) ->  (w2.2) ->  계층2(2)  ->o2         e2

2개의 출력 노드 모두 오차를 가질 수 있습니다. 특히 우리가 아직 네트워크를 학습시키지 않은 경우에는 거으 ㅣ무조건 오차를 가지고 있겠죠. 그러므로 2개의 오차 모두 가중치를 어떻게 업데이트해야 할지에 대한 정보를 줘야 합니다. 우리는 앞에서 했던 대로 출력 노드의오차를 앞 단의 각 가중치에 비례해 나누어 주는 접근 방법을 활용할 수 있습니다.

출력노드가 여러 개라고 해서 변하는 것은 아무것도 없습니다. 그저 첫 번째 출력 노드에서 했던 작업을 두 번째 출력 노드에서 동일하게 반복하면 되는 것입니ㅏㄷ. 어떻게 이렇게 간단할 수 있냐고요? 그 이유는 하나의 출력 노드로의 연결은 다른 출력 노드로의 연결과 완전 별개이기 때문입니다. 각각의 연결 간에 는 전혀 의존 관계가 존재하지 않는다느 ㄴ말입니다.

앞의 그림을 다시 한번 보면 첫 번째 출력 노드의 오차를 e1이라고 표기했습니다. 이 초차는 학습데이터 t1에 의해 제공된 실제 값과 우리가 계산한 ㅜㅊㄹ력 값 o1사이의 차이를 의미합니다. 다시 말해 e1 = t1 -01입니다. 그리고 두 번째 출력 노드에서의 오차는 e2라고 표기해습니다.

그림을 자세히 보면 e1은 w11과 w21의 가중치의 값에 비례해서 나눠어 연결된 노드로 전달됩니다. 이와 유사하게 e2는 w12와 w22의 값에 비례해 나뉩니다.

좀 더 이해가 쉽도록 이를 수식으로 표현해 보겠습니다. 오차 e1은 w11과 w21의 업데이트에 영향을 주게 됩니다. 그러므로 w11을 업데이트 하기 위해 사용되는 e1의 일부는 다음과 같이 표현 가능합니다.

w11 / (w11 + w21)

그리고 w21을 업데이트 하기 위해 사용되는 e1의 일부는 다음과 같이 표현할 수 있습니다.

w21 /( w21 + w11)

이러한 수식의 의미는 결국 무엇일까요? 오차 e1은 나뉘어 전달될 때, 작은 가중치를 가지는 연결 노드보다 큰 가중치를 가지는 연결 노드에 더 많이 전달된다는 것입니다.
만약 w11 = 6이고 w21 = 3이라는 값을 가진다고 하면 w11은 w21보다2배 큽니다. 이때 w11을 업데이트하기 위해 전달되는 오차 e1의 일부는 6 / (6 +3) =6 /9 = 2/3 가 됩니다. 그럼 1/3이 남는데요. 실제로 오차 e1에서 w21을 업데이트 하기 위해 전달되는 부분은 3/ (6 +3) = 3/ 9 = 1/3입니다.

만약 가중치가 같다면 1/2씩 전달되게 될 것입니다. 예를 들어 w11 = 4, w21 = 4라면 약쪽으로 나눠어 전달되는 오차는 2개 모두 4 /( 4+4) = 4/8 = 1/2이 될 것 입니다.

지금까지 공부한 내용을 정리해보겠ㅅ브니다. 우리는 오차를 이용해서 네트워크 내의 매개변수들의 값(가중치)을 조정해나간다는 사실을 알게 되었습니다. 최종 출력 계층으로부터 그 직전 계층으로 가는 연결 노드으 ㅣ가중치를 업데이트하는 방식을 살펴봤는데 여러개의 출력 노드가 있는 경우에도 그 각각으 ㅣ출력노드를 개별적으로 처리하면 될 뿐, 복잡해질 것이 전혀 없다는 사실을 알게 되었습니다.
그렇다면 2개 보다 더 많은 꼐층을 가지는 신경망에서는 어떻게 하면 될까요? 다시 말해 최종 출력 계층에 인접하는 계층이 아니라, 멀리 떨어져 있는 꼐층과 관련된 가중치는 어떻게 업데이트를 해야 할 까요? 이데 대해서는 다음장에서 살펴보겠습니다.