├── 1장.ipynb
├── 2장.ipynb
├── 3장.ipynb
├── 4장.ipynb
├── 5.png
├── 5장.ipynb
├── 5장.ipynb
├── 6장.ipynb
├── README.md
├── common
├── __init__.py
├── functions.py
├── gradient.py
├── layers.py
├── multi_layer_net.py
├── multi_layer_net_extend.py
├── optimizer.py
├── trainer.py
└── util.py
├── dataset
├── __init__.py
└── mnist.py
├── decision.png
├── gates.jpg
├── layers.png
├── lena.png
├── neurons.png
├── perceptron.png
├── sample_weight.pkl
├── xor.png
└── 목차.ipynb
/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SDRLurker/deep-learning/f95b0a2c7c4ccec3c7395ad5a648c7664b168866/5.png
--------------------------------------------------------------------------------
/5장.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# 밑바닥부터 시작하는 딥러닝\n",
8 | "\n",
9 | "# Deep Learning from Scratch\n",
10 | "\n",
11 | "## Github \n",
12 | "\n",
13 | "https://github.com/WegraLee/deep-learning-from-scratch\n",
14 | "\n",
15 | "## 목차\n",
16 | "\n",
17 | "http://nbviewer.jupyter.org/github/SDRLurker/deep-learning/blob/master/%EB%AA%A9%EC%B0%A8.ipynb"
18 | ]
19 | },
20 | {
21 | "cell_type": "markdown",
22 | "metadata": {},
23 | "source": [
24 | "# 5 오차역전파법\n",
25 | "\n",
26 | "오차역전파법(backpropagation): 가중치 매개변수의 기울기를 효율적으로 계산\n",
27 | "\n",
28 | "오차를 역(반대 방향)으로 전파하는 방법(backward propagation of errors)\n",
29 | "\n",
30 | "안드레 카패시(Andrej Karpathy)의 블로그\n",
31 | "\n",
32 | "* 참고주소 : http://karpathy.github.io/neuralnets\n",
33 | "\n",
34 | "* 오차역전파법을 계산 그래프로 설명\n",
35 | "\n",
36 | "페이페이 리(Fei-Fei Li) 교수가 진행한 스탠퍼드 대학교 딥러닝 수업 CS231n 참고\n",
37 | "\n",
38 | "* 참고주소 : http://cs231n.github.io"
39 | ]
40 | },
41 | {
42 | "cell_type": "markdown",
43 | "metadata": {},
44 | "source": [
45 | "## 5.1 계산 그래프\n",
46 | "\n",
47 | "계산 그래프(computational graph): 계산 과정을 그래프로 나타낸 것\n",
48 | "\n",
49 | "복수의 노드(node)와 에지(edge)로 표현됨.\n",
50 | "\n",
51 | "에지: 노드 사이의 직선"
52 | ]
53 | },
54 | {
55 | "cell_type": "markdown",
56 | "metadata": {},
57 | "source": [
58 | "### 5.1.1 계산 그래프로 풀다\n",
59 | "\n",
60 | "계산 그래프를 이용한 문제풀이는 다음 흐름으로 진행\n",
61 | "\n",
62 | "* 계산 그래프를 구성한다.\n",
63 | "* 그래프에서 계산을 왼쪽에서 오른쪽으로 진행한다.\n",
64 | "\n",
65 | "순전파: 계산을 왼쪽에서 오른쪽으로 진행. 계산 그래프의 출발점부터 종착점으로의 전파."
66 | ]
67 | },
68 | {
69 | "cell_type": "markdown",
70 | "metadata": {},
71 | "source": [
72 | "### 5.1.2 국소적 계산\n",
73 | "\n",
74 | "국소적: 자신과 직접 관련된 작은 범위\n",
75 | "\n",
76 | "국소적 계산: 자신과 관계된 정보만으로 다음 결과를 출력할 수 있음\n",
77 | "\n",
78 | "각 노드는 자신과 관련된 계산 외에는 아무 것도 신경 쓸게 없음\n",
79 | "\n",
80 | "복잡한 계산을 '단순하고 국소적 계산'으로 분할하고 계산 결과를 다음 노드로 전달\n",
81 | "\n",
82 | "복잡한 계산도 분해하면 단순한 계산으로 구성됨"
83 | ]
84 | },
85 | {
86 | "cell_type": "markdown",
87 | "metadata": {},
88 | "source": [
89 | "### 5.1.3 왜 계산 그래프로 푸는가?\n",
90 | "\n",
91 | "역전파를 통해 '미분'을 효율적으로 계산할 수 있음\n",
92 | "\n",
93 | "중간까지 구한 미분 결과를 공유할 수 있어 다수의 미분을 효율적으로 계산할 수 있음"
94 | ]
95 | },
96 | {
97 | "cell_type": "markdown",
98 | "metadata": {},
99 | "source": [
100 | "## 5.2 연쇄법칙\n",
101 | "\n",
102 | "'국소적 미분'을 전달하는 원리는 연쇄 법칙(chain rule)에 따른 것"
103 | ]
104 | },
105 | {
106 | "cell_type": "markdown",
107 | "metadata": {},
108 | "source": [
109 | "### 5.2.1 계산 그래프의 역전파\n",
110 | "\n",
111 | "계산 그래프의 역전파: 순방향과는 반대 방향으로 국소적 미분을 곱한다.\n",
112 | "\n",
113 | "역전파의 계산 절차는 신호 E에 노드의 국소적 미분을 곱한 후 다음 노드로 전달\n",
114 | "\n",
115 | "역전파의 계산 순에 따르면 목표로 하는 미분 값을 효율적으로 구할 수 있음"
116 | ]
117 | },
118 | {
119 | "cell_type": "markdown",
120 | "metadata": {},
121 | "source": [
122 | "### 5.2.2 연쇄법칙이란?\n",
123 | "\n",
124 | "합성 함수: 여러 함수로 구성된 함수\n",
125 | "\n",
126 | "#### 식 5.1\n",
127 | "\n",
128 | "\\begin{equation*}\n",
129 | "z = t^{2}\n",
130 | "\\end{equation*}\n",
131 | "\n",
132 | "\\begin{equation*}\n",
133 | "t = x + y\n",
134 | "\\end{equation*}\n",
135 | "\n",
136 | "연쇄법칙은 함성 함수의 미분에 대한 성질\n",
137 | "\n",
138 | "합성 함수의 미분은 합성 함수를 구성하는 각 함수의 미분의 곱으로 나타낼 수 있다.\n",
139 | "\n",
140 | "#### 식 5.2\n",
141 | "\n",
142 | "\\begin{equation*}\n",
143 | "\\frac{\\partial z}{\\partial x} = \\frac{\\partial z}{\\partial t} \\frac{\\partial t}{\\partial x}\n",
144 | "\\end{equation*}\n",
145 | "\n",
146 | "x에 대한 z의 미분은 t에 대한 z의 미분과 x에 대한 t의 미분의 곱으로 나타낼 수 있음\n",
147 | "\n",
148 | "∂t를 서로 지울 수 있음.\n",
149 | "\n",
150 | "\\begin{equation*}\n",
151 | "\\frac{\\partial z}{\\partial x} = \\frac{\\partial z}{} \\frac{}{\\partial x}\n",
152 | "\\end{equation*}\n",
153 | "\n",
154 | "#### 식 5.3\n",
155 | "\n",
156 | "식 5.1에 대한 국소적 미분(편미분)을 구함\n",
157 | "\n",
158 | "\\begin{equation*}\n",
159 | "\\frac{\\partial z}{\\partial t} = 2t\n",
160 | "\\end{equation*}\n",
161 | "\n",
162 | "\\begin{equation*}\n",
163 | "\\frac{\\partial t}{\\partial x} = 1\n",
164 | "\\end{equation*}\n",
165 | "\n",
166 | "최종적으로 구하고 싶은 x에 대한 z의 미분은 다음 두 미분을 곱해 계산\n",
167 | "\n",
168 | "#### 식 5.4\n",
169 | "\n",
170 | "\\begin{equation*}\n",
171 | "\\frac{\\partial z}{\\partial x} = \\frac{\\partial z}{\\partial t} \\frac{\\partial t}{\\partial x} = 2t · 1 = 2(x+y)\n",
172 | "\\end{equation*}"
173 | ]
174 | },
175 | {
176 | "cell_type": "markdown",
177 | "metadata": {
178 | "collapsed": true
179 | },
180 | "source": [
181 | "### 5.2.3 연쇄법칙과 계산 그래프\n",
182 | "\n",
183 | "계산 그래프의 역전파는 오른쪽에서 왼쪽으로 신호를 전파\n",
184 | "\n",
185 | "노드로 들어온 입력신호에 그 노드의 국소적 미분(편미분)을 곱한 후 다음 노드로 전달\n",
186 | "\n",
187 | "역전파가 하는 일은 연쇄 법칙의 원리와 같음."
188 | ]
189 | },
190 | {
191 | "cell_type": "markdown",
192 | "metadata": {},
193 | "source": [
194 | "## 5.3 역전파"
195 | ]
196 | },
197 | {
198 | "cell_type": "markdown",
199 | "metadata": {},
200 | "source": [
201 | "### 5.3.1 덧셈 노드의 역전파\n",
202 | "\n",
203 | "z = x + y 의 미분. 다음은 해석적으로 계산\n",
204 | "\n",
205 | "#### 식 5.5\n",
206 | "\n",
207 | "\\begin{equation*}\n",
208 | "\\frac{\\partial z}{\\partial x} = 1\n",
209 | "\\end{equation*}\n",
210 | "\n",
211 | "\\begin{equation*}\n",
212 | "\\frac{\\partial z}{\\partial y} = 1\n",
213 | "\\end{equation*}\n",
214 | "\n",
215 | "덧셈 노드의 역전파는 1을 곱하기만 할 뿐 입력된 값을 그대로 다음 노드로 보내게 됨."
216 | ]
217 | },
218 | {
219 | "cell_type": "markdown",
220 | "metadata": {},
221 | "source": [
222 | "### 5.3.2 곱셈 노드의 역전파\n",
223 | "\n",
224 | "z = xy 의 미분\n",
225 | "\n",
226 | "#### 식 5.6\n",
227 | "\n",
228 | "\\begin{equation*}\n",
229 | "\\frac{\\partial z}{\\partial x} = y\n",
230 | "\\end{equation*}\n",
231 | "\n",
232 | "\\begin{equation*}\n",
233 | "\\frac{\\partial z}{\\partial y} = x\n",
234 | "\\end{equation*}\n",
235 | "\n",
236 | "곱셈 노드의 역전파는 상류의 값에 순전파 때의 입력 신호들을 '서로 바꾼 값'을 곱해서 하류로 보냄\n",
237 | "\n",
238 | "순전파 때 x 였다면 역전파에서는 y. 순전파 때 y 였다면 역전파에서는 x로 바꿈"
239 | ]
240 | },
241 | {
242 | "cell_type": "markdown",
243 | "metadata": {},
244 | "source": [
245 | "### 5.3.3 사과 쇼핑의 예"
246 | ]
247 | },
248 | {
249 | "cell_type": "markdown",
250 | "metadata": {},
251 | "source": [
252 | "## 5.4 단순한 계층 구현하기\n",
253 | "\n",
254 | "계산 그래프의 곱셈 노드를 'MultiLayer', 덧셈 노드를 'AddLayer'로 구현"
255 | ]
256 | },
257 | {
258 | "cell_type": "markdown",
259 | "metadata": {},
260 | "source": [
261 | "### 5.4.1 곱셈 계층\n",
262 | "\n",
263 | "모든 계층은 forward() 순전파, backward() 역전파 라는 공통의 메서드(인터페이스)를 갖도록 수현\n",
264 | "\n",
265 | "곱셈 계층을 MultiLayer 클래스로 다음처럼 구현"
266 | ]
267 | },
268 | {
269 | "cell_type": "code",
270 | "execution_count": 1,
271 | "metadata": {
272 | "collapsed": true
273 | },
274 | "outputs": [],
275 | "source": [
276 | "# https://github.com/WegraLee/deep-learning-from-scratch/blob/master/ch05/layer_naive.py 소스 참고\n",
277 | "class MulLayer:\n",
278 | " def __init__(self):\n",
279 | " self.x = None\n",
280 | " self.y = None\n",
281 | " \n",
282 | " def forward(self, x, y):\n",
283 | " self.x = x\n",
284 | " self.y = y\n",
285 | " out = x * y\n",
286 | " \n",
287 | " return out\n",
288 | " \n",
289 | " def backward(self, dout):\n",
290 | " dx = dout * self.y # x와 y를 바꾼다.\n",
291 | " dy = dout * self.x \n",
292 | " \n",
293 | " return dx, dy"
294 | ]
295 | },
296 | {
297 | "cell_type": "markdown",
298 | "metadata": {},
299 | "source": [
300 | "\\__init\\__() : 인스턴스 변수인 x와 y를 초기화. 순전파 시 입력 값을 유지하기 위해 사용.\n",
301 | "\n",
302 | "forward() : x와 y를 인수로 받고 두 값을 곱해 반환\n",
303 | "\n",
304 | "backward() : 상류에서 넘어온 미분(dout)에 순전파 때 값을 '서로 바꿔' 곱한 후 하류로 흘림.\n",
305 | "\n",
306 | "MultiLayer를 사용하여 순전파 구현"
307 | ]
308 | },
309 | {
310 | "cell_type": "code",
311 | "execution_count": 2,
312 | "metadata": {
313 | "collapsed": false
314 | },
315 | "outputs": [
316 | {
317 | "name": "stdout",
318 | "output_type": "stream",
319 | "text": [
320 | "220.00000000000003\n"
321 | ]
322 | }
323 | ],
324 | "source": [
325 | "# https://github.com/WegraLee/deep-learning-from-scratch/blob/master/ch05/buy_apple.py 소스 참고\n",
326 | "apple = 100\n",
327 | "apple_num = 2\n",
328 | "tax = 1.1\n",
329 | "\n",
330 | "# 계층들\n",
331 | "mul_apple_layer = MulLayer()\n",
332 | "mul_tax_layer = MulLayer()\n",
333 | "\n",
334 | "# 순전파\n",
335 | "apple_price = mul_apple_layer.forward(apple, apple_num)\n",
336 | "price = mul_tax_layer.forward(apple_price, tax)\n",
337 | "\n",
338 | "print(price) # 220"
339 | ]
340 | },
341 | {
342 | "cell_type": "markdown",
343 | "metadata": {},
344 | "source": [
345 | "각 변수에 대한 미분은 backward()로 구할 수 있음"
346 | ]
347 | },
348 | {
349 | "cell_type": "code",
350 | "execution_count": 3,
351 | "metadata": {
352 | "collapsed": false
353 | },
354 | "outputs": [
355 | {
356 | "name": "stdout",
357 | "output_type": "stream",
358 | "text": [
359 | "2.2 110.00000000000001 200\n"
360 | ]
361 | }
362 | ],
363 | "source": [
364 | "dprice = 1\n",
365 | "dapple_price, dtax = mul_tax_layer.backward(dprice)\n",
366 | "dapple, dapple_num = mul_apple_layer.backward(dapple_price)\n",
367 | "\n",
368 | "print(dapple, dapple_num, dtax) # 2.2 110 200"
369 | ]
370 | },
371 | {
372 | "cell_type": "markdown",
373 | "metadata": {},
374 | "source": [
375 | "backward() 호출 순서는 forward() 때와 반대\n",
376 | "\n",
377 | "backward()가 받는 인수는 '순전파의 출력에 대한 미분'"
378 | ]
379 | },
380 | {
381 | "cell_type": "markdown",
382 | "metadata": {},
383 | "source": [
384 | "### 5.4.2 덧셈 계층\n",
385 | "\n",
386 | "모든 계층은 forward() 순전파, backward() 역전파 라는 공통의 메서드(인터페이스)를 갖도록 수현\n",
387 | "\n",
388 | "덧셈 계층을 MultiLayer 클래스"
389 | ]
390 | },
391 | {
392 | "cell_type": "code",
393 | "execution_count": 4,
394 | "metadata": {
395 | "collapsed": true
396 | },
397 | "outputs": [],
398 | "source": [
399 | "class AddLayer:\n",
400 | " def __init__(self):\n",
401 | " pass\n",
402 | " \n",
403 | " def forward(self, x, y):\n",
404 | " out = x + y\n",
405 | " return out\n",
406 | "\n",
407 | " def backward(self, dout):\n",
408 | " dx = dout * 1\n",
409 | " dy = dout * 1\n",
410 | " return dx, dy"
411 | ]
412 | },
413 | {
414 | "cell_type": "markdown",
415 | "metadata": {},
416 | "source": [
417 | "\\__init\\__() : pass를 통해 아무 일도 하지 않음\n",
418 | "\n",
419 | "forward() : x와 y를 인수로 받고 두 값을 더해 반환\n",
420 | "\n",
421 | "backward() : 상류에서 넘어온 미분(dout)을 그대로 하류로 흘림\n",
422 | "\n",
423 | "그림 5-17의 계산 그래프 파이썬 구현"
424 | ]
425 | },
426 | {
427 | "cell_type": "code",
428 | "execution_count": 5,
429 | "metadata": {
430 | "collapsed": false
431 | },
432 | "outputs": [
433 | {
434 | "name": "stdout",
435 | "output_type": "stream",
436 | "text": [
437 | "price: 715\n",
438 | "dApple: 2.2\n",
439 | "dApple_num: 110\n",
440 | "dOrange: 3.3000000000000003\n",
441 | "dOrange_num: 165\n",
442 | "dTax: 650\n"
443 | ]
444 | }
445 | ],
446 | "source": [
447 | "# https://github.com/WegraLee/deep-learning-from-scratch/blob/master/ch05/buy_apple.py 소스 참고\n",
448 | "apple = 100\n",
449 | "apple_num = 2\n",
450 | "orange = 150\n",
451 | "orange_num = 3\n",
452 | "tax = 1.1\n",
453 | "\n",
454 | "# 계층들\n",
455 | "mul_apple_layer = MulLayer()\n",
456 | "mul_orange_layer = MulLayer()\n",
457 | "add_apple_orange_layer = AddLayer()\n",
458 | "mul_tax_layer = MulLayer()\n",
459 | "\n",
460 | "# 순전파\n",
461 | "apple_price = mul_apple_layer.forward(apple, apple_num) # (1)\n",
462 | "orange_price = mul_orange_layer.forward(orange, orange_num) # (2)\n",
463 | "all_price = add_apple_orange_layer.forward(apple_price, orange_price) # (3)\n",
464 | "price = mul_tax_layer.forward(all_price, tax) # (4)\n",
465 | "\n",
466 | "# 역전파\n",
467 | "dprice = 1\n",
468 | "dall_price, dtax = mul_tax_layer.backward(dprice) # (4)\n",
469 | "dapple_price, dorange_price = add_apple_orange_layer.backward(dall_price) # (3)\n",
470 | "dorange, dorange_num = mul_orange_layer.backward(dorange_price) # (2)\n",
471 | "dapple, dapple_num = mul_apple_layer.backward(dapple_price) # (1)\n",
472 | "\n",
473 | "print(\"price:\", int(price)) # 715\n",
474 | "print(\"dApple:\", dapple) # 2.2\n",
475 | "print(\"dApple_num:\", int(dapple_num)) # 110\n",
476 | "print(\"dOrange:\", dorange) # 3.3\n",
477 | "print(\"dOrange_num:\", int(dorange_num)) # 165\n",
478 | "print(\"dTax:\", dtax) # 650"
479 | ]
480 | },
481 | {
482 | "cell_type": "markdown",
483 | "metadata": {
484 | "collapsed": true
485 | },
486 | "source": [
487 | "## 5.5 활성화 함수 계층 구현하기\n",
488 | "\n",
489 | "활성화 함수인 ReLU와 Sigmoid 계층을 구현"
490 | ]
491 | },
492 | {
493 | "cell_type": "markdown",
494 | "metadata": {},
495 | "source": [
496 | "### 5.5.1 ReLU 계층\n",
497 | "\n",
498 | "#### 식 5.7 ReLU 식\n",
499 | "\n",
500 | "\\begin{equation*}\n",
501 | "y = x ( x > 0 )\n",
502 | "\\end{equation*}\n",
503 | "\n",
504 | "\\begin{equation*}\n",
505 | "y = 0 ( x <= 0 )\n",
506 | "\\end{equation*}\n",
507 | "\n",
508 | "#### 식 5.8 ReLU x에 대한 y 미분 식\n",
509 | "\n",
510 | "\\begin{equation*}\n",
511 | "\\frac{\\partial y}{\\partial x} = 1 ( x > 0 )\n",
512 | "\\end{equation*}\n",
513 | "\n",
514 | "\\begin{equation*}\n",
515 | "\\frac{\\partial y}{\\partial x} = 0 ( x <= 0 )\n",
516 | "\\end{equation*}\n",
517 | "\n",
518 | "순전파 때 입력인 x가 0보다 크면 역전파는 상류의 값을 그대로 하류로 흘림\n",
519 | "\n",
520 | "순전파 때 x가 0 이하면 역전파 때는 하류로 신호를 보내지 않음\n",
521 | "\n",
522 | "ReLU 계층을 구현한 코드"
523 | ]
524 | },
525 | {
526 | "cell_type": "code",
527 | "execution_count": 6,
528 | "metadata": {
529 | "collapsed": true
530 | },
531 | "outputs": [],
532 | "source": [
533 | "# https://github.com/WegraLee/deep-learning-from-scratch/blob/master/common/layers.py 소스 참고\n",
534 | "class Relu:\n",
535 | " def __init__(self):\n",
536 | " self.mask = None\n",
537 | " \n",
538 | " def forward(self, x):\n",
539 | " self.mask = (x <= 0)\n",
540 | " out = x.copy()\n",
541 | " out[self.mask] = 0\n",
542 | " \n",
543 | " return out\n",
544 | " \n",
545 | " def backward(self, dout):\n",
546 | " dout[self.mask] = 0\n",
547 | " dx = dout\n",
548 | " \n",
549 | " return dx"
550 | ]
551 | },
552 | {
553 | "cell_type": "markdown",
554 | "metadata": {},
555 | "source": [
556 | "Relu 클래스는 mask 인스턴스 변수를 가짐\n",
557 | "\n",
558 | "mask는 순전파의 입력인 x의 원소 값이 0 이하인 인덱스는 True, 그 외(0보다 큰 원소)는 False로 유지"
559 | ]
560 | },
561 | {
562 | "cell_type": "code",
563 | "execution_count": 7,
564 | "metadata": {
565 | "collapsed": false
566 | },
567 | "outputs": [
568 | {
569 | "name": "stdout",
570 | "output_type": "stream",
571 | "text": [
572 | "[[ 1. 0.5]\n",
573 | " [-2. 3. ]]\n"
574 | ]
575 | }
576 | ],
577 | "source": [
578 | "import numpy as np\n",
579 | "x = np.array([[1.0, 0.5], [-2.0, 3.0]])\n",
580 | "print(x)"
581 | ]
582 | },
583 | {
584 | "cell_type": "code",
585 | "execution_count": 8,
586 | "metadata": {
587 | "collapsed": false
588 | },
589 | "outputs": [
590 | {
591 | "name": "stdout",
592 | "output_type": "stream",
593 | "text": [
594 | "[[False False]\n",
595 | " [ True False]]\n"
596 | ]
597 | }
598 | ],
599 | "source": [
600 | "mask = (x <= 0)\n",
601 | "print(mask)"
602 | ]
603 | },
604 | {
605 | "cell_type": "code",
606 | "execution_count": 9,
607 | "metadata": {
608 | "collapsed": false
609 | },
610 | "outputs": [
611 | {
612 | "data": {
613 | "text/plain": [
614 | "array([[ 1. , 0.5],\n",
615 | " [ 0. , 3. ]])"
616 | ]
617 | },
618 | "execution_count": 9,
619 | "metadata": {},
620 | "output_type": "execute_result"
621 | }
622 | ],
623 | "source": [
624 | "out = x.copy()\n",
625 | "out[mask] = 0\n",
626 | "out"
627 | ]
628 | },
629 | {
630 | "cell_type": "markdown",
631 | "metadata": {},
632 | "source": [
633 | "ReLU 계층은 전기 회로의 '스위치'에 비유\n",
634 | "\n",
635 | "순전파 때 전류가 흐르고 있으면 스위치를 ON, 흐르지 않으면 OFF\n",
636 | "\n",
637 | "역전파 때 스위치가 ON이라면 전류가 그대로 흐르고, OFF면 더 이상 흐르지 않음"
638 | ]
639 | },
640 | {
641 | "cell_type": "markdown",
642 | "metadata": {},
643 | "source": [
644 | "### 5.5.2 Sigmoid 계층\n",
645 | "\n",
646 | "#### 식 5.9 시그모이드 함수\n",
647 | "\n",
648 | "\\begin{equation*}\n",
649 | "y = \\frac{1}{1 + exp(-x)}\n",
650 | "\\end{equation*}\n",
651 | "\n",
652 | "**1단계** '/' 노드, y = 1 / x를 미분하면 다음식이 됨\n",
653 | "\n",
654 | "#### 식 5.10\n",
655 | "\n",
656 | "\\begin{equation*}\n",
657 | "\\frac{\\partial y}{\\partial x} = \\frac{1}{x^{2}}\n",
658 | "\\end{equation*}\n",
659 | "\n",
660 | "\\begin{equation*}\n",
661 | "= - y^{2}\n",
662 | "\\end{equation*}\n",
663 | "\n",
664 | "역전파 때는 상류의 예측값에 -y\\**2 을 곱해서 하류로 전달\n",
665 | "\n",
666 | "**2단계** 상류의 값을 여과 없이 하류로 보냄\n",
667 | "\n",
668 | "**3단계** y = exp(x) 연산을 수행\n",
669 | "\n",
670 | "#### 식 5.11\n",
671 | "\n",
672 | "\\begin{equation*}\n",
673 | "\\frac{\\partial y}{\\partial x} = exp(x)\n",
674 | "\\end{equation*}\n",
675 | "\n",
676 | "계산 그래프에서는 상류의 순전파 때의 출력(exp(-x))을 곱해 하류로 전파\n",
677 | "\n",
678 | "**4단계** y = exp(x) 연산을 수행\n",
679 | "\n",
680 | "'X' 노드, 순전파 때의 값을 서로 바꿔 곱함. 이 예에서는 -1을 곱함\n",
681 | "\n",
682 | "시그모이드 간소화버전\n",
683 | "\n",
684 | "노드를 그룹화하여 Sigmoid 계층의 세세한 내용을 노출하지 않고 입력과 출력에만 집중\n",
685 | "\n",
686 | "\\begin{equation*}\n",
687 | "\\frac{\\partial L}{\\partial y} y^{2} exp(-x) = \\frac{\\partial L}{\\partial y} \\frac{1} { (1+exp(-x))^{2}} exp(-x)\n",
688 | "\\end{equation*}\n",
689 | "\n",
690 | "\\begin{equation*}\n",
691 | "= \\frac{\\partial L}{\\partial y} \\frac{1} { 1+exp(-x)} \\frac{exp(-x)} {1+exp(-x)}\n",
692 | "\\end{equation*}\n",
693 | "\n",
694 | "\\begin{equation*}\n",
695 | "= \\frac{\\partial L}{\\partial y} y (1-y)\n",
696 | "\\end{equation*}\n",
697 | "\n",
698 | "Sigmoid 계층의 계산 그래프: 순전파의 출력 y만으로 역전파를 계산\n",
699 | "\n",
700 | "Sigmoid 계층을 파이썬으로 구현"
701 | ]
702 | },
703 | {
704 | "cell_type": "code",
705 | "execution_count": 10,
706 | "metadata": {
707 | "collapsed": true
708 | },
709 | "outputs": [],
710 | "source": [
711 | "class Sigmoid:\n",
712 | " def __init__(self):\n",
713 | " self.out = None\n",
714 | " \n",
715 | " def forward(self, x):\n",
716 | " out = 1 / (1 + np.exp(-x))\n",
717 | " self.out = out\n",
718 | " \n",
719 | " return out\n",
720 | " \n",
721 | " def backward(self, dout):\n",
722 | " dx = dout * (1.0 - self.out) * self.out\n",
723 | " \n",
724 | " return dx"
725 | ]
726 | },
727 | {
728 | "cell_type": "markdown",
729 | "metadata": {
730 | "collapsed": true
731 | },
732 | "source": [
733 | "## 5.6 Affine/Softmax 계층 구현하기\n",
734 | "\n",
735 | "### 5.6.1 Affine 계층\n",
736 | "\n",
737 | "신경망의 순전파에서는 가중치 신호의 총합을 계산하기 위해 행렬의 내적(np.dot())을 사용"
738 | ]
739 | },
740 | {
741 | "cell_type": "code",
742 | "execution_count": 11,
743 | "metadata": {
744 | "collapsed": false
745 | },
746 | "outputs": [
747 | {
748 | "name": "stdout",
749 | "output_type": "stream",
750 | "text": [
751 | "(2,)\n",
752 | "(2, 3)\n",
753 | "(3,)\n"
754 | ]
755 | }
756 | ],
757 | "source": [
758 | "X = np.random.rand(2) # 입력\n",
759 | "W = np.random.rand(2,3) # 가중치\n",
760 | "B = np.random.rand(3) # 편향\n",
761 | "\n",
762 | "print(X.shape) # (2,)\n",
763 | "print(W.shape) # (2, 3)\n",
764 | "print(B.shape) # (3,)\n",
765 | "\n",
766 | "Y = np.dot(X, W) + B"
767 | ]
768 | },
769 | {
770 | "cell_type": "markdown",
771 | "metadata": {},
772 | "source": [
773 | "X와 W의 내적은 대응하는 차원의 원소 수를 일치 시켜야 함\n",
774 | "\n",
775 | "어파인 변환(affine transformation): 신경망의 순전파 때 수행하는 행렬의 내적. 기하학 용어\n",
776 | "\n",
777 | "이 계산 그래프는 '행렬'이 흐름\n",
778 | "\n",
779 | "#### 식 5.13 행렬을 사용한 역전파 전개식\n",
780 | "\n",
781 | "\\begin{equation*}\n",
782 | "\\frac{\\partial L}{\\partial X} = \\frac{\\partial L}{\\partial Y} W^{T}\n",
783 | "\\end{equation*}\n",
784 | "\n",
785 | "\\begin{equation*}\n",
786 | "\\frac{\\partial L}{\\partial W} = X^{T} \\frac{\\partial L}{\\partial Y}\n",
787 | "\\end{equation*}\n",
788 | "\n",
789 | "전치행렬 : W의 (i,j) 위치의 원소를 (j,i) 위치로 변경\n",
790 | "\n",
791 | "#### 식 5.14 전치행렬 수식\n",
792 | "\n",
793 | "\\begin{equation*}\n",
794 | "W = \\begin{vmatrix}\n",
795 | "w_{11} w_{21} w_{31}\\\\\n",
796 | "w_{12} w_{22} w_{32}\\\n",
797 | "\\end{vmatrix}\n",
798 | "\\end{equation*}\n",
799 | "\n",
800 | "\\begin{equation*}\n",
801 | "W^{T} = \\begin{vmatrix}\n",
802 | "w_{11} w_{12}\\\\\n",
803 | "w_{21} w_{22}\\\\\n",
804 | "w_{31} w_{32}\\\n",
805 | "\\end{vmatrix}\n",
806 | "\\end{equation*}\n",
807 | "\n",
808 | "W의 형상이 (2,3) 이면 W.T의 형상은 (3,2)\n",
809 | "\n",
810 | "#### 그림 5.25 Affine 계층의 역전파: 역전파에서의 변수 형상은 해당 변수명 옆에 표기\n",
811 | "\n",
812 | "\\begin{equation*}\n",
813 | "\\frac{\\partial L}{\\partial X}(2,) = \\frac{\\partial L}{\\partial Y}(3,) W^{T} (3,2)\n",
814 | "\\end{equation*}\n",
815 | "\n",
816 | "\\begin{equation*}\n",
817 | "X(2,) 와 \\frac{\\partial L}{\\partial X}(2,) 은 같은 형상\n",
818 | "\\end{equation*}\n",
819 | "\n",
820 | "\\begin{equation*}\n",
821 | "\\frac{\\partial L}{\\partial W}(2,3) = X^{T}(2,1) \\frac{\\partial L}{\\partial Y} (1,3)\n",
822 | "\\end{equation*}\n",
823 | "\n",
824 | "\\begin{equation*}\n",
825 | "W(2,3) 와 \\frac{\\partial L}{\\partial W}(2,3) 은 같은 형상\n",
826 | "\\end{equation*}"
827 | ]
828 | },
829 | {
830 | "cell_type": "markdown",
831 | "metadata": {},
832 | "source": [
833 | "### 5.6.2 배치용 Affine 계층\n",
834 | "\n",
835 | "#### 그림 5-27 배치용 Affine 계층의 계산 그래프\n",
836 | "\n",
837 | "\\begin{equation*}\n",
838 | "\\frac{\\partial L}{\\partial X}(N,2) = \\frac{\\partial L}{\\partial Y}(N,3) W^{T} (3,2)\n",
839 | "\\end{equation*}\n",
840 | "\n",
841 | "\\begin{equation*}\n",
842 | "\\frac{\\partial L}{\\partial W}(2,3) = X^{T}(2,N) \\frac{\\partial L}{\\partial Y} (N,3)\n",
843 | "\\end{equation*}\n",
844 | "\n",
845 | "\\begin{equation*}\n",
846 | "\\frac{\\partial L}{\\partial B}(3) = \\frac{\\partial L}{\\partial Y} (N,3) 의 첫 번째(제 0축, 열방향)의 합\n",
847 | "\\end{equation*}\n",
848 | "\n",
849 | "기존과 다른 부분은 입력인 X의 형상이 (N,2)가 됨\n",
850 | "\n",
851 | "예를 들어 N=2(데이터가 2개)로 한 경우, 편향은 그 두 데이터 각각에 더해집니다."
852 | ]
853 | },
854 | {
855 | "cell_type": "code",
856 | "execution_count": 12,
857 | "metadata": {
858 | "collapsed": false
859 | },
860 | "outputs": [
861 | {
862 | "data": {
863 | "text/plain": [
864 | "array([[ 0, 0, 0],\n",
865 | " [10, 10, 10]])"
866 | ]
867 | },
868 | "execution_count": 12,
869 | "metadata": {},
870 | "output_type": "execute_result"
871 | }
872 | ],
873 | "source": [
874 | "X_dot_W = np.array([[0, 0, 0], [10, 10, 10]])\n",
875 | "B = np.array([1, 2, 3])\n",
876 | "\n",
877 | "X_dot_W"
878 | ]
879 | },
880 | {
881 | "cell_type": "code",
882 | "execution_count": 13,
883 | "metadata": {
884 | "collapsed": false
885 | },
886 | "outputs": [
887 | {
888 | "data": {
889 | "text/plain": [
890 | "array([[ 1, 2, 3],\n",
891 | " [11, 12, 13]])"
892 | ]
893 | },
894 | "execution_count": 13,
895 | "metadata": {},
896 | "output_type": "execute_result"
897 | }
898 | ],
899 | "source": [
900 | "X_dot_W + B"
901 | ]
902 | },
903 | {
904 | "cell_type": "markdown",
905 | "metadata": {},
906 | "source": [
907 | "순전파의 편향 덧셈은 각각의 데이터(1번째 데이터, 2번째 데이터)에 더해짐\n",
908 | "\n",
909 | "역전파 때는 각 데이터의 역전파 값이 편향의 원소에 모여야 함"
910 | ]
911 | },
912 | {
913 | "cell_type": "code",
914 | "execution_count": 14,
915 | "metadata": {
916 | "collapsed": false
917 | },
918 | "outputs": [
919 | {
920 | "data": {
921 | "text/plain": [
922 | "array([[1, 2, 3],\n",
923 | " [4, 5, 6]])"
924 | ]
925 | },
926 | "execution_count": 14,
927 | "metadata": {},
928 | "output_type": "execute_result"
929 | }
930 | ],
931 | "source": [
932 | "dY = np.array([[1, 2, 3], [4, 5, 6]])\n",
933 | "dY"
934 | ]
935 | },
936 | {
937 | "cell_type": "code",
938 | "execution_count": 15,
939 | "metadata": {
940 | "collapsed": false
941 | },
942 | "outputs": [
943 | {
944 | "data": {
945 | "text/plain": [
946 | "array([5, 7, 9])"
947 | ]
948 | },
949 | "execution_count": 15,
950 | "metadata": {},
951 | "output_type": "execute_result"
952 | }
953 | ],
954 | "source": [
955 | "dB = np.sum(dY, axis=0)\n",
956 | "dB"
957 | ]
958 | },
959 | {
960 | "cell_type": "markdown",
961 | "metadata": {},
962 | "source": [
963 | "np.sum()에서 0번째 축(데이터를 단위로 한 축)에 대해서 (axis=0)의 총합을 구함\n",
964 | "\n",
965 | "Affine 구현\n",
966 | "\n",
967 | "common/layer.py 파일의 Affine 구현은 입력 데이터가 텐서(4차원 데이터)인 경우도 고려. 다음 구현과 약간 차이가 있음."
968 | ]
969 | },
970 | {
971 | "cell_type": "code",
972 | "execution_count": 16,
973 | "metadata": {
974 | "collapsed": true
975 | },
976 | "outputs": [],
977 | "source": [
978 | "class Affine:\n",
979 | " def __init__(self, W, b):\n",
980 | " self.W = W\n",
981 | " self.b = b\n",
982 | " self.x = None\n",
983 | " self.dW = None\n",
984 | " self.db = None\n",
985 | " \n",
986 | " def forward(self, x):\n",
987 | " self.x = x\n",
988 | " out = np.dot(x, self.W) + self.b\n",
989 | " \n",
990 | " return out\n",
991 | " \n",
992 | " def backward(self, dout):\n",
993 | " dx = np.dot(dout, self.W.T)\n",
994 | " self.dW = np.dot(self.x.T, dout)\n",
995 | " self.db = np.sum(dout, axis=0)\n",
996 | " \n",
997 | " return dx"
998 | ]
999 | },
1000 | {
1001 | "cell_type": "markdown",
1002 | "metadata": {},
1003 | "source": [
1004 | "### 5.6.3 Softmax-with-Loss 계층\n",
1005 | "\n",
1006 | "소프트맥스 함수는 입력 값을 정규화하여 출력\n",
1007 | "\n",
1008 | "추론할 때는 일반적으로 Softmax 계층을 사용하지 않음\n",
1009 | "\n",
1010 | "점수(score): Softmax 앞의 Affine 계층의 출력\n",
1011 | "\n",
1012 | "신경망을 학습할 때는 Softmax 계층이 필요\n",
1013 | "\n",
1014 | "소프트맥스 계층 구현: 손실 함수인 교차 엔트로피 오차도 포함하여 'Softmax-with-Loss 계층'이라는 이름으로 구현\n",
1015 | "\n",
1016 | "Softmax 계층: 입력 (a1, a2, a3)를 정규화하여 (y1, y2, y3)를 출력\n",
1017 | "\n",
1018 | "Cross Entropy 계층: Softmax의 출력(y1, y2, y3)과 정답 레이블(t1, t2, t3)를 받고, 손실 L을 출력\n",
1019 | "\n",
1020 | "Softmax 계층의 역전파는 (y1-t1, y2-t2, y3-t3)로 말끔한 결과임\n",
1021 | "\n",
1022 | "Softmax 계층의 출력과 정답 레이블의 차분.\n",
1023 | "\n",
1024 | "신경망의 역전파에서는 이 차이인 오차가 앞 계층에 전해지는 것\n",
1025 | "\n",
1026 | "소프트맥스 함수의 손실 함수로 교차 엔트로피 오차를 사용하니 역전파가 (y1-t1, y2-t2, y3-t3)로 말끔히 떨어짐\n",
1027 | "\n",
1028 | "=> 교차 엔트로피 함수가 그렇게 설계되었기 때문\n",
1029 | "\n",
1030 | "항등 함수의 손실 함수로 '평균 제곱 오차'를 사용하면 역전파의 결과가 말끔히 떨어짐\n",
1031 | "\n",
1032 | "구체적인 예\n",
1033 | "\n",
1034 | "정답 레이블 (0, 1, 0), 소프트맥스 계층이 (0.3, 0.2, 0.5)를 출력\n",
1035 | "\n",
1036 | "=> 소프트맥스 계층의 역전파는 (0.3, -0.8, 0.5)라는 커다란 오차를 전파\n",
1037 | "\n",
1038 | "정답 레이블 (0, 1, 0), 소프트맥스 계층이 (0.01, 0.99, 0)을 출력\n",
1039 | "\n",
1040 | "=> 소프트맥스 계층의 역전파가 보내는 오차는 (0.01, -0.01, 0)이 됨. 학습하는 정도가 작아짐\n",
1041 | "\n",
1042 | "Softmax-with-Loss 계층을 구현한 코드"
1043 | ]
1044 | },
1045 | {
1046 | "cell_type": "code",
1047 | "execution_count": 17,
1048 | "metadata": {
1049 | "collapsed": false
1050 | },
1051 | "outputs": [],
1052 | "source": [
1053 | "# https://github.com/WegraLee/deep-learning-from-scratch/blob/master/common/functions.py 소스 참고\n",
1054 | "# 3.5.2 소프트맥스 함수 구현시 주의점 참고\n",
1055 | "def sigmoid(x):\n",
1056 | " return 1 / (1 + np.exp(-x))\n",
1057 | "\n",
1058 | "# 4.2.2. 교차 엔트로피 오차 참고\n",
1059 | "def cross_entropy_error(y, t):\n",
1060 | " if y.ndim == 1:\n",
1061 | " t = t.reshape(1, t.size)\n",
1062 | " y = y.reshape(1, y.size)\n",
1063 | " \n",
1064 | " # 훈련 데이터가 원-핫 벡터라면 정답 레이블의 인덱스로 반환\n",
1065 | " if t.size == y.size:\n",
1066 | " t = t.argmax(axis=1)\n",
1067 | " \n",
1068 | " batch_size = y.shape[0]\n",
1069 | " return -np.sum(np.log(y[np.arange(batch_size), t])) / batch_size\n",
1070 | "\n",
1071 | "class SoftmaxWithLoss:\n",
1072 | " def __init__(self):\n",
1073 | " self.loss = None # 손실\n",
1074 | " self.y = None # softmax의 출력\n",
1075 | " self.t = None # 정답 레이블(원-핫 벡터)\n",
1076 | " \n",
1077 | " def forward(self, x, t):\n",
1078 | " self.t = t\n",
1079 | " self.y = softmax(x)\n",
1080 | " self.loss = cross_entropy_error(self.y, self.t)\n",
1081 | " return self.loss\n",
1082 | " \n",
1083 | " def backward(self, dout=1):\n",
1084 | " batch_size = self.t.shape[0]\n",
1085 | " dx = (self.y - self.t) / batch_size\n",
1086 | " \n",
1087 | " return dx"
1088 | ]
1089 | },
1090 | {
1091 | "cell_type": "markdown",
1092 | "metadata": {},
1093 | "source": [
1094 | "주의. 역전파 때는 전파하는 값을 배치의 수(batch_size)로 나눠 데이터 1개당 오차를 앞 계층으로 전파함"
1095 | ]
1096 | },
1097 | {
1098 | "cell_type": "markdown",
1099 | "metadata": {},
1100 | "source": [
1101 | "## 5.7 오차역전파법 구현하기"
1102 | ]
1103 | },
1104 | {
1105 | "cell_type": "markdown",
1106 | "metadata": {},
1107 | "source": [
1108 | "### 5.7.1 신경망 학습의 전체 그림\n",
1109 | "\n",
1110 | "**전제**\n",
1111 | "\n",
1112 | "학습: 가중치와 편향을 훈련 데이터에 적응하도록 조정하는 과정\n",
1113 | "\n",
1114 | "**1단계 - 미니배치**\n",
1115 | "\n",
1116 | "미니배치: 훈련 데이터 중 일부를 무작위로 가져옴\n",
1117 | "\n",
1118 | "목표: 미니배치의 손실 함수 값을 줄이기\n",
1119 | "\n",
1120 | "**2단계 - 기울기 산출**\n",
1121 | "\n",
1122 | "가중치 매개변수의 기울기를 구함. 기울기는 손실 함수의 값을 가장 작게하는 방향을 제시\n",
1123 | "\n",
1124 | "**3단계 - 매개변수 갱신**\n",
1125 | "\n",
1126 | "가중치 매개변수를 기울기 방향으로 아주 조금 갱신\n",
1127 | "\n",
1128 | "**4단계 - 반복**\n",
1129 | "\n",
1130 | "1~3 단계를 반복\n",
1131 | "\n",
1132 | "오차역전법이 등장하는 단계는 두 번째인 '기울기 산출'\n",
1133 | "\n",
1134 | "느린 수치 미분과 달리 기울기를 효율적이고 빠르게 구할 수 있음"
1135 | ]
1136 | },
1137 | {
1138 | "cell_type": "markdown",
1139 | "metadata": {},
1140 | "source": [
1141 | "### 5.7.2 오차역전파법을 적용한 신경망 구현하기\n",
1142 | "\n",
1143 | "계층을 사용함으로써 \n",
1144 | "\n",
1145 | "인식 결과를 얻는 처리(predict())와 기울기를 구하는 처리(gradient()) 계층의 전파만으로 동작이 이루어짐."
1146 | ]
1147 | },
1148 | {
1149 | "cell_type": "code",
1150 | "execution_count": 18,
1151 | "metadata": {
1152 | "collapsed": false
1153 | },
1154 | "outputs": [],
1155 | "source": [
1156 | "# https://github.com/WegraLee/deep-learning-from-scratch/blob/master/ch05/two_layer_net.py 참고\n",
1157 | "# coding: utf-8\n",
1158 | "#import sys, os\n",
1159 | "#sys.path.append(os.pardir) # 부모 디렉터리의 파일을 가져올 수 있도록 설정\n",
1160 | "import numpy as np\n",
1161 | "#from common.layers import *\n",
1162 | "#from common.gradient import numerical_gradient\n",
1163 | "from collections import OrderedDict\n",
1164 | "\n",
1165 | "# https://github.com/WegraLee/deep-learning-from-scratch/blob/master/common/functions.py\n",
1166 | "def softmax(x):\n",
1167 | " if x.ndim == 2:\n",
1168 | " x = x.T\n",
1169 | " x = x - np.max(x, axis=0)\n",
1170 | " y = np.exp(x) / np.sum(np.exp(x), axis=0)\n",
1171 | " return y.T \n",
1172 | "\n",
1173 | " x = x - np.max(x) # 오버플로 대책\n",
1174 | " return np.exp(x) / np.sum(np.exp(x))\n",
1175 | "\n",
1176 | "# https://github.com/WegraLee/deep-learning-from-scratch/blob/master/common/gradient.py 참고\n",
1177 | "def numerical_gradient(f, x):\n",
1178 | " h = 1e-4 # 0.0001\n",
1179 | " grad = np.zeros_like(x)\n",
1180 | " \n",
1181 | " it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])\n",
1182 | " while not it.finished:\n",
1183 | " idx = it.multi_index\n",
1184 | " tmp_val = x[idx]\n",
1185 | " x[idx] = float(tmp_val) + h\n",
1186 | " fxh1 = f(x) # f(x+h)\n",
1187 | " \n",
1188 | " x[idx] = tmp_val - h \n",
1189 | " fxh2 = f(x) # f(x-h)\n",
1190 | " grad[idx] = (fxh1 - fxh2) / (2*h)\n",
1191 | " \n",
1192 | " x[idx] = tmp_val # 값 복원\n",
1193 | " it.iternext() \n",
1194 | " \n",
1195 | " return grad\n",
1196 | "\n",
1197 | "\n",
1198 | "class TwoLayerNet:\n",
1199 | "\n",
1200 | " def __init__(self, input_size, hidden_size, output_size, weight_init_std = 0.01):\n",
1201 | " # 가중치 초기화\n",
1202 | " self.params = {}\n",
1203 | " self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)\n",
1204 | " self.params['b1'] = np.zeros(hidden_size)\n",
1205 | " self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size) \n",
1206 | " self.params['b2'] = np.zeros(output_size)\n",
1207 | "\n",
1208 | " # 계층 생성\n",
1209 | " self.layers = OrderedDict() ###\n",
1210 | " self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1']) ###\n",
1211 | " self.layers['Relu1'] = Relu() ###\n",
1212 | " self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2']) ###\n",
1213 | "\n",
1214 | " self.lastLayer = SoftmaxWithLoss() ###\n",
1215 | " \n",
1216 | " def predict(self, x):\n",
1217 | " for layer in self.layers.values(): ###\n",
1218 | " x = layer.forward(x) ###\n",
1219 | " \n",
1220 | " return x\n",
1221 | " \n",
1222 | " # x : 입력 데이터, t : 정답 레이블\n",
1223 | " def loss(self, x, t):\n",
1224 | " y = self.predict(x)\n",
1225 | " return self.lastLayer.forward(y, t)\n",
1226 | " \n",
1227 | " def accuracy(self, x, t):\n",
1228 | " y = self.predict(x)\n",
1229 | " y = np.argmax(y, axis=1)\n",
1230 | " if t.ndim != 1 : t = np.argmax(t, axis=1)\n",
1231 | " \n",
1232 | " accuracy = np.sum(y == t) / float(x.shape[0])\n",
1233 | " return accuracy\n",
1234 | " \n",
1235 | " # x : 입력 데이터, t : 정답 레이블\n",
1236 | " def numerical_gradient(self, x, t):\n",
1237 | " loss_W = lambda W: self.loss(x, t)\n",
1238 | " \n",
1239 | " grads = {}\n",
1240 | " grads['W1'] = numerical_gradient(loss_W, self.params['W1'])\n",
1241 | " grads['b1'] = numerical_gradient(loss_W, self.params['b1'])\n",
1242 | " grads['W2'] = numerical_gradient(loss_W, self.params['W2'])\n",
1243 | " grads['b2'] = numerical_gradient(loss_W, self.params['b2'])\n",
1244 | " \n",
1245 | " return grads\n",
1246 | " \n",
1247 | " def gradient(self, x, t):\n",
1248 | " # forward\n",
1249 | " self.loss(x, t) ###\n",
1250 | "\n",
1251 | " # backward\n",
1252 | " dout = 1 ###\n",
1253 | " dout = self.lastLayer.backward(dout) ###\n",
1254 | " \n",
1255 | " layers = list(self.layers.values()) ###\n",
1256 | " layers.reverse() ###\n",
1257 | " for layer in layers: ###\n",
1258 | " dout = layer.backward(dout) ###\n",
1259 | "\n",
1260 | " # 결과 저장\n",
1261 | " grads = {}\n",
1262 | " grads['W1'], grads['b1'] = self.layers['Affine1'].dW, self.layers['Affine1'].db\n",
1263 | " grads['W2'], grads['b2'] = self.layers['Affine2'].dW, self.layers['Affine2'].db\n",
1264 | "\n",
1265 | " return grads"
1266 | ]
1267 | },
1268 | {
1269 | "cell_type": "markdown",
1270 | "metadata": {},
1271 | "source": [
1272 | "\\### 으로 중요코드 표시. 집중해서 살펴보세요.\n",
1273 | "\n",
1274 | "OrderedDict: 딕셔너리에 추가한 순서를 기억하는 (순서가 있는) 딕셔너리\n",
1275 | "\n",
1276 | "순전파 때는 추가한 순서대로 각 계층의 forward() 메서드를 호출\n",
1277 | "\n",
1278 | "역전파 때는 계층을 반대 순서로 호출\n",
1279 | "\n",
1280 | "신경망의 구성 요소를 '계층'으로 구현한 덕분에 신경망을 쉽게 구축\n",
1281 | "\n",
1282 | "=> 레고 블록을 조립하듯 필요한 만큼 계층을 더 추가하면 됨"
1283 | ]
1284 | },
1285 | {
1286 | "cell_type": "markdown",
1287 | "metadata": {},
1288 | "source": [
1289 | "### 5.7.3 오차역전파법으로 구한 기울기 검증하기\n",
1290 | "\n",
1291 | "수치미분은 느립니다."
1292 | ]
1293 | },
1294 | {
1295 | "cell_type": "code",
1296 | "execution_count": 19,
1297 | "metadata": {
1298 | "collapsed": true
1299 | },
1300 | "outputs": [],
1301 | "source": [
1302 | "from dataset.mnist import load_mnist\n",
1303 | "(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)\n",
1304 | "network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)\n",
1305 | "x_batch = x_train[:3]\n",
1306 | "t_batch = t_train[:3]"
1307 | ]
1308 | },
1309 | {
1310 | "cell_type": "code",
1311 | "execution_count": 20,
1312 | "metadata": {
1313 | "collapsed": false
1314 | },
1315 | "outputs": [
1316 | {
1317 | "name": "stdout",
1318 | "output_type": "stream",
1319 | "text": [
1320 | "1 loop, best of 3: 9.95 s per loop\n"
1321 | ]
1322 | }
1323 | ],
1324 | "source": [
1325 | "%timeit network.numerical_gradient(x_batch, t_batch)"
1326 | ]
1327 | },
1328 | {
1329 | "cell_type": "code",
1330 | "execution_count": 21,
1331 | "metadata": {
1332 | "collapsed": false
1333 | },
1334 | "outputs": [
1335 | {
1336 | "name": "stdout",
1337 | "output_type": "stream",
1338 | "text": [
1339 | "The slowest run took 5.15 times longer than the fastest. This could mean that an intermediate result is being cached.\n",
1340 | "1000 loops, best of 3: 248 µs per loop\n"
1341 | ]
1342 | }
1343 | ],
1344 | "source": [
1345 | "%timeit network.gradient(x_batch, t_batch)"
1346 | ]
1347 | },
1348 | {
1349 | "cell_type": "markdown",
1350 | "metadata": {},
1351 | "source": [
1352 | "수치미분(numerical_gradient) 속도: 9.95초\n",
1353 | "\n",
1354 | "오차역전법(gradient) 속도: 248 µs, 0.000248초\n",
1355 | "\n",
1356 | "약 42,000배 속도차이가 남"
1357 | ]
1358 | },
1359 | {
1360 | "cell_type": "markdown",
1361 | "metadata": {},
1362 | "source": [
1363 | "수치 미분을 오차역전파법을 정확히 구현했는지 확인하기 위해 필요.\n",
1364 | "\n",
1365 | "수치 미분의 이점은 구현하기 쉬움\n",
1366 | "\n",
1367 | "기울기 확인(gradient check): 수치 미분의 결과와 오차역전파법의 결과를 비교하여 오차역전파법을 제대로 구현했는지 검증함."
1368 | ]
1369 | },
1370 | {
1371 | "cell_type": "code",
1372 | "execution_count": 22,
1373 | "metadata": {
1374 | "collapsed": false
1375 | },
1376 | "outputs": [
1377 | {
1378 | "name": "stdout",
1379 | "output_type": "stream",
1380 | "text": [
1381 | "b1:8.3863151124e-13\n",
1382 | "W2:7.71846137622e-13\n",
1383 | "W1:2.16518280464e-13\n",
1384 | "b2:1.20348177257e-10\n"
1385 | ]
1386 | }
1387 | ],
1388 | "source": [
1389 | "# https://github.com/WegraLee/deep-learning-from-scratch/blob/master/ch05/gradient_check.py 참고\n",
1390 | "# coding: utf-8\n",
1391 | "#import sys, os\n",
1392 | "#sys.path.append(os.pardir) # 부모 디렉터리의 파일을 가져올 수 있도록 설정\n",
1393 | "import numpy as np\n",
1394 | "from dataset.mnist import load_mnist\n",
1395 | "\n",
1396 | "# 데이터 읽기\n",
1397 | "(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)\n",
1398 | "\n",
1399 | "network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)\n",
1400 | "\n",
1401 | "x_batch = x_train[:3]\n",
1402 | "t_batch = t_train[:3]\n",
1403 | "\n",
1404 | "grad_numerical = network.numerical_gradient(x_batch, t_batch)\n",
1405 | "grad_backprop = network.gradient(x_batch, t_batch)\n",
1406 | "\n",
1407 | "# 각 가중치의 절대 오차의 평균을 구한다.\n",
1408 | "for key in grad_numerical.keys():\n",
1409 | " diff = np.average( np.abs(grad_backprop[key] - grad_numerical[key]) )\n",
1410 | " print(key + \":\" + str(diff))"
1411 | ]
1412 | },
1413 | {
1414 | "cell_type": "markdown",
1415 | "metadata": {},
1416 | "source": [
1417 | "이 결과는 수치 미분과 오차역전파법으로 구한 기울기의 차이가 매우 작다고 말해줌\n",
1418 | "\n",
1419 | "오차역전법이 실수 없이 구현했다는 믿음이 커짐\n",
1420 | "\n",
1421 | "수치 미분과 오차역전파법의 결과 오차가 0이 되는 일은 드뭄\n",
1422 | "\n",
1423 | "올바르게 구현했다면 0에 아주 가까운 작은 값이 됨"
1424 | ]
1425 | },
1426 | {
1427 | "cell_type": "markdown",
1428 | "metadata": {},
1429 | "source": [
1430 | "### 5.7.4 오차역전파법을 사용한 학습 구현하기"
1431 | ]
1432 | },
1433 | {
1434 | "cell_type": "code",
1435 | "execution_count": 23,
1436 | "metadata": {
1437 | "collapsed": false
1438 | },
1439 | "outputs": [
1440 | {
1441 | "name": "stdout",
1442 | "output_type": "stream",
1443 | "text": [
1444 | "0.163 0.168\n",
1445 | "0.90245 0.9076\n",
1446 | "0.920516666667 0.9231\n",
1447 | "0.93425 0.9368\n",
1448 | "0.944716666667 0.9424\n",
1449 | "0.950316666667 0.9467\n",
1450 | "0.9546 0.9516\n",
1451 | "0.9601 0.9568\n",
1452 | "0.963266666667 0.9575\n",
1453 | "0.964683333333 0.9588\n",
1454 | "0.968233333333 0.961\n",
1455 | "0.968616666667 0.9615\n",
1456 | "0.970616666667 0.9637\n",
1457 | "0.973433333333 0.9678\n",
1458 | "0.976033333333 0.9686\n",
1459 | "0.976383333333 0.968\n",
1460 | "0.977083333333 0.9709\n"
1461 | ]
1462 | }
1463 | ],
1464 | "source": [
1465 | "# coding: utf-8\n",
1466 | "#import sys, os\n",
1467 | "#sys.path.append(os.pardir)\n",
1468 | "import numpy as np\n",
1469 | "from dataset.mnist import load_mnist\n",
1470 | "#from two_layer_net import TwoLayerNet\n",
1471 | "\n",
1472 | "# 데이터 읽기\n",
1473 | "(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)\n",
1474 | "\n",
1475 | "network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)\n",
1476 | "\n",
1477 | "iters_num = 10000\n",
1478 | "train_size = x_train.shape[0]\n",
1479 | "batch_size = 100\n",
1480 | "learning_rate = 0.1\n",
1481 | "\n",
1482 | "train_loss_list = []\n",
1483 | "train_acc_list = []\n",
1484 | "test_acc_list = []\n",
1485 | "\n",
1486 | "iter_per_epoch = max(train_size / batch_size, 1)\n",
1487 | "\n",
1488 | "for i in range(iters_num):\n",
1489 | " batch_mask = np.random.choice(train_size, batch_size)\n",
1490 | " x_batch = x_train[batch_mask]\n",
1491 | " t_batch = t_train[batch_mask]\n",
1492 | " \n",
1493 | " # 기울기 계산\n",
1494 | " #grad = network.numerical_gradient(x_batch, t_batch) # 수치 미분 방식\n",
1495 | " grad = network.gradient(x_batch, t_batch) # 오차역전파법 방식(훨씬 빠르다)\n",
1496 | " \n",
1497 | " # 갱신\n",
1498 | " for key in ('W1', 'b1', 'W2', 'b2'):\n",
1499 | " network.params[key] -= learning_rate * grad[key]\n",
1500 | " \n",
1501 | " loss = network.loss(x_batch, t_batch)\n",
1502 | " train_loss_list.append(loss)\n",
1503 | " \n",
1504 | " if i % iter_per_epoch == 0:\n",
1505 | " train_acc = network.accuracy(x_train, t_train)\n",
1506 | " test_acc = network.accuracy(x_test, t_test)\n",
1507 | " train_acc_list.append(train_acc)\n",
1508 | " test_acc_list.append(test_acc)\n",
1509 | " print(train_acc, test_acc)"
1510 | ]
1511 | },
1512 | {
1513 | "cell_type": "markdown",
1514 | "metadata": {},
1515 | "source": [
1516 | "## 5.8 정리\n",
1517 | "\n",
1518 | "계산 그래프를 이용하여 신경망의 동작과 오차역전파법을 설명\n",
1519 | "\n",
1520 | "모든 계층에서 forward와 backward 메서드를 구현\n",
1521 | "\n",
1522 | "forward는 데이터를 순방향으로 backward는 역방향으로 전파함\n",
1523 | "\n",
1524 | "가중치 매개변수의 기울기를 효율적으로 구할 수 있음"
1525 | ]
1526 | },
1527 | {
1528 | "cell_type": "markdown",
1529 | "metadata": {},
1530 | "source": [
1531 | "**이번 장에서 배운 것**\n",
1532 | "\n",
1533 | "계산그래프를 이용하면 계산 과정을 시각적으로 파악 가능\n",
1534 | "\n",
1535 | "계산그래프 노드는 국소적 계산으로 구성. 국소적 계산을 조합해 전체 계산을 구성\n",
1536 | "\n",
1537 | "순전파는 통상의 계산을 수행. 역전파는 노드의 미분을 구함\n",
1538 | "\n",
1539 | "오차역전파법: 신경망의 구성 요소를 계층으로 구현하여 기울기를 효율적으로 계산\n",
1540 | "\n",
1541 | "기울기 확인: 수치 미분과 오차역전파법의 결과를 비교하면 오차역전파법 구현에 잘못이 없는지 확인가능"
1542 | ]
1543 | }
1544 | ],
1545 | "metadata": {
1546 | "anaconda-cloud": {},
1547 | "kernelspec": {
1548 | "display_name": "Python [conda root]",
1549 | "language": "python",
1550 | "name": "conda-root-py"
1551 | },
1552 | "language_info": {
1553 | "codemirror_mode": {
1554 | "name": "ipython",
1555 | "version": 3
1556 | },
1557 | "file_extension": ".py",
1558 | "mimetype": "text/x-python",
1559 | "name": "python",
1560 | "nbconvert_exporter": "python",
1561 | "pygments_lexer": "ipython3",
1562 | "version": "3.5.2"
1563 | }
1564 | },
1565 | "nbformat": 4,
1566 | "nbformat_minor": 0
1567 | }
1568 |
--------------------------------------------------------------------------------
/5장.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# 밑바닥부터 시작하는 딥러닝\n",
8 | "\n",
9 | "# Deep Learning from Scratch\n",
10 | "\n",
11 | "## Github \n",
12 | "\n",
13 | "https://github.com/WegraLee/deep-learning-from-scratch\n",
14 | "\n",
15 | "## 목차\n",
16 | "\n",
17 | "http://nbviewer.jupyter.org/github/SDRLurker/deep-learning/blob/master/%EB%AA%A9%EC%B0%A8.ipynb"
18 | ]
19 | },
20 | {
21 | "cell_type": "markdown",
22 | "metadata": {},
23 | "source": [
24 | "# 5 오차역전파법\n",
25 | "\n",
26 | "오차역전파법(backpropagation): 가중치 매개변수의 기울기를 효율적으로 계산\n",
27 | "\n",
28 | "오차를 역(반대 방향)으로 전파하는 방법(backward propagation of errors)\n",
29 | "\n",
30 | "안드레 카패시(Andrej Karpathy)의 블로그\n",
31 | "\n",
32 | "* 참고주소 : http://karpathy.github.io/neuralnets\n",
33 | "\n",
34 | "* 오차역전파법을 계산 그래프로 설명\n",
35 | "\n",
36 | "페이페이 리(Fei-Fei Li) 교수가 진행한 스탠퍼드 대학교 딥러닝 수업 CS231n 참고\n",
37 | "\n",
38 | "* 참고주소 : http://cs231n.github.io"
39 | ]
40 | },
41 | {
42 | "cell_type": "markdown",
43 | "metadata": {},
44 | "source": [
45 | "## 5.1 계산 그래프\n",
46 | "\n",
47 | "계산 그래프(computational graph): 계산 과정을 그래프로 나타낸 것\n",
48 | "\n",
49 | "복수의 노드(node)와 에지(edge)로 표현됨.\n",
50 | "\n",
51 | "에지: 노드 사이의 직선"
52 | ]
53 | },
54 | {
55 | "cell_type": "markdown",
56 | "metadata": {},
57 | "source": [
58 | "### 5.1.1 계산 그래프로 풀다\n",
59 | "\n",
60 | "계산 그래프를 이용한 문제풀이는 다음 흐름으로 진행\n",
61 | "\n",
62 | "* 계산 그래프를 구성한다.\n",
63 | "* 그래프에서 계산을 왼쪽에서 오른쪽으로 진행한다.\n",
64 | "\n",
65 | "순전파: 계산을 왼쪽에서 오른쪽으로 진행. 계산 그래프의 출발점부터 종착점으로의 전파."
66 | ]
67 | },
68 | {
69 | "cell_type": "markdown",
70 | "metadata": {},
71 | "source": [
72 | "### 5.1.2 국소적 계산\n",
73 | "\n",
74 | "국소적: 자신과 직접 관련된 작은 범위\n",
75 | "\n",
76 | "국소적 계산: 자신과 관계된 정보만으로 다음 결과를 출력할 수 있음\n",
77 | "\n",
78 | "각 노드는 자신과 관련된 계산 외에는 아무 것도 신경 쓸게 없음\n",
79 | "\n",
80 | "복잡한 계산을 '단순하고 국소적 계산'으로 분할하고 계산 결과를 다음 노드로 전달\n",
81 | "\n",
82 | "복잡한 계산도 분해하면 단순한 계산으로 구성됨"
83 | ]
84 | },
85 | {
86 | "cell_type": "markdown",
87 | "metadata": {},
88 | "source": [
89 | "### 5.1.3 왜 계산 그래프로 푸는가?\n",
90 | "\n",
91 | "역전파를 통해 '미분'을 효율적으로 계산할 수 있음\n",
92 | "\n",
93 | "중간까지 구한 미분 결과를 공유할 수 있어 다수의 미분을 효율적으로 계산할 수 있음"
94 | ]
95 | },
96 | {
97 | "cell_type": "markdown",
98 | "metadata": {},
99 | "source": [
100 | "## 5.2 연쇄법칙\n",
101 | "\n",
102 | "'국소적 미분'을 전달하는 원리는 연쇄 법칙(chain rule)에 따른 것"
103 | ]
104 | },
105 | {
106 | "cell_type": "markdown",
107 | "metadata": {},
108 | "source": [
109 | "### 5.2.1 계산 그래프의 역전파\n",
110 | "\n",
111 | "계산 그래프의 역전파: 순방향과는 반대 방향으로 국소적 미분을 곱한다.\n",
112 | "\n",
113 | "역전파의 계산 절차는 신호 E에 노드의 국소적 미분을 곱한 후 다음 노드로 전달\n",
114 | "\n",
115 | "역전파의 계산 순에 따르면 목표로 하는 미분 값을 효율적으로 구할 수 있음"
116 | ]
117 | },
118 | {
119 | "cell_type": "markdown",
120 | "metadata": {},
121 | "source": [
122 | "### 5.2.2 연쇄법칙이란?\n",
123 | "\n",
124 | "합성 함수: 여러 함수로 구성된 함수\n",
125 | "\n",
126 | "#### 식 5.1\n",
127 | "\n",
128 | "\\begin{equation*}\n",
129 | "z = t^{2}\n",
130 | "\\end{equation*}\n",
131 | "\n",
132 | "\\begin{equation*}\n",
133 | "t = x + y\n",
134 | "\\end{equation*}\n",
135 | "\n",
136 | "연쇄법칙은 함성 함수의 미분에 대한 성질\n",
137 | "\n",
138 | "합성 함수의 미분은 합성 함수를 구성하는 각 함수의 미분의 곱으로 나타낼 수 있다.\n",
139 | "\n",
140 | "#### 식 5.2\n",
141 | "\n",
142 | "\\begin{equation*}\n",
143 | "\\frac{\\partial z}{\\partial x} = \\frac{\\partial z}{\\partial t} \\frac{\\partial t}{\\partial x}\n",
144 | "\\end{equation*}\n",
145 | "\n",
146 | "x에 대한 z의 미분은 t에 대한 z의 미분과 x에 대한 t의 미분의 곱으로 나타낼 수 있음\n",
147 | "\n",
148 | "∂t를 서로 지울 수 있음.\n",
149 | "\n",
150 | "\\begin{equation*}\n",
151 | "\\frac{\\partial z}{\\partial x} = \\frac{\\partial z}{} \\frac{}{\\partial x}\n",
152 | "\\end{equation*}\n",
153 | "\n",
154 | "#### 식 5.3\n",
155 | "\n",
156 | "식 5.1에 대한 국소적 미분(편미분)을 구함\n",
157 | "\n",
158 | "\\begin{equation*}\n",
159 | "\\frac{\\partial z}{\\partial t} = 2t\n",
160 | "\\end{equation*}\n",
161 | "\n",
162 | "\\begin{equation*}\n",
163 | "\\frac{\\partial t}{\\partial x} = 1\n",
164 | "\\end{equation*}\n",
165 | "\n",
166 | "최종적으로 구하고 싶은 x에 대한 z의 미분은 다음 두 미분을 곱해 계산\n",
167 | "\n",
168 | "#### 식 5.4\n",
169 | "\n",
170 | "\\begin{equation*}\n",
171 | "\\frac{\\partial z}{\\partial x} = \\frac{\\partial z}{\\partial t} \\frac{\\partial t}{\\partial x} = 2t · 1 = 2(x+y)\n",
172 | "\\end{equation*}"
173 | ]
174 | },
175 | {
176 | "cell_type": "markdown",
177 | "metadata": {
178 | "collapsed": true
179 | },
180 | "source": [
181 | "### 5.2.3 연쇄법칙과 계산 그래프\n",
182 | "\n",
183 | "계산 그래프의 역전파는 오른쪽에서 왼쪽으로 신호를 전파\n",
184 | "\n",
185 | "노드로 들어온 입력신호에 그 노드의 국소적 미분(편미분)을 곱한 후 다음 노드로 전달\n",
186 | "\n",
187 | "역전파가 하는 일은 연쇄 법칙의 원리와 같음."
188 | ]
189 | },
190 | {
191 | "cell_type": "markdown",
192 | "metadata": {},
193 | "source": [
194 | "## 5.3 역전파"
195 | ]
196 | },
197 | {
198 | "cell_type": "markdown",
199 | "metadata": {},
200 | "source": [
201 | "### 5.3.1 덧셈 노드의 역전파\n",
202 | "\n",
203 | "z = x + y 의 미분. 다음은 해석적으로 계산\n",
204 | "\n",
205 | "#### 식 5.5\n",
206 | "\n",
207 | "\\begin{equation*}\n",
208 | "\\frac{\\partial z}{\\partial x} = 1\n",
209 | "\\end{equation*}\n",
210 | "\n",
211 | "\\begin{equation*}\n",
212 | "\\frac{\\partial z}{\\partial y} = 1\n",
213 | "\\end{equation*}\n",
214 | "\n",
215 | "덧셈 노드의 역전파는 1을 곱하기만 할 뿐 입력된 값을 그대로 다음 노드로 보내게 됨."
216 | ]
217 | },
218 | {
219 | "cell_type": "markdown",
220 | "metadata": {},
221 | "source": [
222 | "### 5.3.2 곱셈 노드의 역전파\n",
223 | "\n",
224 | "z = xy 의 미분\n",
225 | "\n",
226 | "#### 식 5.6\n",
227 | "\n",
228 | "\\begin{equation*}\n",
229 | "\\frac{\\partial z}{\\partial x} = y\n",
230 | "\\end{equation*}\n",
231 | "\n",
232 | "\\begin{equation*}\n",
233 | "\\frac{\\partial z}{\\partial y} = x\n",
234 | "\\end{equation*}\n",
235 | "\n",
236 | "곱셈 노드의 역전파는 상류의 값에 순전파 때의 입력 신호들을 '서로 바꾼 값'을 곱해서 하류로 보냄\n",
237 | "\n",
238 | "순전파 때 x 였다면 역전파에서는 y. 순전파 때 y 였다면 역전파에서는 x로 바꿈"
239 | ]
240 | },
241 | {
242 | "cell_type": "markdown",
243 | "metadata": {},
244 | "source": [
245 | "### 5.3.3 사과 쇼핑의 예"
246 | ]
247 | },
248 | {
249 | "cell_type": "markdown",
250 | "metadata": {},
251 | "source": [
252 | "## 5.4 단순한 계층 구현하기\n",
253 | "\n",
254 | "계산 그래프의 곱셈 노드를 'MultiLayer', 덧셈 노드를 'AddLayer'로 구현"
255 | ]
256 | },
257 | {
258 | "cell_type": "markdown",
259 | "metadata": {},
260 | "source": [
261 | "### 5.4.1 곱셈 계층\n",
262 | "\n",
263 | "모든 계층은 forward() 순전파, backward() 역전파 라는 공통의 메서드(인터페이스)를 갖도록 구현\n",
264 | "\n",
265 | "곱셈 계층을 MultiLayer 클래스로 다음처럼 구현"
266 | ]
267 | },
268 | {
269 | "cell_type": "code",
270 | "execution_count": 1,
271 | "metadata": {
272 | "collapsed": true
273 | },
274 | "outputs": [],
275 | "source": [
276 | "# https://github.com/WegraLee/deep-learning-from-scratch/blob/master/ch05/layer_naive.py 소스 참고\n",
277 | "class MulLayer:\n",
278 | " def __init__(self):\n",
279 | " self.x = None\n",
280 | " self.y = None\n",
281 | " \n",
282 | " def forward(self, x, y):\n",
283 | " self.x = x\n",
284 | " self.y = y\n",
285 | " out = x * y\n",
286 | " \n",
287 | " return out\n",
288 | " \n",
289 | " def backward(self, dout):\n",
290 | " dx = dout * self.y # x와 y를 바꾼다.\n",
291 | " dy = dout * self.x \n",
292 | " \n",
293 | " return dx, dy"
294 | ]
295 | },
296 | {
297 | "cell_type": "markdown",
298 | "metadata": {},
299 | "source": [
300 | "\\__init\\__() : 인스턴스 변수인 x와 y를 초기화. 순전파 시 입력 값을 유지하기 위해 사용.\n",
301 | "\n",
302 | "forward() : x와 y를 인수로 받고 두 값을 곱해 반환\n",
303 | "\n",
304 | "backward() : 상류에서 넘어온 미분(dout)에 순전파 때 값을 '서로 바꿔' 곱한 후 하류로 흘림.\n",
305 | "\n",
306 | "MultiLayer를 사용하여 순전파 구현"
307 | ]
308 | },
309 | {
310 | "cell_type": "code",
311 | "execution_count": 2,
312 | "metadata": {
313 | "collapsed": false
314 | },
315 | "outputs": [
316 | {
317 | "name": "stdout",
318 | "output_type": "stream",
319 | "text": [
320 | "220.00000000000003\n"
321 | ]
322 | }
323 | ],
324 | "source": [
325 | "# https://github.com/WegraLee/deep-learning-from-scratch/blob/master/ch05/buy_apple.py 소스 참고\n",
326 | "apple = 100\n",
327 | "apple_num = 2\n",
328 | "tax = 1.1\n",
329 | "\n",
330 | "# 계층들\n",
331 | "mul_apple_layer = MulLayer()\n",
332 | "mul_tax_layer = MulLayer()\n",
333 | "\n",
334 | "# 순전파\n",
335 | "apple_price = mul_apple_layer.forward(apple, apple_num)\n",
336 | "price = mul_tax_layer.forward(apple_price, tax)\n",
337 | "\n",
338 | "print(price) # 220"
339 | ]
340 | },
341 | {
342 | "cell_type": "markdown",
343 | "metadata": {},
344 | "source": [
345 | "각 변수에 대한 미분은 backward()로 구할 수 있음"
346 | ]
347 | },
348 | {
349 | "cell_type": "code",
350 | "execution_count": 3,
351 | "metadata": {
352 | "collapsed": false
353 | },
354 | "outputs": [
355 | {
356 | "name": "stdout",
357 | "output_type": "stream",
358 | "text": [
359 | "2.2 110.00000000000001 200\n"
360 | ]
361 | }
362 | ],
363 | "source": [
364 | "dprice = 1\n",
365 | "dapple_price, dtax = mul_tax_layer.backward(dprice)\n",
366 | "dapple, dapple_num = mul_apple_layer.backward(dapple_price)\n",
367 | "\n",
368 | "print(dapple, dapple_num, dtax) # 2.2 110 200"
369 | ]
370 | },
371 | {
372 | "cell_type": "markdown",
373 | "metadata": {},
374 | "source": [
375 | "backward() 호출 순서는 forward() 때와 반대\n",
376 | "\n",
377 | "backward()가 받는 인수는 '순전파의 출력에 대한 미분'"
378 | ]
379 | },
380 | {
381 | "cell_type": "markdown",
382 | "metadata": {},
383 | "source": [
384 | "### 5.4.2 덧셈 계층\n",
385 | "\n",
386 | "모든 계층은 forward() 순전파, backward() 역전파 라는 공통의 메서드(인터페이스)를 갖도록 구현\n",
387 | "\n",
388 | "덧셈 계층을 MultiLayer 클래스"
389 | ]
390 | },
391 | {
392 | "cell_type": "code",
393 | "execution_count": 4,
394 | "metadata": {
395 | "collapsed": true
396 | },
397 | "outputs": [],
398 | "source": [
399 | "class AddLayer:\n",
400 | " def __init__(self):\n",
401 | " pass\n",
402 | " \n",
403 | " def forward(self, x, y):\n",
404 | " out = x + y\n",
405 | " return out\n",
406 | "\n",
407 | " def backward(self, dout):\n",
408 | " dx = dout * 1\n",
409 | " dy = dout * 1\n",
410 | " return dx, dy"
411 | ]
412 | },
413 | {
414 | "cell_type": "markdown",
415 | "metadata": {},
416 | "source": [
417 | "\\__init\\__() : pass를 통해 아무 일도 하지 않음\n",
418 | "\n",
419 | "forward() : x와 y를 인수로 받고 두 값을 더해 반환\n",
420 | "\n",
421 | "backward() : 상류에서 넘어온 미분(dout)을 그대로 하류로 흘림\n",
422 | "\n",
423 | "그림 5-17의 계산 그래프 파이썬 구현"
424 | ]
425 | },
426 | {
427 | "cell_type": "code",
428 | "execution_count": 5,
429 | "metadata": {
430 | "collapsed": false
431 | },
432 | "outputs": [
433 | {
434 | "name": "stdout",
435 | "output_type": "stream",
436 | "text": [
437 | "price: 715\n",
438 | "dApple: 2.2\n",
439 | "dApple_num: 110\n",
440 | "dOrange: 3.3000000000000003\n",
441 | "dOrange_num: 165\n",
442 | "dTax: 650\n"
443 | ]
444 | }
445 | ],
446 | "source": [
447 | "# https://github.com/WegraLee/deep-learning-from-scratch/blob/master/ch05/buy_apple.py 소스 참고\n",
448 | "apple = 100\n",
449 | "apple_num = 2\n",
450 | "orange = 150\n",
451 | "orange_num = 3\n",
452 | "tax = 1.1\n",
453 | "\n",
454 | "# 계층들\n",
455 | "mul_apple_layer = MulLayer()\n",
456 | "mul_orange_layer = MulLayer()\n",
457 | "add_apple_orange_layer = AddLayer()\n",
458 | "mul_tax_layer = MulLayer()\n",
459 | "\n",
460 | "# 순전파\n",
461 | "apple_price = mul_apple_layer.forward(apple, apple_num) # (1)\n",
462 | "orange_price = mul_orange_layer.forward(orange, orange_num) # (2)\n",
463 | "all_price = add_apple_orange_layer.forward(apple_price, orange_price) # (3)\n",
464 | "price = mul_tax_layer.forward(all_price, tax) # (4)\n",
465 | "\n",
466 | "# 역전파\n",
467 | "dprice = 1\n",
468 | "dall_price, dtax = mul_tax_layer.backward(dprice) # (4)\n",
469 | "dapple_price, dorange_price = add_apple_orange_layer.backward(dall_price) # (3)\n",
470 | "dorange, dorange_num = mul_orange_layer.backward(dorange_price) # (2)\n",
471 | "dapple, dapple_num = mul_apple_layer.backward(dapple_price) # (1)\n",
472 | "\n",
473 | "print(\"price:\", int(price)) # 715\n",
474 | "print(\"dApple:\", dapple) # 2.2\n",
475 | "print(\"dApple_num:\", int(dapple_num)) # 110\n",
476 | "print(\"dOrange:\", dorange) # 3.3\n",
477 | "print(\"dOrange_num:\", int(dorange_num)) # 165\n",
478 | "print(\"dTax:\", dtax) # 650"
479 | ]
480 | },
481 | {
482 | "cell_type": "markdown",
483 | "metadata": {
484 | "collapsed": true
485 | },
486 | "source": [
487 | "## 5.5 활성화 함수 계층 구현하기\n",
488 | "\n",
489 | "활성화 함수인 ReLU와 Sigmoid 계층을 구현"
490 | ]
491 | },
492 | {
493 | "cell_type": "markdown",
494 | "metadata": {},
495 | "source": [
496 | "### 5.5.1 ReLU 계층\n",
497 | "\n",
498 | "#### 식 5.7 ReLU 식\n",
499 | "\n",
500 | "\\begin{equation*}\n",
501 | "y = x ( x > 0 )\n",
502 | "\\end{equation*}\n",
503 | "\n",
504 | "\\begin{equation*}\n",
505 | "y = 0 ( x <= 0 )\n",
506 | "\\end{equation*}\n",
507 | "\n",
508 | "#### 식 5.8 ReLU x에 대한 y 미분 식\n",
509 | "\n",
510 | "\\begin{equation*}\n",
511 | "\\frac{\\partial y}{\\partial x} = 1 ( x > 0 )\n",
512 | "\\end{equation*}\n",
513 | "\n",
514 | "\\begin{equation*}\n",
515 | "\\frac{\\partial y}{\\partial x} = 0 ( x <= 0 )\n",
516 | "\\end{equation*}\n",
517 | "\n",
518 | "순전파 때 입력인 x가 0보다 크면 역전파는 상류의 값을 그대로 하류로 흘림\n",
519 | "\n",
520 | "순전파 때 x가 0 이하면 역전파 때는 하류로 신호를 보내지 않음\n",
521 | "\n",
522 | "ReLU 계층을 구현한 코드"
523 | ]
524 | },
525 | {
526 | "cell_type": "code",
527 | "execution_count": 6,
528 | "metadata": {
529 | "collapsed": true
530 | },
531 | "outputs": [],
532 | "source": [
533 | "# https://github.com/WegraLee/deep-learning-from-scratch/blob/master/common/layers.py 소스 참고\n",
534 | "class Relu:\n",
535 | " def __init__(self):\n",
536 | " self.mask = None\n",
537 | " \n",
538 | " def forward(self, x):\n",
539 | " self.mask = (x <= 0)\n",
540 | " out = x.copy()\n",
541 | " out[self.mask] = 0\n",
542 | " \n",
543 | " return out\n",
544 | " \n",
545 | " def backward(self, dout):\n",
546 | " dout[self.mask] = 0\n",
547 | " dx = dout\n",
548 | " \n",
549 | " return dx"
550 | ]
551 | },
552 | {
553 | "cell_type": "markdown",
554 | "metadata": {},
555 | "source": [
556 | "Relu 클래스는 mask 인스턴스 변수를 가짐\n",
557 | "\n",
558 | "mask는 순전파의 입력인 x의 원소 값이 0 이하인 인덱스는 True, 그 외(0보다 큰 원소)는 False로 유지"
559 | ]
560 | },
561 | {
562 | "cell_type": "code",
563 | "execution_count": 7,
564 | "metadata": {
565 | "collapsed": false
566 | },
567 | "outputs": [
568 | {
569 | "name": "stdout",
570 | "output_type": "stream",
571 | "text": [
572 | "[[ 1. 0.5]\n",
573 | " [-2. 3. ]]\n"
574 | ]
575 | }
576 | ],
577 | "source": [
578 | "import numpy as np\n",
579 | "x = np.array([[1.0, 0.5], [-2.0, 3.0]])\n",
580 | "print(x)"
581 | ]
582 | },
583 | {
584 | "cell_type": "code",
585 | "execution_count": 8,
586 | "metadata": {
587 | "collapsed": false
588 | },
589 | "outputs": [
590 | {
591 | "name": "stdout",
592 | "output_type": "stream",
593 | "text": [
594 | "[[False False]\n",
595 | " [ True False]]\n"
596 | ]
597 | }
598 | ],
599 | "source": [
600 | "mask = (x <= 0)\n",
601 | "print(mask)"
602 | ]
603 | },
604 | {
605 | "cell_type": "code",
606 | "execution_count": 9,
607 | "metadata": {
608 | "collapsed": false
609 | },
610 | "outputs": [
611 | {
612 | "data": {
613 | "text/plain": [
614 | "array([[ 1. , 0.5],\n",
615 | " [ 0. , 3. ]])"
616 | ]
617 | },
618 | "execution_count": 9,
619 | "metadata": {},
620 | "output_type": "execute_result"
621 | }
622 | ],
623 | "source": [
624 | "out = x.copy()\n",
625 | "out[mask] = 0\n",
626 | "out"
627 | ]
628 | },
629 | {
630 | "cell_type": "markdown",
631 | "metadata": {},
632 | "source": [
633 | "ReLU 계층은 전기 회로의 '스위치'에 비유\n",
634 | "\n",
635 | "순전파 때 전류가 흐르고 있으면 스위치를 ON, 흐르지 않으면 OFF\n",
636 | "\n",
637 | "역전파 때 스위치가 ON이라면 전류가 그대로 흐르고, OFF면 더 이상 흐르지 않음"
638 | ]
639 | },
640 | {
641 | "cell_type": "markdown",
642 | "metadata": {},
643 | "source": [
644 | "### 5.5.2 Sigmoid 계층\n",
645 | "\n",
646 | "#### 식 5.9 시그모이드 함수\n",
647 | "\n",
648 | "\\begin{equation*}\n",
649 | "y = \\frac{1}{1 + exp(-x)}\n",
650 | "\\end{equation*}\n",
651 | "\n",
652 | "**1단계** '/' 노드, y = 1 / x를 미분하면 다음식이 됨\n",
653 | "\n",
654 | "#### 식 5.10\n",
655 | "\n",
656 | "\\begin{equation*}\n",
657 | "\\frac{\\partial y}{\\partial x} = -\\frac{1}{x^{2}}\n",
658 | "\\end{equation*}\n",
659 | "\n",
660 | "\\begin{equation*}\n",
661 | "= - y^{2}\n",
662 | "\\end{equation*}\n",
663 | "\n",
664 | "역전파 때는 상류의 예측값에 -y\\**2 을 곱해서 하류로 전달\n",
665 | "\n",
666 | "**2단계** 상류의 값을 여과 없이 하류로 보냄\n",
667 | "\n",
668 | "**3단계** y = exp(x) 연산을 수행\n",
669 | "\n",
670 | "#### 식 5.11\n",
671 | "\n",
672 | "\\begin{equation*}\n",
673 | "\\frac{\\partial y}{\\partial x} = exp(x)\n",
674 | "\\end{equation*}\n",
675 | "\n",
676 | "계산 그래프에서는 상류의 순전파 때의 출력(exp(-x))을 곱해 하류로 전파\n",
677 | "\n",
678 | "**4단계** y = exp(x) 연산을 수행\n",
679 | "\n",
680 | "'X' 노드, 순전파 때의 값을 서로 바꿔 곱함. 이 예에서는 -1을 곱함\n",
681 | "\n",
682 | "시그모이드 간소화버전\n",
683 | "\n",
684 | "노드를 그룹화하여 Sigmoid 계층의 세세한 내용을 노출하지 않고 입력과 출력에만 집중\n",
685 | "\n",
686 | "\\begin{equation*}\n",
687 | "\\frac{\\partial L}{\\partial y} y^{2} exp(-x) = \\frac{\\partial L}{\\partial y} \\frac{1} { (1+exp(-x))^{2}} exp(-x)\n",
688 | "\\end{equation*}\n",
689 | "\n",
690 | "\\begin{equation*}\n",
691 | "= \\frac{\\partial L}{\\partial y} \\frac{1} { 1+exp(-x)} \\frac{exp(-x)} {1+exp(-x)}\n",
692 | "\\end{equation*}\n",
693 | "\n",
694 | "\\begin{equation*}\n",
695 | "= \\frac{\\partial L}{\\partial y} y (1-y)\n",
696 | "\\end{equation*}\n",
697 | "\n",
698 | "Sigmoid 계층의 계산 그래프: 순전파의 출력 y만으로 역전파를 계산\n",
699 | "\n",
700 | "Sigmoid 계층을 파이썬으로 구현"
701 | ]
702 | },
703 | {
704 | "cell_type": "code",
705 | "execution_count": 10,
706 | "metadata": {
707 | "collapsed": true
708 | },
709 | "outputs": [],
710 | "source": [
711 | "class Sigmoid:\n",
712 | " def __init__(self):\n",
713 | " self.out = None\n",
714 | " \n",
715 | " def forward(self, x):\n",
716 | " out = 1 / (1 + np.exp(-x))\n",
717 | " self.out = out\n",
718 | " \n",
719 | " return out\n",
720 | " \n",
721 | " def backward(self, dout):\n",
722 | " dx = dout * (1.0 - self.out) * self.out\n",
723 | " \n",
724 | " return dx"
725 | ]
726 | },
727 | {
728 | "cell_type": "markdown",
729 | "metadata": {
730 | "collapsed": true
731 | },
732 | "source": [
733 | "## 5.6 Affine/Softmax 계층 구현하기\n",
734 | "\n",
735 | "### 5.6.1 Affine 계층\n",
736 | "\n",
737 | "신경망의 순전파에서는 가중치 신호의 총합을 계산하기 위해 행렬의 내적(np.dot())을 사용"
738 | ]
739 | },
740 | {
741 | "cell_type": "code",
742 | "execution_count": 11,
743 | "metadata": {
744 | "collapsed": false
745 | },
746 | "outputs": [
747 | {
748 | "name": "stdout",
749 | "output_type": "stream",
750 | "text": [
751 | "(2,)\n",
752 | "(2, 3)\n",
753 | "(3,)\n"
754 | ]
755 | }
756 | ],
757 | "source": [
758 | "X = np.random.rand(2) # 입력\n",
759 | "W = np.random.rand(2,3) # 가중치\n",
760 | "B = np.random.rand(3) # 편향\n",
761 | "\n",
762 | "print(X.shape) # (2,)\n",
763 | "print(W.shape) # (2, 3)\n",
764 | "print(B.shape) # (3,)\n",
765 | "\n",
766 | "Y = np.dot(X, W) + B"
767 | ]
768 | },
769 | {
770 | "cell_type": "markdown",
771 | "metadata": {},
772 | "source": [
773 | "X와 W의 내적은 대응하는 차원의 원소 수를 일치 시켜야 함\n",
774 | "\n",
775 | "어파인 변환(affine transformation): 신경망의 순전파 때 수행하는 행렬의 내적. 기하학 용어\n",
776 | "\n",
777 | "이 계산 그래프는 '행렬'이 흐름\n",
778 | "\n",
779 | "#### 식 5.13 행렬을 사용한 역전파 전개식\n",
780 | "\n",
781 | "\\begin{equation*}\n",
782 | "\\frac{\\partial L}{\\partial X} = \\frac{\\partial L}{\\partial Y} W^{T}\n",
783 | "\\end{equation*}\n",
784 | "\n",
785 | "\\begin{equation*}\n",
786 | "\\frac{\\partial L}{\\partial W} = X^{T} \\frac{\\partial L}{\\partial Y}\n",
787 | "\\end{equation*}\n",
788 | "\n",
789 | "전치행렬 : W의 (i,j) 위치의 원소를 (j,i) 위치로 변경\n",
790 | "\n",
791 | "#### 식 5.14 전치행렬 수식\n",
792 | "\n",
793 | "\\begin{equation*}\n",
794 | "W = \\begin{vmatrix}\n",
795 | "w_{11} w_{21} w_{31}\\\\\n",
796 | "w_{12} w_{22} w_{32}\\\n",
797 | "\\end{vmatrix}\n",
798 | "\\end{equation*}\n",
799 | "\n",
800 | "\\begin{equation*}\n",
801 | "W^{T} = \\begin{vmatrix}\n",
802 | "w_{11} w_{12}\\\\\n",
803 | "w_{21} w_{22}\\\\\n",
804 | "w_{31} w_{32}\\\n",
805 | "\\end{vmatrix}\n",
806 | "\\end{equation*}\n",
807 | "\n",
808 | "W의 형상이 (2,3) 이면 W.T의 형상은 (3,2)\n",
809 | "\n",
810 | "#### 그림 5.25 Affine 계층의 역전파: 역전파에서의 변수 형상은 해당 변수명 옆에 표기\n",
811 | "\n",
812 | "\\begin{equation*}\n",
813 | "\\frac{\\partial L}{\\partial X}(2,) = \\frac{\\partial L}{\\partial Y}(3,) W^{T} (3,2)\n",
814 | "\\end{equation*}\n",
815 | "\n",
816 | "\\begin{equation*}\n",
817 | "X(2,) 와 \\frac{\\partial L}{\\partial X}(2,) 은 같은 형상\n",
818 | "\\end{equation*}\n",
819 | "\n",
820 | "\\begin{equation*}\n",
821 | "\\frac{\\partial L}{\\partial W}(2,3) = X^{T}(2,1) \\frac{\\partial L}{\\partial Y} (1,3)\n",
822 | "\\end{equation*}\n",
823 | "\n",
824 | "\\begin{equation*}\n",
825 | "W(2,3) 와 \\frac{\\partial L}{\\partial W}(2,3) 은 같은 형상\n",
826 | "\\end{equation*}"
827 | ]
828 | },
829 | {
830 | "cell_type": "markdown",
831 | "metadata": {},
832 | "source": [
833 | "### 5.6.2 배치용 Affine 계층\n",
834 | "\n",
835 | "#### 그림 5-27 배치용 Affine 계층의 계산 그래프\n",
836 | "\n",
837 | "\\begin{equation*}\n",
838 | "\\frac{\\partial L}{\\partial X}(N,2) = \\frac{\\partial L}{\\partial Y}(N,3) W^{T} (3,2)\n",
839 | "\\end{equation*}\n",
840 | "\n",
841 | "\\begin{equation*}\n",
842 | "\\frac{\\partial L}{\\partial W}(2,3) = X^{T}(2,N) \\frac{\\partial L}{\\partial Y} (N,3)\n",
843 | "\\end{equation*}\n",
844 | "\n",
845 | "\\begin{equation*}\n",
846 | "\\frac{\\partial L}{\\partial B}(3) = \\frac{\\partial L}{\\partial Y} (N,3) 의 첫 번째(제 0축, 열방향)의 합\n",
847 | "\\end{equation*}\n",
848 | "\n",
849 | "기존과 다른 부분은 입력인 X의 형상이 (N,2)가 됨\n",
850 | "\n",
851 | "예를 들어 N=2(데이터가 2개)로 한 경우, 편향은 그 두 데이터 각각에 더해집니다."
852 | ]
853 | },
854 | {
855 | "cell_type": "code",
856 | "execution_count": 12,
857 | "metadata": {
858 | "collapsed": false
859 | },
860 | "outputs": [
861 | {
862 | "data": {
863 | "text/plain": [
864 | "array([[ 0, 0, 0],\n",
865 | " [10, 10, 10]])"
866 | ]
867 | },
868 | "execution_count": 12,
869 | "metadata": {},
870 | "output_type": "execute_result"
871 | }
872 | ],
873 | "source": [
874 | "X_dot_W = np.array([[0, 0, 0], [10, 10, 10]])\n",
875 | "B = np.array([1, 2, 3])\n",
876 | "\n",
877 | "X_dot_W"
878 | ]
879 | },
880 | {
881 | "cell_type": "code",
882 | "execution_count": 13,
883 | "metadata": {
884 | "collapsed": false
885 | },
886 | "outputs": [
887 | {
888 | "data": {
889 | "text/plain": [
890 | "array([[ 1, 2, 3],\n",
891 | " [11, 12, 13]])"
892 | ]
893 | },
894 | "execution_count": 13,
895 | "metadata": {},
896 | "output_type": "execute_result"
897 | }
898 | ],
899 | "source": [
900 | "X_dot_W + B"
901 | ]
902 | },
903 | {
904 | "cell_type": "markdown",
905 | "metadata": {},
906 | "source": [
907 | "순전파의 편향 덧셈은 각각의 데이터(1번째 데이터, 2번째 데이터)에 더해짐\n",
908 | "\n",
909 | "역전파 때는 각 데이터의 역전파 값이 편향의 원소에 모여야 함"
910 | ]
911 | },
912 | {
913 | "cell_type": "code",
914 | "execution_count": 14,
915 | "metadata": {
916 | "collapsed": false
917 | },
918 | "outputs": [
919 | {
920 | "data": {
921 | "text/plain": [
922 | "array([[1, 2, 3],\n",
923 | " [4, 5, 6]])"
924 | ]
925 | },
926 | "execution_count": 14,
927 | "metadata": {},
928 | "output_type": "execute_result"
929 | }
930 | ],
931 | "source": [
932 | "dY = np.array([[1, 2, 3], [4, 5, 6]])\n",
933 | "dY"
934 | ]
935 | },
936 | {
937 | "cell_type": "code",
938 | "execution_count": 15,
939 | "metadata": {
940 | "collapsed": false
941 | },
942 | "outputs": [
943 | {
944 | "data": {
945 | "text/plain": [
946 | "array([5, 7, 9])"
947 | ]
948 | },
949 | "execution_count": 15,
950 | "metadata": {},
951 | "output_type": "execute_result"
952 | }
953 | ],
954 | "source": [
955 | "dB = np.sum(dY, axis=0)\n",
956 | "dB"
957 | ]
958 | },
959 | {
960 | "cell_type": "markdown",
961 | "metadata": {},
962 | "source": [
963 | "np.sum()에서 0번째 축(데이터를 단위로 한 축)에 대해서 (axis=0)의 총합을 구함\n",
964 | "\n",
965 | "Affine 구현\n",
966 | "\n",
967 | "common/layer.py 파일의 Affine 구현은 입력 데이터가 텐서(4차원 데이터)인 경우도 고려. 다음 구현과 약간 차이가 있음."
968 | ]
969 | },
970 | {
971 | "cell_type": "code",
972 | "execution_count": 16,
973 | "metadata": {
974 | "collapsed": true
975 | },
976 | "outputs": [],
977 | "source": [
978 | "class Affine:\n",
979 | " def __init__(self, W, b):\n",
980 | " self.W = W\n",
981 | " self.b = b\n",
982 | " self.x = None\n",
983 | " self.dW = None\n",
984 | " self.db = None\n",
985 | " \n",
986 | " def forward(self, x):\n",
987 | " self.x = x\n",
988 | " out = np.dot(x, self.W) + self.b\n",
989 | " \n",
990 | " return out\n",
991 | " \n",
992 | " def backward(self, dout):\n",
993 | " dx = np.dot(dout, self.W.T)\n",
994 | " self.dW = np.dot(self.x.T, dout)\n",
995 | " self.db = np.sum(dout, axis=0)\n",
996 | " \n",
997 | " return dx"
998 | ]
999 | },
1000 | {
1001 | "cell_type": "markdown",
1002 | "metadata": {},
1003 | "source": [
1004 | "### 5.6.3 Softmax-with-Loss 계층\n",
1005 | "\n",
1006 | "소프트맥스 함수는 입력 값을 정규화하여 출력\n",
1007 | "\n",
1008 | "추론할 때는 일반적으로 Softmax 계층을 사용하지 않음\n",
1009 | "\n",
1010 | "점수(score): Softmax 앞의 Affine 계층의 출력\n",
1011 | "\n",
1012 | "신경망을 학습할 때는 Softmax 계층이 필요\n",
1013 | "\n",
1014 | "소프트맥스 계층 구현: 손실 함수인 교차 엔트로피 오차도 포함하여 'Softmax-with-Loss 계층'이라는 이름으로 구현\n",
1015 | "\n",
1016 | "Softmax 계층: 입력 (a1, a2, a3)를 정규화하여 (y1, y2, y3)를 출력\n",
1017 | "\n",
1018 | "Cross Entropy 계층: Softmax의 출력(y1, y2, y3)과 정답 레이블(t1, t2, t3)를 받고, 손실 L을 출력\n",
1019 | "\n",
1020 | "Softmax 계층의 역전파는 (y1-t1, y2-t2, y3-t3)로 말끔한 결과임\n",
1021 | "\n",
1022 | "Softmax 계층의 출력과 정답 레이블의 차분.\n",
1023 | "\n",
1024 | "신경망의 역전파에서는 이 차이인 오차가 앞 계층에 전해지는 것\n",
1025 | "\n",
1026 | "소프트맥스 함수의 손실 함수로 교차 엔트로피 오차를 사용하니 역전파가 (y1-t1, y2-t2, y3-t3)로 말끔히 떨어짐\n",
1027 | "\n",
1028 | "=> 교차 엔트로피 함수가 그렇게 설계되었기 때문\n",
1029 | "\n",
1030 | "항등 함수의 손실 함수로 '평균 제곱 오차'를 사용하면 역전파의 결과가 말끔히 떨어짐\n",
1031 | "\n",
1032 | "구체적인 예\n",
1033 | "\n",
1034 | "정답 레이블 (0, 1, 0), 소프트맥스 계층이 (0.3, 0.2, 0.5)를 출력\n",
1035 | "\n",
1036 | "=> 소프트맥스 계층의 역전파는 (0.3, -0.8, 0.5)라는 커다란 오차를 전파\n",
1037 | "\n",
1038 | "정답 레이블 (0, 1, 0), 소프트맥스 계층이 (0.01, 0.99, 0)을 출력\n",
1039 | "\n",
1040 | "=> 소프트맥스 계층의 역전파가 보내는 오차는 (0.01, -0.01, 0)이 됨. 학습하는 정도가 작아짐\n",
1041 | "\n",
1042 | "Softmax-with-Loss 계층을 구현한 코드"
1043 | ]
1044 | },
1045 | {
1046 | "cell_type": "code",
1047 | "execution_count": 17,
1048 | "metadata": {
1049 | "collapsed": false
1050 | },
1051 | "outputs": [],
1052 | "source": [
1053 | "# https://github.com/WegraLee/deep-learning-from-scratch/blob/master/common/functions.py 소스 참고\n",
1054 | "# 3.5.2 소프트맥스 함수 구현시 주의점 참고\n",
1055 | "def sigmoid(x):\n",
1056 | " return 1 / (1 + np.exp(-x))\n",
1057 | "\n",
1058 | "# 4.2.2. 교차 엔트로피 오차 참고\n",
1059 | "def cross_entropy_error(y, t):\n",
1060 | " if y.ndim == 1:\n",
1061 | " t = t.reshape(1, t.size)\n",
1062 | " y = y.reshape(1, y.size)\n",
1063 | " \n",
1064 | " # 훈련 데이터가 원-핫 벡터라면 정답 레이블의 인덱스로 반환\n",
1065 | " if t.size == y.size:\n",
1066 | " t = t.argmax(axis=1)\n",
1067 | " \n",
1068 | " batch_size = y.shape[0]\n",
1069 | " return -np.sum(np.log(y[np.arange(batch_size), t])) / batch_size\n",
1070 | "\n",
1071 | "class SoftmaxWithLoss:\n",
1072 | " def __init__(self):\n",
1073 | " self.loss = None # 손실\n",
1074 | " self.y = None # softmax의 출력\n",
1075 | " self.t = None # 정답 레이블(원-핫 벡터)\n",
1076 | " \n",
1077 | " def forward(self, x, t):\n",
1078 | " self.t = t\n",
1079 | " self.y = softmax(x)\n",
1080 | " self.loss = cross_entropy_error(self.y, self.t)\n",
1081 | " return self.loss\n",
1082 | " \n",
1083 | " def backward(self, dout=1):\n",
1084 | " batch_size = self.t.shape[0]\n",
1085 | " dx = (self.y - self.t) / batch_size\n",
1086 | " \n",
1087 | " return dx"
1088 | ]
1089 | },
1090 | {
1091 | "cell_type": "markdown",
1092 | "metadata": {},
1093 | "source": [
1094 | "주의. 역전파 때는 전파하는 값을 배치의 수(batch_size)로 나눠 데이터 1개당 오차를 앞 계층으로 전파함"
1095 | ]
1096 | },
1097 | {
1098 | "cell_type": "markdown",
1099 | "metadata": {},
1100 | "source": [
1101 | "## 5.7 오차역전파법 구현하기"
1102 | ]
1103 | },
1104 | {
1105 | "cell_type": "markdown",
1106 | "metadata": {},
1107 | "source": [
1108 | "### 5.7.1 신경망 학습의 전체 그림\n",
1109 | "\n",
1110 | "**전제**\n",
1111 | "\n",
1112 | "학습: 가중치와 편향을 훈련 데이터에 적응하도록 조정하는 과정\n",
1113 | "\n",
1114 | "**1단계 - 미니배치**\n",
1115 | "\n",
1116 | "미니배치: 훈련 데이터 중 일부를 무작위로 가져옴\n",
1117 | "\n",
1118 | "목표: 미니배치의 손실 함수 값을 줄이기\n",
1119 | "\n",
1120 | "**2단계 - 기울기 산출**\n",
1121 | "\n",
1122 | "가중치 매개변수의 기울기를 구함. 기울기는 손실 함수의 값을 가장 작게하는 방향을 제시\n",
1123 | "\n",
1124 | "**3단계 - 매개변수 갱신**\n",
1125 | "\n",
1126 | "가중치 매개변수를 기울기 방향으로 아주 조금 갱신\n",
1127 | "\n",
1128 | "**4단계 - 반복**\n",
1129 | "\n",
1130 | "1~3 단계를 반복\n",
1131 | "\n",
1132 | "오차역전법이 등장하는 단계는 두 번째인 '기울기 산출'\n",
1133 | "\n",
1134 | "느린 수치 미분과 달리 기울기를 효율적이고 빠르게 구할 수 있음"
1135 | ]
1136 | },
1137 | {
1138 | "cell_type": "markdown",
1139 | "metadata": {},
1140 | "source": [
1141 | "### 5.7.2 오차역전파법을 적용한 신경망 구현하기\n",
1142 | "\n",
1143 | "계층을 사용함으로써 \n",
1144 | "\n",
1145 | "인식 결과를 얻는 처리(predict())와 기울기를 구하는 처리(gradient()) 계층의 전파만으로 동작이 이루어짐."
1146 | ]
1147 | },
1148 | {
1149 | "cell_type": "code",
1150 | "execution_count": 18,
1151 | "metadata": {
1152 | "collapsed": false
1153 | },
1154 | "outputs": [],
1155 | "source": [
1156 | "# https://github.com/WegraLee/deep-learning-from-scratch/blob/master/ch05/two_layer_net.py 참고\n",
1157 | "# coding: utf-8\n",
1158 | "#import sys, os\n",
1159 | "#sys.path.append(os.pardir) # 부모 디렉터리의 파일을 가져올 수 있도록 설정\n",
1160 | "import numpy as np\n",
1161 | "#from common.layers import *\n",
1162 | "#from common.gradient import numerical_gradient\n",
1163 | "from collections import OrderedDict\n",
1164 | "\n",
1165 | "# https://github.com/WegraLee/deep-learning-from-scratch/blob/master/common/functions.py\n",
1166 | "def softmax(x):\n",
1167 | " if x.ndim == 2:\n",
1168 | " x = x.T\n",
1169 | " x = x - np.max(x, axis=0)\n",
1170 | " y = np.exp(x) / np.sum(np.exp(x), axis=0)\n",
1171 | " return y.T \n",
1172 | "\n",
1173 | " x = x - np.max(x) # 오버플로 대책\n",
1174 | " return np.exp(x) / np.sum(np.exp(x))\n",
1175 | "\n",
1176 | "# https://github.com/WegraLee/deep-learning-from-scratch/blob/master/common/gradient.py 참고\n",
1177 | "def numerical_gradient(f, x):\n",
1178 | " h = 1e-4 # 0.0001\n",
1179 | " grad = np.zeros_like(x)\n",
1180 | " \n",
1181 | " it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])\n",
1182 | " while not it.finished:\n",
1183 | " idx = it.multi_index\n",
1184 | " tmp_val = x[idx]\n",
1185 | " x[idx] = float(tmp_val) + h\n",
1186 | " fxh1 = f(x) # f(x+h)\n",
1187 | " \n",
1188 | " x[idx] = tmp_val - h \n",
1189 | " fxh2 = f(x) # f(x-h)\n",
1190 | " grad[idx] = (fxh1 - fxh2) / (2*h)\n",
1191 | " \n",
1192 | " x[idx] = tmp_val # 값 복원\n",
1193 | " it.iternext() \n",
1194 | " \n",
1195 | " return grad\n",
1196 | "\n",
1197 | "\n",
1198 | "class TwoLayerNet:\n",
1199 | "\n",
1200 | " def __init__(self, input_size, hidden_size, output_size, weight_init_std = 0.01):\n",
1201 | " # 가중치 초기화\n",
1202 | " self.params = {}\n",
1203 | " self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)\n",
1204 | " self.params['b1'] = np.zeros(hidden_size)\n",
1205 | " self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size) \n",
1206 | " self.params['b2'] = np.zeros(output_size)\n",
1207 | "\n",
1208 | " # 계층 생성\n",
1209 | " self.layers = OrderedDict() ###\n",
1210 | " self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1']) ###\n",
1211 | " self.layers['Relu1'] = Relu() ###\n",
1212 | " self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2']) ###\n",
1213 | "\n",
1214 | " self.lastLayer = SoftmaxWithLoss() ###\n",
1215 | " \n",
1216 | " def predict(self, x):\n",
1217 | " for layer in self.layers.values(): ###\n",
1218 | " x = layer.forward(x) ###\n",
1219 | " \n",
1220 | " return x\n",
1221 | " \n",
1222 | " # x : 입력 데이터, t : 정답 레이블\n",
1223 | " def loss(self, x, t):\n",
1224 | " y = self.predict(x)\n",
1225 | " return self.lastLayer.forward(y, t)\n",
1226 | " \n",
1227 | " def accuracy(self, x, t):\n",
1228 | " y = self.predict(x)\n",
1229 | " y = np.argmax(y, axis=1)\n",
1230 | " if t.ndim != 1 : t = np.argmax(t, axis=1)\n",
1231 | " \n",
1232 | " accuracy = np.sum(y == t) / float(x.shape[0])\n",
1233 | " return accuracy\n",
1234 | " \n",
1235 | " # x : 입력 데이터, t : 정답 레이블\n",
1236 | " def numerical_gradient(self, x, t):\n",
1237 | " loss_W = lambda W: self.loss(x, t)\n",
1238 | " \n",
1239 | " grads = {}\n",
1240 | " grads['W1'] = numerical_gradient(loss_W, self.params['W1'])\n",
1241 | " grads['b1'] = numerical_gradient(loss_W, self.params['b1'])\n",
1242 | " grads['W2'] = numerical_gradient(loss_W, self.params['W2'])\n",
1243 | " grads['b2'] = numerical_gradient(loss_W, self.params['b2'])\n",
1244 | " \n",
1245 | " return grads\n",
1246 | " \n",
1247 | " def gradient(self, x, t):\n",
1248 | " # forward\n",
1249 | " self.loss(x, t) ###\n",
1250 | "\n",
1251 | " # backward\n",
1252 | " dout = 1 ###\n",
1253 | " dout = self.lastLayer.backward(dout) ###\n",
1254 | " \n",
1255 | " layers = list(self.layers.values()) ###\n",
1256 | " layers.reverse() ###\n",
1257 | " for layer in layers: ###\n",
1258 | " dout = layer.backward(dout) ###\n",
1259 | "\n",
1260 | " # 결과 저장\n",
1261 | " grads = {}\n",
1262 | " grads['W1'], grads['b1'] = self.layers['Affine1'].dW, self.layers['Affine1'].db\n",
1263 | " grads['W2'], grads['b2'] = self.layers['Affine2'].dW, self.layers['Affine2'].db\n",
1264 | "\n",
1265 | " return grads"
1266 | ]
1267 | },
1268 | {
1269 | "cell_type": "markdown",
1270 | "metadata": {},
1271 | "source": [
1272 | "\\### 으로 중요코드 표시. 집중해서 살펴보세요.\n",
1273 | "\n",
1274 | "OrderedDict: 딕셔너리에 추가한 순서를 기억하는 (순서가 있는) 딕셔너리\n",
1275 | "\n",
1276 | "순전파 때는 추가한 순서대로 각 계층의 forward() 메서드를 호출\n",
1277 | "\n",
1278 | "역전파 때는 계층을 반대 순서로 호출\n",
1279 | "\n",
1280 | "신경망의 구성 요소를 '계층'으로 구현한 덕분에 신경망을 쉽게 구축\n",
1281 | "\n",
1282 | "=> 레고 블록을 조립하듯 필요한 만큼 계층을 더 추가하면 됨"
1283 | ]
1284 | },
1285 | {
1286 | "cell_type": "markdown",
1287 | "metadata": {},
1288 | "source": [
1289 | "### 5.7.3 오차역전파법으로 구한 기울기 검증하기\n",
1290 | "\n",
1291 | "수치미분은 느립니다."
1292 | ]
1293 | },
1294 | {
1295 | "cell_type": "code",
1296 | "execution_count": 19,
1297 | "metadata": {
1298 | "collapsed": true
1299 | },
1300 | "outputs": [],
1301 | "source": [
1302 | "from dataset.mnist import load_mnist\n",
1303 | "(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)\n",
1304 | "network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)\n",
1305 | "x_batch = x_train[:3]\n",
1306 | "t_batch = t_train[:3]"
1307 | ]
1308 | },
1309 | {
1310 | "cell_type": "code",
1311 | "execution_count": 20,
1312 | "metadata": {
1313 | "collapsed": false
1314 | },
1315 | "outputs": [
1316 | {
1317 | "name": "stdout",
1318 | "output_type": "stream",
1319 | "text": [
1320 | "1 loop, best of 3: 14.1 s per loop\n"
1321 | ]
1322 | }
1323 | ],
1324 | "source": [
1325 | "%timeit network.numerical_gradient(x_batch, t_batch)"
1326 | ]
1327 | },
1328 | {
1329 | "cell_type": "code",
1330 | "execution_count": 21,
1331 | "metadata": {
1332 | "collapsed": false
1333 | },
1334 | "outputs": [
1335 | {
1336 | "name": "stdout",
1337 | "output_type": "stream",
1338 | "text": [
1339 | "The slowest run took 16.66 times longer than the fastest. This could mean that an intermediate result is being cached.\n",
1340 | "1000 loops, best of 3: 470 µs per loop\n"
1341 | ]
1342 | }
1343 | ],
1344 | "source": [
1345 | "%timeit network.gradient(x_batch, t_batch)"
1346 | ]
1347 | },
1348 | {
1349 | "cell_type": "markdown",
1350 | "metadata": {},
1351 | "source": [
1352 | "수치미분(numerical_gradient) 속도: 9.95초, 14.1초\n",
1353 | "\n",
1354 | "오차역전법(gradient) 속도: 248 µs(0.000248초), 470 µs(0.000470초)\n",
1355 | "\n",
1356 | "약 42,000배 속도차이가 남"
1357 | ]
1358 | },
1359 | {
1360 | "cell_type": "markdown",
1361 | "metadata": {},
1362 | "source": [
1363 | "수치 미분을 오차역전파법을 정확히 구현했는지 확인하기 위해 필요.\n",
1364 | "\n",
1365 | "수치 미분의 이점은 구현하기 쉬움\n",
1366 | "\n",
1367 | "기울기 확인(gradient check): 수치 미분의 결과와 오차역전파법의 결과를 비교하여 오차역전파법을 제대로 구현했는지 검증함."
1368 | ]
1369 | },
1370 | {
1371 | "cell_type": "code",
1372 | "execution_count": 22,
1373 | "metadata": {
1374 | "collapsed": false
1375 | },
1376 | "outputs": [
1377 | {
1378 | "name": "stdout",
1379 | "output_type": "stream",
1380 | "text": [
1381 | "b2:1.20126118774e-10\n",
1382 | "W1:2.80100167994e-13\n",
1383 | "W2:9.12804904606e-13\n",
1384 | "b1:7.24036213471e-13\n"
1385 | ]
1386 | }
1387 | ],
1388 | "source": [
1389 | "# https://github.com/WegraLee/deep-learning-from-scratch/blob/master/ch05/gradient_check.py 참고\n",
1390 | "# coding: utf-8\n",
1391 | "#import sys, os\n",
1392 | "#sys.path.append(os.pardir) # 부모 디렉터리의 파일을 가져올 수 있도록 설정\n",
1393 | "import numpy as np\n",
1394 | "from dataset.mnist import load_mnist\n",
1395 | "\n",
1396 | "# 데이터 읽기\n",
1397 | "(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)\n",
1398 | "\n",
1399 | "network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)\n",
1400 | "\n",
1401 | "x_batch = x_train[:3]\n",
1402 | "t_batch = t_train[:3]\n",
1403 | "\n",
1404 | "grad_numerical = network.numerical_gradient(x_batch, t_batch)\n",
1405 | "grad_backprop = network.gradient(x_batch, t_batch)\n",
1406 | "\n",
1407 | "# 각 가중치의 절대 오차의 평균을 구한다.\n",
1408 | "for key in grad_numerical.keys():\n",
1409 | " diff = np.average( np.abs(grad_backprop[key] - grad_numerical[key]) )\n",
1410 | " print(key + \":\" + str(diff))"
1411 | ]
1412 | },
1413 | {
1414 | "cell_type": "markdown",
1415 | "metadata": {},
1416 | "source": [
1417 | "이 결과는 수치 미분과 오차역전파법으로 구한 기울기의 차이가 매우 작다고 말해줌\n",
1418 | "\n",
1419 | "오차역전법이 실수 없이 구현했다는 믿음이 커짐\n",
1420 | "\n",
1421 | "수치 미분과 오차역전파법의 결과 오차가 0이 되는 일은 드뭄\n",
1422 | "\n",
1423 | "올바르게 구현했다면 0에 아주 가까운 작은 값이 됨"
1424 | ]
1425 | },
1426 | {
1427 | "cell_type": "markdown",
1428 | "metadata": {},
1429 | "source": [
1430 | "### 5.7.4 오차역전파법을 사용한 학습 구현하기"
1431 | ]
1432 | },
1433 | {
1434 | "cell_type": "code",
1435 | "execution_count": 23,
1436 | "metadata": {
1437 | "collapsed": false
1438 | },
1439 | "outputs": [
1440 | {
1441 | "name": "stdout",
1442 | "output_type": "stream",
1443 | "text": [
1444 | "0.1359 0.1349\n",
1445 | "0.898666666667 0.9015\n",
1446 | "0.921233333333 0.9229\n",
1447 | "0.935483333333 0.9355\n",
1448 | "0.946366666667 0.9449\n",
1449 | "0.95215 0.9502\n",
1450 | "0.956916666667 0.9527\n",
1451 | "0.96005 0.9557\n",
1452 | "0.9626 0.9573\n",
1453 | "0.966833333333 0.9597\n",
1454 | "0.968366666667 0.9616\n",
1455 | "0.9704 0.9622\n",
1456 | "0.971483333333 0.963\n",
1457 | "0.974283333333 0.9663\n",
1458 | "0.976 0.9669\n",
1459 | "0.977116666667 0.967\n",
1460 | "0.978 0.9677\n"
1461 | ]
1462 | }
1463 | ],
1464 | "source": [
1465 | "# coding: utf-8\n",
1466 | "#import sys, os\n",
1467 | "#sys.path.append(os.pardir)\n",
1468 | "import numpy as np\n",
1469 | "from dataset.mnist import load_mnist\n",
1470 | "#from two_layer_net import TwoLayerNet\n",
1471 | "\n",
1472 | "# 데이터 읽기\n",
1473 | "(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)\n",
1474 | "\n",
1475 | "network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)\n",
1476 | "\n",
1477 | "iters_num = 10000\n",
1478 | "train_size = x_train.shape[0]\n",
1479 | "batch_size = 100\n",
1480 | "learning_rate = 0.1\n",
1481 | "\n",
1482 | "train_loss_list = []\n",
1483 | "train_acc_list = []\n",
1484 | "test_acc_list = []\n",
1485 | "\n",
1486 | "iter_per_epoch = max(train_size / batch_size, 1)\n",
1487 | "\n",
1488 | "for i in range(iters_num):\n",
1489 | " batch_mask = np.random.choice(train_size, batch_size)\n",
1490 | " x_batch = x_train[batch_mask]\n",
1491 | " t_batch = t_train[batch_mask]\n",
1492 | " \n",
1493 | " # 기울기 계산\n",
1494 | " #grad = network.numerical_gradient(x_batch, t_batch) # 수치 미분 방식\n",
1495 | " grad = network.gradient(x_batch, t_batch) # 오차역전파법 방식(훨씬 빠르다)\n",
1496 | " \n",
1497 | " # 갱신\n",
1498 | " for key in ('W1', 'b1', 'W2', 'b2'):\n",
1499 | " network.params[key] -= learning_rate * grad[key]\n",
1500 | " \n",
1501 | " loss = network.loss(x_batch, t_batch)\n",
1502 | " train_loss_list.append(loss)\n",
1503 | " \n",
1504 | " if i % iter_per_epoch == 0:\n",
1505 | " train_acc = network.accuracy(x_train, t_train)\n",
1506 | " test_acc = network.accuracy(x_test, t_test)\n",
1507 | " train_acc_list.append(train_acc)\n",
1508 | " test_acc_list.append(test_acc)\n",
1509 | " print(train_acc, test_acc)"
1510 | ]
1511 | },
1512 | {
1513 | "cell_type": "markdown",
1514 | "metadata": {},
1515 | "source": [
1516 | "## 5.8 정리\n",
1517 | "\n",
1518 | "계산 그래프를 이용하여 신경망의 동작과 오차역전파법을 설명\n",
1519 | "\n",
1520 | "모든 계층에서 forward와 backward 메서드를 구현\n",
1521 | "\n",
1522 | "forward는 데이터를 순방향으로 backward는 역방향으로 전파함\n",
1523 | "\n",
1524 | "가중치 매개변수의 기울기를 효율적으로 구할 수 있음"
1525 | ]
1526 | },
1527 | {
1528 | "cell_type": "markdown",
1529 | "metadata": {},
1530 | "source": [
1531 | "**이번 장에서 배운 것**\n",
1532 | "\n",
1533 | "계산그래프를 이용하면 계산 과정을 시각적으로 파악 가능\n",
1534 | "\n",
1535 | "계산그래프 노드는 국소적 계산으로 구성. 국소적 계산을 조합해 전체 계산을 구성\n",
1536 | "\n",
1537 | "순전파는 통상의 계산을 수행. 역전파는 노드의 미분을 구함\n",
1538 | "\n",
1539 | "오차역전파법: 신경망의 구성 요소를 계층으로 구현하여 기울기를 효율적으로 계산\n",
1540 | "\n",
1541 | "기울기 확인: 수치 미분과 오차역전파법의 결과를 비교하면 오차역전파법 구현에 잘못이 없는지 확인가능"
1542 | ]
1543 | }
1544 | ],
1545 | "metadata": {
1546 | "anaconda-cloud": {},
1547 | "kernelspec": {
1548 | "display_name": "Python [Root]",
1549 | "language": "python",
1550 | "name": "Python [Root]"
1551 | },
1552 | "language_info": {
1553 | "codemirror_mode": {
1554 | "name": "ipython",
1555 | "version": 3
1556 | },
1557 | "file_extension": ".py",
1558 | "mimetype": "text/x-python",
1559 | "name": "python",
1560 | "nbconvert_exporter": "python",
1561 | "pygments_lexer": "ipython3",
1562 | "version": "3.5.2"
1563 | }
1564 | },
1565 | "nbformat": 4,
1566 | "nbformat_minor": 0
1567 | }
1568 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 밑바닥부터 시작하는 딥러닝
2 |
3 | # Deep Learning from Scratch
4 |
5 | '밑바닥부터 시작하는 딥러닝' 공부한 내용을 jupyter notebook으로 정리하였습니다.
6 |
7 | ## Github
8 |
9 | https://github.com/WegraLee/deep-learning-from-scratch
10 |
11 | ## 책주소
12 |
13 | http://www.hanbit.co.kr/store/books/look.php?p_code=B8475831198
14 |
15 | 
16 |
17 | ## 1장
18 |
19 | http://nbviewer.jupyter.org/github/SDRLurker/deep-learning/blob/master/1장.ipynb
20 |
21 | ## 2장
22 |
23 | http://nbviewer.jupyter.org/github/SDRLurker/deep-learning/blob/master/2장.ipynb
24 |
25 | ## 3장
26 |
27 | http://nbviewer.jupyter.org/github/SDRLurker/deep-learning/blob/master/3장.ipynb
28 |
29 | ## 4장
30 |
31 | http://nbviewer.jupyter.org/github/SDRLurker/deep-learning/blob/master/4장.ipynb
32 |
33 | ## 5장
34 |
35 | http://nbviewer.jupyter.org/github/SDRLurker/deep-learning/blob/master/5장.ipynb
36 |
37 | ## 6장
38 |
39 | http://nbviewer.jupyter.org/github/SDRLurker/deep-learning/blob/master/6장.ipynb
40 |
41 |
--------------------------------------------------------------------------------
/common/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SDRLurker/deep-learning/f95b0a2c7c4ccec3c7395ad5a648c7664b168866/common/__init__.py
--------------------------------------------------------------------------------
/common/functions.py:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | import numpy as np
3 |
4 |
5 | def identity_function(x):
6 | return x
7 |
8 |
9 | def step_function(x):
10 | return np.array(x > 0, dtype=np.int)
11 |
12 |
13 | def sigmoid(x):
14 | return 1 / (1 + np.exp(-x))
15 |
16 |
17 | def sigmoid_grad(x):
18 | return (1.0 - sigmoid(x)) * sigmoid(x)
19 |
20 |
21 | def relu(x):
22 | return np.maximum(0, x)
23 |
24 |
25 | def relu_grad(x):
26 | grad = np.zeros(x)
27 | grad[x>=0] = 1
28 | return grad
29 |
30 |
31 | def softmax(x):
32 | if x.ndim == 2:
33 | x = x.T
34 | x = x - np.max(x, axis=0)
35 | y = np.exp(x) / np.sum(np.exp(x), axis=0)
36 | return y.T
37 |
38 | x = x - np.max(x) # 오버플로 대책
39 | return np.exp(x) / np.sum(np.exp(x))
40 |
41 |
42 | def mean_squared_error(y, t):
43 | return 0.5 * np.sum((y-t)**2)
44 |
45 |
46 | def cross_entropy_error(y, t):
47 | if y.ndim == 1:
48 | t = t.reshape(1, t.size)
49 | y = y.reshape(1, y.size)
50 |
51 | # 훈련 데이터가 원-핫 벡터라면 정답 레이블의 인덱스로 반환
52 | if t.size == y.size:
53 | t = t.argmax(axis=1)
54 |
55 | batch_size = y.shape[0]
56 | return -np.sum(np.log(y[np.arange(batch_size), t])) / batch_size
57 |
58 |
59 | def softmax_loss(X, t):
60 | y = softmax(X)
61 | return cross_entropy_error(y, t)
--------------------------------------------------------------------------------
/common/gradient.py:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | import numpy as np
3 |
4 | def _numerical_gradient_1d(f, x):
5 | h = 1e-4 # 0.0001
6 | grad = np.zeros_like(x)
7 |
8 | for idx in range(x.size):
9 | tmp_val = x[idx]
10 | x[idx] = float(tmp_val) + h
11 | fxh1 = f(x) # f(x+h)
12 |
13 | x[idx] = tmp_val - h
14 | fxh2 = f(x) # f(x-h)
15 | grad[idx] = (fxh1 - fxh2) / (2*h)
16 |
17 | x[idx] = tmp_val # 값 복원
18 |
19 | return grad
20 |
21 |
22 | def numerical_gradient_2d(f, X):
23 | if X.ndim == 1:
24 | return _numerical_gradient_1d(f, X)
25 | else:
26 | grad = np.zeros_like(X)
27 |
28 | for idx, x in enumerate(X):
29 | grad[idx] = _numerical_gradient_1d(f, x)
30 |
31 | return grad
32 |
33 |
34 | def numerical_gradient(f, x):
35 | h = 1e-4 # 0.0001
36 | grad = np.zeros_like(x)
37 |
38 | it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
39 | while not it.finished:
40 | idx = it.multi_index
41 | tmp_val = x[idx]
42 | x[idx] = float(tmp_val) + h
43 | fxh1 = f(x) # f(x+h)
44 |
45 | x[idx] = tmp_val - h
46 | fxh2 = f(x) # f(x-h)
47 | grad[idx] = (fxh1 - fxh2) / (2*h)
48 |
49 | x[idx] = tmp_val # 값 복원
50 | it.iternext()
51 |
52 | return grad
--------------------------------------------------------------------------------
/common/layers.py:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | import numpy as np
3 | from common.functions import *
4 | from common.util import im2col, col2im
5 |
6 |
7 | class Relu:
8 | def __init__(self):
9 | self.mask = None
10 |
11 | def forward(self, x):
12 | self.mask = (x <= 0)
13 | out = x.copy()
14 | out[self.mask] = 0
15 |
16 | return out
17 |
18 | def backward(self, dout):
19 | dout[self.mask] = 0
20 | dx = dout
21 |
22 | return dx
23 |
24 |
25 | class Sigmoid:
26 | def __init__(self):
27 | self.out = None
28 |
29 | def forward(self, x):
30 | out = sigmoid(x)
31 | self.out = out
32 | return out
33 |
34 | def backward(self, dout):
35 | dx = dout * (1.0 - self.out) * self.out
36 |
37 | return dx
38 |
39 |
40 | class Affine:
41 | def __init__(self, W, b):
42 | self.W = W
43 | self.b = b
44 |
45 | self.x = None
46 | self.original_x_shape = None
47 | # 가중치와 편향 매개변수의 미분
48 | self.dW = None
49 | self.db = None
50 |
51 | def forward(self, x):
52 | # 텐서 대응
53 | self.original_x_shape = x.shape
54 | x = x.reshape(x.shape[0], -1)
55 | self.x = x
56 |
57 | out = np.dot(self.x, self.W) + self.b
58 |
59 | return out
60 |
61 | def backward(self, dout):
62 | dx = np.dot(dout, self.W.T)
63 | self.dW = np.dot(self.x.T, dout)
64 | self.db = np.sum(dout, axis=0)
65 |
66 | dx = dx.reshape(*self.original_x_shape) # 입력 데이터 모양 변경(텐서 대응)
67 | return dx
68 |
69 |
70 | class SoftmaxWithLoss:
71 | def __init__(self):
72 | self.loss = None # 손실함수
73 | self.y = None # softmax의 출력
74 | self.t = None # 정답 레이블(원-핫 인코딩 형태)
75 |
76 | def forward(self, x, t):
77 | self.t = t
78 | self.y = softmax(x)
79 | self.loss = cross_entropy_error(self.y, self.t)
80 |
81 | return self.loss
82 |
83 | def backward(self, dout=1):
84 | batch_size = self.t.shape[0]
85 | if self.t.size == self.y.size: # 정답 레이블이 원-핫 인코딩 형태일 때
86 | dx = (self.y - self.t) / batch_size
87 | else:
88 | dx = self.y.copy()
89 | dx[np.arange(batch_size), self.t] -= 1
90 | dx = dx / batch_size
91 |
92 | return dx
93 |
94 |
95 | class Dropout:
96 | """
97 | http://arxiv.org/abs/1207.0580
98 | """
99 | def __init__(self, dropout_ratio=0.5):
100 | self.dropout_ratio = dropout_ratio
101 | self.mask = None
102 |
103 | def forward(self, x, train_flg=True):
104 | if train_flg:
105 | self.mask = np.random.rand(*x.shape) > self.dropout_ratio
106 | return x * self.mask
107 | else:
108 | return x * (1.0 - self.dropout_ratio)
109 |
110 | def backward(self, dout):
111 | return dout * self.mask
112 |
113 |
114 | class BatchNormalization:
115 | """
116 | http://arxiv.org/abs/1502.03167
117 | """
118 | def __init__(self, gamma, beta, momentum=0.9, running_mean=None, running_var=None):
119 | self.gamma = gamma
120 | self.beta = beta
121 | self.momentum = momentum
122 | self.input_shape = None # 합성곱 계층은 4차원, 완전연결 계층은 2차원
123 |
124 | # 시험할 때 사용할 평균과 분산
125 | self.running_mean = running_mean
126 | self.running_var = running_var
127 |
128 | # backward 시에 사용할 중간 데이터
129 | self.batch_size = None
130 | self.xc = None
131 | self.std = None
132 | self.dgamma = None
133 | self.dbeta = None
134 |
135 | def forward(self, x, train_flg=True):
136 | self.input_shape = x.shape
137 | if x.ndim != 2:
138 | N, C, H, W = x.shape
139 | x = x.reshape(N, -1)
140 |
141 | out = self.__forward(x, train_flg)
142 |
143 | return out.reshape(*self.input_shape)
144 |
145 | def __forward(self, x, train_flg):
146 | if self.running_mean is None:
147 | N, D = x.shape
148 | self.running_mean = np.zeros(D)
149 | self.running_var = np.zeros(D)
150 |
151 | if train_flg:
152 | mu = x.mean(axis=0)
153 | xc = x - mu
154 | var = np.mean(xc**2, axis=0)
155 | std = np.sqrt(var + 10e-7)
156 | xn = xc / std
157 |
158 | self.batch_size = x.shape[0]
159 | self.xc = xc
160 | self.xn = xn
161 | self.std = std
162 | self.running_mean = self.momentum * self.running_mean + (1-self.momentum) * mu
163 | self.running_var = self.momentum * self.running_var + (1-self.momentum) * var
164 | else:
165 | xc = x - self.running_mean
166 | xn = xc / ((np.sqrt(self.running_var + 10e-7)))
167 |
168 | out = self.gamma * xn + self.beta
169 | return out
170 |
171 | def backward(self, dout):
172 | if dout.ndim != 2:
173 | N, C, H, W = dout.shape
174 | dout = dout.reshape(N, -1)
175 |
176 | dx = self.__backward(dout)
177 |
178 | dx = dx.reshape(*self.input_shape)
179 | return dx
180 |
181 | def __backward(self, dout):
182 | dbeta = dout.sum(axis=0)
183 | dgamma = np.sum(self.xn * dout, axis=0)
184 | dxn = self.gamma * dout
185 | dxc = dxn / self.std
186 | dstd = -np.sum((dxn * self.xc) / (self.std * self.std), axis=0)
187 | dvar = 0.5 * dstd / self.std
188 | dxc += (2.0 / self.batch_size) * self.xc * dvar
189 | dmu = np.sum(dxc, axis=0)
190 | dx = dxc - dmu / self.batch_size
191 |
192 | self.dgamma = dgamma
193 | self.dbeta = dbeta
194 |
195 | return dx
196 |
197 |
198 | class Convolution:
199 | def __init__(self, W, b, stride=1, pad=0):
200 | self.W = W
201 | self.b = b
202 | self.stride = stride
203 | self.pad = pad
204 |
205 | # 중간 데이터(backward 시 사용)
206 | self.x = None
207 | self.col = None
208 | self.col_W = None
209 |
210 | # 가중치와 편향 매개변수의 기울기
211 | self.dW = None
212 | self.db = None
213 |
214 | def forward(self, x):
215 | FN, C, FH, FW = self.W.shape
216 | N, C, H, W = x.shape
217 | out_h = 1 + int((H + 2*self.pad - FH) / self.stride)
218 | out_w = 1 + int((W + 2*self.pad - FW) / self.stride)
219 |
220 | col = im2col(x, FH, FW, self.stride, self.pad)
221 | col_W = self.W.reshape(FN, -1).T
222 |
223 | out = np.dot(col, col_W) + self.b
224 | out = out.reshape(N, out_h, out_w, -1).transpose(0, 3, 1, 2)
225 |
226 | self.x = x
227 | self.col = col
228 | self.col_W = col_W
229 |
230 | return out
231 |
232 | def backward(self, dout):
233 | FN, C, FH, FW = self.W.shape
234 | dout = dout.transpose(0,2,3,1).reshape(-1, FN)
235 |
236 | self.db = np.sum(dout, axis=0)
237 | self.dW = np.dot(self.col.T, dout)
238 | self.dW = self.dW.transpose(1, 0).reshape(FN, C, FH, FW)
239 |
240 | dcol = np.dot(dout, self.col_W.T)
241 | dx = col2im(dcol, self.x.shape, FH, FW, self.stride, self.pad)
242 |
243 | return dx
244 |
245 |
246 | class Pooling:
247 | def __init__(self, pool_h, pool_w, stride=1, pad=0):
248 | self.pool_h = pool_h
249 | self.pool_w = pool_w
250 | self.stride = stride
251 | self.pad = pad
252 |
253 | self.x = None
254 | self.arg_max = None
255 |
256 | def forward(self, x):
257 | N, C, H, W = x.shape
258 | out_h = int(1 + (H - self.pool_h) / self.stride)
259 | out_w = int(1 + (W - self.pool_w) / self.stride)
260 |
261 | col = im2col(x, self.pool_h, self.pool_w, self.stride, self.pad)
262 | col = col.reshape(-1, self.pool_h*self.pool_w)
263 |
264 | arg_max = np.argmax(col, axis=1)
265 | out = np.max(col, axis=1)
266 | out = out.reshape(N, out_h, out_w, C).transpose(0, 3, 1, 2)
267 |
268 | self.x = x
269 | self.arg_max = arg_max
270 |
271 | return out
272 |
273 | def backward(self, dout):
274 | dout = dout.transpose(0, 2, 3, 1)
275 |
276 | pool_size = self.pool_h * self.pool_w
277 | dmax = np.zeros((dout.size, pool_size))
278 | dmax[np.arange(self.arg_max.size), self.arg_max.flatten()] = dout.flatten()
279 | dmax = dmax.reshape(dout.shape + (pool_size,))
280 |
281 | dcol = dmax.reshape(dmax.shape[0] * dmax.shape[1] * dmax.shape[2], -1)
282 | dx = col2im(dcol, self.x.shape, self.pool_h, self.pool_w, self.stride, self.pad)
283 |
284 | return dx
--------------------------------------------------------------------------------
/common/multi_layer_net.py:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | import sys, os
3 | sys.path.append(os.pardir) # 부모 디렉터리의 파일을 가져올 수 있도록 설정
4 | import numpy as np
5 | from collections import OrderedDict
6 | from common.layers import *
7 | from common.gradient import numerical_gradient
8 |
9 |
10 | class MultiLayerNet:
11 | """완전연결 다층 신경망
12 | Parameters
13 | ----------
14 | input_size : 입력 크기(MNIST의 경우엔 784)
15 | hidden_size_list : 각 은닉층의 뉴런 수를 담은 리스트(e.g. [100, 100, 100])
16 | output_size : 출력 크기(MNIST의 경우엔 10)
17 | activation : 활성화 함수 - 'relu' 혹은 'sigmoid'
18 | weight_init_std : 가중치의 표준편차 지정(e.g. 0.01)
19 | 'relu'나 'he'로 지정하면 'He 초깃값'으로 설정
20 | 'sigmoid'나 'xavier'로 지정하면 'Xavier 초깃값'으로 설정
21 | weight_decay_lambda : 가중치 감소(L2 법칙)의 세기
22 | """
23 | def __init__(self, input_size, hidden_size_list, output_size,
24 | activation='relu', weight_init_std='relu', weight_decay_lambda=0):
25 | self.input_size = input_size
26 | self.output_size = output_size
27 | self.hidden_size_list = hidden_size_list
28 | self.hidden_layer_num = len(hidden_size_list)
29 | self.weight_decay_lambda = weight_decay_lambda
30 | self.params = {}
31 |
32 | # 가중치 초기화
33 | self.__init_weight(weight_init_std)
34 |
35 | # 계층 생성
36 | activation_layer = {'sigmoid': Sigmoid, 'relu': Relu}
37 | self.layers = OrderedDict()
38 | for idx in range(1, self.hidden_layer_num+1):
39 | self.layers['Affine' + str(idx)] = Affine(self.params['W' + str(idx)],
40 | self.params['b' + str(idx)])
41 | self.layers['Activation_function' + str(idx)] = activation_layer[activation]()
42 |
43 | idx = self.hidden_layer_num + 1
44 | self.layers['Affine' + str(idx)] = Affine(self.params['W' + str(idx)],
45 | self.params['b' + str(idx)])
46 |
47 | self.last_layer = SoftmaxWithLoss()
48 |
49 | def __init_weight(self, weight_init_std):
50 | """가중치 초기화
51 |
52 | Parameters
53 | ----------
54 | weight_init_std : 가중치의 표준편차 지정(e.g. 0.01)
55 | 'relu'나 'he'로 지정하면 'He 초깃값'으로 설정
56 | 'sigmoid'나 'xavier'로 지정하면 'Xavier 초깃값'으로 설정
57 | """
58 | all_size_list = [self.input_size] + self.hidden_size_list + [self.output_size]
59 | for idx in range(1, len(all_size_list)):
60 | scale = weight_init_std
61 | if str(weight_init_std).lower() in ('relu', 'he'):
62 | scale = np.sqrt(2.0 / all_size_list[idx - 1]) # ReLU를 사용할 때의 권장 초깃값
63 | elif str(weight_init_std).lower() in ('sigmoid', 'xavier'):
64 | scale = np.sqrt(1.0 / all_size_list[idx - 1]) # sigmoid를 사용할 때의 권장 초깃값
65 | self.params['W' + str(idx)] = scale * np.random.randn(all_size_list[idx-1], all_size_list[idx])
66 | self.params['b' + str(idx)] = np.zeros(all_size_list[idx])
67 |
68 | def predict(self, x):
69 | for layer in self.layers.values():
70 | x = layer.forward(x)
71 |
72 | return x
73 |
74 | def loss(self, x, t):
75 | """손실 함수를 구한다.
76 |
77 | Parameters
78 | ----------
79 | x : 입력 데이터
80 | t : 정답 레이블
81 |
82 | Returns
83 | -------
84 | 손실 함수의 값
85 | """
86 | y = self.predict(x)
87 |
88 | weight_decay = 0
89 | for idx in range(1, self.hidden_layer_num + 2):
90 | W = self.params['W' + str(idx)]
91 | weight_decay += 0.5 * self.weight_decay_lambda * np.sum(W ** 2)
92 |
93 | return self.last_layer.forward(y, t) + weight_decay
94 |
95 | def accuracy(self, x, t):
96 | y = self.predict(x)
97 | y = np.argmax(y, axis=1)
98 | if t.ndim != 1 : t = np.argmax(t, axis=1)
99 |
100 | accuracy = np.sum(y == t) / float(x.shape[0])
101 | return accuracy
102 |
103 | def numerical_gradient(self, x, t):
104 | """기울기를 구한다(수치 미분).
105 |
106 | Parameters
107 | ----------
108 | x : 입력 데이터
109 | t : 정답 레이블
110 |
111 | Returns
112 | -------
113 | 각 층의 기울기를 담은 딕셔너리(dictionary) 변수
114 | grads['W1']、grads['W2']、... 각 층의 가중치
115 | grads['b1']、grads['b2']、... 각 층의 편향
116 | """
117 | loss_W = lambda W: self.loss(x, t)
118 |
119 | grads = {}
120 | for idx in range(1, self.hidden_layer_num+2):
121 | grads['W' + str(idx)] = numerical_gradient(loss_W, self.params['W' + str(idx)])
122 | grads['b' + str(idx)] = numerical_gradient(loss_W, self.params['b' + str(idx)])
123 |
124 | return grads
125 |
126 | def gradient(self, x, t):
127 | """기울기를 구한다(오차역전파법).
128 | Parameters
129 | ----------
130 | x : 입력 데이터
131 | t : 정답 레이블
132 |
133 | Returns
134 | -------
135 | 각 층의 기울기를 담은 딕셔너리(dictionary) 변수
136 | grads['W1']、grads['W2']、... 각 층의 가중치
137 | grads['b1']、grads['b2']、... 각 층의 편향
138 | """
139 | # forward
140 | self.loss(x, t)
141 |
142 | # backward
143 | dout = 1
144 | dout = self.last_layer.backward(dout)
145 |
146 | layers = list(self.layers.values())
147 | layers.reverse()
148 | for layer in layers:
149 | dout = layer.backward(dout)
150 |
151 | # 결과 저장
152 | grads = {}
153 | for idx in range(1, self.hidden_layer_num+2):
154 | grads['W' + str(idx)] = self.layers['Affine' + str(idx)].dW + self.weight_decay_lambda * self.layers['Affine' + str(idx)].W
155 | grads['b' + str(idx)] = self.layers['Affine' + str(idx)].db
156 |
157 | return grads
--------------------------------------------------------------------------------
/common/multi_layer_net_extend.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from collections import OrderedDict
3 | from common.layers import *
4 | from common.gradient import numerical_gradient
5 |
6 | class MultiLayerNetExtend:
7 | """완전 연결 다층 신경망(확장판)
8 | 가중치 감소, 드롭아웃, 배치 정규화 구현
9 | Parameters
10 | ----------
11 | input_size : 입력 크기(MNIST의 경우엔 784)
12 | hidden_size_list : 각 은닉층의 뉴런 수를 담은 리스트(e.g. [100, 100, 100])
13 | output_size : 출력 크기(MNIST의 경우엔 10)
14 | activation : 활성화 함수 - 'relu' 혹은 'sigmoid'
15 | weight_init_std : 가중치의 표준편차 지정(e.g. 0.01)
16 | 'relu'나 'he'로 지정하면 'He 초깃값'으로 설정
17 | 'sigmoid'나 'xavier'로 지정하면 'Xavier 초깃값'으로 설정
18 | weight_decay_lambda : 가중치 감소(L2 법칙)의 세기
19 | use_dropout : 드롭아웃 사용 여부
20 | dropout_ration : 드롭아웃 비율
21 | use_batchNorm : 배치 정규화 사용 여부
22 | """
23 | def __init__(self, input_size, hidden_size_list, output_size,
24 | activation='relu', weight_init_std='relu', weight_decay_lambda=0,
25 | use_dropout = False, dropout_ration = 0.5, use_batchnorm=False):
26 | self.input_size = input_size
27 | self.output_size = output_size
28 | self.hidden_size_list = hidden_size_list
29 | self.hidden_layer_num = len(hidden_size_list)
30 | self.use_dropout = use_dropout
31 | self.weight_decay_lambda = weight_decay_lambda
32 | self.use_batchnorm = use_batchnorm
33 | self.params = {}
34 |
35 | # 가중치 초기화
36 | self.__init_weight(weight_init_std)
37 |
38 | # 계층 생성
39 | activation_layer = {'sigmoid': Sigmoid, 'relu': Relu}
40 | self.layers = OrderedDict()
41 | for idx in range(1, self.hidden_layer_num+1):
42 | self.layers['Affine' + str(idx)] = Affine(self.params['W' + str(idx)],
43 | self.params['b' + str(idx)])
44 | if self.use_batchnorm:
45 | self.params['gamma' + str(idx)] = np.ones(hidden_size_list[idx-1])
46 | self.params['beta' + str(idx)] = np.zeros(hidden_size_list[idx-1])
47 | self.layers['BatchNorm' + str(idx)] = BatchNormalization(self.params['gamma' + str(idx)], self.params['beta' + str(idx)])
48 |
49 | self.layers['Activation_function' + str(idx)] = activation_layer[activation]()
50 |
51 | if self.use_dropout:
52 | self.layers['Dropout' + str(idx)] = Dropout(dropout_ration)
53 |
54 | idx = self.hidden_layer_num + 1
55 | self.layers['Affine' + str(idx)] = Affine(self.params['W' + str(idx)], self.params['b' + str(idx)])
56 |
57 | self.last_layer = SoftmaxWithLoss()
58 |
59 | def __init_weight(self, weight_init_std):
60 | """가중치 초기화
61 |
62 | Parameters
63 | ----------
64 | weight_init_std : 가중치의 표준편차 지정(e.g. 0.01)
65 | 'relu'나 'he'로 지정하면 'He 초깃값'으로 설정
66 | 'sigmoid'나 'xavier'로 지정하면 'Xavier 초깃값'으로 설정
67 | """
68 | all_size_list = [self.input_size] + self.hidden_size_list + [self.output_size]
69 | for idx in range(1, len(all_size_list)):
70 | scale = weight_init_std
71 | if str(weight_init_std).lower() in ('relu', 'he'):
72 | scale = np.sqrt(2.0 / all_size_list[idx - 1]) # ReLUを使う場合に推奨される初期値
73 | elif str(weight_init_std).lower() in ('sigmoid', 'xavier'):
74 | scale = np.sqrt(1.0 / all_size_list[idx - 1]) # sigmoidを使う場合に推奨される初期値
75 | self.params['W' + str(idx)] = scale * np.random.randn(all_size_list[idx-1], all_size_list[idx])
76 | self.params['b' + str(idx)] = np.zeros(all_size_list[idx])
77 |
78 | def predict(self, x, train_flg=False):
79 | for key, layer in self.layers.items():
80 | if "Dropout" in key or "BatchNorm" in key:
81 | x = layer.forward(x, train_flg)
82 | else:
83 | x = layer.forward(x)
84 |
85 | return x
86 |
87 | def loss(self, x, t, train_flg=False):
88 | """손실 함수를 구한다.
89 |
90 | Parameters
91 | ----------
92 | x : 입력 데이터
93 | t : 정답 레이블
94 | """
95 | y = self.predict(x, train_flg)
96 |
97 | weight_decay = 0
98 | for idx in range(1, self.hidden_layer_num + 2):
99 | W = self.params['W' + str(idx)]
100 | weight_decay += 0.5 * self.weight_decay_lambda * np.sum(W**2)
101 |
102 | return self.last_layer.forward(y, t) + weight_decay
103 |
104 | def accuracy(self, X, T):
105 | Y = self.predict(X, train_flg=False)
106 | Y = np.argmax(Y, axis=1)
107 | if T.ndim != 1 : T = np.argmax(T, axis=1)
108 |
109 | accuracy = np.sum(Y == T) / float(X.shape[0])
110 | return accuracy
111 |
112 | def numerical_gradient(self, X, T):
113 | """기울기를 구한다(수치 미분).
114 |
115 | Parameters
116 | ----------
117 | x : 입력 데이터
118 | t : 정답 레이블
119 |
120 | Returns
121 | -------
122 | 각 층의 기울기를 담은 사전(dictionary) 변수
123 | grads['W1']、grads['W2']、... 각 층의 가중치
124 | grads['b1']、grads['b2']、... 각 층의 편향
125 | """
126 | loss_W = lambda W: self.loss(X, T, train_flg=True)
127 |
128 | grads = {}
129 | for idx in range(1, self.hidden_layer_num+2):
130 | grads['W' + str(idx)] = numerical_gradient(loss_W, self.params['W' + str(idx)])
131 | grads['b' + str(idx)] = numerical_gradient(loss_W, self.params['b' + str(idx)])
132 |
133 | if self.use_batchnorm and idx != self.hidden_layer_num+1:
134 | grads['gamma' + str(idx)] = numerical_gradient(loss_W, self.params['gamma' + str(idx)])
135 | grads['beta' + str(idx)] = numerical_gradient(loss_W, self.params['beta' + str(idx)])
136 |
137 | return grads
138 |
139 | def gradient(self, x, t):
140 | # forward
141 | self.loss(x, t, train_flg=True)
142 |
143 | # backward
144 | dout = 1
145 | dout = self.last_layer.backward(dout)
146 |
147 | layers = list(self.layers.values())
148 | layers.reverse()
149 | for layer in layers:
150 | dout = layer.backward(dout)
151 |
152 | # 결과 저장
153 | grads = {}
154 | for idx in range(1, self.hidden_layer_num+2):
155 | grads['W' + str(idx)] = self.layers['Affine' + str(idx)].dW + self.weight_decay_lambda * self.params['W' + str(idx)]
156 | grads['b' + str(idx)] = self.layers['Affine' + str(idx)].db
157 |
158 | if self.use_batchnorm and idx != self.hidden_layer_num+1:
159 | grads['gamma' + str(idx)] = self.layers['BatchNorm' + str(idx)].dgamma
160 | grads['beta' + str(idx)] = self.layers['BatchNorm' + str(idx)].dbeta
161 |
162 | return grads
--------------------------------------------------------------------------------
/common/optimizer.py:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | import numpy as np
3 |
4 | class SGD:
5 |
6 | """확률적 경사 하강법(Stochastic Gradient Descent)"""
7 |
8 | def __init__(self, lr=0.01):
9 | self.lr = lr
10 |
11 | def update(self, params, grads):
12 | for key in params.keys():
13 | params[key] -= self.lr * grads[key]
14 |
15 |
16 | class Momentum:
17 |
18 | """모멘텀 SGD"""
19 |
20 | def __init__(self, lr=0.01, momentum=0.9):
21 | self.lr = lr
22 | self.momentum = momentum
23 | self.v = None
24 |
25 | def update(self, params, grads):
26 | if self.v is None:
27 | self.v = {}
28 | for key, val in params.items():
29 | self.v[key] = np.zeros_like(val)
30 |
31 | for key in params.keys():
32 | self.v[key] = self.momentum*self.v[key] - self.lr*grads[key]
33 | params[key] += self.v[key]
34 |
35 |
36 | class Nesterov:
37 |
38 | """Nesterov's Accelerated Gradient (http://arxiv.org/abs/1212.0901)"""
39 | # NAG는 모멘텀에서 한 단계 발전한 방법이다. (http://newsight.tistory.com/224)
40 |
41 | def __init__(self, lr=0.01, momentum=0.9):
42 | self.lr = lr
43 | self.momentum = momentum
44 | self.v = None
45 |
46 | def update(self, params, grads):
47 | if self.v is None:
48 | self.v = {}
49 | for key, val in params.items():
50 | self.v[key] = np.zeros_like(val)
51 |
52 | for key in params.keys():
53 | self.v[key] *= self.momentum
54 | self.v[key] -= self.lr * grads[key]
55 | params[key] += self.momentum * self.momentum * self.v[key]
56 | params[key] -= (1 + self.momentum) * self.lr * grads[key]
57 |
58 |
59 | class AdaGrad:
60 |
61 | """AdaGrad"""
62 |
63 | def __init__(self, lr=0.01):
64 | self.lr = lr
65 | self.h = None
66 |
67 | def update(self, params, grads):
68 | if self.h is None:
69 | self.h = {}
70 | for key, val in params.items():
71 | self.h[key] = np.zeros_like(val)
72 |
73 | for key in params.keys():
74 | self.h[key] += grads[key] * grads[key]
75 | params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)
76 |
77 |
78 | class RMSprop:
79 |
80 | """RMSprop"""
81 |
82 | def __init__(self, lr=0.01, decay_rate = 0.99):
83 | self.lr = lr
84 | self.decay_rate = decay_rate
85 | self.h = None
86 |
87 | def update(self, params, grads):
88 | if self.h is None:
89 | self.h = {}
90 | for key, val in params.items():
91 | self.h[key] = np.zeros_like(val)
92 |
93 | for key in params.keys():
94 | self.h[key] *= self.decay_rate
95 | self.h[key] += (1 - self.decay_rate) * grads[key] * grads[key]
96 | params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)
97 |
98 |
99 | class Adam:
100 |
101 | """Adam (http://arxiv.org/abs/1412.6980v8)"""
102 |
103 | def __init__(self, lr=0.001, beta1=0.9, beta2=0.999):
104 | self.lr = lr
105 | self.beta1 = beta1
106 | self.beta2 = beta2
107 | self.iter = 0
108 | self.m = None
109 | self.v = None
110 |
111 | def update(self, params, grads):
112 | if self.m is None:
113 | self.m, self.v = {}, {}
114 | for key, val in params.items():
115 | self.m[key] = np.zeros_like(val)
116 | self.v[key] = np.zeros_like(val)
117 |
118 | self.iter += 1
119 | lr_t = self.lr * np.sqrt(1.0 - self.beta2**self.iter) / (1.0 - self.beta1**self.iter)
120 |
121 | for key in params.keys():
122 | #self.m[key] = self.beta1*self.m[key] + (1-self.beta1)*grads[key]
123 | #self.v[key] = self.beta2*self.v[key] + (1-self.beta2)*(grads[key]**2)
124 | self.m[key] += (1 - self.beta1) * (grads[key] - self.m[key])
125 | self.v[key] += (1 - self.beta2) * (grads[key]**2 - self.v[key])
126 |
127 | params[key] -= lr_t * self.m[key] / (np.sqrt(self.v[key]) + 1e-7)
128 |
129 | #unbias_m += (1 - self.beta1) * (grads[key] - self.m[key]) # correct bias
130 | #unbisa_b += (1 - self.beta2) * (grads[key]*grads[key] - self.v[key]) # correct bias
131 | #params[key] += self.lr * unbias_m / (np.sqrt(unbisa_b) + 1e-7)
--------------------------------------------------------------------------------
/common/trainer.py:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | import sys, os
3 | sys.path.append(os.pardir) # 부모 디렉터리의 파일을 가져올 수 있도록 설정
4 | import numpy as np
5 | from common.optimizer import *
6 |
7 | class Trainer:
8 | """신경망 훈련을 대신 해주는 클래스
9 | """
10 | def __init__(self, network, x_train, t_train, x_test, t_test,
11 | epochs=20, mini_batch_size=100,
12 | optimizer='SGD', optimizer_param={'lr':0.01},
13 | evaluate_sample_num_per_epoch=None, verbose=True):
14 | self.network = network
15 | self.verbose = verbose
16 | self.x_train = x_train
17 | self.t_train = t_train
18 | self.x_test = x_test
19 | self.t_test = t_test
20 | self.epochs = epochs
21 | self.batch_size = mini_batch_size
22 | self.evaluate_sample_num_per_epoch = evaluate_sample_num_per_epoch
23 |
24 | # optimzer
25 | optimizer_class_dict = {'sgd':SGD, 'momentum':Momentum, 'nesterov':Nesterov,
26 | 'adagrad':AdaGrad, 'rmsprpo':RMSprop, 'adam':Adam}
27 | self.optimizer = optimizer_class_dict[optimizer.lower()](**optimizer_param)
28 |
29 | self.train_size = x_train.shape[0]
30 | self.iter_per_epoch = max(self.train_size / mini_batch_size, 1)
31 | self.max_iter = int(epochs * self.iter_per_epoch)
32 | self.current_iter = 0
33 | self.current_epoch = 0
34 |
35 | self.train_loss_list = []
36 | self.train_acc_list = []
37 | self.test_acc_list = []
38 |
39 | def train_step(self):
40 | batch_mask = np.random.choice(self.train_size, self.batch_size)
41 | x_batch = self.x_train[batch_mask]
42 | t_batch = self.t_train[batch_mask]
43 |
44 | grads = self.network.gradient(x_batch, t_batch)
45 | self.optimizer.update(self.network.params, grads)
46 |
47 | loss = self.network.loss(x_batch, t_batch)
48 | self.train_loss_list.append(loss)
49 | if self.verbose: print("train loss:" + str(loss))
50 |
51 | if self.current_iter % self.iter_per_epoch == 0:
52 | self.current_epoch += 1
53 |
54 | x_train_sample, t_train_sample = self.x_train, self.t_train
55 | x_test_sample, t_test_sample = self.x_test, self.t_test
56 | if not self.evaluate_sample_num_per_epoch is None:
57 | t = self.evaluate_sample_num_per_epoch
58 | x_train_sample, t_train_sample = self.x_train[:t], self.t_train[:t]
59 | x_test_sample, t_test_sample = self.x_test[:t], self.t_test[:t]
60 |
61 | train_acc = self.network.accuracy(x_train_sample, t_train_sample)
62 | test_acc = self.network.accuracy(x_test_sample, t_test_sample)
63 | self.train_acc_list.append(train_acc)
64 | self.test_acc_list.append(test_acc)
65 |
66 | if self.verbose: print("=== epoch:" + str(self.current_epoch) + ", train acc:" + str(train_acc) + ", test acc:" + str(test_acc) + " ===")
67 | self.current_iter += 1
68 |
69 | def train(self):
70 | for i in range(self.max_iter):
71 | self.train_step()
72 |
73 | test_acc = self.network.accuracy(self.x_test, self.t_test)
74 |
75 | if self.verbose:
76 | print("=============== Final Test Accuracy ===============")
77 | print("test acc:" + str(test_acc))
--------------------------------------------------------------------------------
/common/util.py:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | import numpy as np
3 |
4 |
5 | def smooth_curve(x):
6 | """손실 함수의 그래프를 매끄럽게 하기 위해 사용
7 |
8 | 참고:http://glowingpython.blogspot.jp/2012/02/convolution-with-numpy.html
9 | """
10 | window_len = 11
11 | s = np.r_[x[window_len-1:0:-1], x, x[-1:-window_len:-1]]
12 | w = np.kaiser(window_len, 2)
13 | y = np.convolve(w/w.sum(), s, mode='valid')
14 | return y[5:len(y)-5]
15 |
16 |
17 | def shuffle_dataset(x, t):
18 | """데이터셋을 뒤섞는다.
19 | Parameters
20 | ----------
21 | x : 훈련 데이터
22 | t : 정답 레이블
23 |
24 | Returns
25 | -------
26 | x, t : 뒤섞은 훈련 데이터와 정답 레이블
27 | """
28 | permutation = np.random.permutation(x.shape[0])
29 | x = x[permutation,:] if x.ndim == 2 else x[permutation,:,:,:]
30 | t = t[permutation]
31 |
32 | return x, t
33 |
34 | def conv_output_size(input_size, filter_size, stride=1, pad=0):
35 | return (input_size + 2*pad - filter_size) / stride + 1
36 |
37 |
38 | def im2col(input_data, filter_h, filter_w, stride=1, pad=0):
39 | """다수의 이미지를 입력받아 2차원 배열로 변환한다(평탄화).
40 |
41 | Parameters
42 | ----------
43 | input_data : 4차원 배열 형태의 입력 데이터(이미지 수, 채널 수, 높이, 너비)
44 | filter_h : 필터의 높이
45 | filter_w : 필터의 너비
46 | stride : 스트라이드
47 | pad : 패딩
48 |
49 | Returns
50 | -------
51 | col : 2차원 배열
52 | """
53 | N, C, H, W = input_data.shape
54 | out_h = (H + 2*pad - filter_h)//stride + 1
55 | out_w = (W + 2*pad - filter_w)//stride + 1
56 |
57 | img = np.pad(input_data, [(0,0), (0,0), (pad, pad), (pad, pad)], 'constant')
58 | col = np.zeros((N, C, filter_h, filter_w, out_h, out_w))
59 |
60 | for y in range(filter_h):
61 | y_max = y + stride*out_h
62 | for x in range(filter_w):
63 | x_max = x + stride*out_w
64 | col[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride]
65 |
66 | col = col.transpose(0, 4, 5, 1, 2, 3).reshape(N*out_h*out_w, -1)
67 | return col
68 |
69 |
70 | def col2im(col, input_shape, filter_h, filter_w, stride=1, pad=0):
71 | """(im2col과 반대) 2차원 배열을 입력받아 다수의 이미지 묶음으로 변환한다.
72 |
73 | Parameters
74 | ----------
75 | col : 2차원 배열(입력 데이터)
76 | input_shape : 원래 이미지 데이터의 형상(예:(10, 1, 28, 28))
77 | filter_h : 필터의 높이
78 | filter_w : 필터의 너비
79 | stride : 스트라이드
80 | pad : 패딩
81 |
82 | Returns
83 | -------
84 | img : 변환된 이미지들
85 | """
86 | N, C, H, W = input_shape
87 | out_h = (H + 2*pad - filter_h)//stride + 1
88 | out_w = (W + 2*pad - filter_w)//stride + 1
89 | col = col.reshape(N, out_h, out_w, C, filter_h, filter_w).transpose(0, 3, 4, 5, 1, 2)
90 |
91 | img = np.zeros((N, C, H + 2*pad + stride - 1, W + 2*pad + stride - 1))
92 | for y in range(filter_h):
93 | y_max = y + stride*out_h
94 | for x in range(filter_w):
95 | x_max = x + stride*out_w
96 | img[:, :, y:y_max:stride, x:x_max:stride] += col[:, :, y, x, :, :]
97 |
98 | return img[:, :, pad:H + pad, pad:W + pad]
--------------------------------------------------------------------------------
/dataset/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SDRLurker/deep-learning/f95b0a2c7c4ccec3c7395ad5a648c7664b168866/dataset/__init__.py
--------------------------------------------------------------------------------
/dataset/mnist.py:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | try:
3 | import urllib.request
4 | except ImportError:
5 | raise ImportError('You should use Python 3.x')
6 | import os.path
7 | import gzip
8 | import pickle
9 | import os
10 | import numpy as np
11 |
12 |
13 | url_base = 'http://yann.lecun.com/exdb/mnist/'
14 | key_file = {
15 | 'train_img':'train-images-idx3-ubyte.gz',
16 | 'train_label':'train-labels-idx1-ubyte.gz',
17 | 'test_img':'t10k-images-idx3-ubyte.gz',
18 | 'test_label':'t10k-labels-idx1-ubyte.gz'
19 | }
20 |
21 | dataset_dir = os.path.dirname(os.path.abspath(__file__))
22 | save_file = dataset_dir + "/mnist.pkl"
23 |
24 | train_num = 60000
25 | test_num = 10000
26 | img_dim = (1, 28, 28)
27 | img_size = 784
28 |
29 |
30 | def _download(file_name):
31 | file_path = dataset_dir + "/" + file_name
32 |
33 | if os.path.exists(file_path):
34 | return
35 |
36 | print("Downloading " + file_name + " ... ")
37 | urllib.request.urlretrieve(url_base + file_name, file_path)
38 | print("Done")
39 |
40 | def download_mnist():
41 | for v in key_file.values():
42 | _download(v)
43 |
44 | def _load_label(file_name):
45 | file_path = dataset_dir + "/" + file_name
46 |
47 | print("Converting " + file_name + " to NumPy Array ...")
48 | with gzip.open(file_path, 'rb') as f:
49 | labels = np.frombuffer(f.read(), np.uint8, offset=8)
50 | print("Done")
51 |
52 | return labels
53 |
54 | def _load_img(file_name):
55 | file_path = dataset_dir + "/" + file_name
56 |
57 | print("Converting " + file_name + " to NumPy Array ...")
58 | with gzip.open(file_path, 'rb') as f:
59 | data = np.frombuffer(f.read(), np.uint8, offset=16)
60 | data = data.reshape(-1, img_size)
61 | print("Done")
62 |
63 | return data
64 |
65 | def _convert_numpy():
66 | dataset = {}
67 | dataset['train_img'] = _load_img(key_file['train_img'])
68 | dataset['train_label'] = _load_label(key_file['train_label'])
69 | dataset['test_img'] = _load_img(key_file['test_img'])
70 | dataset['test_label'] = _load_label(key_file['test_label'])
71 |
72 | return dataset
73 |
74 | def init_mnist():
75 | download_mnist()
76 | dataset = _convert_numpy()
77 | print("Creating pickle file ...")
78 | with open(save_file, 'wb') as f:
79 | pickle.dump(dataset, f, -1)
80 | print("Done!")
81 |
82 | def _change_ont_hot_label(X):
83 | T = np.zeros((X.size, 10))
84 | for idx, row in enumerate(T):
85 | row[X[idx]] = 1
86 |
87 | return T
88 |
89 |
90 | def load_mnist(normalize=True, flatten=True, one_hot_label=False):
91 | """MNISTデータセットの読み込み
92 |
93 | Parameters
94 | ----------
95 | normalize : 画像のピクセル値を0.0~1.0に正規化する
96 | one_hot_label :
97 | one_hot_labelがTrueの場合、ラベルはone-hot配列として返す
98 | one-hot配列とは、たとえば[0,0,1,0,0,0,0,0,0,0]のような配列
99 | flatten : 画像を一次元配列に平にするかどうか
100 |
101 | Returns
102 | -------
103 | (訓練画像, 訓練ラベル), (テスト画像, テストラベル)
104 | """
105 | if not os.path.exists(save_file):
106 | init_mnist()
107 |
108 | with open(save_file, 'rb') as f:
109 | dataset = pickle.load(f)
110 |
111 | if normalize:
112 | for key in ('train_img', 'test_img'):
113 | dataset[key] = dataset[key].astype(np.float32)
114 | dataset[key] /= 255.0
115 |
116 | if one_hot_label:
117 | dataset['train_label'] = _change_ont_hot_label(dataset['train_label'])
118 | dataset['test_label'] = _change_ont_hot_label(dataset['test_label'])
119 |
120 | if not flatten:
121 | for key in ('train_img', 'test_img'):
122 | dataset[key] = dataset[key].reshape(-1, 1, 28, 28)
123 |
124 | return (dataset['train_img'], dataset['train_label']), (dataset['test_img'], dataset['test_label'])
125 |
126 |
127 | if __name__ == '__main__':
128 | init_mnist()
129 |
--------------------------------------------------------------------------------
/decision.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SDRLurker/deep-learning/f95b0a2c7c4ccec3c7395ad5a648c7664b168866/decision.png
--------------------------------------------------------------------------------
/gates.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SDRLurker/deep-learning/f95b0a2c7c4ccec3c7395ad5a648c7664b168866/gates.jpg
--------------------------------------------------------------------------------
/layers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SDRLurker/deep-learning/f95b0a2c7c4ccec3c7395ad5a648c7664b168866/layers.png
--------------------------------------------------------------------------------
/lena.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SDRLurker/deep-learning/f95b0a2c7c4ccec3c7395ad5a648c7664b168866/lena.png
--------------------------------------------------------------------------------
/neurons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SDRLurker/deep-learning/f95b0a2c7c4ccec3c7395ad5a648c7664b168866/neurons.png
--------------------------------------------------------------------------------
/perceptron.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SDRLurker/deep-learning/f95b0a2c7c4ccec3c7395ad5a648c7664b168866/perceptron.png
--------------------------------------------------------------------------------
/sample_weight.pkl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SDRLurker/deep-learning/f95b0a2c7c4ccec3c7395ad5a648c7664b168866/sample_weight.pkl
--------------------------------------------------------------------------------
/xor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SDRLurker/deep-learning/f95b0a2c7c4ccec3c7395ad5a648c7664b168866/xor.png
--------------------------------------------------------------------------------
/목차.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# 밑바닥부터 시작하는 딥러닝\n",
8 | "\n",
9 | "# Deep Learning from Scratch\n",
10 | "\n",
11 | "## Github \n",
12 | "\n",
13 | "https://github.com/WegraLee/deep-learning-from-scratch\n",
14 | "\n",
15 | "## 책주소 \n",
16 | "\n",
17 | "http://www.hanbit.co.kr/store/books/look.php?p_code=B8475831198\n",
18 | "\n",
19 | "\n",
20 | "\n",
21 | "## 1장\n",
22 | "\n",
23 | "http://nbviewer.jupyter.org/github/SDRLurker/deep-learning/blob/master/1장.ipynb\n",
24 | "\n",
25 | "## 2장\n",
26 | "\n",
27 | "http://nbviewer.jupyter.org/github/SDRLurker/deep-learning/blob/master/2장.ipynb\n",
28 | "\n",
29 | "## 3장\n",
30 | "\n",
31 | "http://nbviewer.jupyter.org/github/SDRLurker/deep-learning/blob/master/3장.ipynb\n",
32 | "\n",
33 | "## 4장\n",
34 | "\n",
35 | "http://nbviewer.jupyter.org/github/SDRLurker/deep-learning/blob/master/4장.ipynb\n",
36 | "\n",
37 | "## 5장\n",
38 | "\n",
39 | "http://nbviewer.jupyter.org/github/SDRLurker/deep-learning/blob/master/5장.ipynb\n",
40 | "\n",
41 | "## 6장\n",
42 | "\n",
43 | "http://nbviewer.jupyter.org/github/SDRLurker/deep-learning/blob/master/6장.ipynb"
44 | ]
45 | },
46 | {
47 | "cell_type": "code",
48 | "execution_count": null,
49 | "metadata": {
50 | "collapsed": true
51 | },
52 | "outputs": [],
53 | "source": []
54 | }
55 | ],
56 | "metadata": {
57 | "anaconda-cloud": {},
58 | "kernelspec": {
59 | "display_name": "Python [Root]",
60 | "language": "python",
61 | "name": "Python [Root]"
62 | },
63 | "language_info": {
64 | "codemirror_mode": {
65 | "name": "ipython",
66 | "version": 3
67 | },
68 | "file_extension": ".py",
69 | "mimetype": "text/x-python",
70 | "name": "python",
71 | "nbconvert_exporter": "python",
72 | "pygments_lexer": "ipython3",
73 | "version": "3.5.2"
74 | }
75 | },
76 | "nbformat": 4,
77 | "nbformat_minor": 0
78 | }
79 |
--------------------------------------------------------------------------------