In [1]:
%install_ext https://raw.githubusercontent.com/joelkim/ipython-tikzmagic/master/tikzmagic.py
In [2]:
%load_ext tikzmagic
신경망(neural network) 모형은 퍼셉트론, 서포트 벡터 머신, 로지스틱 회귀 등의 분류 모형과 달리 기저 함수(basis function)도 사용자 파라미터에 의해 변화할 수 있는 적응형 기저 함수 모형(adaptive basis function model)이며 구조적으로는 여러개의 퍼셉트론을 쌓아놓은 형태이므로 MLP(multi-layer perceptron)으로도 불린다.
다음 그림과 같이 독립 변수 벡터가 3차원인 간단한 퍼셉트론 모형을 가정한다.
In [5]:
%%tikz
\tikzstyle{neuron}=[circle, draw, minimum size=23pt,inner sep=0pt]
\tikzstyle{bias}=[text centered]
\node[neuron] (node) at (2,0) {$z$};
\node[neuron] (x1) at (0, 1) {$x_1$};
\node[neuron] (x2) at (0, 0) {$x_2$};
\node[neuron] (x3) at (0,-1) {$x_3$};
\node[neuron] (b) at (0,-2) {$1$};
\node[neuron] (output) at (4,0) {$y$};
\draw[->] (x1) -- node[above] {$w_1$} (node);
\draw[->] (x2) -- node[above] {$w_2$} (node);
\draw[->] (x3) -- node[above] {$w_3$} (node);
\draw[->] (b) -- node[above] {$b$} (node);
\draw[->] (node) -- (output);
입력 $x$ $$ x_1,x_2,x_3 $$
가중치 $w$ $$ w_1, w_2, w_3 $$
상수항(bias) $b$를 포함한 활성화값(activation) $$ a = \sum_{j=1}^3 w_j x_j + b $$
비선형 활성화 함수 $f$ $$ z = f(a) = f \left( \sum_{j=1}^3 w_j x_j + b \right) $$
이런 퍼셉트론에서 $x$ 대신 기저 함수를 적용한 $\phi(x)$를 사용하면 XOR 문제 등의 비선형 문제를 해결할 수 있다. 그러나 고정된 기저 함수를 사용해야 하므로 문제에 맞는 기저 함수를 찾아야 한다는 단점이 있다.
만약 기저 함수 $\phi(x)$의 형태를 추가적인 모수 $w^{(1)}$를 사용하여 조절할 수 있다면 즉, 기저함수 $\phi(x;w^{(1)})$ 를 사용하면 $w^{(1)}$ 값을 바꾸는 것만으로 다양한 기저 함수를 시도할 수 있다.
$$ z = f \left( \sum_{j=1} w_j^{(2)} \phi(x_j ; w^{(1)}, b^{(1)}) + b^{(2)} \right) $$
신경망은 다음과 같이 원래 퍼셉트론과 같은 형태의 적응형 기저함수를 사용한 모형이다.
$$ \phi(x_j ; w^{(1)}, b^{(1)}) = f \left( \sum_{i=1} w_i^{(1)} w + b^{(1)} \right) $$즉 전체 모형은 다음과 같다.
$$ z = f \left( \sum_{j=1} w_j^{(2)} f \left( \sum_{i=1} w_i^{(1)} w + b^{(1)} \right) + b^{(2)} \right) $$일반적으로 활성화 함수 $f$ 는 다음과 같은 시그모이드 함수 $\sigma$를 사용한다.
$$ \begin{eqnarray} z = \sigma(a) \equiv \frac{1}{1+e^{-a}}. \end{eqnarray} $$$$ \begin{eqnarray} \frac{1}{1+\exp(-\sum_j w_j x_j-b)} \end{eqnarray} $$이 시그모이드 함수의 특징은 다음과 같은 미분값을 가진다는 것이다.
$$ \sigma' = \sigma(1-\sigma) $$퍼셉트론를 연속적으로 연결하여 비선형 문제를 해결하는 방법은 이미 디지털 회로 설계에서 사용되던 방법이다.
퍼셉트론의 가중치를 적절히 조정하면 다음과 같은 AND / OR 등의 디지털 게이트(gate)를 제작할 수 있다.
예를 들어 $w_1 = -2$, $w_2 = -2$, $b = 3$ 인 퍼셉트론은 NAND 게이트를 구현한다.
In [11]:
%%tikz
\tikzstyle{neuron}=[circle, draw, minimum size=23pt,inner sep=0pt, node distance=2cm]
\node[neuron] (node) {$z$};
\node[neuron] (x2) [left of=node] {$x_2$};
\node[neuron] (x1) [above of=x2] {$x_1$};
\node[neuron] (b) [below of=x2] {$1$};
\node[neuron] (output) [right of=node] {$y$};
\draw[->] (x1) -- node[above=0.1] {$-2$} (node);
\draw[->] (x2) -- node[above] {$-2$} (node);
\draw[->] (b) -- node[above=0.1] {$3$} (node);
\draw[->] (node) -- (output);
INPUT | OUTPUT | |
A | B | A NAND B |
0 | 0 | 1 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
시그모이드를 통과하면 1,1,1,0의 값들로 된다.
디지털 회로에서는 복수개의 NAND 게이트를 조합하면 어떤 디지털 로직이라도 구현 가능하다. 예를 들어 다음 회로는 두 입력 신호의 합과 자릿수를 반환하는 반가산기(half adder) 회로이다.
이 퍼셉트론 조합을 보면 4개의 퍼셉트론을 연결하여 XOR 로직을 구현하였음을 알 수 있다.
신경망은 퍼셉트론을 여러개 연결한 것으로 다계층 퍼셉트론(MLP: Multi-Layer Perceptrons)이라고도 한다. 신경망에 속한 퍼셉트론은 뉴론(neuron) 또는 노드(node)라고 불린다.
각 계층(layer)은 다음 계층에 대해 적응형 기저 함수의 역할을 한다. 최초의 계층은 입력 계층(input layer), 마지막 계측은 출력 계층(output layer)이라고 하며 중간은 은닉 계층(hidden layer)라고 한다.
MLP의 또다른 특징은 출력 계층에 복수개의 출력 뉴런를 가지고 각 뉴런값으로 출력 클래스의 조건부 확률을 반환하도록 설계하여 멀티 클래스 문제를 해결할 수도 있다는 점이다.
다음은 필기 숫자에 대한 영상 정보를 입력 받아 숫자 0 ~ 9 까지의 조건부 확률을 출력하는 MLP의 예이다. 입력 영상이 28 x 28 해상도를 가진다면 입력 계층의 뉴런 수는 $28 \times 28 = 784$ 개가 된다. 출력은 숫자 0 ~ 9 까지의 조건부 확률을 출력하는 $10$ 개의 뉴런을 가진다.
그림의 모형은 $15$개의 뉴런을 가지는 $1$ 개의 은닉 계층을 가진다.
신경망의 가중치는 $w^{l}_{j,k}$ 과 같이 표기한다. 이 가중치는 $l$ 번째 계층의 $k$번째 뉴런와 $l+1$ 번째 계층의 $j$번째 뉴런을 연결하는 가중치를 뜻한다. 첨자의 순서에 주의한다.
In [6]:
%%tikz --size 600,400
\tikzstyle{neuron}=[circle, draw, minimum size=2 cm,inner sep=5pt, node distance=2cm]
\node () at (0, 4.5) {$l$th layer};
\node () at (4, 4.5) {$l+1$th layer};
\node[neuron] (i1) at (0, 2) {l, k};
\node[neuron] (i2) at (0, -2) {l, k+1};
\node[neuron] (h11) at (4, 3) {l+1, j-1};
\node[neuron] (h12) at (4, 0) {l+1, j};
\node[neuron] (h13) at (4, -3) {l+1, j+1};
\draw[->] (i1) -- (h11);
\draw[->] (i2) -- (h11);
\draw[->, line width=0.5mm] (i1) -- node[above=0.2] {$w^{l}_{j,k}$ } (h12);
\draw[->] (i2) -- (h12);
\draw[->] (i1) -- (h13);
\draw[->] (i2) -- (h13);
#이거 코드는 무시
앞단은 l번째 레이어. j, k의 순서는 뒤바뀌어 있다. 왜 그러냐면 나중에 수식할 때 행렬식으로 나타낼 수 있기 때문
오메가 값이 정해져 있다. j번째 레이어 전체를 벡터로 표시한 것이 j라는 인덱스를 뺀 밑의 수식과 같다. w값을 메트릭스 하나로 표시할 수 있다.
신경망의 계산 과정은 실제 신경망에서 신호가 전달되는 과정과 유사하므로 Feedforward propagation 이라고 불린다.
$l$번째 계층의 $j$번째 뉴런에서의 출력값 $z^l$은 다음과 같이 정의된다.
$$ \begin{eqnarray} z^{l}_j = \sigma \left( \sum_k w^{l}_{jk} z^{l-1}_k + b^l_j \right) = \sigma \left( w^{l}_{j} \cdot z^{l-1} + b^l_j \right) \end{eqnarray} $$$l$번째 계층 전체의 출력은 다음과 같이 표시할 수 있다.
$$ \begin{eqnarray} z^{l} = \sigma\left( \sum_k w^{l}_{k} z^{l-1}_k + b^l \right) = \sigma\left( w^{l} \cdot z^{l-1} + b^l \right) \end{eqnarray} $$$$ a^l \equiv w^l \cdot z^{l-1}+b^l $$$$ \begin{eqnarray} z^{l} = \sigma\left( a^l \right) \end{eqnarray} $$아래에 Feedforward propagation 예를 보였다.
a는 미분 구하려고 남겨둔 것이다. z만 쓰는데 a를 남겨둔 이유는 그렇다.
In [13]:
%%tikz --size 600,400
\tikzstyle{neuron}=[circle, draw, minimum size=2 cm,inner sep=5pt, node distance=2cm]
\node[neuron, fill=gray!10] (i1) at (0, 2) {input 1};
\node[neuron, fill=gray!10] (i2) at (0, -2) {input 2};
\node[neuron] (h11) at (4, 3) {hidden 11};
\node[neuron] (h12) at (4, 0) {hidden 12};
\node[neuron] (h13) at (4, -3) {hidden 13};
\draw[->] (i1) -- (h11);
\draw[->] (i2) -- (h11);
\draw[->] (i1) -- (h12);
\draw[->] (i2) -- (h12);
\draw[->] (i1) -- (h13);
\draw[->] (i2) -- (h13);
\node[neuron] (h21) at (8, 3) {hidden 21};
\node[neuron] (h22) at (8, 0) {hidden 22};
\node[neuron] (h23) at (8, -3) {hidden 23};
\draw[->] (h11) -- (h21);
\draw[->] (h11) -- (h22);
\draw[->] (h11) -- (h23);
\draw[->] (h12) -- (h21);
\draw[->] (h12) -- (h22);
\draw[->] (h12) -- (h23);
\draw[->] (h13) -- (h21);
\draw[->] (h13) -- (h22);
\draw[->] (h13) -- (h23);
\node[neuron] (o1) at (12, 2) {output 1};
\node[neuron] (o2) at (12, -2) {output 2};
\draw[->] (h21) -- (o1);
\draw[->] (h21) -- (o2);
\draw[->] (h22) -- (o1);
\draw[->] (h22) -- (o2);
\draw[->] (h23) -- (o1);
\draw[->] (h23) -- (o2);
In [14]:
%%tikz --size 600,400
\tikzstyle{neuron}=[circle, draw, minimum size=2 cm,inner sep=5pt, node distance=2cm]
\node[neuron, fill=gray!10] (i1) at (0, 2) {input 1};
\node[neuron, fill=gray!10] (i2) at (0, -2) {input 2};
\node[neuron, fill=gray!10] (h11) at (4, 3) {hidden 11};
\node[neuron, fill=gray!10] (h12) at (4, 0) {hidden 12};
\node[neuron, fill=gray!10] (h13) at (4, -3) {hidden 13};
\draw[->, line width=1mm] (i1) -- (h11);
\draw[->, line width=1mm] (i2) -- (h11);
\draw[->, line width=1mm] (i1) -- (h12);
\draw[->, line width=1mm] (i2) -- (h12);
\draw[->, line width=1mm] (i1) -- (h13);
\draw[->, line width=1mm] (i2) -- (h13);
\node[neuron] (h21) at (8, 3) {hidden 21};
\node[neuron] (h22) at (8, 0) {hidden 22};
\node[neuron] (h23) at (8, -3) {hidden 23};
\draw[->] (h11) -- (h21);
\draw[->] (h11) -- (h22);
\draw[->] (h11) -- (h23);
\draw[->] (h12) -- (h21);
\draw[->] (h12) -- (h22);
\draw[->] (h12) -- (h23);
\draw[->] (h13) -- (h21);
\draw[->] (h13) -- (h22);
\draw[->] (h13) -- (h23);
\node[neuron] (o1) at (12, 2) {output 1};
\node[neuron] (o2) at (12, -2) {output 2};
\draw[->] (h21) -- (o1);
\draw[->] (h21) -- (o2);
\draw[->] (h22) -- (o1);
\draw[->] (h22) -- (o2);
\draw[->] (h23) -- (o1);
\draw[->] (h23) -- (o2);
In [15]:
%%tikz --size 600,400
\tikzstyle{neuron}=[circle, draw, minimum size=2 cm,inner sep=5pt, node distance=2cm]
\node[neuron, fill=gray!10] (i1) at (0, 2) {input 1};
\node[neuron, fill=gray!10] (i2) at (0, -2) {input 2};
\node[neuron, fill=gray!10] (h11) at (4, 3) {hidden 11};
\node[neuron, fill=gray!10] (h12) at (4, 0) {hidden 12};
\node[neuron, fill=gray!10] (h13) at (4, -3) {hidden 13};
\draw[-] (i1) -- (h11);
\draw[-] (i2) -- (h11);
\draw[-] (i1) -- (h12);
\draw[-] (i2) -- (h12);
\draw[-] (i1) -- (h13);
\draw[-] (i2) -- (h13);
\node[neuron, fill=gray!10] (h21) at (8, 3) {hidden 21};
\node[neuron, fill=gray!10] (h22) at (8, 0) {hidden 22};
\node[neuron, fill=gray!10] (h23) at (8, -3) {hidden 23};
\draw[->, line width=1mm] (h11) -- (h21);
\draw[->, line width=1mm] (h11) -- (h22);
\draw[->, line width=1mm] (h11) -- (h23);
\draw[->, line width=1mm] (h12) -- (h21);
\draw[->, line width=1mm] (h12) -- (h22);
\draw[->, line width=1mm] (h12) -- (h23);
\draw[->, line width=1mm] (h13) -- (h21);
\draw[->, line width=1mm] (h13) -- (h22);
\draw[->, line width=1mm] (h13) -- (h23);
\node[neuron] (o1) at (12, 2) {output 1};
\node[neuron] (o2) at (12, -2) {output 2};
\draw[-] (h21) -- (o1);
\draw[-] (h21) -- (o2);
\draw[-] (h22) -- (o1);
\draw[-] (h22) -- (o2);
\draw[-] (h23) -- (o1);
\draw[-] (h23) -- (o2);
In [17]:
%%tikz --size 600,400
\tikzstyle{neuron}=[circle, draw, minimum size=2 cm,inner sep=5pt, node distance=2cm]
\node[neuron, fill=gray!10] (i1) at (0, 2) {input 1};
\node[neuron, fill=gray!10] (i2) at (0, -2) {input 2};
\node[neuron, fill=gray!10] (h11) at (4, 3) {hidden 11};
\node[neuron, fill=gray!10] (h12) at (4, 0) {hidden 12};
\node[neuron, fill=gray!10] (h13) at (4, -3) {hidden 13};
\draw[-] (i1) -- (h11);
\draw[-] (i2) -- (h11);
\draw[-] (i1) -- (h12);
\draw[-] (i2) -- (h12);
\draw[-] (i1) -- (h13);
\draw[-] (i2) -- (h13);
\node[neuron, fill=gray!10] (h21) at (8, 3) {hidden 21};
\node[neuron, fill=gray!10] (h22) at (8, 0) {hidden 22};
\node[neuron, fill=gray!10] (h23) at (8, -3) {hidden 23};
\draw[-] (h11) -- (h21);
\draw[-] (h11) -- (h22);
\draw[-] (h11) -- (h23);
\draw[-] (h12) -- (h21);
\draw[-] (h12) -- (h22);
\draw[-] (h12) -- (h23);
\draw[-] (h13) -- (h21);
\draw[-] (h13) -- (h22);
\draw[-] (h13) -- (h23);
\node[neuron, fill=gray!10] (o1) at (12, 2) {output 1};
\node[neuron, fill=gray!10] (o2) at (12, -2) {output 2};
\draw[->, line width=1mm] (h21) -- (o1);
\draw[->, line width=1mm] (h21) -- (o2);
\draw[->, line width=1mm] (h22) -- (o1);
\draw[->, line width=1mm] (h22) -- (o2);
\draw[->, line width=1mm] (h23) -- (o1);
\draw[->, line width=1mm] (h23) -- (o2);
신경망의 오차 함수는 조건부 확률이라는 실수 값을 출력해야 하므로 퍼셉트론과 달리 제곱합 오차 함수를 사용한다.
$$ \begin{eqnarray} C(w,b) \equiv \frac{1}{2n} \sum_i \| y_i - y(x_i; w, b)\|^2 \end{eqnarray} $$오차함수를 최소화하는 최적의 가중치를 찾기 위해 다음과 같이 미분(gradient)을 사용한 steepest gradient descent 방법을 적용한다.
$$ \begin{eqnarray} \Delta w = -\eta \nabla C, \end{eqnarray} $$여기에서 $\eta$는 최적화 속도(learning rate)이다.
$$ \begin{eqnarray} \nabla C \equiv \left(\frac{\partial C}{\partial v_1}, \ldots, \frac{\partial C}{\partial v_m}\right)^T \end{eqnarray} $$가중치 갱신 공식은 다음과 같다.
$$ \begin{eqnarray} w_k & \rightarrow & w_k' = w_k-\eta \frac{\partial C}{\partial w_k} \\ b_l & \rightarrow & b_l' = b_l-\eta \frac{\partial C}{\partial b_l} \end{eqnarray} $$실제로는 단순 Steepest Gradient Descent 방법보다 (SGC: Stochastic Gradient Descent)를 주로 사용한다. SGD는 미분 계산을 위해 전체 데이터 샘플을 모두 사용하지 않고 $m$개의 일부 데이터만 사용하여 미분을 계산하는 방법이다.
$$ \begin{eqnarray} \frac{\sum_{j=1}^m \nabla C_{X_{j}}}{m} \approx \frac{\sum_x \nabla C_x}{n} = \nabla C \end{eqnarray} $$이 경우 가중치 갱신 공식은 다음과 같다.
$$ \begin{eqnarray} w_k & \rightarrow & w_k' = w_k-\frac{\eta}{m} \sum_j \frac{\partial C_{X_j}}{\partial w_k} \\ b_l & \rightarrow & b_l' = b_l-\frac{\eta}{m} \sum_j \frac{\partial C_{X_j}}{\partial b_l}, \end{eqnarray} $$단순하게 수치적으로 미분을 계산한다면 모든 가중치에 대해서 개별적으로 미분을 계산해야 한다. 그러나 back propagation 방법을 사용하면 모든 가중치에 대한 미분값을 한번에 계산할 수 있다.
back propagation 방법을 수식으로 표현하면 다음과 같다.
$$ \begin{eqnarray} \delta^{l-1}_j = h'(a^{l-1}_j) \sum_k w_{kj} \delta^l_k \end{eqnarray} $$최종단의 $\delta$는 다음과 같이 예측 오차 그 자체이다.
$$ \delta^L_j = y_j - z_j $$이 오차값을 위 식에 따라 앞쪽으로 다시 전파하면 전체 가중치에 대한 미분을 구할 수 있다.
In [18]:
%%tikz --size 600,400
\tikzstyle{neuron}=[circle, draw, minimum size=2 cm,inner sep=5pt, node distance=2cm]
\node[neuron] (i1) at (0, 2) {input 1};
\node[neuron] (i2) at (0, -2) {input 2};
\node[neuron] (h11) at (4, 3) {hidden 11};
\node[neuron] (h12) at (4, 0) {hidden 12};
\node[neuron] (h13) at (4, -3) {hidden 13};
\draw[-] (i1) -- (h11);
\draw[-] (i2) -- (h11);
\draw[-] (i1) -- (h12);
\draw[-] (i2) -- (h12);
\draw[-] (i1) -- (h13);
\draw[-] (i2) -- (h13);
\node[neuron] (h21) at (8, 3) {hidden 21};
\node[neuron] (h22) at (8, 0) {hidden 22};
\node[neuron] (h23) at (8, -3) {hidden 23};
\draw[-] (h11) -- (h21);
\draw[-] (h11) -- (h22);
\draw[-] (h11) -- (h23);
\draw[-] (h12) -- (h21);
\draw[-] (h12) -- (h22);
\draw[-] (h12) -- (h23);
\draw[-] (h13) -- (h21);
\draw[-] (h13) -- (h22);
\draw[-] (h13) -- (h23);
\node[neuron, fill=gray!10] (o1) at (12, 2) {output 1};
\node[neuron, fill=gray!10] (o2) at (12, -2) {output 2};
\draw[-] (h21) -- (o1);
\draw[-] (h21) -- (o2);
\draw[-] (h22) -- (o1);
\draw[-] (h22) -- (o2);
\draw[-] (h23) -- (o1);
\draw[-] (h23) -- (o2);
In [19]:
%%tikz --size 600,400
\tikzstyle{neuron}=[circle, draw, minimum size=2 cm,inner sep=5pt, node distance=2cm]
\node[neuron] (i1) at (0, 2) {input 1};
\node[neuron] (i2) at (0, -2) {input 2};
\node[neuron] (h11) at (4, 3) {hidden 11};
\node[neuron] (h12) at (4, 0) {hidden 12};
\node[neuron] (h13) at (4, -3) {hidden 13};
\draw[-] (i1) -- (h11);
\draw[-] (i2) -- (h11);
\draw[-] (i1) -- (h12);
\draw[-] (i2) -- (h12);
\draw[-] (i1) -- (h13);
\draw[-] (i2) -- (h13);
\node[neuron, fill=gray!10] (h21) at (8, 3) {hidden 21};
\node[neuron, fill=gray!10] (h22) at (8, 0) {hidden 22};
\node[neuron, fill=gray!10] (h23) at (8, -3) {hidden 23};
\draw[-] (h11) -- (h21);
\draw[-] (h11) -- (h22);
\draw[-] (h11) -- (h23);
\draw[-] (h12) -- (h21);
\draw[-] (h12) -- (h22);
\draw[-] (h12) -- (h23);
\draw[-] (h13) -- (h21);
\draw[-] (h13) -- (h22);
\draw[-] (h13) -- (h23);
\node[neuron, fill=gray!10] (o1) at (12, 2) {output 1};
\node[neuron, fill=gray!10] (o2) at (12, -2) {output 2};
\draw[<-, line width=0.5mm] (h21) -- (o1);
\draw[<-, line width=0.5mm] (h21) -- (o2);
\draw[<-, line width=0.5mm] (h22) -- (o1);
\draw[<-, line width=0.5mm] (h22) -- (o2);
\draw[<-, line width=0.5mm] (h23) -- (o1);
\draw[<-, line width=0.5mm] (h23) -- (o2);
In [20]:
%%tikz --size 600,400
\tikzstyle{neuron}=[circle, draw, minimum size=2 cm,inner sep=5pt, node distance=2cm]
\node[neuron] (i1) at (0, 2) {input 1};
\node[neuron] (i2) at (0, -2) {input 2};
\node[neuron, fill=gray!10] (h11) at (4, 3) {hidden 11};
\node[neuron, fill=gray!10] (h12) at (4, 0) {hidden 12};
\node[neuron, fill=gray!10] (h13) at (4, -3) {hidden 13};
\draw[-] (i1) -- (h11);
\draw[-] (i2) -- (h11);
\draw[-] (i1) -- (h12);
\draw[-] (i2) -- (h12);
\draw[-] (i1) -- (h13);
\draw[-] (i2) -- (h13);
\node[neuron, fill=gray!10] (h21) at (8, 3) {hidden 21};
\node[neuron, fill=gray!10] (h22) at (8, 0) {hidden 22};
\node[neuron, fill=gray!10] (h23) at (8, -3) {hidden 23};
\draw[<-, line width=0.5mm] (h11) -- (h21);
\draw[<-, line width=0.5mm] (h11) -- (h22);
\draw[<-, line width=0.5mm] (h11) -- (h23);
\draw[<-, line width=0.5mm] (h12) -- (h21);
\draw[<-, line width=0.5mm] (h12) -- (h22);
\draw[<-, line width=0.5mm] (h12) -- (h23);
\draw[<-, line width=0.5mm] (h13) -- (h21);
\draw[<-, line width=0.5mm] (h13) -- (h22);
\draw[<-, line width=0.5mm] (h13) -- (h23);
\node[neuron, fill=gray!10] (o1) at (12, 2) {output 1};
\node[neuron, fill=gray!10] (o2) at (12, -2) {output 2};
\draw[-] (h21) -- (o1);
\draw[-] (h21) -- (o2);
\draw[-] (h22) -- (o1);
\draw[-] (h22) -- (o2);
\draw[-] (h23) -- (o1);
\draw[-] (h23) -- (o2);
In [21]:
%%tikz --size 600,400
\tikzstyle{neuron}=[circle, draw, minimum size=2 cm,inner sep=5pt, node distance=2cm]
\node[neuron, fill=gray!10] (i1) at (0, 2) {input 1};
\node[neuron, fill=gray!10] (i2) at (0, -2) {input 2};
\node[neuron, fill=gray!10] (h11) at (4, 3) {hidden 11};
\node[neuron, fill=gray!10] (h12) at (4, 0) {hidden 12};
\node[neuron, fill=gray!10] (h13) at (4, -3) {hidden 13};
\draw[<-, line width=0.5mm] (i1) -- (h11);
\draw[<-, line width=0.5mm] (i2) -- (h11);
\draw[<-, line width=0.5mm] (i1) -- (h12);
\draw[<-, line width=0.5mm] (i2) -- (h12);
\draw[<-, line width=0.5mm] (i1) -- (h13);
\draw[<-, line width=0.5mm] (i2) -- (h13);
\node[neuron, fill=gray!10] (h21) at (8, 3) {hidden 21};
\node[neuron, fill=gray!10] (h22) at (8, 0) {hidden 22};
\node[neuron, fill=gray!10] (h23) at (8, -3) {hidden 23};
\draw[-] (h11) -- (h21);
\draw[-] (h11) -- (h22);
\draw[-] (h11) -- (h23);
\draw[-] (h12) -- (h21);
\draw[-] (h12) -- (h22);
\draw[-] (h12) -- (h23);
\draw[-] (h13) -- (h21);
\draw[-] (h13) -- (h22);
\draw[-] (h13) -- (h23);
\node[neuron, fill=gray!10] (o1) at (12, 2) {output 1};
\node[neuron, fill=gray!10] (o2) at (12, -2) {output 2};
\draw[-] (h21) -- (o1);
\draw[-] (h21) -- (o2);
\draw[-] (h22) -- (o1);
\draw[-] (h22) -- (o2);
\draw[-] (h23) -- (o1);
\draw[-] (h23) -- (o2);
정리하면 신경망은 다음과 같이 최적화한다.