From 6f00bbc2660a917b74c6e28e0f8c2dc1cc8f3074 Mon Sep 17 00:00:00 2001 From: keon Date: Mon, 25 Mar 2019 20:46:01 -0700 Subject: [PATCH] simplify the descriptions --- .../check_installation.py | 54 ++ .../torchvision-and-torchtext.ipynb | 32 - .../torchvision-and-torchtext.py | 2 - .../00-image-recovery.ipynb | 178 ++-- .../00-image-recovery.py | 63 ++ .../01-basic-feed-forward_nn.ipynb | 847 +++++------------- .../01-basic-feed-forward_nn.py | 136 +++ .../01-basic_feed_forward_nn.py | 122 --- .../basic-feed-forward_nn.ipynb | 699 --------------- 03-Coding-Neural-Networks-In-PyTorch/model.pt | Bin 0 -> 893 bytes .../01-fashion-mnist.ipynb | 226 ++++- .../01-fashion-mnist.py | 4 +- .../02-neural-network.ipynb | 346 ++++--- .../02-neural-network.py | 12 +- .../03-overfitting-and-regularization.ipynb | 121 +-- .../03-overfitting-and-regularization.py | 6 +- 05-CNN-For-Image-Classification/01-cnn.ipynb | 614 +++++++------ 05-CNN-For-Image-Classification/01-cnn.py | 8 +- .../02-cifar-cnn.ipynb | 10 +- .../02-cifar-cnn.py | 9 +- 06-Autoencoder/01-basic-autoencoder.py | 166 ++++ 06-Autoencoder/02-denoising-autoencoder.py | 18 + .../01-text-classification.py | 451 ++++++++-- .../02-sequence-to-sequence.py | 176 +++- 07-RNN-For-Sequential-Data/03-Seq2Seq_gru.py | 112 +++ 08-Hacking-Deep-Learning/01-fgsm-attack.py | 199 ++++ .../02-iterative-target-attack.py | 87 ++ .../01-gan-explanation.py | 323 +++++++ 09-Generative-Adversarial-Networks/01-gan.py | 132 +++ .../02-conditional-gan.py | 194 ++++ .../01-cartpole-dqn.py | 171 +++- 31 files changed, 3333 insertions(+), 2185 deletions(-) create mode 100644 02-Getting-Started-With-PyTorch/check_installation.py delete mode 100644 02-Getting-Started-With-PyTorch/torchvision-and-torchtext.ipynb delete mode 100644 02-Getting-Started-With-PyTorch/torchvision-and-torchtext.py create mode 100644 03-Coding-Neural-Networks-In-PyTorch/00-image-recovery.py create mode 100644 03-Coding-Neural-Networks-In-PyTorch/01-basic-feed-forward_nn.py delete mode 100644 03-Coding-Neural-Networks-In-PyTorch/01-basic_feed_forward_nn.py delete mode 100644 03-Coding-Neural-Networks-In-PyTorch/basic-feed-forward_nn.ipynb create mode 100644 03-Coding-Neural-Networks-In-PyTorch/model.pt create mode 100644 06-Autoencoder/01-basic-autoencoder.py create mode 100644 06-Autoencoder/02-denoising-autoencoder.py create mode 100644 07-RNN-For-Sequential-Data/03-Seq2Seq_gru.py create mode 100644 08-Hacking-Deep-Learning/01-fgsm-attack.py create mode 100644 08-Hacking-Deep-Learning/02-iterative-target-attack.py create mode 100644 09-Generative-Adversarial-Networks/01-gan-explanation.py create mode 100644 09-Generative-Adversarial-Networks/01-gan.py create mode 100644 09-Generative-Adversarial-Networks/02-conditional-gan.py diff --git a/02-Getting-Started-With-PyTorch/check_installation.py b/02-Getting-Started-With-PyTorch/check_installation.py new file mode 100644 index 0000000..edef05d --- /dev/null +++ b/02-Getting-Started-With-PyTorch/check_installation.py @@ -0,0 +1,54 @@ +is_ready = True +installed_packages = [] +uninstalled_packages = [] +try: + import torch + installed_packages.append("파이토치 버전:%s" % torch.__version__) +except: + is_ready = False + uninstalled_packages.append("파이토치") +try: + import torchvision + installed_packages.append("토치비젼 버전:%s" % torchvision.__version__) +except: + is_ready = False + uninstalled_packages.append("토치비전") +try: + import torchtext + installed_packages.append("토치텍스트 버전:%s" % torchtext.__version__) +except: + is_ready = False + uninstalled_packages.append("토치텍스트") +try: + import numpy + installed_packages.append("넘파이 버전:%s" % numpy.__version__) +except: + is_ready = False + uninstalled_packages.append("넘파이") +try: + import matplotlib + installed_packages.append("맷플랏립 버전:%s" % matplotlib.__version__) +except: + is_ready = False + uninstalled_packages.append("맷플랏립") +try: + import sklearn + installed_packages.append("사이킷런 버전:%s" % sklearn.__version__) +except: + is_ready = False + uninstalled_packages.append("사이킷런") + +if is_ready: + print("축하합니다! 3분 딥러닝 파이토치맛 예제 실행을 위한 환경설정이 끝났습니다.") + print("설치된 라이브러리 정보:") + for pkg in installed_packages: + print(" * " + pkg) +else: + print("미설치된 라이브러리가 있습니다.") + print("설치된 라이브러리 정보:") + for pkg in installed_packages: + print(" * " + pkg) + print("미설치된 라이브러리 정보:") + for pkg in uninstalled_packages: + print(" * " + pkg) + diff --git a/02-Getting-Started-With-PyTorch/torchvision-and-torchtext.ipynb b/02-Getting-Started-With-PyTorch/torchvision-and-torchtext.ipynb deleted file mode 100644 index 9e2543a..0000000 --- a/02-Getting-Started-With-PyTorch/torchvision-and-torchtext.ipynb +++ /dev/null @@ -1,32 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.5.2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/02-Getting-Started-With-PyTorch/torchvision-and-torchtext.py b/02-Getting-Started-With-PyTorch/torchvision-and-torchtext.py deleted file mode 100644 index e56ad26..0000000 --- a/02-Getting-Started-With-PyTorch/torchvision-and-torchtext.py +++ /dev/null @@ -1,2 +0,0 @@ - -# coding: utf-8 diff --git a/03-Coding-Neural-Networks-In-PyTorch/00-image-recovery.ipynb b/03-Coding-Neural-Networks-In-PyTorch/00-image-recovery.ipynb index f82b41f..7af3055 100644 --- a/03-Coding-Neural-Networks-In-PyTorch/00-image-recovery.ipynb +++ b/03-Coding-Neural-Networks-In-PyTorch/00-image-recovery.ipynb @@ -6,81 +6,31 @@ "source": [ "## 프로젝트 1. 경사 하강법으로 이미지 복원하기\n", "\n", - "지금까지 Autograd의 사용법과 경사 하강법에 대해서 봤지만, 아직 정확한 구현법과 머신러닝에서의 쓰임새에 대해서는 깊게 다뤄보지 못했습니다. \n", - "물론 파이토치는 대부분의 학습과 최적화 알고리즘들을 제공해 주기 때문에 직접 경사 하강법을 구현할 일은 거의 없습니다.\n", - "그래도 기본적인 머신러닝의 학습방법을 이해하는 것은 앞으로 더 깊게 딥러닝을 공부하기 위해선 필수적이라고 거듭 말씀드리고 싶습니다.\n", - "그런 취지에서 이번 프로젝트를 통해 아주 간단한 모델과 오차함수, 그리고 모델의 학습동작까지 파이토치의 도움을 살짝 배제하고 코딩해 보도록 하겠습니다.\n", - "직접 구현한 경사 하강법을 이용해 모델의 오차값을 최소화 시키므로써 일반적의 머신러닝 학습 메카니즘을 배워 보는것이 이번 프로젝트의 목표입니다.\n", - "\n", "### 프로젝트 개요와 목표\n", "\n", "이번 프로젝트에서 우리가 풀 문제는 다음과 같습니다.\n", - "weird_function() 이라는 함수가 original_image 라고 하는 어느 이미지 파일을 입력받아 broken_image 라는 오염된 이미지를 리턴했습니다. 우리는 이 오염된 이미지와 원본 이미지를 동시에 파일로 저장하려고 했으나, 모종의 이유로 원본 이미지 파일은 삭제된 상황입니다.\n", + "치명적인 버그가 있는 weird_function() 이라는 함수가 original_image 라고 하는 어느 이미지 파일을 입력받아 broken_image 라는 이미지를 리턴했습니다. 우리는 이 오염된 이미지를 삭제하려고 했으나 실수로 원본 이미지 파일을 삭제해버린 상황입니다.\n", "다행히도 weird_function()의 소스코드는 삭제되지 않았습니다.\n", "우리의 목표는 오염된 이미지와 weird_function()의 코드만을 가지고 원본 이미지 파일을 복원하는 것입니다.\n", "\n", - "### 문제 접근\n", - "\n", - "대부분의 프로그래머는 다음과 같이 문제에 접근할겁니다.\n", - "\n", - "```python\n", - "1. weird_function()의 소스코드를 분석한다.\n", - "\n", - "2. 분석을 토대로 weird_function()의 동작을 반대로 이행하는 함수를 구현한다.\n", - "\n", - "3. 2에서 구현한 함수에 broken_image를 입력시켜 복구된 이미지를 출력한다.\n", - "```\n", - "\n", - "솔직히 위의 해결책은 꽤 합리적인 해결책입니다. \n", - "하지만 weird_function()의 모든 동작을 되돌리려면 함수의 동작을 모두 직접 일일히 파악해야 하며,\n", - "이것은 함수에 대한 설명이나 사전지식이 없으면 아주 오래걸리고 까다로운 작업입니다.\n", - "\n", - "우리가 택할 해결책은 좀 더 머신러닝과 수학적 최적화에 가까운 방법입니다만, 이 방법을 선택하기까진 다음과 같은 사고과정이 필요합니다.\n", - "\n", - "```python\n", - "1. broken_image 와 사이즈가 같은 random_tensor 라는 랜덤한 이미지를 생성한다.\n", - "\n", - "2. random_tensor를 weird_function()에 입력시켜 hypothesis 라고 하는 이미지를 출력한다.\n", - "\n", - "[팩트] 원본 이미지인 original_image가 weird_function()에 입력되어 broken_image를 출력했다.\n", - "\n", - "[팩트] 인위적으로 생성한 random_tensor가 weird_function()에 입력되어 hypothesis를 출력했다.\n", - "\n", - "3. 그러므로 hypothesis 와 broken_image 가 같은 이미지라면, random_tensor 와 original_image도 같은 이미지일 것이다.\n", - "\n", - "4. weird_function()을 수정키지 않는 대신, random_tensor를 조금씩 변경시켜 weird_functino(random_tensor) = broken_image 라는 관계가 성립하도록 한다.\n", - "```\n", - "\n", - "이번 장에서 배워봤듯, 모델이 학습한다는 것은 모델이 출력한 결과값과 정답의 차이, 즉 오차(Loss)값이 최소화 된다는 뜻이기도 합니다.\n", - "우리가 이 문제에서 최소화 시키고자 하는 오차값은 모델의 결과값인 hypothesis 와 '정답'이라고 할 수 있는 broken_image 사이의 거리값 입니다.\n", - "\n", - "이 거리값을 파이토치의 Autograd를 이용해 random_tensor로 미분하여 오차의 최소값이 있는 방향을 알아냅니다. 그리고 그 방향으로 random_tensor를 Learning Rate라는 수만큼 조금씩 변경시켜 주는 것이 바로 경사하강법의 기본입니다.\n", - "\n", - "지금까지 이 문제의 해결법과 접근법, 그리고 구체적인 경사하강법에 대해서 전보다 깊게 배워봤습니다. 지금부터는 이 문제의 답안 코드를 함께 코딩 해 보고, 결과를 확인해 보겠습니다." + "*Sources are based on https://github.com/jcjohnson/pytorch-examples, NYU Intro2ML*" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 577, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", "import torch\n", "import pickle\n", - "import matplotlib.pyplot as plot\n" + "import matplotlib.pyplot as plot" ] }, { "cell_type": "code", - "execution_count": 578, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -90,27 +40,29 @@ }, { "cell_type": "code", - "execution_count": 579, + "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 579, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "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\ngg==\n", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAP4AAAD8CAYAAABXXhlaAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvnQurowAAIABJREFUeJzsvWeYZdd1Hbjuy69y7Krq7uocgU4gMgEQIEiKUZEai5It0rL88ftGlocK4xEtf/Zoxp7Plj1DyTMey6JGgRxrLMmSzSCDGQABEEToRupG51QdKnTlelX18rvzY619z3nV3ehmUIl23f2nwrv33HPPve/stPbaQRiGiCWWWNaWJP66JxBLLLGsvsRf/FhiWYMSf/FjiWUNSvzFjyWWNSjxFz+WWNagxF/8WGJZgxJ/8WOJZQ3K9/TFD4LgfUEQnAqC4GwQBJ/8fk0qllhi+auV4LsF8ARBkARwGsB7AFwB8DKAnw7D8Pj3b3qxxBLLX4Wkvodz7wNwNgzD8wAQBMGfAPhRADf94ifbWsNUdw+Q1GaT8DadGo2PRKbOnwE/C6aS/DgfAAA29M5Ep4wWO3nsnI5p4znJdAMAUK84gyah/4VFHpuo8P91nZNO1wAA1Zq3JJpeKsU51Yv8LFnSlDt0vWQ9OsWOsXsMUvwZ1mwu/kbLewqq+iQdNs21UdU5jcCdkrzFRv1WH9sUGivGCt34QTnQXOwYHhzokIT3zGx9g3rQNG6Y4TGZLNe0UnZrmlzS+JpLppsPoiNVBABMldrcrSzrWWl9Ghn+zLWX+bnmXV7KuPH1bBq6ZFvPMgCgL7UIADg/3w8ASC+6e67r9Ibu2d7BZII3VFtKR8dGc2nnZ+1ZXnChmOc5Nq5nSwedXIf2NOc9u9jaNNe6mz6Qbl7vsGHviPcOAAj998A+agSozcygvrjUfPAN5Hv54m8AcNn7+wqA+1ceFATBxwF8HACS3d1Y/6u/hEYrFzbVXo2Oa1zLAQDatswDADL6suV/rwsAMLWPU/2nH/v30Tm/8eaHAAC5L3ADmHo7F7hrXQEAMDfWER3b0r8EAKgd5//aNPPZt/PFWz84CwAYvdblJq+17evTeEf7OP4J/n/qXXyQ3d2L0SkLx3t5nQ7OP93FY2ozvD//gdkXJjfOF7y4gfPvGOT1FsbbAQCJZfcWNdrdJuPPMdpEa94b12g+NGjl+KE2p0Qb179RSUbH5Eb4FpbXaQPu5vzTaf6dy7hnZuubnuX5yRLvp7SBx2zbNgEAOH9hIDqn9wVeu9rGY4d//AIA4N39XNTfO/mQu7VX+VxbJnhvixt5zq7HzvNWG7zXsy9sjs7pPM2f5W4e++BHXgUAfLz/mwCAn/zKLwIAhp526zS/lb8X1/MeWzdy/Tvz/GZOveTmn5/guMVHeczjW88AAL785p0AgO7nuX71rPvu5T/AdXjH4DkAwH/8Fr8mnSe5bgvb3INKDPKaSb3/5cUsACA97jYfAKj2eO9BSpvFchJj//Jf43bke/ni35aEYfhpAJ8GgOym4TBMh0BGO2nJu3yeN2Ive2pei/Iz/MKapvmNT/+t6JSlLXoZPzwNAAgudgMACmf45W3dVoiObddDnNjEhaxroxn4Chd05sMtAID/4e4no3PsJSy8RC3R6OC8Z+/kQ02PcKyZovviBAPcSNq7qGnac/zijF2jRsiPu2NLg5y/feETJb6AC5PSell+3vT9tZ1faxhplrJ+8ff6LI9JLHCdQ6nx/ftGAADHRtYDALb8mTvp6mNc53/67j8HAHzq9LsAAItvcEOb66u58TWFes4sBzT9NKsNdTd+epn/K/Xzf3d2jgEA2hN8PuXzbrPuucJjp96mC3VyQzl+dRAAcGDjVQBA/pobv/cNKo7qb/LZ/9K6bwAAPvDU3wcAdJziWszc4TbgWqusQa1pWhZcoZTV/bjxa3yMSOnLdr7AdUmN8Qtf6eCxizvdBpnRBvWfTx4EAAQ1HjN3B9cyO+XeidTrfA/zj0wBAPYNcX1en9wJX2wMAGi5wGsvba4BwS0sQsn3Ety7CmDY+3uj/hdLLLH8gMv3ovFfBrAzCIKt4Bf+IwB+5q1OSGTqaB9ewNIyd9LIhwXQepIaeHmIO+nj73wNAPDsF+4CALRp9+/56Mh14546S81lGi41o9t6qTM6ZuYu7qrrB+YAAFf3cademuOxrc9Q0/xO/R3ROR/acQwA8I1vPsBxi5zvD/3UCwCAz53gDj7wpWx0zuwejrdvJ83RhSrva6LS7AcDQKD/hZ3S7PLvIp++fv05CWn2hmIHibzM95L84WXPd81pXLlWNm4q4N/hMueamStF51TXcdz2BH3u+QI1EOSHpmfdK2NzSG+mVVaal1l6jZbF2fqQznU3MP6YWQH835+9eJ8G1jEdzqKYfFC/yD1KXtP4W+haXV6ghVdtjU7ByAf5zOuTnOcv4iM8J6dxQ46R8tzgzAG+Eyn59JVa89citex+N7djusgF+eBuviOf6twAAOg5xuezvNFp8dl5TrBW5v9aN9EaKZW4TomrLe4CmtbMLM/pyPHZ1IdoOWbP8X3KzLvxy916Fzqqt44B2T3d1lE3kDAMa0EQ/CKArwBIAviDMAzf/G7HiyWWWFZPvicfPwzDJwA88X2aSyyxxLJK8lce3PMlnaxjsL2A0wrg5cbc5Uu9Sn8N0LR5/upWAEBmgZ9PH+TnD3Rci8754pFDAIDsBMcp9ysSOkCzruyZmMEEozJjozSr2rcqCNSjKPMc5xSccnbjxDDN//n9DNTs/re0+T5/6AAA4MVH/w3nNP6r0TlDz/Oar+7ZCADYOzDOOc7Shqt0OVNs0z4Gbq68QlclNy0770GanpkU76NwrDc6xyLn1XU0/VJKQ4azNAGjQBuAdJ7zriUV3FP67dUTW3ivcjUu/rC750Sa6/+J536ac2jhGAcfZLh85Hd3RccuD3G8f/TDfwkA+IdP/ncAgC5F1ufex7EyGReBXh5j4NLShtkZjlFSFmHnnS5MNFbgMym/yWCtpQDNpahWlJ24cyk6J7zE55s6wXtaaKfLYkEveTkMhEkeGeA1nz22GwCQv0QTvLFfweG7XZC4WNW7e5Tv07e2bAcAPHSIN/36+TsAAN3H3HOYvpfjDW9hwG58lmMkz3OMet4dW+y1CfKc5Sp/9vbQvakckalfcOcs72RA+f27T+Avcs5teyuJIbuxxLIGZVU1fiMMsFjNoOMEL5ubcbvW5D38ffsAU3PJgJrz1AGlNy4ymPLE03e7Abu4a9cttmYaXkHD7GWHjGg5ROCPgT5Kr/Tw5xDHWP8oNfPohb7onJee2avrcBceewcDR9mj/PeL91ETH7jvXHTO9DdpqVQvUrMt93IOy5upOVNecOzKq9T0NQX3FhWkgbRiooPnhBvK0TnhuNKRBQWGOi0Vxc8DL3VWrzIAlGuhRigueUEkAKl11IapcQeaqU9w/KCP125roQYZzNH0OusORVLK9MjSFl2cPyylVS1yjoGXYgxbuN6ZaU7YAmeBgDyb2majY89P8FlkC3pmZhXqObe2cm6Fqy4F2CqraXEPxytNUrtCgbXSI9Le0/nonOcO6zkrWFnazvXe2kOr8OKx9dGxnZM8ZpZpeyxUFJSuKp2nZ1hrczed0D2Pz3CetUmeEwg85gdv0wKjpXdxvVvSfA4XTzKlnNe0Kwk3fkpp33d0nMJXk7HGjyWWWG4iq6rxa8tpTB0ZwKYj3OZHf9mBHO4bpL/76pP0s+ot2t3ls9Za+bPvjqnonB1d/P35V3hOz0vyi4a4Gzb2OkTd/FmmflJFftZ53yTHVapl/Pg6AEBy0O2Y2UHhek9R07eNcWu+9jaO8YUZphobods/L7+Hn/W/zL9PZwh1ePx+pn2e+9r+69Zl2w5aGyOvU7NkZjleTai5urc/yxBCoDSewWLrSk810s6KSimFZeAnA30YUKUqqGvazwCt5/2b5TB7ies21cP1KjiQHFr4yPDKDO8x10sToNJJLZueEIpt2EOmGRzZYLL6mRqhFqztdPf6d/d/CwDwB+feDQBouyyI7ha+N0Wl1Fov3iC1pbhCQylLswbN4vMto9Si1lsArS7507PLguF6KdLCJq2zADwnLjJluX6IlkptE9cvd9xZFHZvyT20NtIbOP7yBN+9zIybf1UWXErzvDhGq9JiIcUBrV+7i1Ekxzj+Pz7yo7i6dHtQmljjxxLLGpRV1fjINoAdS7j0fhUpvOJ8zhc3Kdq+nxHtxTlFZwWFrPZyh5uYcKCc0peppYN7qWk+8ctfBAD8xlc/DADY8Cdu173yAWHPh7kjLz1Hn6m2i1r14N3EjL/xxpbonNok51BVHGBqP3fmugo0Ts0Rw33l9aHoHCvKyf4tavHUq4SXvvg5ZgIy989Fxy5e4zqMvErwR3ITo9ONJf4/+wp/Jh5yfm9KEezZaWrVpXnu9kI8I+kV9FTl43e10cJa7qD/bkCYerc05wYXdU+NSMvJMup9O+/jR/oIqDo6tTc6Nr3Eex1qoT96/iKfR4cyMaXttJhSXhFT4hSDBJZ96Ht8FABwaYwxlxe+7Cyipwd1rUEV+2zkurcIaFMqyFfucCZLtVvP2WDDS1yDRpfiJfp/Ztpp2YaKilqGqImtBmBpUfUVbc5iSQooVVd8qUd1HKMXGY8wqHlx0LNypJxrVxQg6eU716brLSZc4CRZUCxilM8+6FF8Zit/pido1Ybd7p4bffys7XBLU13HW0ms8WOJZQ3Kqmr8sJJAY6QVXQcZuZ8ac9o7ucCdbrHKyGcoFRZupjYPpgW1nHZTXtipYxSx/Z+f/Al+IK0787dcfjdvJZzadRPKp7ed4LgnLzEfe9+7T0bnzJao8Sc+v4lzkOJa0O7boZJMv7y4/zB/Lm6npfK+d/Mf3/hP9wIAqsfcPef2cMevFri7V8dlAW1SbEFFQNUL7pz8ZmoY82Hr87KIFCFuZD2HXdp/fIR+YqKVWi+/g9Hq+ivMjydd0gAdjyu7MUbffvQUtfhXuvbxvu50BwdHOb9vHWUBSSDI9JL59IvCSCw77ZoRpNQyGYHy61Yx2FSRpHhAUtDjurRtcdGKZ/jD4kE8WOugd8KC3xZbqAkm27RMCnJkFUEvVXhM8hI1fnWoEh2786AKnN5gsKPlD7lOywd4vR/50ecBAE+NuqKawsu0LstWUTfD+Vc0fnJrMTo2Y0VQr/N7UCnzmG130Xe/lKNl1HbYWcsWJ6k8UED4n1eUZN5EYo0fSyxrUFZV4ydzNXTsncbsCZUy+jwcIitoGeFuW91PbforB78OAPjU538EADD8dbf7nv8wd9n797Ig5sgINXPyLP3U2qzL7yZ2Ubv27GA+f74g1NQCtW3bZU7mxKSrvX5041kAwETIcbNz3E0PbroCALhSoMZsP+f2Tysh3dzCXfxLp5nwjXK23lZryLO6ykIjsgVZEEb6kC54JZhZ+XMq951YoAZIGoGGU66RPxtIY4Z1Xtx8fwvn+xHujPzxYNlIR/hZa9KYS9yxlswwXgUrs04IEdjIXX/PRnZhUfY5Rc7NN/XnH0hLpzPKXKj82QqSbFzfygmE4QiNeMVSFpp3RMjily8btkOafnlS/rXwE/u3jEaHHj3NDEbnac5hnrANlGSlXVjiux16F0jISGq/IIvlPlqi/btpvV290uPmf4ya3NbJrLNJkXeEiuDXvMKkomJQLclGZEHdSmKNH0ssa1DiL34ssaxBWVVTv15MYeF4L1Iyfcp9XhpJ5mLiXppBVcFW/+VTpNdKKOUy9nZX+54W0GJEddmNcZpBjXYFeFrd+LljTH9Nr6MpdWDfRQDAcTDdVmzQdipddIG0JxssSLFFSmi4B3voWjS6uW9+bu5d0TmNS2LR2a2iGZmegQJQqSFX3N0QpDU3zitUOo1Vh8cW1zcz8wDA1AWahdkBjpPuElXTCNfLwxKhtZPHFMuM/pQKXLvyDM1rW8lSvwsImYmaKnAgS+sV64Lfev6ZmZu5PM3cRQUaze2oZq8PNFmANIi4/OTWCFzkW6r2mfEhljTjhJn8CuRZIJgf6qfxLeq9Ms7DhvgXLIXHc8QRKLqrZUFgIVj01QXnMgbiZLB7N0CNTfzUFIOhjSZfollaW1RgpbSkT5dmgdaKUpTv2MA089MXGCzsI5MY5jxCng3bCGSbL+Zues2VEmv8WGJZg7KqGj8ICZnNCXWbPeDKHd89fAoA8PmnycjSEWlOlZ2uU9FFxhXepC5w253oUIBoQBBX7dStfU67ltr4v+5v8thTE0zfrRNAZfYeXifwON+shDQ9IAYYBa3+/VnO8d8e+GMAwGe3vCc6Z/Pnmao8+yB3348dIlvPZ559hGMc81hk7xCf4AFq7ZSIQIPzPLesoE0j5VlGxi6kGOTmfoJ7rqQ5rpGIAsDMAMe7ey+1xkie1sL0BVpIRqhpZc0AMP1Vwoare7neP3SAFUmvTRNkNPglR/o4v40/3zlMwsm/nCdIKRDFbUKQ4dADFQV1KxGW1jbNaPx9niZOChZbmJZ61TjJQT7XqiyYRMHNPyFro7FBxUX9DI4tfIOWnQUPH/vwkeicJ44zABt+g+vT/jjXtGWA1kLh2XVu/B7O79CPkEz6W8eoejf9BQe+9BOc6z9/6C+ic3599G8AAHoP852eFSdkbTuRTnnvPV0UnVByicd+9TWmUS21ee1BvYteWa4FB4NUiEY9BvDEEkssN5HvuqHGdyPZzRvDwX/0iSh91HXU7TtzD1DD/F8PU4v+4tc+CgAYeI7H2E4X5jztNy0feRd39VpN/Prnqf1qPR4jrBExGCOu3bYUTqqPGqJacBZF/jLHL/eKrVYa36izHztESujnvrkvOqf9ou7nQVofgfzHlOipK4tu/K5X+Pv8Lo6/58AlAMDJy9ROxuLrEzVY6s/mbT5+PqeU2kvd0bEdFwRx/Xmmo35mw0sAgH/2TaZGW0bEuTcfnYKlR2iF3LOJc3njC4TNLgvW+/C9J6JjX7qsip0TjJ+0j3Ce8zv47+H7CDqxQhMACCwOI+ss4v0T02zFi/tY0U9ZsRDzuUNZAlF1kd8/QWnBRpdx7PFHZpTnVtZzndZv8PozCODUdobHlMQ72Bji2g71uwXa202q7Ge+Suum7yiPHX0/x31kD62fZ1/dE53Tfo73WNipORkvnt7J4eFpN5djNOW631SR1Ls4hwe20mp7aUTAoW+7fF7XGV577G+XcfmT/w6lc1dvyasfa/xYYlmDsqo+/u7OCXzhA5/CTx79OQBA7qtOO2W/yt3217t+HACwZQd31kvFoeZBvL3MAC5L8/TbNw5xFx8LqPF9eG+tX1kDRcjt3JpAGnesp69/6prz51pe4vkp+Vul+1TmK639zcOkWcpsdeW/s12KrApkEgFJBM8MWp32nr9DHVsUQT8zxmtnRZlV6tP8PUhwZqoZ2poS3di6ds7hYofXEERrFWlcuuloG+CxuReZwai2u0Xt6aTG//abUtt7uD4/fYh1xl/4k4ejY7OKQO/8MGmn3vgW/d0WKZyZFcQfgCsrju5R2jqn+2qkHIJnQH0RCjmu99yUfFkDILVyAg0vFZBQkUyo4pywTXEGDxgENLcfSKrvQF01XTWzFmRhzLe5aHn/AOdkMNkoi6LszWsTWmRPpRYUL0mKkAOjHK/nGGdx5TEH4PmjH/93AICPdf1dAMDWz3Dir97Nd+2+H2Zs4dXMhuic2QSfY9hw4LZbSazxY4llDcqqavzRaif+6dh7MT1LjZw56C7fc4Kaq3qYVsCGD5Dl4dI65a1Pczsub3fkHeYPWr7XimYmtPG1jLt9fU6MWnXl9uvys/pVVnl8jL5Vddbt7mmjkFIg3ggtbOdOtvNC1VHnb/WckLY7yOu8/y4yjr8yQPLNpaedRdF6VZHa+zjuO7cRIvzmDH382jm7cHQKKir0yF5TAcxpRu4b9/I+1h2aiI6dKXIcTDFW8H+feRQAUFTmoiHCEr8sN2lRYevZpoj58QVaXqV1Ljdv3PRTRc7TCCssV59QnrqlzRX21AWVLokEZPMmpnhm32Q2oeOCu9fsvYKitnGd58B3IWpDZtmCslPn9XxzbwKDHlf7lbVRwdDohLOMwja9Exonredq8ZlFj5rsT5dI/RaK534iLTITWSyFhqi+Um6dMmOCoW/i/+4TcelhECeS9ijifvU4MwBWfluQYl8WOWhXmnGPuhe9T30XYbpY48cSyxqU+IsfSyxrUFYXshsmMFvJR91ZfdhkLdccdJspMzA02MdUyswZmoiZEQfZTdxB8zavirXjF2kuWnyo2O+unZqi6WR14N1DBE/UZDI1rvB6SQ9qWRF6t67qr7vWMy12ZpoDh1+n6Vn1mGdn7udcMq38+cIY0y+zU+2aqzt2do+1pOacnjpJ7sA2NdxMbGWgLf2Kx9CiuvW8WIPnrnHc82dk1qc9mKwqxnZtYeDy4gusLBt+lmbj5Y/SNfrkXV+NTvnfXyMYqeMkr7Owj2NMFunODH7bC05u49p9ctuXAACfmP0pAEDjec53QXyGxgsI3KD9s9XjW89Pb/pTy3wmmzsJqLEUnaVkw5VtugEEGtd4C61q7sE9ZEJ+7UtMT7YdcTpv4QMMdvZpvSeMDddciIw/KT7ArMaPYOdDqqkX578Pw61uVmp3hib9i28SPIYWjpsfd1/D2QXe844HmE4dqfD9aTvPY17bQtu/XnPujUHJs7lqXJ0XSyyx3FxWVePnklXsaZ/AazNMFWXm3E49e4fVoPPnyTepnZI93C2Td1K710ac9kucprbruZ/18bW6+NBOiuHE61pTEze+1bwvLimIZ8AeFYlYc0IAqIvD3kA58/fS6rCmnwNX1dr7owvROR/d+gYA4HN/yEDa4jpqvYceI/Dl+eXd0bHWH73aqa4yshIK19RtRsUiiXsctDn9koJHX6G10fcBBscO9RMs840XHGedac/LswxkZcU5P3mQ1s+e9Vy33z3rUnS1aa6Lcdi39lCTGb97fcEriFGObDDp7h8AWifUfFKa/h2bz0afvfQ0mYmzs+LV36x058PkIpw/5YqkGud4jzNtgjJL86Z6uU7GppPw3iN7jtbQM1CQbWxZzE5SdTVHx4h2Fc305anxRyeVVhWzbX2PY3JKtAt4NM/nkFGxVEVzGXyG50y8z6XW/s4hsvL80VF2AU2O8f1pGeWxnRfdmtbu5++/solW2H/f+fOc2+u8n2t3893o7XIp5MVWWgk+NPpWEmv8WGJZg7KqGj+BELlENWJMyU957KgCkdR2UMMYlNNKbWvt0theXMDYbq0EckHFHHndVc2Duhp002C3QaLZFwo3idvvmkvnDX2b17z8Q/z754deBwD89mGWCltBSU+rK7KYrqiwZ1Hccnlery+rHbrDpSMT8lFDaaeedmqWKTHRVtupUbdvHovOOdeiWME1nlOWr9eqWueEh1K2ktqkSmAXD/CYAYGlRv4L6WP+wc//WXRObjfn92/+Af316nFe7+/9r58HAPzyRz4SHdtJ4l382nmyGlenxGqUlvUkP7dQdWu60pc3RluDW/vwZAPfJNV9qK74TK8gtONzBCZl5p3+KvWJW89KnBXDuTjCuExGMN+5+51G3p7nsx/5U/re7cqu/ZNf+PcAgN+78kh07MxnycZU2MK/7d0zSPbUh8TDOOWCOX/8F49zKhu4ti1bBUzqoKau5V3hU2mSa/jPL3yA4y4qpayMcTbLMTqzrv+DhRUqxQwat6n1Y40fSyxrUFa3k06YwGy1BWFOnVxavcikUbqJmdV44ho5aS1BMesdXhFHN3e985fEry+YZnGjor9Vb/czzIesjboAJEYIgXntup4lcPWxZvX0fzzJXTghDT3+MH/2lt3u/sQJlnimxcVWkyXxl0/fAwDIT7u9duY+sfX2UdNPi//P4KXmY55Q0Q4A5A9S283l6bNmXiDg6Ykirzt4p+smfPU8UUs1sdIa31yyYuQXPG5T2hWsXKzynFD0tPUMf35xmp2JUXbzXx6U5ZYS2YWANZblqM7yutPrHMCpsF2R7DFp4vN6dgZx9sg7dm8ViKuT91g7S+tjckHpGkXFS4PunbA52LMPA81XzzXUG5/0OvgW9PxKGtaskf/xW+z+a73pAKD9J7hWdcWI6uq+bJZkNI9+Z9mlhqnhA/UBwGHGMRIiQKntd/56yxtcvJE6n/kdj1wEAJzKbuEBJxmvOd3jYl0pwcCH+uYx6ZVwv5XEGj+WWNagrC6vPgKUG+kokp6oOe3aIqRpUZWeQwOM8hq/e5Sq9fCJ1pE2qd280S+zwfKvnnayUkjrG2fWAQIr5lAMYdFZIZHFIL8zaFNkW7n0hOCfy99yHXbT2n0PPkZYZqf6zT85yWi7XwKLc7Q6iuK77+pgrGBWRUblHs61s8PxrhdfY6Q7oTVc2mmsu5qbh0NIq1dBMKuS1/U8dvRDVPUp+Yk/99Tfic7JXhVP/zsUP5Gf/dQpwkvhhUbMYnj9DDMw/cPMt08mqJWM8fb0iLNY0oo7LKn4x/L67cdpcS3c4TRWX46W0HlZIVGkXi9DYHn8Hq8xwIK1Ddb9KCtRvSpMgTIAxZyLO1TaeEzmLs5/0ejXhBdItbl4wOy03jmVhKfEQmyxCeuwk/SshKKyQOGS4MNGDSeLxSjYAADGxqxY0MUZPu/clPpCqINOptPLPs1z3OlCa5TZupXEGj+WWNag3FLjB0EwDOCzINlTCODTYRj+6yAIegD8KYAtAC4C+BthGM7ebBwACBAilagjkEauZ512Wl4vK8CKKOSfBq0qqzTf2wNR1Xu5K5qWNmRU5Cd2u506oc8aooO6YxeRUeeepDOen+QemPnhyeicGSHPsm+IiHNA17FuLR0ca/1+Vxhz9jy129Gv79Y5Uova3ReH3T0bUUhG9zZ1XmWnxkev+RfLTiOUB9W3/py6vZxVrOJ+asePbX4hOvY3LzEmMfQU5z22jveYUi66pghyqtdpj8ZurZm6+uQvipxiUOSb3rF11bkkFEmesX4J0siGkrSuvQAQqtgkc57XNu1df5SmUE/a+cbPH+YaJmS51dUjLq2S3kBzzJxzSXlD0qX6qMU7WmnVTKXVi9FuYx4zAAAgAElEQVQSMJ7l0ibk5/Si8uGK/Ldc5tej2OrW/7G9tORe+BItuKzCI4n38BejEls86UrOzTIK1Um3pnc6f4ZWR9FV2CKzh5iIqghRrQdCXcVRVso7nXclzwfvJ07izHT/9xW5VwPwq2EY3gHgAQB/LwiCOwB8EsA3wjDcCeAb+juWWGL5r0Bu+cUPw3AsDMNX9HsBwAmQ0uFHAXxGh30GwI/9VU0yllhi+f7Kd8S5FwTBFgDPANgH4FIYhl36fwBg1v6+meQ2DIfDv/DLUZPD7BYHRV1eoNmTmpRJrkBOmLU0THOdNeAKMpLdYtcVYCQnE8oaSQJArc8od3hOukO19GLTaZXpvLTJmaVZmYuVZcFLBUFtjKqVtAI7HQcdZ9rMHN2C9pd4zKIgqRv3s1Bm5IKrHMpfMTNaLa4sICRXwtJItQWPB/CKAkR30Gatq+Ape14w0HunsFKWXqUJHgWV5KIYawy8QFRCaU2rra+u47plVKNe8TgJO47x94X9YuTdT+6Bp7/G1F9mVgGpux3k1Zhzy8uC29pznuD804vu+ZYFeGnr4b0uqrVVywUVXOn5dh5y97xU4pwqZ5jurA9ybqFcitwlfl7e5gAw7Z18zguCSueuqoeAsRzd7TxYC9Qlzwh8I8BZt5iLpkdo4icXnU61ppwN4wqwQiq7VS8TaPwH1pvA+B0TCiw3FrRuHs9gZgOvXZrNYfyf/Z8oX7zy/ePcC4KgDcBfAPilMAybwNkhd48b7iBBEHw8CILDQRAcri8t3eiQWGKJZZXlttJ5QRCkwS/9H4dh+J/074kgCIbCMBwLgmAIwLUbnRuG4acBfBoAcuuHw0Q1QFUpNWsaCQAd3dzVFxT4y17jz7LSIwlBIhuLXupDW03rC9x95+/krvgvPvpHAIBf+srPRofu/ne0Cs5/mLv6QwfZDvuFy1sAAKVesb1Oe+m8CQJGQvHbD4rTb1Qw00DNOeffcCyy/YcYHLy2m5olN6pgpUo9A69cs7iB4+7ayXLf02fEQvMm57I8xBvs2u00Ttd2aphp8dklBODpOc77Gz/gNPI/2f9fAAD/cIrtw60leGWJa1gX7ig/4AAknUO81uwzDFK2iWm49ycZwDy080p07DeffYDzfYPX3HYfNe9Xu/msWkYFxspfzwVXBsdtzGmdDNjkqaJ0C5/nfUMMxD5VYEqx56R6Lmzm2v7C9m9G5zwxxaDbqeeYkluGGHJ20Los9XBOLSdcOm9hO8fZto33eD5JUFGL0q3L57zCIaWMaxt1T+Llm57ubpq/z/BszT8Ts3p3pb2Nmah80o1vZemVbqWdxfbU38P5z2X4zoVnHICnPKGS8u5KEwDtreSWGl9m/O8DOBGG4ae8j74A4GP6/WMAPn9bV4wlllj+2uWWPn4QBA8DeBbAUbhk2q8DeBHAnwHYBGAETOfN3HAQSW79cLj547+CmkAuO+8biT47cZo5jRalj8K76U0Yz13tKHfFpHPNUNlPK2HLAH3sSy+Q186Kc9q2ObRM7UXtyPJ+cg9SO1UFeFi8So2cKDn3qGEGicFABcuM+PUHuOvn212Kq6b0SzjSnBpKDdPNqV128NWEAEINwXqtmKYi/zc9LjDNgFfYI6hpRsQkVvTSdz9jCOmk89dn/pJrWlPmJ7iX62FceBGw5KpLh7WOquBJHPBbdlILXrzM2ERiwVlpDfWsSwgMZUQZXb20ICyl5XMfWjzg/t3sP3j4BWrxnNKp2Yecv96QZTUnyysr7rq0PMZFpR4H1zuLaEJceqHAN5YKtOdqxVj1Oa8jk4BOHXfyPZpVp6HWK/z/+vdeio61EufWJzinqXu5Bh99+7cAAJ994e0AgI1fcTp19BFe+x+/n8by715g0U/tz2hZ1Frd+pQeVfm53qNghM8mv5eAtmU9s/qks1hyG7jewZEOXPyDT6E4dvmWPv4tTf0wDJ9DMxuxL++6yf9jiSWWH2BZ1U46rb3D4b73/RIm1BXnAw++Fn3WkeJO/J8/T1KIzjPUSjM/TK3eq6jp3IsD0TkGjtm8neGFsZfJBJteECx0twfgsRhBSX6n+PVDse7mO2hKlK843ykrv9PKbyuKMu/YLM5/WRi+pPfSUmkcoYViZCCPPHIMAPDKuDtn+RS1h1GQGTutbbMWDQ66r/eRG/LTowixwVh9mLJ+TakjTU0xldY3qC0Wd0irb3cApKtHGGewHnTZg9SmpmmCi846qPZZMZTWUtmBvdsZszj3PPHXRgACAMlHaRQWFtXv8LI660gTD/S4uPH0i4wzWK/FxHuokVsyfA6TR/gu1J3yRmKj+uqpVjXqrNMpmHKeP2tLLlZkHXBTOrYq/7xjHTVp8bhLVqULsvYO8bPudl5v4jKthLR6OVSHrn9mBvOFyF5CyxqMOO1tXYPrezh+m0hClgXVbn8b16At66zMwp/ymS1tCDDyu59CafTWGj+G7MYSyxqUVS3SQW8N4c9OIbzC3fFLz94VfdS5g5qlvEU73ILyuipTnDnI4+5/77HonGffIKRz8inueJUoBy//qOgi9D98kNbF5198GwBg3YvcFCfezr1vjzr3vHrNQSGT0gSt93OXnZ7hXK4+yaKU2l5qqUbJXac2Qt8vuJOaoKHS1KePca4t3a7gxnAAlru2fLFp19Ql+fF9ziozooWcIuZlddLJDhoFlOeDS6v5vQIAYGlY5bnqEDTi9bbL76TGLV/QfbzGZ9V2l8I3d7ogS+UFkY0KH7DvYfrtVwryg9W5t+5dfkMn4wyZbq7pscukYUsKwjta9F5J9bmrdiiGcJVW1KLKsa0wxrQkAOzbQGvjdXVDNojrwEdJTTaqXvfBN53lMv8on8lP38luQZ99/iHe8x/z2NIBN6Xh9zAudX6CkPLJaVGhyeLqVmvBKY9c46OPPwMA+KMXOG77EV678TDXorTNPd/cy3z/FseEE9jKNajoe9El0pCzp12HKeP7LG2oIsx8n6L6scQSy397sqo+fsvOoXD3b/8dzItwInnBqQKLoHftk3YVAiojP7sqpFXoUSlH3VMt6q4oc2qUTl/CI+Lovo8a3cpZp+Qf2jFmaRi1NeC6ldTUtz5rpI6PUDPb6Nb9BwAqsgLaVepZfZ5acUnFKS0DDsS0PK38q/K6wWVRiOs61j3l4F6X/bg0r7jA16ily2q71nk/4xwLy25NS+rwkzQ/Vz3jE1qv9HFlHrztf/vjbGVjlFhXn6CfXrOmPvtdpsSi7qU5XjN/ic+juI1ruW2YmIaR19ZH51jcxO7NOuLW9VzDEZf1sCKfgU2iEj/MzEKroAQz93CMR/afis45MkprLPECrYOlOzmXh3ezi+3LX2Zn464zrtrL6LJ2DnENT13lu5HS+5mbce9RYbu6Bt9N1X5+npp/7inGIwztt7jLZWLSKuu1voQWD+hQKXJhl8vEtG2gxVUQjZyhK628uDImZOh599Bq7+Azeeems/jzn/0Srh2fjn38WGKJ5XqJv/ixxLIGZVVNfSvSyV+jJTJ/0KU8tmyWWahAU/6k2HUVwDHQT2bYmcrv2UrY7RePMvrS+SqDYmVDTx5wZmlK4Bgzyy0g1SXQxryaOdYnXNDHGE4bitO07CaIIikATOEE7Wy/P0DmAZqlfW2c5+TnaHpWlBHa9i7XFfLSHP+5fEaMNR7XAP/BHxsOOZbdUo0+UeULNHsXt/D/DzzKApnnTu+IjrU1LN1BM3FI7LSL/4VmqbV6fvgjr0TnvDxBFlljFVpWwDTVyWeV8GrfsYv3uL6H4155hSa9pbxsjcNhF9A0ltjimHwH3fMNg1IGdVXhStglbj8F81rPCFK716W2dg7TpTt9jsGvlBh6zc0xVzGYdTlAY1qyhqrG8mTcEME6F9CsK3iaGeVLUVFKuU28iUvi1UtMOB7GpKaX3cf3x9KRlS/yGZa73Ptz948weH1skvMvf5vfh5oK28IddEXzHgy6oDbxmMtg7F/9NsqX4nReLLHEcgNZXc69BDX3EpVgU7ptQp1JrPDGNHxdbDRhj1I7VXfON69Iu6mwxwJdBuxJFN2ubsU9GVkQpr2nxaGWO6uy4IMOQFIRp5/1YTs4wJTQpYLSWIqh1L26oYUFsdrIwpjfy7m0jnCpfcbcB7dT+790ghrfOvbMPSztJDaXK1MOQNItXr4lsbZkFXh6+QqDcCmPPbZi/GxiwCmUVFKqbkVFdexen52Lzpm6yiKXvnG1BL+fENJcWmPUXLqzLOtoQvdaWyeroMJ1z8yrbHq308gblc47q/beBn8ub/J480x0/40W3ZM0sJWkLm0RRLjbWYGnz1NTdrypMuv7aG28eyetw2eeYAo55eqSkH4Hrb7uFh57foQL03aR11mEC5iu38MA4MQ0A4A5FTEF/c2tuxPuMSAnTV9XMLTwnKC6Yp3quMvBlO054qjSqVLN1n/vDgUgT4+5duvtr4nJZyC83mq8icQaP5ZY1qCsqsZP56sYuuMaro5SY7addBp5MUft8dCdTLs8XyDgZcOT/PzKB7mT/vie16NznrhwBwAgMyU2VNvFhepta3O+WUHlsNU2/ZRfZEQQVVkY6Vc7onNCdWUZeifzR+eUurn2Oi+QlqYv7XTaqr2d15w9yXtMSQOUD1JTmyUAAM8f4T1KmWLmLqkJWTXWQQaDbnzjVDOihkqnWH2HCFx5+Y3t0bFtk9KY4rI3YFBjl7TTOo77h288GJ1jPvHCNv5dETnFsnjiEl4fOZTlex9VulNqJH2AGs783XC0PTrlrFlh1jVX6z8woK7I8y6dZ4QnGJIFpC5HbRekie/h/D+81UG/f3/sHZzLnFiI9f9XJwmVrnSIi3+dpxrVcXhuSSQaskKW7qUF0Nrq3qPRC3wHLCVa1jjlSwIG6XnXvB6MbTn+PnqR57ZZ6a1SmoVlFw+wUvVgN69tkOb0ZR5zJsu4QK3iLN8lWQ719nqU2r6VxBo/lljWoKyqxq9WU7g61o2kfM5Kh1coIf/t1Ax9FwOdjJPrAYPr6Qf9+at3ewOKams3VX1Z5AQdp6S1kq7gZmAjQTfXAu7MrSrKWbrK3b6fQXFce8gRKAxvZabBOq1MCngx9Ap31bF3UkP/Lw84KoLf+Br7yPWcFBvqQ/R7++SbW0wBcP5tQpFmKyhpHVRppkFV33RWyFQX59J5r0AtY/zsjSdZ3ppscTt+6uHmDMOFVxkY6DwnJtghaqsf3f1GdM4XRrjgXTS8MLOd6mn3ekbL3zzvKGHT1xTZVp86o/SqyJoK5sQ9X3RB5no777F3P5+nrem1c4IN+zRUUmrGlDvXxueaVtmyQZLXpV1cJilKtRbNbWqaFsbBHbSIvjGptfRg1ikVz1h2pqxy7z51pJ15zdGlBW281y0HON6F44wptJ/neAt7uV4D/W5Oo7JwWy5x/kv7eD8/sY+Wyhe/en907OArHP/aj9NKOPQwWX1fPkZLLnNa3ZYG3Htq2YhkIdlETfdWEmv8WGJZg7K6RTpBiES6gRaVGpaSLidsPutiMdt0inXJte6g4x7JYPYap19Kq2RUnWIWcyIkXHK7+jXt9KFlAOSfQp1iSj0awyv42NLBaO+zx6lN28/wnLH3qeus/NL/+emfuO4+Zg41l8tOjcsHLLs51VulKS2H3Snort27esUFXvegjXuoea9eY6S/UzRdlj3YsM1FiGeXuL4XxulbtqhfXdDgeBv66Iuvy3iZDNFm1dMiFBmlj3kuzTGM0x4AQuX4c0cZ6a+LiHPbOy4CAE40mNfPTnl1syqPLghabIVC9ZYVBJRwUOwlLzsDAAUFvrMTvPffPPze6LMd64UH+TlaEqmztLCefJ7ZioTiGuGMszbNIikq+9AluPXEVY4RtLt4gBGrGAw51LyrD3MNk+qBMDHSE52TXLSsE4/NtfI9/dYEAymB1+F4cYOwA8KKjC4Semy9JKsdXC+DRwOuWKl1/wzGvR4GbyWxxo8lljUo8Rc/lljWoKyqqZ9IhMi3VFA7okaY3c6EMiiu8a1bE0vjTDslYIbxiwNApdK8b5kZGopbPeFZPRXFjqy1dkl8+saKYjXqaPdaOJ1nQMWARksbms332QJN3MCvGBToJJTJlVTFYMM47B16NQKiNNqVKlOqriAQUChO+9SkM+tGjwo4spUBwPlDnFtmnMeMTzvG1mxOsM4xujHWvnppWKZ+hqbtVNWl2/JjfCU6L/Kz6fs5x/dtYZDpyZGd0bHVczwvqcsYNNiAKq2nrErS3fO+jVfhy8lvM6XZyCjNepdD1hgHYXebWG6uMvDb86balD/OdXt855nonG9f2QIAKM/TlcgKjtuuAsfZXv79roeORuc8eYauXPoizymMy0zvUiPVLgePNVfR2reb6izON3MewEur1dubW1cXZ/l8iwUBqrxTiwNKK8slSsnkDxWMzM43w6EBoC6+/tmJDtQ9gNtbSazxY4llDcrqQnbDAJVKEqF2Kz/Nk1ixsyHHv7uGGYBaFHd9I+m0X1R4IdYWAzWECsZkp6/f/fo7qFGujFJzZualafZSq2TTbncOT1BFWpFOaqfgqyqymFXALj3v9s+qaQn1DjDeeOsElPCsA+OB61eQcO4oA2jW6doKiKYqDrJrYKWKgkj5TnEFWgcWj79teVBAoPVirBFfnjHOllXwM1NxoBm7drlbr4bQRaNF3mvF64VgnWHqWWlBaZtKQ9cV7sVD+WJXGyGnX7zIuvj0Et+FBYGuNvU6+PC1Atd/7AzTaVmt8wL7nCLfrWe55NbHtKilh624ZV7obnu/vn58D1ZK1WDD4nkw/sJGynuPlC4z4FGk2S0obAvo89unVgQuLeVmzWM9iyCY0xqe43ov30lrY/M2rtuVRUK+0wtePX7nd15oF2v8WGJZg7K6Gr+aQG0yH8Edqzm3U7WkBEJQV5fiae7ihXH1fRO3fMLr82ZFLCnz+8VRZzzuBqYAgLu2kADuxAR3TEttGVQ3YWW7nkarG0hCG3RtnJqxmGmuhKi1ub9T8get310qSlfx8+SgY/jZuY4a/cK3WQrbe0wlyH+TwJuf3PwqAOD3Trw7OscKXxZl5UQrqCnUvNRTe49KRS/S72+9ynMLD3EOD/SxSOiPj93nzlE28No9nHdbF/32V08rh+bFVVo36lk16Osn5jinyznGcLK6Z0s3AcDxBa7/7j5qsNc38jn3iErxwmBfdOx79pLl5uuL1M5pWSxmQaT1zpiFASDSosZneF2a0Cwur7DKwDxRhbpiLdkhrtO6Thd3mPwWY02tYzxm+b20AvcNsnT68NktHGLWSxcuBU3rsPUQ4xznx3ivXc84K63A07HtPnL5Fyq0YC6dokmUVU++aod7zi39fM7LM55pdQuJNX4ssaxBWd2ofgVoHUli+aA41PsccGThWe5oy5uppX/lg08AAH7ryfcBAHb8v9zdz/2kA3N84AFCHr98Zi8AR+ZQ2MndMDXvbu/VU1sAAMlWjl8X370pi9A6l/iQUUXkzS+MwD3qvhNm5bf3uYKMlEVjxYJr0dfeLYQMG5AHAE7NCf7ay3Nm98pXXqQGeGqS0Wa/8KKe0RyUJUjK2rBgbuDxDFpEuCELxQpI6rIWxssqrsm5qHVxUAQoAhfVVGDTdlGZjf2uYOW9m6mR/9P4PQCAnAGq2gSp1aOyEmsAqIUcZ6zQXNRi5CmhBzm9WFBcRz53Wc+s2qGy4jHOrTHozukf1jpXVCRVMK58WYryva0rLeBAOYGV/y6K7ERdkkut7ljr0FvqEehHfINHirTaWgQFz/e5TtAzpzmXrMq7R67x76E+EaPkHJCt73WVc9/Fcd85yIzFfxjhWqQESis7mke058VMXW+9Seva6yXW+LHEsgZldTV+DchPhViaod/yaw98JfrsXwU/BADo/R36PZ994QMAgA/9vcMAgHXqKXbt9x+PznlqlAU7d7yPfO5Hy9SgXUc4/twhp8k2b6LzOvWk6KFkbCTez/8PtXP8N08OR+ek5hV1lZ9oveIiP9E0qXePpSnu3mmjSlL0d2aO8YHh4eno2LmiylafUxcWRbiTb6PW2NBCjXA66eYU6omZZrd+9al+ntPwNObyq9QSbXJRlx7jL49tpm//5Fnl0Cecj2lJEyuSqvXRQlrcoXtdcL7rF88wMm/MwUVR8eauiM9fxTvBkLMSrswz3mAsvmbNWAYgkXK+66a2Wd3/kI7l/zNzPLfSKzxCp6NYO3WRxyYtIr+ZAwczwm0Ik1Ef8Ig/xKYM9QU05uVQ86/V30I/arl7RAayJAyJsUQDQFo+frmX421Qt6B1LXznTrQ6cpbsHONKI2f5PZjoJkS7bR3Hr04IwuuFmaZmBe2uB8BNu901S6zxY4llDcqqavxaHpjZH0aFLL/8jZ+JPtu9i5HOsz8l+iZ1Cf3iK4c4URFEJNY5J6Z1P6PfC2URHM6bL8jPU14hxqWMfL6DQgheVgT0OLXi4hYRHnR6nW/VAibysU2b2nYpn7/mdV61zxrD0sBLnHfqKse6XHFdayx6HGylJZEzzn1pjaefp0ZNld0ubtHc0DrVqnjDtLb5qwCQEgf+0lVq4uyb/Pk8mAjf2E+NOlJzc0pcVDdW48sQ4WVowRBP01TlA9cV10gq1lFK8f8tl9Xtx6Ouyt+p3oLKyJQ1XeP2Dz3tWrN/ymoyLVfP6iQty0TBIQ/Nx62rc28uo8yMLDEbI7vDPWejc6sWdYxlBKK4z1s4zooZlKrCkojwBa0ONtpYtAyPrLS64R10Tpsbv9jP/2XVPfjUHNGKw13EN5zOS+N7xWRRD76FhOu/eAuJNX4ssaxBib/4scSyBmV16/EBmmIbmc4Lpl3t/amzDLoNb2aw7coUP8uLxbS8i+dkWlzAbv4sAyjzhpbs5WclkeGl59y+VhePem4zzd/iRgVwVG9eUWNJq3sGgNBM8XpzwCSU+WvHhh6bS9QmWeZXrl9VOYr11L2mnPkrPG95O+e9q5+15G+MMEhpQaGGR1FgiNBMO8+JOPiu0Uwt9Ttb3Pj/bXsvraf5uV6AlJGrDCAZUw4AlNWoMneF65U+LW7+XXRd+tY7SK3V1FcuM3AZmEui1lfWJgse4GmmwGPNvA506Wr79UGp6bKgxCvWv9YhWLQWY37OrWm2nSZ89SrPLWfEArSX99zyPN2dhcvOPdi1n+CuU0sMDDbEjGSw39lLLlBnxWP11uY5FcWbV5drl8g7U99agbVe4GcT4/RF37afXI5HBxzQrKo6+5SCnVfF01ffzKBwchPdQQvqAgCMQbocxOm8WGKJ5eayygw8ZJsJxZUGLxCVUVDNmGWicseN3A3TCo4V8079tW2j9i7MSDMsW65LqTS/RkcBoiWBYyzollyhaFo7Xd3sYsnSUxyotI47d/sGag9rqlmcdzx6Fo+yFtuWXrMyy/SM22uN9757HdM6Ry/R6gnEE1cRsCe16G4kVGBrSx8Dm6dP85yeGUFIvWaNg0pRLqQ4Pys6mbDSXYFYrCwYcEU/wTkVF+nS7R1cl4LflFMBM0TpTv6ZKOneBXBKeqwwth51QWtTWn9rmuoDeCwAGHHVV1YEV1tUhu1ZBFaOC7u2rLNqoTloGXa4dVoULDahdJ5xB9p1Wy44i6g4wPHaZDla0VJZFmPE+uRp/CiIa6+7DKCqFTO1uGMr7QoEKgVrRVnprTxpoIvP9HLBBWQDK+++TYZdINb4scSyJuW2NX4QBEkAhwFcDcPwQ0EQbAXwJwB6ARwB8LNhGFbeagw0qAnr6oGW8kpgK/MqpxQU1YAvCe2GVe3q6QWn/coqTW1VIUnpPP0207q1Fi/3pPRHw/jITZnI6jB2Viv8AYC0SiSrSrcE3Txm6UJn0xzz610Rh5XL1uelWq60+JdHY6NboqR831kx5Salceoq1812ydGbchYFBHzpz/OaZyytp/sKltwjvbuHhR5X+znf7Jd5nfmd1E4D+1goM7/kIKMGQW21nnYGabY1STvtVDbtrJsLZTmY5kkWBJZJOH80pXbPCWliW/+6EVt4kOPZkuZlhVkJ+c+yKBJKbTYSnpVgRUT6UV8wFc8fyxuvbzUzKYvNnqex7losobjHAZBCAYAWrwp6bS3OZWGYlZL0tK+901EtkY6ZKPF9TaXc96Am4zVT0BqqdN1IWqLUYs3T2ckwOje8TVX+nWj8TwA44f39mwB+KwzDHQBmAfz8dzBWLLHE8tcot6XxgyDYCOCDAP43AL8SBEEA4HEAhsD5DIDfAPA7bzkOCKCwTqKNDc6f3i3ec4vuWzS/uLGZ8MDvUGKeV0mkBQamaEjTm2YAnN8WGCS0KIovI5NQ4crSgtNOgdReBOApWyQ6bPp/ueTOMTBFVCxjAJVeaoasl5Uoz4gYQ/DYCHxjyk9MwEHL9b7bkauE8Xb30ueb2S/u9svOIvoPh8nX3jdEf3ThXYwI10VvNvMKwSHW3RYABh8lRHSPrIEXv0h22uTXlEG5z2m/dB+fn1lrJqbpTfv4mZKK1j2iKNNzqVfSTecCwPyy4M+iVGukReUlgE3D+hF42rVhj9xqbGWVGATZ5uRDgxuK1RgoxpidU2YFjnqQZmngiiw360sYkcCoiMq66gIONGRAHfPJDb7c3uLe6dkOqvzGtKwaLd0l0YGtX8esSteQK3Cbk0WYncx83wE8vw3gf4LDbfUCmAvD0Oy+KwA23OjEIAg+HgTB4SAIDteXlm50SCyxxLLKckuNHwTBhwBcC8PwSBAEj32nFwjD8NMAPg0A+aHhMF0IsLyDu6W/041Mc0ezPHhdJbbJNiPDkJb1or6VaXW41S4XEWIY7ZVPf2S/qsTT/NKgbcUWueylAuxStj1a9NgIOeVnNbxe6xElk/l8SVN7skb8gg9Ls1t/dotJJJrvtdHt/GrLLZfHGDsod6mHwKAouGrOX08oG1DXOFVp2/S4oLYiQql5FkWX5plPSsvq1rJzsn68vnLm7toAACAASURBVAADg4T8XjHLRIUwUYRemj5YuL4ENmlRdXscXVyXzKTXE+4atV/fRmq5qR4+79y4yn+VHUp6MOuICst8ff1d13MO9MzCgovUR9FwdaQJFFOoC0uS8uIORs+VlRVSVkzE0WrpT4+wxKDMlYygzOqcPJujj3//nvPRsUeG+Fwbig3lJgUJnuS5rRv43WlNe3iWWa5TsuRlDm4ht2PqPwTgR4Ig+ACAHIAOAP8aQFcQBClp/Y0Arr7FGLHEEssPkNzS1A/D8B+GYbgxDMMtAD4C4MkwDP8mgKcA/KQO+xiAz99kiFhiieUHTL4XAM+vAfiTIAj+GYBXAfz+rU4IAwZXEhmlSbz2wDUD36iFUwSTtVRRbUXraABQECbcpCChquQCmbh+pVqUJkxbeZY+s/SXmXs5L92zAioa3YeZ6yvHBKKt1MzshsxHCwL5gTAD+VjayNwR4/HPqN1TpeTM0vwo79Fqu3cNjwMAzo6TiTb0pmIsuLNqAx1xB67nuPmTgtx2uXV6aIBm5+dOHwAAtF8Sn8A+ntyxzuOfW1AazCDLyWYOAgOzpBc992wzr92vmvRxY5Yxdtl+j3FWrkLUfkzmc9mOkUtXK3vvhD0zc/Nkphu8N3pWvhtoqTF5H1ElorVQ7/VcLf0sC7xkfR8seJwWlLrmPbOaILXJiC9PQT7NZWLZwYeT8ltr8iByom+wwKPBsKsez2Coz+pZB+m+lXxHX/wwDJ8G8LR+Pw/gvrc6PpZYYvnBlFWH7NazIRrGauqz1VqnGe2CmR5qBoNCRuCWHq8tiyRULXqUZjOt7TWbtIIbFI0/T9o6v0IDVG/g/dhnFlg06Khx5XvMv8YPl1pW2ke7e4t47SoLTuMbBDUMrKOOaTJqj/KiQE0eZLckyCh6uT4nzjKZEih1ue7AZHTs1AkWeHSc4fn5H2Oq7q5ehmOefY0MRumrTk0U65z/+h5x/eep1Y0hp1T2mjWa5WNc87bulmarOE1kEsgqWBD0N7LsokCqZ6UlLaDYbJVFAdS6WYOemrPTzYIzAJKWNrL8fHirvYf2Olpgzob13yODesuaMbBVWvDh6qIRDbr3yLo/hQrMBopFWhpyoeQWyNKdSXHll5Q+TIvC79w1PtO9gxPROe194pgYS98uAU8M2Y0llrUoq6rxgwaQKQSo9nDna+lxAJ4l7d7G6VYx1hXrPaddOPDSbWYxNFpWpOSSzRoBcH6zaQsrr4wgoqa9PO1tmizw2m0DQCi/3WIVDR+sIY1i7aZNgy2oH54PSTVgh8UirJTT0niB74dKUgLbVAVmyfaT+z2hgh9rBw445p753UrbSbN84yLZe61BTMPv3aYqFoMuW2rO5uqnIw1GWl/ZTUZ/W/vvxA00cslaX5t/ahhhL15i63pdfMZiN6Ub6K30ijVbkZINw+vX1Gl4fbaibwLK3nXsdbF3QO9jVWZNIOadhFd4Y7DhjJ5dpY/nDnRTjU9MuH6HBrm2uE9dMS0D5hioK+GR7rWos1MphVjjxxJLLDeX1SfiAJCQ31L3tEdeBApFA4gsN0dWIy1+OzvaDfz0yP+0Db+2wre8UfHDCv8zXKFNgpXAHrh+gFWBibKt9O2jWMWyRw6izELE627wXhvPIMLeNWvtFsdQ9FfZjggi6gFHqn21pnsujKu7rQBI5c0qMz7rLJrn1W32F/Y+AwD4rQ0kpxj6Nu9nrMMBhLrvIGnKvCwTIzMxS6mhNfd98FAR7kirCugUQZx9K00Wla2HlRVHWttYjz1QkUX+w5Ulqiuj+R55isUZwmbDzr17no8fWWwW3rD527rX1Hev14GKEurlEFxrrnjKp6+PV2XU79G4FZf3cpykQFeWiXkj5YCyj2w7BwB4dlN7c9zsLSTW+LHEsgZldTV+yF7p1tfMl9rN+nrbZmu7rr+RW9LS/rdSe/tJzcaKa0bjruit5mnMSBOYprcuLAYD1bGJoqfF5dem5K9HOXiN1fD6yFlxjuV3oQxAWxtjH0tXqaGzM25tKttFQSZtiNMq6hCUdNO2qejYS2fYncgoyGp2bV0nIa1UnXDUVUX1X3txfivHlUZbWne9Jl0ue/RPQGSVRVo31RzlB4CUleqK1CTVqk6+o7xuctnLGgxK28lCqYtFObBsSELr5mm50J6f/csejVmByeu1eLgiaxNlfJKWCvAyDY3m8Y3t2KyPiMbskrOMsJH3WNkiuK+YjC/miL3YvNF7ZgFxDabhE1ea17i0jhfO55y18Oa0ePnHszfOSt1AYo0fSyxrUFa3W24CqLYBlUHRaXmaoGqINiMcUNQ9tB1M/l3gafEoN2/bV3KFVXCjgoWV55iYpvDrerIr8rvmq65QdBElFIC6fHsjGakVm8kj/DiB/d5Y4asuFqn9EtZ5d9GF3RNj/L1nP8tmxwdUSDLOn9MdTntbHMAKhQxdltQaJi6tKDCBK+Gdq3Ac6/Zb6ZLF4mVQSrJmwhVoObMKTHP6HPBGvBGusMAMZZiseJafxQO0DpFPbyjPiAgEt5ZgxbvRlDFZUUJt8ze8SdZZFCmVIleFx0hNpZvOtWh8ctCVLxuvviH1yrJ22k7y/yOVddGx777rTQDAC52bOf7XuppuY+4Az8168QErwgqTQBzVjyWWWG4q8Rc/lljWoKx6Oi9MAIFM2lrKBXIilhYzFw2wU25OgYR+amVlyuYmRTUcaEU6MDp2RVGHvxXaMSsDRTaWUmh+wCsUACgKVlrAyC7vuwWC6CYF+qjLhM2OqpfAAP+/+/6R6JwTp5nGKXyTgbtgvYAkWwjbzKSut3vrCuoFtnZjNFNbRjmXwnZnyj4+wGsdm2UaL70gE9+oCj32WDPxG/aMopTrCkht6gY+l1yfWkJ8AoK11vw6drkZtWwzG23d2pZbkM8LmEbB2hV8gNFzvgG4K/pfFAhc8R55f9eVPo0AOnN6B+xcpTCrM849M1enTZz4haqI9WzZPdDYpJidFyf4s0WWflkALUt7zow70E+mg0HD61KYbyGxxo8lljUoq16kEybDCHrp889Z2isq8YwCFiu0SBOaZcXOnFhxjB9AioKCt9gVffzDyjShpY00VkqMv3WvlLetlxDaxWkGx4xRyFJpPrAjsHsUB6EVGZWHqk3zP3FuvTtHAa7lvdL0YmYx8Ey6fTk61kqY2y9yf19+hOMefOAiAOCNJbbJbr3k9v/X9tCiaMso1SfAkPHyhV7wzViAS6bx7dkpGGZAG9PcANDINcNizbILTWV6gbR6YMAdrbO9CwbGuUEjTyvKiZ79zazCJssOzf8zWLf93xuiIUh5oCBnY736ECjomhkT5Hy9e7ctqLd0UQ0vxTK9tJ/nZi67Ip3XG+RSjFiHGlT5GUvpBlYo5p5DLacgZK6xImh5c4k1fiyxrEH5a9D4TlPcN+x812df3wMAaDvLKS0fYtrkzuExAMCxY0xvWL85AKiqdLFqnHRW2LPcXJADuOKJKHZgrKuZFeANr71xvlWlwedFBDGvcUX8YZreT1dZ4Yr1tquYzypNHRbdkqdmmpffwD/XpZw8y8Xv0wcA9Y56098TV1yfN+gzSwFBKdOX57fxb1kW5SF3SuGS18YbAAT7rfbZBNxHpdlc87Eri3U0f4tlNMlKQNUNynJDCwHJOjAufqxk9e32UlvGdiuFG8iSCOWTZ1Q+6xOiWKccY8Hd/fAFHqN21ude2hQda+XWjYziVFv4Lh964AwA4MipLQCA1pNufIMCVw+QxOTQRpZFHzm8k9cfc2tRa1VbdcVuNgiQdTXV2zRXnzwlJ1j40vLtf51jjR9LLGtQVrcstwbkpgIstjCqmd7knDOL6malVZfUiSYxLHipQByNaw4KaRzntTbjTJffeIMoss8OC7jS2mhuVrbradTlZUVfBXzJdMnvFe1SZlZ0UX1urKUl9VAT3LN7ozjtF0XVNObQP8auW+3UvE2j3SYI4795WRGrsSyCAWoMFFXznq11J6oHep4wK1CxFkX9N29xhCWTFxhDMc1r3XCm9fxrXd67onENdp5XnOrNcZpNVla7tMNZISnFeXCR4832i0nXipiSXgxE72VO2ZmU3ml7P/PGutt/+xH8G0ms8WOJZQ3Kqmr8RhpYWh9G8MynTu+KPmtpozadvps+TMdJTu31JH37O/ewh/mbSxujc/KXmgtgLN9q3WVrnv+bbuPOXLUikHIznNR8/aTn40dc+JPi73+dudXqBo6741H6giOzzq9uvMQobK2V53beT42fF1nCtcse770C8FH3k5WZh7Wo+ZuQtBbj0EcWF7DsihFk+HEPy4mvKOqymE6jeP0rXxwUzFrFVide4zu3YS/prR46cDo69sVn9gIAWsYUXzqgTMwSLbncJMdofWAmOqdNxTlzn2PGZHKUkfv3/+wrAIAX1m+Oju36Cn35ywu0IKy/YecWRvnnS3zX2rxMzGKCxVxb7hzDTPZ28Muxxo8lljUp8Rc/lljWoKxucC/TQHLTErKv0WSudDoT7Y6HBRVt0MTJLIgtRvXbHWmmTZq4zHJiPC01c+PV1RIq7bVWMghtosBzGgYgMe49Q3LeICWi+ApKffzFmkVenGHbr+JFx4sudCkqSpXlU/w5PqeUoGfKGpTWuOliWSErGY6s2k/mvKsCvLlPZJx4FjQzMM3YrOMmTPaLE0BtydoEeKrv4bhbWqajY7+l1HHyNN+T+QscJztMOG5pQO9k1b1HFcF8ywMKVMsl/dLRfQBcU1AAqO6zlnH8MTnLd8sCjlG806Hdo2D2QikbVerdSmKNH0ssa1BWtx6/EaCylEFdXWDCQaeRXz62nb8Yu817uYOmTtI6ePlbBPjsvOdSdM7ZgHXMLa8wYFaV4q0OqOFjwWt5XTHuerXLNo72ldx+/o5px/QyMJgeISijJraYvr0MvCy1uxr4qhVk9DByd1HNQGsXeB8JLzNUbdc6ZOI03ltKBJ2WxjQWX6ut8QqfLACYFM9d3awBC/KJIafiWQnDw9To412WpuVnY+MMpP1F4VB07OatTANebuFnXc8x8Lu4zOd7z2MMBJ6acjX21cM8NmkxyTvFoqSuOfXL7v0J+sRVISsg4jE0YJiASV5vVHRtYuBvbq61icfyrSTW+LHEsgZldX38coD8uQzCQ+QTb29xLCXzI8SEWjFLtp873mIPd8X0PPeos2NuJ7U+cctDzcyikR/vFc8YGw2iohB9sJLF13e37V/y+y120FD3lGJFXXM86G1mjtrCymOH1ZHmjQmCN1LXXFwjao/djEC97vpr1hJYef/G3ms8+3q+jcDpr4jF19J6K0BRjRXdfgBgXH50tdPMMZX9yjooe22+CzlaqR/cewwA8PVT9wIAuk9w3Nd2MGV3x5DrdPP6dmr0jjdkMV6kFm/dz5TfwpD3Tozys/p6jrd/Ly3cc18mzLr3BN+rub9diM65e+AKAODZVw9GTMS3kljjxxLLGpS/Fl79nnb672PXHJ9YqzRlbROtgB09LE44dpzH5Ka0Q+9zVkJnnr+PXyXLaEpdWUvrblAUYsUyK1lWTatIiwSeJrCosfGi1xMqoJikJpitKVI/5GIVxTZ+VhV092xVGsg6xnikEVE0/zq24DjK3yQripXMj7fOQ1hyJpMRVSSV0Yk6Aqm/YqTxPSvKOtu2D1GLLi+zfLbrBA+afdBF3T+0iZx4/9+b1PRqiYi5XcoEKHs0VWx1FxDc1jrgVlT4tLGFvv7sVUeq0SFylNo2Fd5UBV3XrS4M8x3c2DkfnXNpkTGEzIIHBruFxBo/lljWoKyujx9y5xq9SH++fdD5Ke3vYmRy6Ry5xo8/vQMAUN2oLqRd3KMyx5yVsLSNO2afOsROXOXOl7vSzHwKAKX1iuYbSYTBPCNefSsp9fZC+YeRCynNEzGpiogD856Pv5X31Janxpm8rIiuIs81T+NfV0x0I7KRWNx62HLZI7PovreO1qmopvLcdI7PvWZFWbIWUt4zq/XyHeuSBVloZ4Q+UOPA5DWXHZpXOL1N/QCWuvm3FYylxFy8bce56Jz0Tl679JwKeZSEb9/DdyTf78hTSjOMN9SWec3zM6RYy8uosXjWbMmF9XvzPL/SGV7fDegmEmv8WGJZg7KqGj9VDNF3tIrSOm5LP7vjpeizz5x+AADQcomfFbWztXVzN1ua5w6XuuymXFXxTN96RlAnEqIpovGApWEX1Q+E+AtXIvPMpw+NuLF+3WeNOflZnfS7ct20NGon6ePnJ5yKrrABDR4YuAgAeGKO2iM9psyAx11h7j9Wav5wxc8byVq0ClYQpZqv39QvTjRfRhGWFElFRLQiy6s+4FGgqUPP5SsskBkcZrR9skDr05B8APDFZ+4BANx7H/P1mcdGAQBHvkgUnmVqXhjZEp3TuEx/P8n6HiT3LvDcJN/J0rKzKLLqcJw7wfe9LqqygUdJ3rFYpuqfe2EgOmdyF9/HRlcj1vixxBLLzeW2vvhBEHQFQfDnQRCcDILgRBAEDwZB0BMEwdeCIDijn923HimWWGL5QZAgajn8VgcFwWcAPBuG4f8TBEEGQAuAXwcwE4bhvwiC4JMAusMw/LW3Gqdl3XC4+8O/jLmHGRjp7HRBjdkJms1R3fwSzS/jmK+q0WPY5bUWFlDHWlG1rmOacGmWZlJuxJlQDbWkrgjOG5h5WBBrqkw0H/SzkifeeN2Twl4mj8qMdzFKtL1/nPeW5T2evMRUo3HttZ/xijfEGdh9L6G/C8v0A0qXGeCJsnwe8+yaDgBGLtCKunxPrFDLVFqimyZ9w0z/Cb4TdS8FG7kBAuxkN5Ibr2jv0RVXEWMw68QGmtdb1xHue2WOKbnUc/xZcTVASB2i71ks8toNCyDPqob/mtO/xR1qFKp7Cy7znQiHVaQmZifj9weAUON1vZjB6f/4W1i+dvmWb8ctNX4QBJ0A3gHg9wEgDMNKGIZzAH4UwGd02GcA/Nitxoolllh+MOR2gntbAUwC+MMgCA4COALgEwAGwjAc0zHjAAZucn4kYRIo9QRobefutVBwxQmmTbNq/1sf425rgJrEOgUwPIBN/qr4+bbxnI1d3FlPCR7bVAIrjW9AHdsl7aAw16zdm37XNevidguUWklo0/UDdgd7R5vueeELZFuptvB67/nVZ6PPJiu0GJ79/F2cgz2NHbzXUGCQoOA9Jus0tKJ195qQqNNN87+TOa+7j9J0wQqcjjH01sX+5Ad5LfBrDUGLU2rZ3aGmpS3u2Ly0c+sdtC7XtxJIc+41MkPZu1HpctZIi+Db5YCWQ9vLfGHmD/BGfuanno6O/YNnHwUAdD/D68yxNi1691p6+N3Z1OU4A0++vIX3lLh9M/B2fPwUgLcB+J0wDO8CsATgk/4BIf2FG76BQRB8PAiCw0EQHK4tL932xGKJJZa/Ormljx8EwSCAF8Iw3KK/HwG/+DsAPBaG4VgQBEMAng7DcPdbjZXdvDEc/EefcH3yPE2WGaC/bxDL+hXuuvWW5q07O+H5yJvpD20fpo987iTZUnNjYr/1dt1onHSzFr+uL57vNtqx8vWTKvwwsg7z/coeRLhtA1M1hUmBQFQ0ERqXn9cbLjDrYwWM2MgiIo45r/Ai6r6zsmvQD4LvvxJoE/UHuMGxK93zFS3u3nIcK6nVOqV8cpYpqtykMfD2U2u3dlJTLhf4efqKg/kayzHaFf/R8zAG3cppj7RDvPrlbYIEq6w7108r7T1bTwIAvvzVe6Jzeo9xvPFH+Z4MbyEcfXaZVm3xnBvfOPuW9qzw9a/SSsjvolWbTbl3rvAK05CJaoCLv/cplEa/Dz5+GIbjAC4HQWBf6ncBOA7gCwA+pv99DMDnbzVWLLHE8oMhtwvg+fsA/lgR/fMAfg7cNP4sCIKfBzAC4G/ccpQAQCJEIJhsI+2sjfKMeOdVEBPKJw9WdFytdnigHEXXRyaVSdRHlgHwC25MUwY5I+mgtk4aXZe6vQRtnr9ocQDz8S0DII1TWi9n0wPgFMYZkbcsgZWORtDRggsIRAQS6xV51vVCcbNHUWsf4FNeWdCz4ud/i7LSkjBrp2aFMZ7+Ug+EsMyIebAoPv1WFWEZ7LrVg06bxVVq7sBUy8hqaPPME3XmsQyAPd/+DmYCXp5k1x0D3gBAYZPiAuf5c3GIc3v7BrI0P3nGEX0kLH5hc7HYhCi/Cuqiu+RZOQ1ZnMnFxG0DeG7rix+G4WsA7rnBR++6vcvEEkssP0jy11KWG0YdS92uaL5wMK68pXwzOyZziT5ZZcDtdIN99KfHL5HeKi+SC7MkfN/bIrR1g9/KB6xb37WIiuv6CHqkcW3e9lMwgUTGXcdlHVQeqs5ADUX12zYtRMeWThJiHF6htdMu/205xXsN1YE19BSa9ST4gSzd/V6m9J1YLJGvr8t6hVUJxWEawj5Yp17Le+cF4S1OOx/fCnsaeufsHbAOztkBhzeplpgxyk3wnHIPJ55V5H5jjpp/ItUXnZOZV/ebPh67rZu5/zemGZPqOOtubXYf5/9Ddx8FAHzthQMAgDZB2a2DTrq3GJ0zMEiI8ZUj62/7GcSQ3VhiWYMSf/FjiWUNyurW41cC5EfSKO2iOZPvceZKUWkWlGg/h2ItiYJyMqEDz6y2OuTxFIN7gZl+GsrnW68LAmxtsBsp8eyLTy0KJsolALzqL8E9rbIrSrsp+NbwTM0ofWdswWq9bNDgUtLNvzbIayWm+NmCBQatktA4/70gpTPl9L8fRJN/pTS1xforGN9jRg4s9hlxKOgQHROx0HpzMgRwVsy2JQUEgym+i227FqNjZ4f4Wf4k3TBjdt7bSaj2t8dZnpksuneioA5ZdT3PV14Xo7S1BnvQvXM9fcR/P3eZHHv5cXNfNYaChrWC14ZbgUVsKt4QxnwjiTV+LLGsQVnd4F7AYpnkGHerYo+Xe7CuKCp+CBR8yU4p3baXu1pfu7MSTh7hVppW6qy40aU1gGaNnxI3frXIrTOhNF7DtKpBYb2mita1xzBOEbuLuM+Tsj4aUx5NriGBDbBjdf5KzzSuOJgyxMaT28ZdvibLoTKm+m2rHfdbet+IDfhmcrOU33dz7ncjNzr3ZuN+J4bLyrSeZxFZcDUU/BZ6F0IVxGRU7FLucVo2NcrnV1oQuEeFPQYbnxp3AJsWcTEUtlLjG8fd01fIGBWlc/NO8+47OAIAuKjmqqkvK6griG36g67BZl8L0a0nThACnFD3JjPsUktickq593R8rr1pWW5HYo0fSyxrUFa3k04KqPQ2EBrPnb9FyTeuJ+V7G+RSWjGT5tY6O+/YS83vr7UK4moFN/LB/RLbxix3c4sZNKy812II1lGn01WAbOhneu3aC+RK6z7J8UofofXxY1veAAB85rlHonNyo+qTJisEfdQeCfH0hcteRY/VDakYp6IW3kkzFiyN6G/P3w9NeTvyXwMgaEVaDwBCgXksrVdvUTmurLXCgjjzOp3lWJA1lhZ7clU97iBrMCg6y7R1iJbC5Hr+7H6JlsR8K8txD95NUM65md7onGOv0zK1Vuy5AYNd83OvDR7GCwoaJJsflpXuLu3kdTdsdFbC+FHWxyVqiDpG3UpijR9LLGtQVjeqn2og078cRVhrU44p1MgmEtPc/9IFHlPd3dxnbHnCafyk4YDKzU5svaPZbwfcrh2u7JFnLrP5hB788/Jl7doCDV3rENBDBTifLdzPsctOPZbWyfoQ1DIt66a1hZp/yWNHNfhtTkzClkVoSAdEHOl+oPY72ar/a9Da328xC85iNlLWtpaBrMK0l12JtKS9LiqB7V7PktvlkiN0KbxIHr6UuioVHlG5uLI2R19lVL9j61x0zsbdLCKbOKz+DzI2igN8sJPXXAxh92ZmB+byfM/bTnDcwg7Od8tmluOOnBqMzukc4T3PHahez9x8E4k1fiyxrEFZVY2fTdewY90UTlzhbmV+FwA05BNbcYvx4LdJU86NcldMLTh/q9Yj5846pAqeGWn3dq/gJr/CX2voGKPTUv6z7hM0KBdrvpn56zBMwBh/NrwIbqqXUeOqOvXWxARc36SswpCLJucuMO5g+ft8H3EJRfmWyUIzlgFwcQz3jxU/16KW9+/ZHoVlU6K+CbLWhMlYLnl5cMWCqlY0ph+zo/Tb///2vjxKr7LM8/d+a+17ak9SlR2yk4QkBJQIIqJIazPCOONxVBz0jMt42tPTHs8cdY692baO3TPHkXHplqGhuwEBQWgWAyKQELJAQshWqSS1L6n9q6pvvfPH73nue78vlVQJWqlY9zkn50t9313e+95732f7Pb9Hn0kAgJTjlpYz+j7Uw3tX+qbgNMRIvLrujLvL6VF+GR6Rkt4ywXhID0Z4Ygjnxj0deDxjQQmtwhWltB66exvcTQq7+LyMbE3NmJjF1/i++DIPZXY1fiCFJUX9eMsRAsoptFNoUvxciernR4QcQfqaTYx6+u21cJVNi9s8WSsdaqWH+UTSxkuHD3HV1f56Ewu5bX4xV3DlNnej+7D9ypxyaumIltZK8UxA+RrrrRYPKBGnUEBlhAAkLywc6h6aqFQRz6mWyoTDHL9mKzL5mqXwloXmfKrMR02fm88HXOsvLZF6I5ZeOl/+FlReImp9/JCWgmun2jLeo9qGQQDA+FOWVS5eyXt199VPAQD+ZuQmAECJ+NnDa87XuC0HmJOHxH+WbmB32xOH+X1Bp9W//YbPd7RP+ktsZwbpa+ufBgD8xZOktly0xz5zndfxOV9W34dzYU+K4yLia3xffJmH4r/4vvgyD2VWTf3xVBj7+xciKKZr2pN6KOgQc1fAE2VLCVAYFcBL5hhTaF6rbrxBQDF6HDHf2o9X8/sCa84tWEees75OmlL5Z2gepQTuaBql/Xal5VtPj4irIDDeyTFJNYrlnWriPiEP/1nmFMepjClhKfrpOyuFRN4WXdrGSVo4BbTAJ6ypqCl49HK55S+HIp1LIQrkkVbaizLUsAAAIABJREFUaWFWivRwrhNR6wbmSz+GRIMAvyQgO/wyTfyJK6z5/NGtbPv2N/tp4i+6j89G57U0zW/btBcAsKd3sbtPUNK2yu3XJSAdTWEbD+FC2WEeL3Azn9cvLH8eAPCX//rHAIAa4e87fZvdJ1pFd6B7tBjJ9MwoeHyN74sv81BmVeNnnABi8YjtkjPiWXeU3KZUC2P4RWJSymaLudJFB+w+BQKPja2k5lxYL0wkovHDvXZVHyuVoh9hW02UynG11bJoc+NhtIV2sFGLQrnY6jjGUNv5DDlaaltfy8DQZFKshUNM02TCdsrLtrHZ50CepHBO8FN5+tPK/uoF8ORq+tzCmz+kIN9vU1SkotsqO60eSoK2On0hT/l1rJfzHi7lc7RiDYNv5+4lf15hu9WiH7jpIABgYhWfl2ND7IQZHeDfDVHe91h8ub0MKRZTDa+lwfp9wA4FMbE6FkiQ7i8O3gwACMq2XTtpMYbLJt19tKgrXpxCJu1Ddn3xxZcLyKxq/IDJoDCawJD4X/k99rfRpVwNK+oIkxySYpxIC3N1KSEg0JQdAKuRJYXTu4dpQiPWQcV6221kULr2JAUm7FRIMY7ANSPnpPw3atVLRvx0hX/C5WwXn62Cq29RjSVqSHXQf+s+QqujbDmtkPFmni/aZae8p0/6rUUkTSjjVp/QSErTyernpwEG7QuQo+Lnos8/FRGHfubyRlxMFc2kzFi/k2csExFtK2CujLDvBk9Z6HSBtK2+fRm1+c9/9m4AQNE4B7fyy2+62961h4zy6R7uv+Vv2S677Sm2vHng7+n7p95rGypq/wct464v4/nOCF+/cvIBwMgq0egBARUNSupYewFq6jFjJ0qfl1QhZmwd+RrfF1/mocxukY4hG6n6NsG4R7tWciWrKGAFw8RuspS6ruxKwlkDHporJVcICKQyUSF+nRy/p8127l7QyKKJAfWv2sSSKOUKm6jj+ZWKC4BLl5VRkhDRJloGrBLzcOVrcY4j4YW4goiULMKDWnJLSJWDXQtL9ER6qd5VPH0BJ34uavrft0zFMqWZEDUGFV4t0GyNIYU9HXBDYkW93E+6q7xzPEbvZs71LQXWcnzpzBoeTzIvKdG8QSm8SYuFEfUAaTIF/K6kkH55SxutwcoT/L1/hy0F335FCwDgwDOMHWiLvKHNtBoW1zCGcLarwt0nKt19zKKE2wVoOvE1vi++zEOZVY2fTAfQNVSCqPjT57bYVVH96I5d7C6r/i6amGNNCi2SN+pucrSf5vMj1ePIlYEjYkEoB8hCKfeV1T7ZLXBZT0FGRgt7RDOrb5/R+g6t5vRYIa5lMST5/NfpxwelA1BgteXVj4j1kexkPCM0kUMsMhVhyXl+7ixr+ov56xcaysUouC4mem1TYbunk5zsR0A14cj5fRWXl1OLHnqLUXxtx5ARnMVwysYDklIYFhrgq/NGO4tlHOG7z+/leUe7bKntkmUste0ZkWKsY3yA+q9hOP/96w+72z73FDsnV74lOf/r+VlfxzEOT0gmyUMRp9ZNKhFys2HTia/xffFlHsrsUm9lDBKJEDISDQ8Wexqdd3MVrDrEFbXtA1xB37z2RwCAjT/9EgCg6b+/4u4y9hR9skdX3wsAuOberwAASl6hth1Y6yHiEBLPAinzHRmglg0Iksup5HkD1Tapmunn6hpSwsbcjrpFQrrpQSD29YiGl2KcScn5q3UyMWi1R2hQUFra80/GoLTdLinIVH69q/mnQPf9PuVi2vudYAmmPG5uxmIG++ZaEuKLa99DSB88k2+tzcOn2dEmIGXiY43ZZB4DCVsqGy6W50M0fuAELUW1ICa3iLM/YOM+7btpFai1Gd1K7f3Zpa8BAO551naiq32DG3Vv5xgamqWzbozPzeSEZCXGPLEuL7mMH9X3xRdfLiT+i++LL/NQZpdXP2OQnggBAqaoKrcgh9HDNGXyexiYK6yiO/BYjIUSCWlJ3fmVa9x9iv+BZtGWG+kG/OCjPwEAfO5ZgizKDtnLG5La91EpwFDutbQU5+RLF5WMBxCT1yOgHqmLT9YIN4DAP/OkAWPmTRvIiQqScudH2PTw2dYV/P4VBnbGNnkYeKoYuBxv5f7BEe2aIhBRhZ16CnvchqPqBlwIwgucn+7KDcIFpwie5e6jvymIyZsu0rlK54whV6YKCOYe3x2/c/5vF+IemMr0nybQqC5XKOpJt0lhS3Ai+4ABueZo0G6rcNuA4qeEji88Ku5gtWzouWchaXs+eSXdgHVVZNH5Py/tBAAU9lj9271dgsC1fJC6B/hspAVS7nZx8rThhjJPDYQtsGsa8TW+L77MQ5ldBp5oEiuaunGyk0ylw69Wu7+pImn5YwZSFhZ3AgC+9uQdAIDyY8Ks8r5Bd5+SEqbGxp5gCeSXuj4FANj4bvYdPhCypZGFJ7k0x5okgCbBHdMrXX2KOBULm/rdfdqWSu+0tzR/JyWd5VzNlbUnsMxytCcG+N0vj6zmbwroqciG/QJAOs8T3AQQkM45jqz2qnHSI5bl1dXIumRfiIPPKy48Vv6jgCTRHo4HBW2kKMrNpEnHmbBoyMRoxLNtdp9AXKgi9GJKKJcjLheCfLH9LwT7BVxrKShBONQxqNu4gM9P3656e0q5JLOWz1P8TFHWofrjNrinvRa6hTey6iDHP9zMuSiUTk/FVZZlt72Dwb3QaVqbR4vluZdL92QLkZG27dpHYlJ75KWz750W/ABAQPoyhGLGDSBOJ77G98WXeSizXKTjIBpK4fY1BwAAzz2/3f2t+uGjAIC+e2kN7Fr9KABg+f7PAQBKzlLjnOm1q3FK/K1UuWjGJVxt20YI1S1osdopUcptimsZVxiVdJ7CexWk03mu1N1HkbOTlVK6qyys+imQYafCknco+Uf+Ca7uE40cd8VVkpbxdAKalB4BwaQCg7JLhF0zyOtXq1bNhQBfDMijm2hfOTlepIDaPO7pvKpMsCqhhVLyXE4NdixWZ3+TlJJ2cM14iE94oguP5R1Jrh/vjVHoPOTw6ufl8Vo7+knEUjxkBze4juN+T+NpAMALpwnLLX6JqbqRWpuau2MhU3DfaXk/ACAt8ZjiNqraYSGO2VZz2t2ntZRzltcrDL8tfMa08Ca+0MZ9lAMyEZdnIJFdwusIWzA8nIHaMyATnjnWydf4vvgyD2VGGt8Y82UAd4Fr7CEAnwRQB+ABAJUA9gH4uOM4iQseBJZl95m2lQCA6jdsOWvnx1mU8LM13wUALNtFTb/4aR6y9Y841K2rT7j7dMcY8Tw3xBW04bvUrn1f5+9/d9cP3W0//493AwCSB2kNFK2nBkufEtZeQeds3NDm7nPgLOHDoXFP2SNsDwDl6g9H7OqblCU3VSRQUeHm7+/jWLVrLmCVkxYKKRxZ+wZmpI9fwMOym1Go5kzAMrmwXo34SpaiQLRgfMxq/LyB7Eh6tZSQ3lzD0tTjbZZxNjIs5ckyvEQ02w89bxy/rVzoGnMtCW+cQP4f6hFrTLrjXiPafPdD6wEA+f32nhUISCag8F6Zp1ijWEaerju/6F7Hfdp4Hwq7qKF7PsfzfHgJszlP/MO17j7SFAdmM0vO0/0CD1f4uccvnxgR68Jtjys/KqWWxmdi9tV1iVvq4tYanUam1fjGmAYAXwSw2XGcNaCxeSeAvwbwPcdxlgEYBPDpGZ3RF198ueRiHOfiK4S8+LsBrAcwAuARAH8P4D4AtY7jpIwx2wF8w3Gc913sWHlLG5yFf/VZlwwjcs6uO4lKrmyRBczjh/Yx760R14XXnwUAtPRUufuEj3PlzFxByyHVwb/rf8Nr6rvTFut8Yc3zAIC/feYDAID8bp57crX0PhNNGhy2Gjm0iHn2VEq6qR7LhmcqVNJL6tnQQOKNzhbGKvKFtmmiUbS6h/IpI9FY9ePUOlANlymU43p59dW3n6LMlwfx3M9cX9jV+DxuSDvKDnp8/CGJm0hvuIImavzCKMfd025LnRVyrD6+G2nOjdRfzNc/z19/G9t6+8XFs33i/AZ5NuQemmMSV/Hk7CfqOQ/Xbn4LANAzTuvs9Mu0+JLlnk5JVXxeovsYa9IYwtY/Yufkg33MFkTvs2Wzo40cU8mNLNZJSylvz1lu4953eLoyqdUk99n18eVZ8/Z/iErsYLIhie4//zvET7dPa2NNq/Edx+kA8B0AZwF0ARgGTfshx3ETQe0AGqba3xjzn40xrxljXkuPxKY7nS+++DILMhNTvxzAbQCaAdQDKARw80xP4DjOPY7jbHYcZ3OwpHD6HXzxxZffu8wkuHcjgFbHcfoAwBjzMIAdAMqMMSHR+o0AOqY7UH44ibX1nTh8lDDWgi5roi3ewd0V3FNxWuqRd/KzsZDBuDMnFtkDyu41ZUzRtY3RLxir5Wemxab+7i/ZAgCoFL7+/nxJqQigpvisVGY1WbP9jpX7AQD3vs522CWtPOE5xneQt4hm5HifXdA6umgKh8olLTNGtyYs7MCJPE9wT0x4ZdxRM0+De277Zk/qxp2x1G+RkBHTOzCpjUJ5PIVM9wx4gnt9wmZUws+iVbyOd9WSGebRsbV2/O10x5RHIC5ewHkm/8Wq5y4Ex51Kzgvqyaentbn2JtAGp0urzgEAjr3YDACo3sdr777TpmDvXvMyAOAnR5heTvbyngUVjjtkj5+epLuX3koXqLyI7uSuVwnYCgtz9Og6zyWJO93VLYHlMu6jIDInbdmAXJNevQv16Fx4tQ7Ew+QUmmKep5GZPD1nAWwzxhQYYwyAGwAcAbALwO2yzScAPDrz0/riiy+XUqbV+I7j7DHGPAhgP4AUgAMA7gHwBIAHjDHfku9+PN2xUk4Ag/ECd/VKlngCLCnhtxOOtIwugrJtLC1tpwvssh8e5f5trbQSSgScM7yVy2XR6xZ40XGWTTPzK6Quv5UnGF/Blb/mAyycSL640N3n3heYktl8FSHA+64iV3peL9fL8TJqhpAnIJiSep0aYUzpELht4WkJhA16plxgpAXC0qvcfeFe6R4k/G0ZT3AJQYEux7Xdt3w/1RKeE/xSbsKUwGIXl3CMPcYGTPP7MnJuHrAowqDe6nxyzb9QtMzddjRJja/NRZNiYKWtAZE9DuDCWmkm6cmcbYzAoc2Y1Ziq/ZbWM0V36DjvZwVjw2j7kDDaeArEtB4+U8IL2X4VmXN376NlWrPXDqHjRh7/k6vIC/FEJy2gxU9oVycef9P/2Ofu0zVJ6/LwQ0xZj9fxmShfRWtkIG5BY6EhPh+pBdmZ8cCg8EYoQ5QHsJWQ3YOFM2+TPaM8vuM4Xwfw9ZyvTwG4ekZn8cUXX+aUTJvO+11KtLnRqf3G5wHxNdWXBYB8KU2MLeKqW1jLDEBsiFo1ICuht5POZA1X2ZplXN27W6nVjfiwjqfgY+Fj3K/jen5++5Z/AgB85bk7AQBVrwoP4AY7HzUrSHGqwI7ONh4/0i0tsEWzpYo88Ek9Z4mAb9Q3E4YhL09gSnn7tZDHTUVJ8UsoJ7UDWH9WD6ON/NyUnUf16+ovY9JzR4WTcFJiIoVHrYqOLaem0RbRvccWyFh4rPw6C7oaHxZfWKwYhTQ7kZwS24v5+G8H3KOHlbkIL7BFUkkpMgq38Zo01RhezOepTHzyc6/bAjGFKUe3UQNra/aBV1iI4+21cNvNuwEAD762GQCw5J85/+3v4Vxu2cmUYDRgK59eeIkQ4JrdPE7/eo67dCOf2wEPjFvLb02ePMPyrmjbde2rmPH0WtD3KNIQw5k//SEmWzreeTrPF198+cOT2SXiSBsEhsJu54+GTZ3uT12jBD6UHeGQJqpkNZdCkrSseKlCu/qaMikZFdK6/E5Z7QXOuONj+91tn3S46i75J66kX635MADgMzteAAD838C7AACFp6y/2JegVigVX6xkAbVdQvn6RaMV13sIRfq5ekdPU+Mo2McIY2vaA7UMihWTLhZQhnC/O+M54JygXd2NdtmREt6MaPyg8saPe26pZgXUSNBiILEACkom5TKsxg+qL1kv2wppikaR097ebMlsS+I8uOjvmgdQrSm1ciQwnxi2429YxHvVNSDc9QcFftvMfa6tOQUAeCxtNb5aBQqs6TwtMY8aXnu0yloUjz69jUOQSz11u/BHyly+fHwpAMAMeXj7G2lldO9gRqDkBMfUX0IAT/Eiy7yclK5KkzFaELmlz+798GQylP053luQxfh8MfE1vi++zEOZ3bLcSBpFzcMY6WY0uP/fLNgvvowrWfkWrtjDLVx1NYca1HoGu5C6XUNKhO+qbZH0pzvIjZ7ca5OpNU3M33dt40pvhJjj8CJaGhX1LKBIHfVEuHtE663gyauK6Ce2VjKMGpGxjXYW20GJ7+VaJjLuYIiaIePYCwgKTVcmj+fReEBG87uq4TyreFBoljLicwfFF3Stg6lWfPH1jSgLDeuUFHAAsZSNKmvhTaFE8yNF/ExID7dU0vPIyDmdqfL1wDtj3Z3qOKLptduRU8mxVZTbuEPfXhYRFUjGJ3AHszX1UZoHTzzEXH2m2FonDVu6AABn2njvI/30q9UH72+x8NtSQcOObqcV0FTN5+rMUcYDCtu479gqG5UvLeK2Q+PaMVkuS/tAhHLKmQFMyH0M6DOgc6gQXk//h5QUXYUHAu49nk58je+LL/NQZpdXH0AyFcTWtcyLn9i/0v2t5CiHUiMFN6P10jGkQzrKips1sc2u7sXSi+zYniYAloCw4EMshkjELKfRyEvU9IWd3GZE0tHVefTP60TjPx20Gr+0lStxxzIm53dsagUAxJbTWhjew2Pmd9hpTEjOPS387QHh3k9LkYg3z+qW7oq/rtzvGtF10VmeNH5Ker9p5D8zJlRZipbzFvSo9lfQl1pN4stq15WMh9lLqZviKeGN1/Hqx1TUTrkafSZ0YDMhEFHRGMWEkJGKn1tQSC0+0F7mbpqnJbUNYnl5ngEAmBCrML/NWl5nzjBzsWIJNf9xQ+09upffhzxR/Ql+hWLR4jqHIbHEJmoEQ3LUTupgP5+pD71vDwDg4f2bAADVv+Yc96Vt4dOilWwhPT7OuEUqkZPFkU99ZnhuwWcsm8ii5LqY+BrfF1/mofgvvi++zEOZ3TbZAMKhNA4+u4pfeAp5l72bZvRb3QzOlD3BQMjAaoH3rmNKJCM19wAwXCu89wKEyZPWx5qWeW/TMXfb53YTZKi4Cg2+PXpgAwDgM1e/CABYedtxd5+Bby4GAFS9Kgy9GyTFIuapBtgcL0uqgleUE1/L50c4Ng3OAUAmX8EYNO0DCtKQwE2kmuZkKmkhwRltBaW19UaLOnKCQIBrRltAEP+OSspoSAqIHE+KVNlnug/TjVFOglu3kCdxb58tkho6yXulQcqJWgEiKVJarc6piOByTfyp2HV1DsV9CfdwfpJiru9cRJfxhT2b3F2q99H8j/0JXbc/X/lzAMBdL3wSAFB+kMcYXmlN4vIaptNOdolp3y/PkdxLs8SWk2s6M+8BuhdndvLe/eNHfwAA+NTPPwsAqPuN3efEXTze2kLCnh8Z5bMYnshu6w4ASXl2Q8Kym9J2asq9J+m9tAc0VijPfSIZmDHpnq/xffFlHsqsQnbrVpc7n7x/Jx565DoAQPSc/W34SmqhkrrRrH2cFxn4iAgravJWy1e+uZb8eC8+z0IJLSmN7mQaZmzCAjsCB5hyU7bd6vUMoihYQwN0oU2Wt39siNaFtjUeFzhreRXHGDvENI/xZGMSi6lxHAHSuPx5JdnwXO82wdHswF+gnpo+IMCdRJ+1cqDZu1xYbCg7CMdts9l6gsL0ktfM8RfnU1WPvGh59AKShYo1C0tPJcdy01JaT92TNnV56HkWLeXLvMfqeXKFIl9U+6g1otBUKTrSbkIA4Kg9KiW2lZK2i03S8go/Lw1KEx6Y9Z1nAAAVUVqILx1cIdclGlOsKQVNAUBUCrd0nrUhpbIeh+ut9s6cYiVSSNKFy24iIKh1gM9C8pCUe8fttestqt7J0vMryvjsvfDzqwAABd12/P3bJa0tVsjwMMfkeHsrAHAi9jkqbKHGjzWl0P3n30f8zO+AgccXX3z5w5NZ9fEn0mEcGalFolRSLXmeVVFW4sk36Tsl6gWOu0Vgsse40gb22tTHqeu5qkdX0p8LdHLffknv1C62JkV/GfdPlXCl7xH+/Mg54cRr5vmiCQ+ktpur7PhirsIhbX29h8U66TKu1DVre+x5XqP2zO8VzbKTFkp5AbVK+3ELFVV/OlMgsFvx/5NSPBMWjnXHU6Rj1InPZbKdspV29rbqF8aE5TUWkODEIg/qIweMkzrHbZ4QZgmFBgNAWiCtyYrstKFKuIwWRdLTMjrax/mNSy/EolLOy1gbU6ZhT/vnvI0ExyjfX+eJBVljm1jB6ylqtVZCy28Ylwm9izGjHRsYs3npMK2Tkjel7Dtq7/NEMf9f3cznpUeeIyMFMulTltAlIIbCRBPHf+gUA1XaMQnNvObwKRv40RTy8ATn4aamwwCAX+VtBACUnZh0tx28ktZlebNYIZJWjcW0d14ma2yAtYZjocyMwVK+xvfFl3kos6rx6yND+MaiX+C2I18EAKz69ln3t1N3LwEA3P3vfgkAuPcUI59DApcMiaIp6LFq5UwH/fMPr2XE+eE1jO7mdXJ17El6GHmlTV1+Fa2Eq+oZYX25nxmGqt9wn6EVFthRs4GavFusg9QQNXFaWFnVFzz3qvWRtVQ3fh396Ig4eO0nqOmD43attb4wPzT6rtHwqPSrS3oi4ErH5SioRA+XmGIND2VHhHXbcKlo4hEOVq0eAEg2ULtqliA8KJDpVfRz19XZwqo9R1iQoiyvmuVICsI1MEWHXc2qKKd8XDvGSBmzqbP+9PAZzvuYxCaq1giEtpUnKDrN845e4elBKNd8tIvzvVB62CnBhXLQx5Z69pH56ekWIFA6+9oTCzxBHDl+pEci/6KZCwtonY0f5TG8Vtp4rVizvYyPvLqQz3pSrIORJmsRaYlw+zmxOnI6A03VDThRJjDe8eDU2ZEpxNf4vvgyD2VWNX5nogzfOHsrFixk5Pzk55vd30pauIT99J5bAADBG+hvXbGBUdojZ9l/LJVvI/XRU/z/w2lGRwNS3BKXiLTjKSEN9FE7xCe4UreOCKe5QB/7t3LfgIevPPY4oZvF76WvedVSdvF5cRezCFHpOjO2xPq9ypGeOUq/MHkFNX/jchaLKE0YAET6lMCCfyu2QOG3SsXl9eeUiMHVojkdcLM6x4oFYbSYRi2IWHaEOLXI+piO+JJFLVIGLQmF7dKJZnWRR+MniXsOSyImLTEb7awTEu0YnyL+4PYs1NJm9fXHrPaLDMo9q+a4V1XQAtv/a85hZIT71i+0sZxzI8R/qLVx6iw1f9VR+X09x3TThsPuPs8cJSVW4WE+T5PS2TgpGRozaq1Ao2XPy2iZpMf528RZ0fRyfxJV9pnQTE7+cV7b/YbW7Be2/goA8L8GbnK3bX6UFld7WLpGX0PLtCvEGMi4dNrxEroo1ZlTmJ4x9Zav8X3xZR6K/+L74ss8lFk19eOpEE4OVLkmrPfk/ZslgCJLUeVjNMWPLePnu6+naXa23qbzOl5mKqXkEE3XkfU0zdYvoXn0xhtN7rZ5ag2upQk2OqkcePw6Wk5zN13sgceelpZZ+ziGI9sZENpwHVNEew8xuFX2pr2SpNCnxaW1tiNwW7chY5ENKmUErKKgGeXXT2s8R1s9e1tEpbLN5oA07HRba3stPWmVhVEZn4BkKhoY8BqQRp4le2zqafgK7lN6Aysce95g4HLXcQJhCq+0fPSmUHgDQlIhqOCiHCYeb/BQKwSjjWNZ1zgq/H3wMOaGpAOaEQacrnEB7MgQBtZJzwVP8DPZWSjnEbdJvldgzLIlvK6nD6xx91HWpXGFHFfIPRITP+BptxUV8FNcQEQlb/A5UvBSaKG4AJ5GpI60Z9PApnIoFAnWWZuyekUrN1MC4Z2ckCag2jfAM8cu90M8MHUl5BTia3xffJmHMqsaP5MxiI3lISNMqIlKmyZRbnq3jv3DTN0UPM1ATus3mXZb+U0blPlvH3sSAPDlH38GAFB0hKtsS5mw7XpYSsZlRVYwyFArLQfVRXXlhEiebrGpuYRkd5IrqXr6hZm13/DzIzeyvvrRkvXuPot/Iim+1RzLgk0MDEaD51OjpIWtJxSTDjeipXRWMsKrZrwBO22TrKX6Cg0uymH1BRA8IZE50YiRddT0DSW81oEualCTtvuEpBPMuVFhi6mjVqospyZ7Y8BWVplBSW+qsZHTC0G1VMAqfCTk/joSYFTgS4GAlcaHPQxFotlj0mS1poEQ7VObaQEU7ef37Y4FRVUu43wPHeEzkBJo7kc3kxz/X/ayo1LDM1bn9W7meBtWM3jY1s59tSsOmm2KUdOPkaM8d0pikVqclZGAckGp5embEAs3nRfK2veRehaIXbfzkLvt3n4CpWpf5rhPR/k8LljMgPhohMeKd1sYd0DmydSm7AM9jfga3xdf5qHMLudewEFefgLxHmrDgk7PunMdV7TGUsJvOx9uAgBExriSnr2DmuGasF19P/fKfwQAlA4KJHIHtdMC0eqZPnv8ZEk2k6pq0VQNt1UfPNJnl0xNU+UJ08+4MLVGXmOq7uF9BAxtX33S3Wff9bRMFv6K+5xczZTgt65/GADw7SGbukkdkbJYOWVaynRdBlWtu/Gyqmj6TtKObg8B0TSpUTv+VI2y9QgLaw/H3SbXGtJ4g2Mfg4B81Sw95462cfz93RIPaOq1QymVjftyWufI+dKivdPl1rILayylixorIAyxqaVSFFRtNeUopFORXNPu000AgCV1tAbPVDcCAAo67TUPJhiPSQs0+9PX/BoA8FgbU7CLfiFlx9vss3H1u8iFv/slpvUKJE0b2UbrYSLuSee1ZDP6JAS2jXJ5jqSwarLNFjMFq3nNFdtuKt19AAALO0lEQVQZX4g/SC1+6kXCiz99533utrua2YOv5jUeLyiFZsqBeG5AioSqbAo2KXXQ0ZN5MJM+gMcXX3y5gMwu555jkEoF4VRnd5IFgITAM48vkA4oawXCKRFQBXr84r5r3X0KZNkavpZaorSEvviYROy1BBew0d24FOFkhLsOwuPW2s5YQtQTNR8X3rbNVdRye09xhdbalqKT1ASvhJa6+6zcQRhye4zbFhHzgx82kbffG3RNiBWiWtYttdUovsYoPGAN7dSjXXaUpCIjRB/epTxUQi1hzuo888cr1tCXPZCgxsw/Zy2KoSv5qWWtED8eYnUEvAQaMi61WJTt1YUVF+RwB8IWIKFQyEcS2Y9gXp7NeiQGqd2iUoldtI6xif/UyO6233rxDgBA7R6r/Vo+znPfuu51AMCP9+3gvhL/OXcHn5Vbr7A9F375DP3+cuFgid2SXRIbPWlBRYkqjjtRLteaY52ViHXYV2KtoPBJzn+/PDipK7lx5UpaVff32E50CgAbXMFzBuU2tPXSkonmc34mhu2YNMKfLHbcezGd+BrfF1/mocxuJ514AM6pQuSv5IoaWG9X6sA+yc8LGcLkAmqY5iXUTsk0l7KRuMdKEDr4sFBJDbZLnldWzQXrrT+qMvJr+leF6uNvoiOfTEzRqUci2oe6CRdWMoSkcrK7oXV7/I5hjiFZlJ1Q7Rmiz5fViUZzsdoTXXPPosWdhObHPX5bDtuqRv6Vzx+eTEa6T7SG/B1ZznkPSW1pcB/HlPGQg6xbz3LWjhivo6SFxx/ZJF1zy7rcbU8aod5SHII8Tcraq1ZJpMRq8XQ772/RGaGQkrLlW6VU9cEnd7jbRoVQeWQl729ESlTv69zK3wWbEauzEOSNy1oAWIqw0v3UvEUfoH/96YXU9Pf/1fvdfcpkCq/+PH97c0hYdp9izwWTsffSWUMVvGMR5+lXRxjTKd3Hue7byPmvqbWEMaNSoDVZLfPRyDhV/BlamQe32LLfH33khwCAzzx4NwCgcRevvT0s0XwaaSiqGHf3CQd5PwfbS33Iri+++HJhmV2N7wDBuIHZTW0yutJ2G7npVpbWPn2QiKoFL3FoZ1LS6Ub612VusNRYaYm2Bt+g5hIgGWLLeNzuM5XuthGJJpuNouEF4ZUZpUYI98qxPFHRa6UoZ18Pl9nQcerO8QbJUy+SSG7URq3HenncPCn+iVdLtFoW4vSkZ8rF7DBizWj03VENr4u3R+E7OR1wL0ZvpfRMaVnftcBjv2HP+FgTJyxRZh3D3v3NWefObGQ8ZtMSxi4Onmt0tw0OSJ89dXP1M5StdaJRq/HHZdx5AxxbX4zzvzSP1plZ6iG2PExNmNfFOeuXQpV0tcyt3N54pdVfQz3U1skzvA8VN7Pj8Q11pA77f9+jps/YBrW4+e6XAACPtDCHnves0LRVSjltlb2ecim/HUzQ8sw7I2QttdzmPVfwPM/vtsjAple5z6nbOc+3LHmT5+sQ377XxgPu7buG1yg9IqJ9QjKbxzHds/1nAIC7nv2Uu8+CVwTLccPk+QQtFxBf4/viyzwU/8X3xZd5KLPLq5+fRmDtMCZaBdzgYY15+nUxjSTQ1HdtNle+tjMqvdoG7BR+219Euy2tprMcI9pugz4ZaV9ddhVNPyMwXA32xSu5T+1V3e4+z73FwE2oU4J6EmTS4pSgmGjpQs/6KWmvjAbuxBIvLaKr0T9izTo1YeMCXY7WckyTso3CmDN5nuBSvgTi5PgB4V/PxLSgxMPbLykzp0h8ICnSCQT0GPwsbLOPwfAqfrf8SjLCnmhnYGrfIbLGhMqse5YupwmfTGcXCCngqKyCZntswt6H4lbhGZRTLq/n/WyK9CFX8nsl2KnD3cjjbaml27FnQKDZHoKcdJjXWrmGx+1o4zYPHGQ6NfpBBt2CnrTkQ08woKhB2/hKfpZqeq/ZnuBddQRrPXKEMO0i6XA9spLb7Be30Nu8susaueeSt3385JqscafzbXB1bzfdsKLFBLId+wzflfLXOW9fOcGgX+ENw+4+jZ/itfa92Tw19+IU4mt8X3yZhzK7AJ7xIJyDpQgKX1zaA7BZ0sy03akTDM4UiBbKbMxOtw3utww2GkzKv5KreIUw2XbsV7YeT+liGZfggYPcX4MnOz7ENNKes00AgOFf1rn7hLYzn1S0Rph+X6b20GBYtXDydXR62igfoHabqBFmmQaqhPQUXGhuieoAj5es4mdBGa8jPijpPE+KDsXZwRsteNI0TsYL780JEhphyB07yyBZUPjqAzePuLuUPcFr7GplOuyDH2NxS2+cmue13SvcbcPKUS9zGdTy1WBOQdSAJ5ImmySK+Z/hOAOO3SlWRIU8LaMzQbEOJIM7Ku3VjxXQShvdInx3B22Kd2yE/9fGoMqqOyn38ntr/wVAdnBs6XOch3OrOZbiW5myLLuKxx9/wTJF/fIX23jODQwyT2zleCMneY1jQ+UyZnufEmW8J9qKfXEp9z1+gMy/hR32/hZ9glbNhnKWlj/xOs+nLDujS8SK67VzeqC/if+ZYUku4Gt8X3yZlzKrnXSMMX0AYgD6Z+2k70yqcPmMFbi8xns5jRW4fMa72HGcBdNtNKsvPgAYY15zHGfzrJ70bcrlNFbg8hrv5TRW4PIb73Tim/q++DIPxX/xffFlHsqlePHvuQTnfLtyOY0VuLzGezmNFbj8xntRmXUf3xdffLn04pv6vvgyD2XWXnxjzM3GmGPGmJPGmD+brfPOVIwxC40xu4wxR4wxbxpjviTfVxhjnjHGnJDP8umONVtijAkaYw4YYx6Xv5uNMXtkjv/ZGBOZ7hizJcaYMmPMg8aYo8aYt4wx2+fq3BpjvizPwGFjzP3GmLy5PLdvR2blxTfGBAH8bwDvB3AlgH9vjLlyNs79W0gKwJ84jnMlgG0A/ouM8c8APOc4znIAz8nfc0W+BOAtz99/DeB7juMsAzAI4NOXZFRTy/cBPOU4zioA68Fxz7m5NcY0APgigM2O46wBeUzuxNye299eHMf5vf8DsB3Av3n+/iqAr87Gud/BmB8F8F4AxwDUyXd1AI5d6rHJWBrBl+U9AB4HwbD9AEJTzfklHmspgFZITMnz/ZybWwANANoAVICQ9scBvG+uzu3b/Tdbpr5Opkq7fDcnxRjTBGAjgD0AahzHUb6pbgA1F9httuV/AvhTWOKvSgBDjuNoXdhcmuNmAH0AfiquyY+MMYWYg3PrOE4HgO8AOAugC8AwgH2Yu3P7tsQP7uWIMaYIwEMA/qvjOCPe3xwu95c8DWKM+SCAXsdx9l3qscxQQgCuAvADx3E2grDtLLN+Ds1tOYDbwMWqHkAhgJsv6aB+DzJbL34HgIWevxvluzklxpgw+NLf5zjOw/J1jzGmTn6vA3A+g+fsyw4AHzLGnAbwAGjufx9AmTFGKy7n0hy3A2h3HGeP/P0guBDMxbm9EUCr4zh9juMkATwMzvdcndu3JbP14u8FsFwioxEwWPLYLJ17RmKMMQB+DOAtx3G+6/npMQCfkP9/AvT9L6k4jvNVx3EaHcdpAufyV47j/AcAuwDcLpvNibECgOM43QDajDEr5asbABzBHJxb0MTfZowpkGdCxzon5/ZtyywGTW4BcBxAC4CvXergxhTjuxY0Nd8AcFD+3QL6zs8BOAHgWQAVl3qsOeO+HsDj8v8lAF4FcBLAvwKIXurxeca5AcBrMr+PACifq3ML4JsAjgI4DOBeANG5PLdv55+P3PPFl3kofnDPF1/mofgvvi++zEPxX3xffJmH4r/4vvgyD8V/8X3xZR6K/+L74ss8FP/F98WXeSj+i++LL/NQ/j8i2+lhnaJdXQAAAABJRU5ErkJggg==\n", "text/plain": [ - "" + "
" ] }, - "metadata": {}, + "metadata": { + "needs_background": "light" + }, "output_type": "display_data" } ], @@ -120,7 +72,7 @@ }, { "cell_type": "code", - "execution_count": 580, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -139,7 +91,7 @@ }, { "cell_type": "code", - "execution_count": 581, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -149,15 +101,15 @@ }, { "cell_type": "code", - "execution_count": 582, + "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "tensor([ 0.2561, -1.4784, -1.9960, ..., -0.1852, 0.7645, -2.1264])\n", - "tensor([ 0.1516, -0.1036, -0.2665, ..., -0.0838, -0.0019, 0.0238])\n" + "tensor([ 1.0819, 0.4414, 0.8810, ..., -0.8080, 0.8716, 1.6529])\n", + "tensor([ 0.1455, 0.1032, -0.0662, ..., 0.0145, -0.1423, -0.0802])\n" ] } ], @@ -169,33 +121,33 @@ }, { "cell_type": "code", - "execution_count": 583, + "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Loss at 0 = 12.090258598327637\n", - "Loss at 1000 = 1.1304563283920288\n", - "Loss at 2000 = 0.5384575724601746\n", - "Loss at 3000 = 0.3744213879108429\n", - "Loss at 4000 = 0.2969752848148346\n", - "Loss at 5000 = 0.24860896170139313\n", - "Loss at 6000 = 0.21340151131153107\n", - "Loss at 7000 = 0.18535934388637543\n", - "Loss at 8000 = 0.16165657341480255\n", - "Loss at 9000 = 0.14076030254364014\n", - "Loss at 10000 = 0.12176588177680969\n", - "Loss at 11000 = 0.10410811007022858\n", - "Loss at 12000 = 0.08742041885852814\n", - "Loss at 13000 = 0.07145684957504272\n", - "Loss at 14000 = 0.056048400700092316\n", - "Loss at 15000 = 0.041077621281147\n", - "Loss at 16000 = 0.026463929563760757\n", - "Loss at 17000 = 0.021158630028367043\n", - "Loss at 18000 = 0.021166445687413216\n", - "Loss at 19000 = 0.021167738363146782\n" + "Loss at 0 = 12.377154350280762\n", + "Loss at 1000 = 1.1459124088287354\n", + "Loss at 2000 = 0.5384519100189209\n", + "Loss at 3000 = 0.36750683188438416\n", + "Loss at 4000 = 0.28883790969848633\n", + "Loss at 5000 = 0.2407495230436325\n", + "Loss at 6000 = 0.20637495815753937\n", + "Loss at 7000 = 0.1792437881231308\n", + "Loss at 8000 = 0.15631511807441711\n", + "Loss at 9000 = 0.1360049545764923\n", + "Loss at 10000 = 0.11742571741342545\n", + "Loss at 11000 = 0.10004911571741104\n", + "Loss at 12000 = 0.08354274928569794\n", + "Loss at 13000 = 0.0676906481385231\n", + "Loss at 14000 = 0.052348289638757706\n", + "Loss at 15000 = 0.03741894289851189\n", + "Loss at 16000 = 0.022839922457933426\n", + "Loss at 17000 = 0.021162766963243484\n", + "Loss at 18000 = 0.021166853606700897\n", + "Loss at 19000 = 0.0211676936596632\n" ] } ], @@ -209,86 +161,72 @@ " with torch.no_grad():\n", " random_tensor = random_tensor - lr*random_tensor.grad\n", " if i % 1000 == 0:\n", - " print('Loss at ', i, ' = ', loss.item())\n", - " \n" + " print('Loss at ', i, ' = ', loss.item())" ] }, { "cell_type": "code", - "execution_count": 584, + "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 584, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "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\n3oXry9nUnHppJoxr1kKYkk74mgoOiY57NGYb2XpboE2D9Pmm2f9Oy3VFRGbI8DvzCoA2hy6CRlbf\nvPlpaMzSQTunIxtw/LKfkfOdeCDDXm7Vv9lnxz/BlBXTbFPzMS/Vm6Apmx+2wB2V1FkoMY48g9Tl\n2AoW1TyM7wMJT8dd5Z0/C5ZQsBfXs+sGXEdZN+5l8+9sJ9xgGvOiVowWSYW24lmJ1OJYTT+xxUbl\nr8CX7/kQxlb3Mq2D968VEZH6O19yt3WYiguN4zyrboaGnmY3n3CS7M1p64NHR2FdOvNhZSgl/sQi\nPoub7f2dWUCG3224psaHMLahSxFTKN/K1HWpfU7ju5kKjUUkmCruFfhq4mt8X3yZg3JcGt8Y82kR\n+VNB5/ntIvInItIsIneLSI2IbBGRDzmOk3nVgwjAN8Ht+yW3GqtX9Q67Ug+ejZUuVQsfct5T0H5/\ntP45ERF5mUUjg+dYRtXGQayyNS9Du5oOFr0UsHKPvMmSUqSrCV6hv5hfjqIfQ+DF+ClwBlML7CW0\nfxJoouGrsPJnGOWfasV6+aaLEAPov9UCVBIX4tpU0yvFk/a6lybbs004lswKWDUBWh3a1z52P6ir\ngtX2mnMr2E/vMfS9W7YDVk3fO2gJ1FuH3fkZzrXkBWjmArnxZxqoFbV9vae/fA2tsV1/D3Om4TmC\nfwYI7PFYLG4n2gij1o8gM1PgNtl1uA9abCNiMy6N+zHuJT/kcRsQ03E8nW8NO//mS9hffhBWWpis\nsspPHxyxz5FmAjKs3k6toJNMcJdqzPGz57m7ON3IhGSq8DxNNbOUdxeLjy6xcRktu1ZCktwBPD99\nH8Xx4mxzWIjYa3ba8H+6hp2GaNS0bsQ/4UPWMtrxOdyzeAcuYP73QftV8zLukaMxqZi1WAIpPLP5\nkpAL1jqWHFPjG2NaROR6EVnnOM5qEQmKyPtF5GYR+YrjOMtEZExEPnJcZ/TFF19OuByvjx8SkVJj\nTFZEYiLSLyJvEZGr+f33ROQLIvLN1zpIoaxE0ucsl8gTgK0mL7fFLhrhTJHAIh/B60+3A766kl1O\nGn9raYySy9nXfBsj3CXwe1bcyg40M57OsaVcKdndJdTNwog6aBol4Bg+3RJ9aM69vJedXevY6XUQ\nq37/pQoltVpQi2jK/xuaINgELaJaMDBl/enCmYhxhHvpo1XD2tFosmGPtdTpi9x9SraTiCGDVb7/\n7dRcTCzUbrfaW/PF02uwjROiJXE+5nJc6jhWTzfeWkSTl/wUx082QrMYzm1h1EKag428NlouvdfC\nKqvbDmstugV+de/1p7j7zN8ILaqxHC1V1U6+gVFPXID5+9I9LP6hBRCeKCac7Pi4jbq3PoZ7tuh7\ntP4qaBb007IgxLbi59vcfUbej2ds/q9ZTpzEtQdHYUnk4jZWlIvhuRw6H+dM/DFeK4mFUP/dmylJ\n1Sjslt9pJyBCnicvsn0HTARjUNLOwgLEeQy76CqEWrW8iMjgBtzHus0TIoXjg+AfU+M7jtMrIreI\nSLfgB58QkRdEZNxxHH3ie0Sk5Wj7G2M+ZozZbIzZnM1OH20TX3zxZZbleEz9ahF5t4gsEpF5IlIm\nIpcdZdOjLjWO43zLcZx1juOsC4fLjraJL774MstyPKb+xSLS6TjOkIiIMeY+EVkvIlXGmBC1fquI\n9B3rQCZfkMhISlJvQ7Bs6HR7+rqXYOqVH8D6MboKdlH9RmzT+SFUsjVtsiZOvoRNFRfA1An3wQw1\nEzAnBy+0AZzarQhwBecRfpkmxxuhrlphtvxmT7qKn5U8BzuuVKG13LdAMJALLRURoUvh0Ix3dnRh\nrGsQ9AsctG2ypxbQ7FTWGZ7PMFhmaA6XvLDfjmkxrilIk7Xx+0g1pdfDzFZ2FxGRSAhuzMhqBiVX\nsR5/P8z5qrGjrNVMLe6/ghWDTBdWvkiT35NGElbNBapRu9+wlW7U84Rfl8N1STfagF1oO6+Fxwmy\nRr3zgwhaznvaHj+6B4G4XDPBLEOYuySblSpP/cpbPI8eQTh6X50quG4HP4b5UXN70e32PtfcBchv\nYAkxwLyvmRayKI/ZisTIKIO1Bkos+gJPSwYeDW+HrEcnQf6fi7EnAjFQg+cSml1vg5MLavCcHkgr\nPyKehTADvyat1259iYanyAcRCBQxSL2WHE86r1tEzjXGxAzqOt8qIjtE5DERuZLbXCci9x/XGX3x\nxZcTLsfU+I7jbDLG3CtI2eVE5EUR+ZaIPCQidxtj/pGf3XGsYxlHxOTzbnFCaNrCC0fbsYIpF/50\nM1lFnsdqVrsJK9nBd9tAzoK7EJTKLITGT7ZDm5e+0CUidoUVEXHChGUmoQkKYwxSrQXUNUCAigtK\nEZHUxQAGTTdgmio7MbbIywwcMdgUKLPpPMOOM6abq3gU15GL41pDpTYNo5z+Jk34cC8bbn4E6aPW\nexl0C9nVfXIx1EVlF1tqUztFN4EBJrd2ibttpooccgPa3YXw0kVkFN7P8wY9679aOUOH6YQxBN3U\nGhERyff286LJbhRjAFAtIU1heuqSNN1UGEE6bORacOLHe8k90GeDewoFDo5ifoffC0uxdEBr06nd\nPClAtb4M5126YQ3Ub8W8RRLsHrTYQnaDE9Dshs+GENQVrKJrmvNeAPeh5g2QJSlTobyATH9mPa2v\n+Uhp/wK3tr6fTMnzLAz6QCMtnlJcU98GXMfiO2D9OJxjYzxj0uejcOQ4X02OK6rvOM7nReTzh328\nX0TOPsrmvvjiy0kuswrZzZcEZaK9SiZbi1tfi3hSHUw5qY904A/h60TpW1V32NSZMvBEupCqiXDl\nG74U4BMvk2rgFZaOUqMFFyBmMNIOH7CqAxo6MN/GBUoPQOPEnqQPGSkugNB0m3KpiYhII8ab78MK\nnb4MZaI5xiNKJ21mI1eKbc1BgDTG7mQxDuGrDlN2zpC1QsLTTKFNeEArIi6YJrzLcrSHmZor6ce4\n914DXzwyjrGUsCBEWpvdfQ5dYDWhiO1tlzwbMYp0pYc99mdkrGGq0jBVWlgHfzrcA5/ciXkgxy6z\nD0unHwV4ZuR8zHv3e2wJ7PyvwqLQlF82TmuBQ4gk6FcnrUNtGPtQi0Lvd8nvEHcIVOB+O3FbRKNW\nznQ77kcJIcyBKcZ/PKmzAq27/ut4z/fCKmh6lulI1fQeHE22lJ1uyAKlMOKGx3Hfd37agrpMGb6s\nfYIWL7kaFb7sdt/x+vJq8RwneEfEh+z64suclFnV+IWQSLIuIFOLsELVbbbrzmQbVqvuS6Cdlt/C\nktpKrNA7b4K2SldYxlPzHXZ9YQ83Xcaq70aoNdhogRfq/ahPXBiAP20K0HAKdnEqPWWnB6CN8lPQ\nrqEmaiNGpA2j1s6kR/uSKCO4DKCbmVpMcckYrtnx+GGDZzJivhNjGHqZ0fEB+v70V/Npa1GEpwkE\n4hjUKijQ6tBov4iIU4K51E63+VgFr5kaqA7jD+21UfH6F9iBtgrHn1wAzTO2DMeaWOEBK91H64nz\nMrMKloP6u+FKWhKezrEuzJev+X5ovXgvwUTdVpMZFsQEeG8KYYw/jKSNW+468H5b7NX8M1h2hqW7\ngXI8P3ne7wLPG/DcByUQKdtJUJfyUAaO1ItOCe9nFM9ccA0si/FTsG06Q5DXExYIVtmFbZMEgFU/\nAPIX1d4rv2atqF03Yb9kI+asdgd/B8189lTT/x7a/Wjia3xffJmDMss+vsjE8oIYltgaj5uy6Mfw\n0wtfx3L+2Wd+JSIitx96s4iIRB6Fb5vrtVHlzCewQkf+iR1VtiHanryE0d/HX3G3Vc2oUel9f4dt\nWjeywOEQWXzHxu2guPIH4tTsM/Ql1aen9nDynsIS0meNnQkNppH7bFmg6PwiIituBfVWZik0Y4CH\nyTIbkRs4VDQOEQ+8l5FnJ8fMAqPYBY/1MbwOFFtja1Dk4wQxlrYHmW/fhayBKbVWguwH1Di0Ajnt\n0hFoo9rNsARKhy0pSKCiomgeos/Aj06945Sia57/sB2/Wlwqyvw70UacgCcwXfM8fWtShikjsvr6\nCotNNnk66fD6u/4K97ftS8zRL0ZRVrYe99IbCxESfGixkVOOe2gmpovei4g4IVxTKk2rKUssQ4Bs\nzWT8Db7FZifGOO9Tk7CiYkOwUGJPkW//gKVLCwwik6Q973PnYS4Dmu1QTe/18Y/SdfdY4mt8X3yZ\ngzK71Ft5kdCkcXudDV9io7EzdfDHK2+D9viLZehxn67GyhYb4ip/oYf3/vvYJ3IQK2ZOaZ1eIVmB\nx8d3pli6y7LGZd/Aij92PqL70VOQCTA9h+x46cNrOaiusdrJ12i5bMKT+18LzVLzLMYw9CZoc9VO\nO75oo+ZVm0nXtADHm/ck+6Y98iL2YX7ceCjKCl3UVIyKuxYE/dH0m1a529Zuh/aLTioJJvbpfTM0\n2MIxWCWZGk+EWzBniSWwAnL8KleCDEFiqdUVkWnEMYLk04/twtxV7oBVkmcHnLEV1qKIqcaidWOI\nhnQJPzxKK8ACJ0XS6XfaYahklJaRx2BRi6v+RcZyiLUI9pPQ4m04Zss+T4ZGUXDaf4D5fCUnVZyF\niEjoEDMtDu59gV1sg5Xsqxhht5+8nacM6deCIXw33cS+e3oPw/ZnGJxRyja89q/HHLZ9h4VopEYr\n8vFV0zvOqwDnjxRf4/viyxwU/4fviy9zUGaXcy+AAF/Vbm0GaeGryXlMYRGdUdav2+B7l7/8czaQ\nNrmCdg0DHem3AywTfA4FGF0ft2meuu3kwicLbctDMJ2iYzQJmaYxCU89exntXAavtEDFBYwoxDNk\npzFNXvT0umJ+tZod2GdikbVLlc1FWyCVbgRPgetSKEDIk87TFJcGydxtTgFUt+sqd1OpfBFBz4kV\nDEIyyNTyM5qGNINDL1pi/UAdgnd1r8BN2Hcj5rD8IN2Rp+xYSjroUjFAl1yFa3ZBWOwaNpExAAAT\nlElEQVQLUN5tATDu+DUgynStwlmzHpi1AlMUGq0FKGXkQ5ipPrKNePpUtlPvhWuXfhueidhOjFWb\nTe75X5bjYMk/oTZfm4lOn9WG8zzL5+gqC+rSAGOO9yQY5/OTZ5Av+Oqcd7kMthl5MwZc/yCfG8/9\nrdmhRWp4r5yKPVcBQNXyEOc85HFVCjxnKFgEHHot8TW+L77MQZnd4J4DuGJkSrW7XXe08KLiAFav\nkdVYHSv3YAVMLMPrvmtsg8pgCvuU7yDr7UKCJ56ChqjfmvNsi+O2PMhSzyaAZWK7kcbL1yHI5E3N\nqcbJnQMI6thynKdxI6Ck+Uoy3fbbNtlBQjaDKVxjRON+TMktudu2me69BNey/OPPYxNNJ1GzGcV1\nBCzAI9hCplwtkOG2By+G5jRJO/54P/7PxrF/lGW4GkhzCHIxzbbwySEAyYXh8nCxblyIE7WaJtOG\nQGDgsCKWDM+nMOXYQFoOF7f0WINvLiutRxdpmopjcXsVBFXzY3CZcrtPcIbblmGc0REWZRGkk67B\nM1LWY1VjoKqy6JqVsbiMwdXUKhuEduFjbH6QTzFAxzFp4DcUsQHBAgN94RKCr5j6c7sfeZ656pfw\nfCSWwNRVKHuaLD5aLCX1Nq3qBvr8dJ4vvvjyWjKrGl8KIqEpI8EMVqaSYavJYoP4bPAMro7EoUyR\nGyE8paANT+ENfTtDYoz6rQBcZM4GcCV20Prro2uh4TNV0JiaGirMh88dPwCfMBi3LEHqW6artHce\nVmztize0ln3OnrCgnFgvtYOSamSwT66avr2nhHTe/wG4RAk/Dge3KOhIy15FRKZOxfjjHFthCtec\nrdDzWU2m3WMaaGFpb4G+N+F12dNIvxWSSXefANNhwnOHmHrNl7GjUcLDGRgiX16eJbUseQ1Pcmxs\neZ2qs/MTV7gzOfx0PvR+RMesplSLxBmGFlRLUUV9/nh36ojPAtqtiYAbJ0r+PqbjyvfYfRwW3hgW\nPmnqdeIC+NXBHjv/SkwSXE1oMwE8BVqvJaU4fjplLSOH1kGI/n96hlBqxpC8RUbOQVhykQQ0ftZF\nkPP55/02NVV2IoK/v/72Nb4vvsxBmV2NHxDJxxwZPA0r3kyr1XCRcaycVQwwj7U77j4iIlW7sdon\nG+xa1fIbLfskUKQd2rpmO/zRoXV2VQwRZBIdZRSW/mKIRS/BAwTuePxd9T/LDkATpBuwQkfHsU9s\ngOQLYQ9Yo4lloaFiWGl4kuQjfRYgVNBorvrwjiJUGK2OKsOtzX6Ep3BuLSs1BJk0bWJsZKW9pd1/\njm3LnsT8JJvICX8IY9v7N4jY5+JWk5Z1YSwtX0Oh08J7AOHtfi+ATvUv2uMHqOnzAX7GW5ZnGepk\nC4tsPIZMrA0WS2I5VFn1IyAQUVBLVYcN0Y+fBq1XRfBNJs5ybhoQ6Qq8LzPWytEiJrUlTZrFQPT5\nsxXshDNp99FyYoeWY+PjZOSlJRaatv70gffwnzHc52CMF0etPp3A507BHj9E3z7LAp7CDPsbnAq6\nMS+1mvYWjEywVBibSICG0Oh7wQhc95hlRtby9KMVFb2a+BrfF1/moMyqxg9kRGJ9RsJTWM0aN1tN\nYxz1Q1n2yL7m6uNUb4Ofl7rQRvWF/tDwe1DIEO9hiWpUqbKs9ogMsuCiFCu/8rg7wWLfyfF0azU8\nTq4SK2owyYgxO7vUb0EMIdnigbxyoVdfU3P16v966ZEMc7HKve92r6UW13Jf7QUgIhLehEZ9phZa\nqEAtFX8Un5c/a3EC6XYwnofpIxvGEA78Eck8qBZDEx4fVqG0hJPqvGjXmok2669X72KxDznfnTBz\n2RkcTwtTszF7/IkluKHBDGMSLBAqYX/AkKcrTrad88qxKGHq+CqW2pLmKpC1c5qLsXfdNHP/nNMI\nSU4jvfT5PXRm7j1hNx+Nkk+swvvEEs+2guOWdmEeMitpUfCag4TsRqI2VhHUIp0RXE+4ggSjV8AS\nW7nFY30w21HdgXkfW4WJz5US11JDq8dTTBYgqaoTDhUVdL2W+BrfF1/moPg/fF98mYMyu8E9ERFH\nJFvOFNG4XXcGz2Rwr4MwVtqJMUJ3D22AaauVYCIi/R8CG23DCzSJmbLRppOhKQ9XWpwttGiOhlhx\nlS9nMOYssO0aj6mkQbtsOQNCBMdoE0jlE1CgCo5L9lWashroCpJf32uIadDObUul0GCaewFWrrk8\nACIyfAWwnFPzcY1xQmmrdiMlN7zWuh0aICrjfIytwHFrduI8Q6cXB8tEPPNL83fnlwFtDREmmyu1\nZulME8ZfvpdcB1m8Bmg558owT/mo3SfDWvrGp8grQN57NzBYY9OpVXuRckufsZjnxr3LluEYJWM4\nkTL+4JoJuiqP8Hr4Pg5zOEi3JDDhSaFpxRvTYrlKuh9sb60uhYhIZFwbYEqRGAXl8DpmRqzLFWbl\nXjhOV5Spv8gox+3hyNeUbrATQDOTBxQ7yOChukhFgTw3dZz1TX1ffPHl1WV2IbsFkcik49Z4TzUH\nPd9hRUtX4bWsR1l1seonm7CCl4za3FC0H8G16cVI23mDPCIiIU8duyFoKJDG/jOthLgyJaX86NmY\np70xh6caK8dCHmVLFQkX7Sti03gZss+4wcPDtLmIuBrG5e4jj7wChwpDgAKr5hcRqduEz+qfoDWj\nbEDkGWjwzIHyCO7+xPyi62l+GNqk521syOhJPU0uxJgalH2IGk6Dft5mkMoem2zF+KOjxdBctby8\nVkIZgVoKaNIgqFpNM022k05oCictRKmJGSSMMDis98V4UNZaJKX3JEhIre5r8kx9GcutqFaCBm+1\n6EjH7wUOZcuCPA+bWua4bZoTU4JjRarsXChLT4FFOsEoz5c6sqJGrUAF9dS8gvOMLyf/IqcnQAZl\nEcsfiQEWg8BeTXyN74svc1Bm18d3APpwC1c8C55qgjQLLtx0DwsPQjPqY1qVU5gPTeiu7vSvVYsb\nT/GI+pvqryv/ua7q6SqcN5TysK/yOy0VzVRQw+gmSnXuKQvV78Icbxk76gRUq3t8MC3zzTWiSMSw\nPXZRoZAUWwmjp8FXVSBTZJLpqim9Hrtfvp2aeIxWVB+2mTwVmj44xZhI6Ch+obLRqkZTqjdPNaha\nbg6R0dlydguiX+1Cs8fsnGqhTWgCllyivZLjp3YPe7ofBdXS4kUpoIZo2xw1vvr8Ilb7qxVTqC7+\nXAt6Ap4pnmyhJiZTjrL4hqcVUGW3VSBNWS9LkQvQ0Nk6AqtmcKxM2j6nAQJ4wjE8KDkCeaoJSvOW\n5WpfAE3lxnuxz+A5GFtsgJbRUpt2jlLjB6oqRYa9qcdXF1/j++LLHJTZJeIIiqSqA66GVn9eRKSG\nHXLCyWI/XSP1rhb3sItarU1/K8IoNTVFwRNN1s9UI2fjxRr+aBF6jeaqpj+c5ECP5QS8GkdLX/nZ\nS4CkFhR269Xm/D90gEy/zmExCnIG5uZ7QEXcJFCMFHUtgLwnhHD4eKda8cF4mc4ph1HlGZM68YQN\nL/0CuvHu/QJYa91+dZ6Tmxx59GfUWmPmhAzDjmccapFMLIe1pvGSfFQzAHbbOC0UjZvYe1R8XWrt\niIhMN9KX1yIst0MTXsPMdORL7KD0nPq8KFhJn4Wizre0RPWzOIidZYo/pVxFcSdfEZHCJAbhMH3i\nsC9eWa9y+1m/PEBAk8M4Q2R7l4iILAoiu5JYjGOMrrQT1fzM8Wl5r/ga3xdf5qDMqsYPphyp2ZmW\ngXOwWlV0WQ2nUXB3dedXyUbmYzPFfryISF6j9i5XFV80Uu/1vbUOhkud+mq6r2s9eBbPDDWA5rZd\nlle+auGPN8+r469gqajbifYonUwVDps/DWXEAcIwA7QO8oTaTl7Q5u7jxjMyxWNKtEOLtH/VkoIk\nToOlMNVCTcy4g/rRZexQm++3jnu6lsdnD0GnhPiHCCfKo74zlZoRod+eLtbQqrVCHuyFq635Xc0r\nCBDMNCNgMN1gb0CmnJbEtNJyMbrP++JaPR4rR6mxMpXF5o4OO1WrLLb2O+3h6MZu+F7nqWCru924\ng+6j8PNYP+MNExjzTLst+w2EaQWwn0SIbLt7/wzbrvi3BXYsHZ0Yi0b3mbXR8VfvxnFD456yYhKI\nOLmcn8f3xRdfXl1mt3dexMjEwoiUH2Qu1+NnqQ+m2lNpo9w+5NTIBU9uXjV84bB8boHH8G7rFnTk\nirWSu6or72GFt2CFVoBuy680w+BGqGe8eV4SibyCsskCsxJKmBnwdq2hBIepUsrLiz7PbQBqq6LD\nqpzOK+EbV7J8WXvyRYdwAeOn23hA1TYUpiTr8VkVEXYl4+yD10Yu/qojtUShAhp48JziLjwFjw+u\nkXON7uucqhmlc+FF7qk/HUqy6KSe+Xy9Lx7tPUOcgGZV1EpTzewey2NRZMoPexZ0ujkEjebnPbdB\ntb/OQyBbbC0YT2pcuwdrUdHIKsx7hnn9XB2Lg7yH6IOZE5qPdEFmmrRjjE31fdFu2vxepaXjs0dt\nPnwqLrZ6NwfjMSDD2tHIOdKqfDXxNb4vvsxB8X/4vvgyB2X2IbtTBdcEdAsORCRLU7y8R2GTeFFT\nXJlbvSkq5zDzrXBYAM8rmnJTk1/NRTVT1YxU817EY+Ix9qXmqQaVdPzedJ6agMrmEiCHX57MKqbM\nFqEoe2zhIDj+g/VgnMm1AqSTriYgZsrWdi/+CbjeOj5KyDHrwEtJt+6Fx8ootm1+vDjAGJrGRQ9c\nSo77GRtQK+3lZyxmStUwUBdWQJXHPTN6TgVX4b3yI7ppMk/AVOc5R/NdOetCTOOWDltzNVkfLNo2\nOs72VBE9Pl89LqPeo0y5jgWvhxfVeFOMmqIMJfV4GgTlmHOewqR6fX4wtghJbzUoGhrGTrlymyKN\nLiJvA1l7hPdMyMk/OW4Lq1qUS4+wbQV+tfwn0qpq+nvTwgVtxRUKFTfTfA3xNb4vvsxBmfVOOtmy\ngKsBvMUhQWYnFFLpgmM0RacLmRc/wmUre1hwT0trvQ0Y3ZbX2eLj6iqvEsh6dtFeilOq7fA+zGCe\nBve8VoiyxBamEcjJvuU0EREp2Y5mlyZsU2eFSWgCXdWTq5FCi4wj2qRpq4kl1kooHcLxFz5IDdmJ\nQpxcHZltXthlx89zDVyAhqAKdKnZBjVV/yg0zdBZdqJcsIrCY/nesC10IeopAkop1FVhz/hcA7EK\nZfamSDVA6oXxitjgrpefLzrBIHBpMXRa71GBitJ7nzVg7G5DqyPKdgYZrXfyjEnH795vZdLlM+mF\nKYf5DCj4rGSUwdsDuLDESl6ABwadHML9K2sgQy4f5qlxZfe1J9j1dwDqtH/R8vCJ2OCw23PB02hT\nmZELk5PiHGeAz9f4vvgyB8U4x5nw/39yMmOGRGRaRIaPte1JInXyxhmryBtrvG+ksYq8cca70HGc\n+mNtNKs/fBERY8xmx3HWzepJX6e8kcYq8sYa7xtprCJvvPEeS3xT3xdf5qD4P3xffJmDciJ++N86\nAed8vfJGGqvIG2u8b6SxirzxxvuaMus+vi+++HLixTf1ffFlDsqs/fCNMZcaYzqMMXuNMZ+drfMe\nrxhj5htjHjPG7DTGvGKMuYGf1xhjfm2M2cPX6mMda7bEGBM0xrxojPk53y8yxmziWO8xxkSOdYzZ\nEmNMlTHmXmPMLs7xeSfr3BpjPs1n4GVjzF3GmJKTeW5fj8zKD98YExSRr4vIZSKySkQ+YIxZNRvn\n/j0kJyJ/6TjOShE5V0Q+wTF+VkQ2Oo6zTEQ28v3JIjeIyE7P+5tF5Csc65iIfOSEjOro8h8i8rDj\nOO0islYw7pNubo0xLSJyvYiscxxntQDj9345uef29xfHcf6//4nIeSLyiOf9TSJy02yc+/9izPeL\nyCUi0iEizfysWUQ6TvTYOJZWwY/lLSLycwFweFhEQkeb8xM81goR6RTGlDyfn3RzKyItInJQRGoE\nkPafi8jbT9a5fb1/s2Xq62Sq9PCzk1KMMW0icrqIbBKRRsdx+kVE+Npw4kZWJP8uIn8tlpKhVkTG\nHcdRtPvJNMeLRWRIRL5D1+R2Y0yZnIRz6zhOr4jcIiLdItIvIgkReUFO3rl9XTJbP/wjW4YUt5E7\nacQYExeRn4rIpxzHmTjW9idCjDHvFJFBx3Fe8H58lE1PljkOicgZIvJNx3FOF8C2T7hZfzRhnOHd\nIrJIROaJSJnART1cTpa5fV0yWz/8HhGZ73nfKiJ9s3Tu4xZjTFjwo/+R4zj38eNDxphmft8sIoOv\ntv8syvki8i5jTJeI3C0w9/9dRKqMMVq2dTLNcY+I9DiOs4nv7xUsBCfj3F4sIp2O4ww5jpMVkftE\nZL2cvHP7umS2fvjPi8gyRkYjgmDJA7N07uMSY4wRkTtEZKfjOLd6vnpARK7j/9cJfP8TKo7j3OQ4\nTqvjOG2CuXzUcZxrROQxEbmSm50UYxURcRxnQEQOGmNW8KO3isgOOQnnVmDin2uMifGZ0LGelHP7\numUWgyaXi8huEdknIv/7RAc3jjK+DQLzbZuIbOXf5QLfeaOI7OFrzYke62HjvlBEfs7/F4vIcyKy\nV0R+IiLREz0+zzhPE5HNnN//FpHqk3VuReSLIrJLRF4WkR+ISPRkntvX8+cj93zxZQ6Kj9zzxZc5\nKP4P3xdf5qD4P3xffJmD4v/wffFlDor/w/fFlzko/g/fF1/moPg/fF98mYPi//B98WUOyv8AvQCw\nI0DLGacAAAAASUVORK5CYII=\n", + "image/png": "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\n", "text/plain": [ - "" + "
" ] }, - "metadata": {}, + "metadata": { + "needs_background": "light" + }, "output_type": "display_data" } ], "source": [ - "\n", "plot.imshow( random_tensor.view(100,100).data )" ] }, { "cell_type": "code", - "execution_count": 585, + "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 585, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "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\nrkJggg==\n", + "image/png": "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\n", "text/plain": [ - "" + "
" ] }, - "metadata": {}, + "metadata": { + "needs_background": "light" + }, "output_type": "display_data" } ], "source": [ - "plot.imshow( random_tensor.view(100,100).data,cmap = 'gray')\n" - ] - }, - { - "cell_type": "code", - "execution_count": 586, - "metadata": {}, - "outputs": [], - "source": [ - "#Sources are based on https://github.com/jcjohnson/pytorch-examples, NYU Intro2ML" + "plot.imshow( random_tensor.view(100,100).data,cmap = 'gray')" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { @@ -307,7 +245,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.0" + "version": "3.7.0" } }, "nbformat": 4, diff --git a/03-Coding-Neural-Networks-In-PyTorch/00-image-recovery.py b/03-Coding-Neural-Networks-In-PyTorch/00-image-recovery.py new file mode 100644 index 0000000..510e9e4 --- /dev/null +++ b/03-Coding-Neural-Networks-In-PyTorch/00-image-recovery.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# coding: utf-8 + +# ## 프로젝트 1. 경사 하강법으로 이미지 복원하기 +# ### 프로젝트 개요와 목표 +# 이번 프로젝트에서 우리가 풀 문제는 다음과 같습니다. +# 치명적인 버그가 있는 weird_function() 이라는 함수가 original_image 라고 하는 어느 이미지 파일을 입력받아 broken_image 라는 이미지를 리턴했습니다. 우리는 이 오염된 이미지를 삭제하려고 했으나 실수로 원본 이미지 파일을 삭제해버린 상황입니다. +# 다행히도 weird_function()의 소스코드는 삭제되지 않았습니다. +# 우리의 목표는 오염된 이미지와 weird_function()의 코드만을 가지고 원본 이미지 파일을 복원하는 것입니다. +# *Sources are based on https://github.com/jcjohnson/pytorch-examples, NYU Intro2ML* + +get_ipython().run_line_magic('matplotlib', 'inline') +import torch +import pickle +import matplotlib.pyplot as plot + + +shp_original_img = (100, 100) +broken_image = torch.FloatTensor( pickle.load(open('./broken_image_t.p', 'rb'),encoding='latin1' ) ) + + +plot.imshow( broken_image.view(100,100) ) + + +def weird_function(x, n_iter=5): + h = x + filt = torch.tensor([-1./3, 1./3, -1./3]) + for ii in range(n_iter): + zero_tensor = torch.tensor([1.0*0]) + h_l = torch.cat( (zero_tensor, h[:-1]), 0) + h_r = torch.cat((h[1:], zero_tensor), 0 ) + h = filt[0] * h + filt[2] * h_l + filt[1] * h_r + if ii % 2 == 0: + h = torch.cat( (h[h.shape[0]//2:],h[:h.shape[0]//2]), 0 ) + return h + + +def distance_loss(hypothesis, broken_image): + return torch.dist(hypothesis, broken_image, 2) + + +random_tensor = torch.randn(10000, dtype = torch.float) +print(random_tensor) +print(weird_function(random_tensor)) + + +lr = 0.8 +for i in range(0,20000): + random_tensor.requires_grad_(True) + hypothesis = weird_function(random_tensor) + loss = distance_loss(hypothesis, broken_image) + loss.backward() + with torch.no_grad(): + random_tensor = random_tensor - lr*random_tensor.grad + if i % 1000 == 0: + print('Loss at ', i, ' = ', loss.item()) + + +plot.imshow( random_tensor.view(100,100).data ) + + +plot.imshow( random_tensor.view(100,100).data,cmap = 'gray') + diff --git a/03-Coding-Neural-Networks-In-PyTorch/01-basic-feed-forward_nn.ipynb b/03-Coding-Neural-Networks-In-PyTorch/01-basic-feed-forward_nn.ipynb index 0ec1340..6798abe 100644 --- a/03-Coding-Neural-Networks-In-PyTorch/01-basic-feed-forward_nn.ipynb +++ b/03-Coding-Neural-Networks-In-PyTorch/01-basic-feed-forward_nn.ipynb @@ -1,19 +1,5 @@ { "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "markdown", "metadata": {}, @@ -21,658 +7,305 @@ "# 파이토치로 구현하는 신경망\n", "\n", "파이토치를 이용하여 가장 기본적인 신경망을 만들어봅니다.\n", - " * [개념] 텐서와 Autograd\n", - " * [프로젝트 1] 텐서와 Autograd\n", - " * [프로젝트 2] 신경망 모델 구현하기\n", - " * [프로젝트 2] 토치비전과 토치텍스트로 데이터셋 다루기\n", - "\n", - "파이토치는 기본적인 수학 계산용 라이브러리를 바탕으로 그 위에 머신러닝에 필요한 그래프 형태의 계산방식을 추가 시킨 라이브러리 입니다. 물론 파이토치의 바탕이 되는 계산 라이브러리에 대한 깊은 지식이 없더라도 파이토치를 이용해 머신러닝 모델을 구현하는데 그리 큰 문제는 없습니다.\n", - "하지만 파이썬 개발자들에게 편리하도록 설계 되었더라도 수리적 계산이 많이 들어가는 머신러닝의 특성 때문에 파이토치의 자료구조는 기존 파이썬의 자료구조와는 사뭇 다릅니다. \n", - "파이토치의 가장 기본적인 자료구조인 텐서(Tensor) 가 그 대표적인 예 인데요,이번 장에선 이 텐서와 텐서를 이용한 연산, 그리고 Autograd 등의 기능을 배워 보겠습니다. 더불어 이들을 이용해 기본적인 신경망 모델을 구현 해 보고 저장, 재사용 하는 방법까지 배워 보겠습니다.\n", - "\n", - "## 프로젝트 1. 텐서와 Autograd\n", - "\n", - "프로그래밍 언어를 배울 때와 마찬가지로, 파이토치 또한 직접 코딩을 하면서 배우는 것이 가장 효율적인 방법이라고 생각합니다. 간단한 파이토치 코드 예제를 같이 코딩하면서 파이토치에 대해 공부 해 보겠습니다.\n", - "\n", - "### 텐서 다루기 기본: 차원(Rank)과 모양(Shpae)\n", - "\n", - "가장 먼저 파이토치를 임포트 합니다.\n", - "\n", - "```python\n", - "import torch\n", - "```\n", - "\n", - "텐서(Tensor)는 파이토치에서 다양한 수식을 계산하기 위한 가장 기본적인 자료구조 입니다. 흔히 수학에서 말하는 벡터나 행렬 과 같은 개념이며, 숫자들을 특정한 모양으로 배열 한 것입니다. 그럼 간단한 텐서를 만들어 보겠습니다. \n", - "\n", - "```python\n", - "x = torch.tensor([[1,2,3], [4,5,6], [7,8,9]])\n", - "print(x)\n", - "```\n", - "\t\n", - "위 코드는 다음과 같은 결과를 출력합니다.\n", - "\n", - "```\n", - "tensor([[1, 2, 3], [ 4, 5, 6], [7, 8, 9]])\n", - "```\n", - "\n", - "즉 x는 1부터 9까지의 숫자를 가로 3줄, 세로 3줄의 모양을 지니도록 배열한 텐서입니다. 그리고 가로와 세로 두 차원으로만 이루어져 있는 2차원 텐서라고 할 수 있습니다.\n", - "이처럼 텐서는 랭크(Rank) 과 모양(Shape) 이라는 개념을 갖고 있습니다. 텐서의 랭크가 0이면 스케일러(Scaler), 1이면 벡터(Vector), 2면 행렬(Matrix), 3이상이면 n 랭크 텐서 라고 부릅니다.\n", - "\n", - "```python\n", - "1 -> 스케일러, 모양은 []\n", - "[1,2,3] -> 벡터, 모양은 [3]\n", - "[[1,2,3]] -> 행렬, 모양은 [1,3]\n", - "```\n", - "\n", - "텐서의 랭크과 모양은 size() 함수 혹은 shape 키워드를 통해 확인 할 수 있습니다.\n", - "\n", - "```python\n", - "print(x.size())\n", - "print(x.shape)\n", - "```\n", - "\n", - "```python\n", - "torch.Size([3, 3])\n", - "torch.Size([3, 3])\n", - "```\n", - " \n", - "unsqueeze(), squeeze(), 그리고 view() 함수를 통해 우리는 인위적으로 텐서의 랭크와 모양을 바꿔 줄 수도 있습니다.\n", - "먼저 unsqueeze() 함수를 통해 텐서 x의 랭크를 늘려 보겠습니다.\n", - "\n", - "```python\n", - "x = torch.unsqueeze(x, 0)\n", - "print(x)\n", - "print(x.shape)\n", - "```\n", - "\n", - "위 코드는 텐서 모양의 첫번째(0 번째) 자리에 1 이라는 차원값을 인위적으로 추가 시켜 [3,3] 모양의 랭크 2 텐서를 [1,3,3] 모양의 랭크 3 텐서로 변경시킵니다. 랭크는 늘어나도, 텐서 속 원소의 수는 유지됩니다.\n", - "\n", - "```python\n", - "tensor([[[ 1, 2, 3], [ 4, 5, 6], [ 7, 8, 9]]])\n", - "torch.Size([1, 3, 3])\n", - "```\n", - "\n", - "squeeze() 함수를 이용하면 텐서의 랭크 중 크기가 1인 랭크를 삭제하여 다시 랭크 2 텐서로 되돌릴 수 있습니다. [1, 3, 3] 모양을 가진 텐서 x 를 다시 [3,3] 모양으로 되돌려 보겠습니다.\n", - "\n", - "```python\n", - "x = torch.squeeze(x)\n", - "print(x)\n", - "print(x.shape)\n", - "```\n", - "\n", - "```python\n", - "tensor([[1, 2, 3], [ 4, 5, 6], [ 7, 8, 9]])\n", - "torch.Size([3, 3])\t#[3,3] 모양의 랭크 2 텐서\n", - "```\n", - "\n", - "x 는 이제 랭크 2의 텐서가 되었지만 이번에도 역시 텐서 속의 총 숫자 수는 계속 9로 영향을 받지 않았습니다.\n", - "view()함수를 이용하면 위와 같은 작업을 더 쉽게 할 수 있을 뿐만 아니라, 직접 텐서의 모양을 바꿔 줄 수도 있습니다. 랭크 2의 [3,3] 모양을 한 x 를 랭크 1의 [1,9] 모양으로 바꿔 보겠습니다.\n", - " \n", - "```python\n", - "x = x.view(9)\n", - "print(x)\n", - "print(x.shape)\n", - "```\n", - "\n", - "```python\n", - "tensor([ 1, 2, 3, 4, 5, 6, 7, 8, 9])\n", - "torch.Size([9])\n", - "```\n", - "\n", - "이제 텐서 x는 [9] 모양을 한 랭크 1 텐서가 되었습니다. \n", - "이처럼 squeeze(), unsqueeze(), view() 함수는 텐서 속 원소의 수를 그대로 유지하면서 텐서의 모양과 차원을 조절합니다. 말인즉슨, view() 함수에 잘못된 모양을 입력하면 함수는 실행 될 수 없습니다.\n", - "예를 들어 view 함수를 이용해 x 의 모양을 [2, 4] 가 되도록 만들어 보겠습니다. \n", - "\n", - "```python\n", - "x = x.view(2,4)\n", - "Print(x)\n", - "```\n", - "\n", - "코드를 실행시키면 다음과 같은 에러 메시지를 보게 됩니다.\n", - "\n", - "```python\n", - "Traceback (most recent call last):\n", - " File \"tensor_autograd.py\", line 12, in \n", - " x = x.view(2,4)\n", - "RuntimeError: invalid argument 2: size '[2 x 4]' is invalid for input with 9 elements at /Users/soumith/minicondabuild3/conda-bld/pytorch_1524590658547/work/aten/src/TH/THStorage.c:41\n", - "```\n", - "\n", - "이처럼 원소가 9 개인 텐서를 2 X 4, 즉 8 개 의 원소를 가진 텐서로 바꿔주는것은 불가능합니다.\n", - "\n", - "### 전체 코드\n", - "```python\n", - "import torch\n", - "\n", - "x = torch.tensor([[1,2,3], [4,5,6], [7,8,9]])\n", - "\n", - "print(x)\n", - "print(x.size())\n", - "print(x.shape)\n", - "\n", - "x = torch.unsqueeze(x, 0)\n", - "print(x)\n", - "print(x.shape)\n", - "\n", - "x = torch.squeeze(x)\n", - "print(x)\n", - "print(x.shape)\n", - "\n", - "x = x.view(9)\n", - "print(x)\n", - "print(x.shape)\n", - "\n", - "x = x.view(2,4)\n", - "Print(x)\n", - "```\n", - "\n", - "### 텐서를 이용한 연산과 행렬곱\n", - "\n", - "딥러닝을 하는데 수준 높은 수학적 지식이 필요하지는 않습니다. 하지만 기본적으로 행렬과 행렬곱은 모든 딥러닝 알고리즘에 사용되므로 꼭 짚고 넘어가면 좋습니다. 앞서 말씀드렸듯이 행렬은 2차원 텐서와 같은 개념입니다. 숫자들을 네모꼴로 배열한 것으로, 네모꼴의 높이를 행, 넓이를 열 이라고 합니다. 만약 A, B 라는 두 행렬을 가지고 행렬곱을 할 시 다음과 같은 조건이 성립해야 합니다.\n", - "\n", - "```\n", - "A 의 열 수와 B 의 행 수는 같아야 한다.\n", - "행렬곱 A X B 를 계산한 행렬은 A의 행 개수, 그리고 B 의 열 개수를 가지게 된다.\n", - "```\n", - "\n", - "\n", - "\n", - "그러면 직접 파이토치를 이용해 행렬곱을 구현해 보겠습니다. 우선 행렬곱에 사용될 두 행렬을 정의합니다.\n", - "\n", - "```python\n", - "w = torch.randn(5,3, dtype = torch.float)\n", - "x = torch.tensor([[1.0,2.0], [3.0,4.0], [5.0,6.0]])\n", - "```\n", - "\n", - "randn() 함수는 정규분포(Normal Distribution)에서 무작위하게 float32 형의 숫자들을 선택해 w 라는 텐서를 채워넣습니다. 그리고 텐서 x에는 직접 float 형의 원소들을 집어넣어 주었습니다.\n", - "행렬곱 외에도 다른 행렬 연산에 쓰일 b 라는 텐서도 추가로 정의해 보겠습니다.\n", - "\n", - "```python\n", - "b = torch.randn(5,2, dtype = torch.float)\n", - "```\n", - "\n", - "행렬곱을 하려면 torch.mm() 함수를 사용하면 됩니다.\n", - "\n", - "```python\n", - "wx = torch.mm(w,x) # w의 행은 5, x의 열은 2 즉 [5,2] 의 형태\n", - "```\n", - "\n", - "이 wx 행렬의 원소들에 b 행렬의 원소들을 더해 보겠습니다.\n", - "\n", - "result = wx + b\n", - "\n", - "위의 텐서들을 출력시켜 보면, x 는 [5, 3], w 는 [3, 2], 그리고 나머지 텐서는 [5, 2] 형태를 띄고 있음을 확인 할 수 있습니다.\n", - "\n", - "#### 전체 코드\n", - "\n", - "```python\n", - "import torch\n", - "\n", - "w = torch.randn(5,3, dtype = torch.float)\n", - "x = torch.tensor([[1.0,2.0], [3.0,4.0], [5.0,6.0]])\n", - "b = torch.randn(5,2, dtype = torch.float)\n", - "wx = torch.mm(w,x)\n", - "result = wx + b\n", - "\n", - "print(x)\n", - "print(w)\n", - "print(b)\n", - "print(wx)\n", - "print(result)\n", - "```\n", - "\n", - "### Autograd \n", - "\n", - "Autograd 는 머신러닝에 필수적인 최적화 알고리즘인 ***경사 하강법(Gradient Descent)*** 에 관련된 기능을 제공합니다. 처음 머신러닝을 접하시는 분들은 이 알고리즘이 무엇인지, 그리고 어떻게 머신러닝에 관련되 있는지 몰라 고개를 갸웃거리실 수도 있습니다. 그런 분들을 위해 이번에는 직접 코드를 짜보기에 앞서 머신러닝의 학습 원리에 대하여 조금 더 깊게 배워보고 이 알고리즘이 어떻게 머신러닝에 사용되는지 알아보겠습니다.\n", - "\n", - "앞 장에서 배웠듯 머신러닝 모델은 입력된 데이터를 기반으로 학습합니다. 다시말해 아직 충분한 데이터를 입력받지 못하거나 학습을 아직 끝내지 않은 모델은 입력된 데이터에 대해 잘못된 결과를 출력하게 됩니다.\n", - "이처럼 입력 데이터에 대해 정해진 답(Ground Truth) 과 머신러닝 모델이 낸 답의 차이를 산술적으로 표현한 것을 ***거리(Distance)*** 라고 합니다. 그리고 학습에 이용되는 데이터들을 가지고 계산된 거리들의 평균을 ***오차(loss)*** 라고 일컫습니다. 즉, 오차 값이 작은 머신러닝 모델일수록 주어진 데이터에 대해 더 정확한 답을 낸다고 볼수 있습니다.\n", - "\n", - "오차값을 최소화 하는데는 여러 알고리즘이 쓰이고 있지만, 가장 유명하고 많이 쓰이는 알고리즘은 바로 전 언급한 경사하강법 이라는 알고리즘입니다. 오차를 수학적 함수로 표현한 후, 오차 함수의 기울기를 구해 오차의 최소값이 있는 곳의 방향을 찾아내는 알고리즘이죠. 간단한 경사하강법은 Numpy와 같은 라이브러리 만으로도 직접 구현이 가능합니다만 복잡한 인공신경망 모델에선 어렵고 머리아픈 미분식의 구현과 계산을 여러번 해 주어야 합니다. 다행히도 파이토치의 Autograd는 이름 그대로 파이토치 라이브러리 내에서 미분과 같은 수학 계산들을 자동화 시켜 우리로부터 직접 경사하강법을 구현하는 수고를 덜어줍니다.\n", - "그럼 Autograd를 어떻게 사용하는지 같이 공부해 보겠습니다.\n", - "\n", - "우선 값이 1인 w 라는 0차원 스케일러 텐서를 만들어 보겠습니다. 방금 전 설명에서 Autograd가 미분 계산을 자동화 해준다고 설명했는데요, 쉽게 말하면 w 가 변수로 들어가는 수식을 w로 미분하고 기울기를 계산해 준다고 이해하면 됩니다. 이를 위해선 텐서 w의 requires_grad 키워드를 True로 설정해야 합니다.\n", - "아주 쉬운 예를 통해 간단한 미분식을 계산 해 보겠습니다.\n", - "\n", - "```python\n", - "w = torch.tensor(1, requires_grad=True)\n", - "a = w*3\n", - "```\n", - "\n", - "a 라는 수식을 w 곱하기 3이라고 정의했습니다. 즉 이 식의 w에 대한 기울기는 3 입니다. backward() 함수를 이용하면 이 수식의 기울기를 구할 수 있습니다.\n", - "\n", - "```python\n", - "a.backward()\n", - "print(w.grad)\n", - "```\n", - "\n", - "예상대로, 위 코드는 다음과 같이 3 이라는 결과를 출력합니다.\n", - "\n", - "```python\n", - "tensor(3)\n", - "```\n", - "\n", - "간단한 미분식과 기울기 계산을 해 봤으니, 이번엔 조금 더 복잡한 미분식 계산을 해 보겠습니다.\n", - "\n", - "```python\n", - "w = torch.tensor(1, requires_grad=True)\n", - "a = w*3\n", - "l = a*2\n", - "```\n", - "\n", - "위의 l은 텐서 a의 모든 값을 제곱한 텐서 입니다.\n", - "텐서 w에 3을 곱해 a를 만들었고, 또 a를 제곱하여 l을 만들었습니다.\n", - "이를 수식으로 표현하면 다음과 같습니다.\n", - "\n", - "```python\n", - "l = 2*a\n", - "a = 3*w\n", - "그러므로\n", - "l = 2*(3*w) = 6w\n", - "```\n", - "\n", - "이러한 l을 w로 미분하려면 연쇄법칙(Chain Rule)을 이용하여 l을 a와 w로 차례대로 미분해 줘야합니다.\n", - "\n", - "```python\n", - "l.backward()\n", - "print('l을 w로 미분한 값은 ', w.grad)\n", - "```\n", - "\n", - "위의 코드를 실행하면 다음과 같은 결과를 확인 하실 수 있습니다.\n", - "\n", - "```python\n", - "l을 w로 미분한 값은 tensor(6)\n", - "```\n", - "\n", - "backward() 함수는 l을 a로 미분한 후, 그 값을 a를 w로 미분한 값에 곱해줘 w.grad 를 계산했습니다.\n", - "여러 겹의 행렬곱을 하는 인공신경망이 경사 하강법을 할때는 위처럼 여러 겹의 미분식을 해야합니다.\n", - "이렇게 연쇄법칙을 사용하여 경사 하강법을 하는 딥러닝 특유의 알고리즘이 바로 그 유명한\n", - "***역전파 알고리즘(Backpropagation Algorithm)*** 입니다.\n", - "\n", - "역전파 알고리즘은 딥러닝에 있어 가장 자주 쓰이는 알고리즘 이지만 직접 구현하는데에는 복잡한 코드와 수학적 지식이 필요합니다.\n", - "다행히 파이토치는 역전파 알고리즘 기법을 제공해주기 때문에 우리가 직접 역전파 알고리즘을 구현할 일은 없습니다만, 아주 중요한 알고리즘이므로\n", - "딥러닝을 좀 더 깊게 공부하고자 하신다면 꼭 자세히 공부하는걸 권하고 싶습니다.\n", - "\n", - "#### 전체 코드\n", - "\n", - "```python\n", - "import torch\n", - "\n", - "w = torch.tensor(1, requires_grad=True)\n", - "a = w*3\n", - "l = a*2\n", - "\n", - "l.backward()\n", - "print('l을 w로 미분한 값은 ', w.grad)\n", - "```\n", "\n", "## 프로젝트 2. 신경망 모델 구현하기\n", "\n", - "이번 장에서는 지금까지 배워 온 개념들을 토대로 간단한 신경망을 함께 구현해 보겠습니다. 지금까지 내용과는 달리, 이번 장에는 딥러닝에 핵심적인 내용을 조금 더 깊게, 그리고 이론적으로 설명하여 처음 딥러닝을 접하는 분들에게는 다소 어려울 수도 있습니다. 하지만 설명을 읽어가며 함께 코딩을 해 보면 어느새 딥러닝을 코딩하는데 익숙해 질 것입니다.\n", - "\n", - "### 딥러닝과 인공신경망\n", - "\n", - "이름에서부터 알 수 있듯이 인공신경망은 인간의 뇌, 혹은 신경계의 작동 방식에서 그 영감을 받았습니다. 신경계가 작동을 하기 위해선 가장 먼저 눈이나 혀 같은 감각 기관을 통해 자극을 입력 받아야 합니다. 이런 자극이 첫번째 신경세포로 전달되고, 이 신경세포는 자극을 처리해 다른 신경세포로 전달합니다. 이러한 자극 처리와 전달 과정을 여러번 반복하다 보면 인간의 신경계는 수많은 자극을 인지하고 그에 따라 다른 반응을 하게됩니다. 그러다 언젠가는 맛을 판별하거나 손가락을 움직이는 등 다양하고 복잡한 작업을 할수 있게 됩니다.\n", - "\n", - "자극을 텐서의 형태로 입력받는 인공신경망에선 이러한 자극의 입력과 전달과정이 행렬곱 과 활성화 함수 라는 수학적 연산으로 표현됩니다.\n", - "실제 인간의 신경세포가 자극을 전달하기 전에 입력받은 자극에 여러 화학적 가공처리를 가하듯 인공신경망도 입력된 텐서에 특정한 수학적 연산을 실행합니다. 바로 ***가중치(Weight)*** 라고 하는 랜덤한 텐서를 행렬곱 시켜주는 것이죠.\n", - "그리고 이 행렬곱의 결과는 ***활성화 함수(Activation Function)*** 를 거쳐 결과값을 산출하게 됩니다. 이 결과값이 곧 인접한 다른 신경세포로 전달되는 자극이라고 보시면 됩니다.\n", - "자극의 처리와 전달, 이러한 과정을 몇겹에 싸여 반복한 후 마지막 결과값 만들어 내는 것이 인공신경망의 기본적인 작동원리입니다.\n", - "\n", - "### 간단한 분류 모델 구현하기\n", - "\n", - "이번 장에서는 인공신경망을 이용해 간단한 분류 모델을 함께 구현해 보겠습니다. 하지만 처음 인공신경망과 머신러닝을 접하는 분들을 위해 이미지 같은 고차원의 복잡한 데이터가 아닌 간단한 2차원의 데이터를 이용하겠습니다. 첫번째 인공신경망을 구현하는 만큼, 이번 프로젝트의 코드는 조금 새롭고 생소한 개념을 다소 포함하고 있습니다. 그러므로 꼭 설명을 자세하게 읽어 보고 코딩해 보시기 바랍니다.\n", - "\n", - "우선 파이토치와 그 외 다른 라이브러리들을 임포트합니다. Numpy는 유명한 수치 해석용 라이브러리 입니다. 행렬과 벡터를 이용한 연산을 하는데 아주 유용한 라이브러리며, 파이토치도 이 넘파이를 기반으로 개발되었을 정도로 긴밀하게 이용됩니다. 이번 프로젝트에서는 인공신경망 학습을 위한 데이터를 만드는데 넘파이와 sklearn 라이브러리를 이용하여 생성하겠습니다. 마지막으로 임포트 되어지는 matplotlib 라이브러리는 데이터를 시각화 하는데 있어 유용한 툴 입니다. 학습데이터가 어떠한 패턴을 보이며 분포되어 있는지 확인하기 위해 matplotlib 을 이용하겠습니다.\n", - "\n", - "```python\n", + "### 간단한 분류 모델 구현하기" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ "import torch\n", "import numpy\n", "from sklearn.datasets import make_blobs\n", "import matplotlib.pyplot as plot\n", - "import torch.nn.functional as F\n", - "```\n", - "\n", + "import torch.nn.functional as F" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "인공신경망을 구현하기 전 인공신경망의 학습과 평가를 위한 데이터셋을 만들어 줍니다.\n", "밑의 코드에서 x_tra 와 y_tra 라고 정의된 실험데이터는 직접 인공신경망을 학습시키는데 쓰이는 데이터 입니다. 반대로 x_tes 와 y_tes 라고 정의된 데이터는 직접 신경망을 학습시키는데는 쓰이지 않지만 학습이 끝난 신경망의 성능을 평가하고 실험하는데 쓰일 데이터 셋입니다.\n", "\n", - "```python\n", - "n_dim = 2\n", - "x_tra, y_tra = make_blobs(n_samples=80, n_features=n_dim, centers=[[1,1],[-1,-1],[1,-1],[-1,1]], shuffle=True, cluster_std=0.3)\n", - "x_tes, y_tes = make_blobs(n_samples=20, n_features=n_dim, centers=[[1,1],[-1,-1],[1,-1],[-1,1]], shuffle=True, cluster_std=0.3)\n", - "```\n", - "\n", "make_blobs() 함수를 이용하여 데이터를 2차원 벡터의 형태로 만들어 주었습니다.\n", "학습데이터(Training Data Set)에는 80개, 실험데이터(Test Data Set)에는 20개의 2차원 벡터 형태의 데이터가 있는 것을 확인하실 수 있습니다.\n", - "데이터를 만든 후, 데이터에 해당하는 정답인 ‘레이블’ 을 달아줍니다. label_map 이라는 간단한 함수를 구현해 데이터가 [-1, -1] 혹은 [1, 1] 주위에 있으면 0 이라는 레이블을 달아 줬습니다. 반대로 [1, -1] 혹은 [-1, 1] 주위에 위치해 있으면 1 이라는 레이블을 달아 줬습니다.\n", - "\n", - "```python\n", + "데이터를 만든 후, 데이터에 해당하는 정답인 ‘레이블’ 을 달아줍니다. label_map 이라는 간단한 함수를 구현해 데이터가 [-1, -1] 혹은 [1, 1] 주위에 있으면 0 이라는 레이블을 달아 줬습니다. 반대로 [1, -1] 혹은 [-1, 1] 주위에 위치해 있으면 1 이라는 레이블을 달아 줬습니다." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ "def label_map(y_, from_, to_):\n", " y = numpy.copy(y_)\n", " for f in from_:\n", " y[y_ == f] = to_\n", " return y\n", - "\n", + " \n", + "n_dim = 2\n", + "x_tra, y_tra = make_blobs(n_samples=80, n_features=n_dim, centers=[[1,1],[-1,-1],[1,-1],[-1,1]], shuffle=True, cluster_std=0.3)\n", + "x_tes, y_tes = make_blobs(n_samples=20, n_features=n_dim, centers=[[1,1],[-1,-1],[1,-1],[-1,1]], shuffle=True, cluster_std=0.3)\n", "y_tra = label_map(y_tra, [0, 1], 0)\n", "y_tra = label_map(y_tra, [2, 3], 1)\n", "y_tes = label_map(y_tes, [0, 1], 0)\n", - "y_tes = label_map(y_tes, [2, 3], 1)\n", - "```\n", - "\n", + "y_tes = label_map(y_tes, [2, 3], 1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "데이터가 제대로 만들어 졌는지, 그리고 제대로 레이블링이 되었는지 확인하기 위해 matplotlib 을 이용해 데이터를 시각화 해 보겠습니다.\n", "\n", - "```python\n", + "레이블이 0 인 학습 데이터는 점으로, 1인 데이터는 십자가로 표시했습니다.\n", + "\n", + "" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ "def vis_data(x,y = None, c = 'r'):\n", - "\tif y is None:\n", - "\t\ty = [None] * len(x)\n", - "\tfor x_, y_ in zip(x,y):\n", - "\t\tif y_ is None:\n", - "\t\t\tplot.plot(x_[0], x_[1], '*',markerfacecolor='none', markeredgecolor=c)\n", - "\t\telse:\n", - "\t\t\tplot.plot(x_[0], x_[1], c+'o' if y_ == 0 else c+'+')\n", + " if y is None:\n", + " y = [None] * len(x)\n", + " for x_, y_ in zip(x,y):\n", + " if y_ is None:\n", + " plot.plot(x_[0], x_[1], '*',markerfacecolor='none', markeredgecolor=c)\n", + " else:\n", + " plot.plot(x_[0], x_[1], c+'o' if y_ == 0 else c+'+')\n", "\n", "plot.figure()\n", "vis_data(x_tra, y_tra, c='r')\n", - "plot.show()\n", - "```\n", - "\n", - "레이블이 0 인 학습 데이터는 점으로, 1인 데이터는 십자가로 표시했습니다.\n", - "\n", - "\n", - "\n", - "마지막으로 신경망을 구현 하기 전, 위에서 정의한 데이터들을 넘파이 리스트가 아닌 파이토치 텐서로 바꿔줍니다.\n", - "\n", - "```python\n", + "plot.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "마지막으로 신경망을 구현 하기 전, 위에서 정의한 데이터들을 넘파이 리스트가 아닌 파이토치 텐서로 바꿔줍니다." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ "x_tra = torch.FloatTensor(x_tra)\n", "x_tes = torch.FloatTensor(x_tes)\n", "y_tra = torch.FloatTensor(y_tra)\n", - "y_tes = torch.FloatTensor(y_tes)\n", - "```\n", - "\n", - "이제 데이터를 준비했으니 본격적으로 신경망 모델을 구현해 보겠습니다. \n", - "파이토치에서 인공신경망은 아래와 같이 신경망 모듈(Neural Network Module)을 상속받는 파이썬 객체 로 나타낼 수 있습니다.\n", - "\n", - "```python\n", + "y_tes = torch.FloatTensor(y_tes)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ "class Feed_forward_nn(torch.nn.Module):\n", - "```\n", - "\n", - "인공신경망의 구조와 동작을 정의하는 컨스트럭터/이니셜라이져(Constructor/Initializer) 를 모델 클래스 안에 정의해 보겠습니다.\n", - "\n", - "```python\n", - "\t\tdef __init__(self, input_size, hidden_size):\n", - "```\n", - "\n", - "__init()__ 함수는 파이썬 객체지향 프로그래밍에서 객체가 생성될 때 객체에 내포된 값을 설정 해 주는 함수이며, 객체가 생성 될 때 자동적으로 호출됩니다. 이번 예제에서는 학습/실험 데이터의 차원인 input_size 라는 변수와 hidden_size 라는 변수를 __init()__ 함수를 통해 설정하도록 구현했습니다.\n", - "input_size 는 신경망에 입력되는 데이터들의 차원입니다. \n", - "2차원 데이터를 입력받는 모델을 구현할 것이므로 input_size는 2라고 정의됩니다.\n", - "[1,2] 사이즈의 입력데이터가 [2,5] 모양을 가진 가중치 텐서와 행렬곱 해 [1,5] 모양의 텐서가 만들어지듯이, 신경망에 입력된 데이터는 신경망 속의 가중치와 활성화 함수를 거치며 차원을 변화시킵니다. 이렇게 중간에 변화된 차원값을 hidden_size 라고 부르겠습니다.\n", - "\n", - "```python\n", - "\t\t\tsuper(Feed_forward_nn, self).__init__()\n", - "\t\t\tself.input_size = input_size\n", - "\t\t\tself.hidden_size = hidden_size\n", - "```\n", - "\n", - "다음은 입력된 데이터가 인공신경망을 통과하면서 거치는 연산들을 정의해 주겠습니다.\n", - "\n", - "```python\n", - "\t\t\tself.linear_1 = torch.nn.Linear(self.input_size, self.hidden_size)\n", - "\t\t\tself.relu = torch.nn.ReLU()\n", - "\t\t\tself.linear_2 = torch.nn.Linear(self.hidden_size, 1)\n", - "\t\t\tself.sigmoid = torch.nn.Sigmoid()\n", - "```\n", - "\n", - "linear_1 함수는 앞서 여러번 반복해 설명드렸던 행렬곱을 하는 함수입니다. [input_size, hidden_size] 사이즈의 가중치를 입력 데이터에 행렬곱 시켜 [1,hidden_size] 꼴의 텐서를 리턴합니다. 이 때 리턴된 값은 torch.nn.ReLU() 라는 활성화 함수를 거치게 됩니다. ReLU 는 입력값이 0보다 작으면 0을, 0보다 크면 입력값을 그대로 출력합니다. 예를 들어 텐서 [-1, 1, 3, -5]가 ReLU 를 거치면 텐서 [0, 1, 3, 0]가 리턴됩니다.\n", - "\n", - "\n", - "\n", - "ReLU 를 통과한 텐서는 다시 한번 linear_2 로 정의된 행렬곱을 거쳐 [1,1] 꼴을 지니게 됩니다. 마지막으로 이 텐서는 sigmoid 활성화 함수에 입력됩니다. Sigmoid 는 입력된 학습데이터가 레이블 1에 해당할 확률값을 리턴하는 함수로써, 머신러닝과 딥러닝에서 가장 중요한 활성화 함수 입니다.\n", - "\n", - "\n", - "\n", - "위의 그림처럼 sigmoid 함수는 0과 1 사이의 값을 리턴합니다. \n", - "다음으로 __init__() 함수에서 정의된 동작들을 차례대로 실행하는 forward() 함수를 구현합니다.\n", - "\n", - "```python\n", - "\t\tdef forward(self, input_tensor):\n", - "\t\t\tlinear1 = self.linear_1(input_tensor)\n", - "\t\t\trelu = self.relu(linear1)\n", - "\t\t\tlinear2 = self.linear_2(relu)\n", - "\t\t\toutput = self.sigmoid(linear2)\n", - "\t\t\treturn output\n", - "```\n", - "\n", - "이로써 인공신경망을 구현이 끝났습니다. 이제 실제로 신경망 객체를 생성하고 학습에 필요한 여러 변수와 알고리즘을 정의하겠습니다.\n", - "\n", - "```python\n", - "model = Feed_forward_nn(2, 5)\n", - "learning_rate = 0.03\n", - "criterion = torch.nn.BCELoss()\n", - "```\n", - "\n", + " def __init__(self, input_size, hidden_size):\n", + " super(Feed_forward_nn, self).__init__()\n", + " self.input_size = input_size\n", + " self.hidden_size = hidden_size\n", + " self.linear_1 = torch.nn.Linear(self.input_size, self.hidden_size)\n", + " self.relu = torch.nn.ReLU()\n", + " self.linear_2 = torch.nn.Linear(self.hidden_size, 1)\n", + " self.sigmoid = torch.nn.Sigmoid()\n", + " def forward(self, input_tensor):\n", + " linear1 = self.linear_1(input_tensor)\n", + " relu = self.relu(linear1)\n", + " linear2 = self.linear_2(relu)\n", + " output = self.sigmoid(linear2)\n", + " return output" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "input_size 를 2로, hidden_size 를 5 로 설정한 신경망 객체를 만들었습니다. learning_rate 은 ‘얼마나 급하게 학습하는가’ 를 설정하는 값입니다. 값이 너무 크면 오차함수의 최소점을 찾지 못하고 지나치게 되고, 값이 너무 작으면 학습속도가 느려집니다.\n", "러닝레이트를 설정했으면 그 다음으로는 오차함수를 만들어야 합니다. 물론 직접 오차함수를 코딩 할 수도 있지만 이는 매우 까다롭고 귀찮은 일입니다. 다행히도 파이토치는 여러 오차함수를 미리 구현해서 바로 사용 할 수 있도록 해놓았습니다. 이번에 우리는 파이토치가 제공해 주는 이진교차 엔트로피(Binary Cross Entropy) 라는 오차함수를 사용하겠습니다.\n", "\n", "epochs는 학습데이터를 총 몇번 반복\n", "동안 오차를 구하고 그 최소점으로 이동 할지 결정해줍니다. \n", - "마지막 변수 optimizer 는 최적화 알고리즘입니다. 최적화 알고리즘 에는 여러 종류가 있고 상황에 따라 다른 알고리즘을 사용합니다. 이번 예제를 통해 처음으로 인공신경망을 구현하는 분들을 위해 그중에서도 가장 기본적인 알고리즘인 스토카스틱 경사 하강법(Stochastic Gradient Descent)을 사용하겠습니다.\n", - "\n", - "```python\n", + "마지막 변수 optimizer 는 최적화 알고리즘입니다. 최적화 알고리즘 에는 여러 종류가 있고 상황에 따라 다른 알고리즘을 사용합니다. 이번 예제를 통해 처음으로 인공신경망을 구현하는 분들을 위해 그중에서도 가장 기본적인 알고리즘인 스토카스틱 경사 하강법(Stochastic Gradient Descent)을 사용하겠습니다." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "model = Feed_forward_nn(2, 5)\n", + "learning_rate = 0.03\n", + "criterion = torch.nn.BCELoss()\n", "epochs = 1000\n", - "optimizer = torch.optim.SGD(model.parameters(), lr = learning_rate)\n", - "```\n", - "\n", - "학습을 시작하기 전 정말 마지막으로 아무 학습도 하지 않은 모델의 성능을 시험해 보겠습니다.\n", - "\n", - "```python\n", + "optimizer = torch.optim.SGD(model.parameters(), lr = learning_rate)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Before Training, test loss is 0.7298532724380493\n" + ] + } + ], + "source": [ "model.eval()\n", "test_loss_before = criterion(model(x_tes).squeeze(), y_tes)\n", - "print('Before Training, test loss is ', test_loss_before.item())\n", - "```\n", - "\n", - "위 코드는 아래와 같은 결과를 출력합니다.\n", - "\n", - "```\n", - "Before Training, test loss is 0.7301096916198730\n", - "```\n", - "\n", + "print('Before Training, test loss is ', test_loss_before.item())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "오차값이 0.73 이 나왔습니다. 이정도의 오차를 가진 모델은 사실상 분류하는 능력이 없다고 봐도 무방합니다.\n", - "자, 이제 드디어 인공신경망을 학습시켜 퍼포먼스를 향상시켜 보겠습니다.\n", - "\n", - "우선 epoch을 반복해주는 for loop 을 만들어 줍니다.\n", - "\n", - "```python\n", + "자, 이제 드디어 인공신경망을 학습시켜 퍼포먼스를 향상시켜 보겠습니다." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train loss at 0 is 0.7381800413131714\n", + "Train loss at 100 is 0.6178547739982605\n", + "Train loss at 200 is 0.5093345046043396\n", + "Train loss at 300 is 0.3766266703605652\n", + "Train loss at 400 is 0.24899113178253174\n", + "Train loss at 500 is 0.16244475543498993\n", + "Train loss at 600 is 0.11236796528100967\n", + "Train loss at 700 is 0.08263880759477615\n", + "Train loss at 800 is 0.06397637724876404\n", + "Train loss at 900 is 0.0515405647456646\n" + ] + } + ], + "source": [ "for epoch in range(epochs):\n", - "```\n", - "\n", - "모델에 train()함수를 호출시켜 학습 모드로 바꿔 줍니다.\n", - "'경사'라고도 할 수 있는 그레디언트(Gradient)는 오차 함수가 최소점을 가진 곳의 방향 입니다.\n", - "매 epoch 마다 우리는 새로운 그레디언트 값을 계산할 것이기 때문에 zero_grad()함수를 통해 그레디언트 값을 0으로 정의해 주겠습니다.\n", - "\n", - "```python\n", " model.train()\n", " optimizer.zero_grad()\n", - "```\n", - "\n", - "이미 생성한 모델에 학습데이터를 입력시켜 결과값을 계산합니다.\n", - "여기서 잠깐, 신경망 객체 속에 정의된 forward() 함수가 곧 신경망의 결과값을 내는 함수인 것은 맞지만, torch.nn.module이 forward() 함수 호출을 대신해줘 우리가 직접 호출할 필요는 없습니다.\n", - "\n", - "```python\n", - " train_output = model(x_tra) #torch.nn.module 을 통해서 forward()호출\n", - "```\n", - "\n", - "신경망의 결과값의 차원을 레이블의 차원과 같도록 만들어 주고 오차를 계산합니다.\n", - "\n", - "```python\n", + " train_output = model(x_tra)\n", " train_loss = criterion(train_output.squeeze(), y_tra)\n", - "```\n", - "\n", - "학습이 잘 되는지 확인하기 위해 100 epoch마다 오차를 출력하도록 설정하겠습니다.\n", - "\n", - "```python\n", - "\tif epoch % 100 == 0:\n", - "\t\tprint('Train loss at ', epoch, 'is ', train_loss.item())\n", - "```\n", - "\n", - "그 다음단계는 오차함수를 가중치 값들로 미분하여 오차함수의 최소점의 방향, 즉 그레디언트(Gradient)를 구하고 그 방향으로 모델을 러닝레이트 만큼 이동시키는 것입니다.\n", - "\n", - "```python\n", + " if epoch % 100 == 0:\n", + " print('Train loss at ', epoch, 'is ', train_loss.item())\n", " train_loss.backward()\n", - " optimizer.step()\n", - "```\n", - "\n", - "위 코드를 실행시켜 보면 오차값이 점점 줄어드는 것을 보실 수 있습니다.\n", - "\n", - "```python\n", - "Train loss at 0 is 0.7301096916198730\n", - "Train loss at 100 is 0.6517783403396606\n", - "Train loss at 200 is 0.5854113101959229\n", - "Train loss at 300 is 0.519926130771637\n", - "Train loss at 400 is 0.4684883952140808\n", - "Train loss at 500 is 0.42419689893722534\n", - "Train loss at 600 is 0.3720306158065796\n", - "Train loss at 700 is 0.3115468919277191\n", - "Train loss at 800 is 0.25684845447540283\n", - "Train loss at 900 is 0.2133386880159378\n", - "```\n", - "\n", - "바야흐로 우리의 첫 인공신경망 학습이 끝났습니다. 이제 학습된 신경망의 퍼포먼스를 시험할 차례입니다.\n", - "모델을 평가 모드(evaluation mode)로 바꿔 주고 실험데이터인 x_tes, y_tes를 이용해 오차값을 구해보겠습니다.\n", - "\n", - "```python\n", + " optimizer.step()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "After Training, test loss is 0.047815386205911636\n" + ] + } + ], + "source": [ "model.eval()\n", - "test_loss_before = criterion(torch.squeeze(model(x_tes) ), y_tes)\n", - "print('Before Training, test loss is ', test_loss_before.item())\n", - "```\n", - "\n", + "test_loss = criterion(model(x_tes).squeeze(), y_tes) \n", + "print('After Training, test loss is ', test_loss.item())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "학습을 하기 전과 비교했을때 현저하게 줄어든 오차값을 확인 하실 수 있습니다.\n", - "\n", - "```python\n", - "After Training, test loss is 0.20166122913360596\n", - "```\n", - "\n", "지금까지 인공신경망을 구현하고 학습시켜 보았습니다.\n", - "이제 학습된 모델을 .pt 파일로 저장해 보겠습니다.\n", - "\n", - "```python\n", - "torch.save(model.state_dict(), './model.pt')\n", - "```\n", - "\n", - "위 코드를 실행하고 나면 학습된 신경망의 가중치를 내포하는 model.pt 라는 파일이 생성됩니다. 아래 코드처럼 새로운 신경망 객체에 model.pt 속의 가중치값을 입력시키는 것 또한 가능합니다.\n", - "\n", - "```python\n", - "new_model = Feed_forward_nn(2, 5)\n", - "new_model.load_state_dict(torch.load('./model.pt'))\n", - "new_model.eval()\n", - "print(new_model(torch.FloatTensor([-1,1])).item() )\n", - "```\n", - "\n", - "여담으로 벡터 [-1,1]을 학습하고 저장된 모델에 입력시켰을 때 레이블이 1일 확률은 90% 이상이 나왔습니다.\n", - "우리의 첫번째 신경망 모델은 이제 꽤 믿을만한 분류 작업이 가능하게 된 것입니다.\n", - "\n", - "```python\n", - "벡터 [-1,1]이 레이블 1 을 가질 확률은 0.9407910108566284\n", - "```" + "이제 학습된 모델을 .pt 파일로 저장해 보겠습니다." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "torch.save(model.state_dict(), './model.pt')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### 전체 코드\n", - "\n", - "```python\n", - "import torch\n", - "import numpy\n", - "from sklearn.datasets import make_blobs\n", - "import matplotlib.pyplot as plot\n", - "import torch.nn.functional as F\n", - "\n", - "def label_map(y_, from_, to_):\n", - " y = numpy.copy(y_)\n", - " for f in from_:\n", - " y[y_ == f] = to_\n", - " return y\n", - " \n", - "n_dim = 2\n", - "x_tra, y_tra = make_blobs(n_samples=80, n_features=n_dim, centers=[[1,1],[-1,-1],[1,-1],[-1,1]], shuffle=True, cluster_std=0.3)\n", - "x_tes, y_tes = make_blobs(n_samples=20, n_features=n_dim, centers=[[1,1],[-1,-1],[1,-1],[-1,1]], shuffle=True, cluster_std=0.3)\n", - "y_tra = label_map(y_tra, [0, 1], 0)\n", - "y_tra = label_map(y_tra, [2, 3], 1)\n", - "y_tes = label_map(y_tes, [0, 1], 0)\n", - "y_tes = label_map(y_tes, [2, 3], 1)\n", - "\n", - "def vis_data(x,y = None, c = 'r'):\n", - "\tif y is None:\n", - "\t\ty = [None] * len(x)\n", - "\tfor x_, y_ in zip(x,y):\n", - "\t\tif y_ is None:\n", - "\t\t\tplot.plot(x_[0], x_[1], '*',markerfacecolor='none', markeredgecolor=c)\n", - "\t\telse:\n", - "\t\t\tplot.plot(x_[0], x_[1], c+'o' if y_ == 0 else c+'+')\n", - "\n", - "plot.figure()\n", - "vis_data(x_tra, y_tra, c='r')\n", - "plot.show()\n", - "\n", - "x_tra = torch.FloatTensor(x_tra)\n", - "x_tes = torch.FloatTensor(x_tes)\n", - "y_tra = torch.FloatTensor(y_tra)\n", - "y_tes = torch.FloatTensor(y_tes)\n", - "\n", - "class Feed_forward_nn(torch.nn.Module):\n", - "\t\tdef __init__(self, input_size, hidden_size):\n", - "\t\t\tsuper(Feed_forward_nn, self).__init__()\n", - "\t\t\tself.input_size = input_size\n", - "\t\t\tself.hidden_size = hidden_size\n", - "\t\t\tself.linear_1 = torch.nn.Linear(self.input_size, self.hidden_size)\n", - "\t\t\tself.relu = torch.nn.ReLU()\n", - "\t\t\tself.linear_2 = torch.nn.Linear(self.hidden_size, 1)\n", - "\t\t\tself.sigmoid = torch.nn.Sigmoid()\n", - "\t\tdef forward(self, input_tensor):\n", - "\t\t\tlinear1 = self.linear_1(input_tensor)\n", - "\t\t\trelu = self.relu(linear1)\n", - "\t\t\tlinear2 = self.linear_2(relu)\n", - "\t\t\toutput = self.sigmoid(linear2)\n", - "\t\t\treturn output\n", - "\n", - "model = Feed_forward_nn(2, 5)\n", - "learning_rate = 0.03\n", - "criterion = torch.nn.BCELoss()\n", - "epochs = 1000\n", - "optimizer = torch.optim.SGD(model.parameters(), lr = learning_rate)\n", - "\n", - "model.eval()\n", - "test_loss_before = criterion(model(x_tes).squeeze(), y_tes)\n", - "print('Before Training, test loss is ', test_loss_before.item())\n", - "\n", - "for epoch in range(epochs):\n", - "\tmodel.train()\n", - "\toptimizer.zero_grad()\n", - "\ttrain_output = model(x_tra)\n", - "\ttrain_loss = criterion(train_output.squeeze(), y_tra)\n", - "\tif epoch % 100 == 0:\n", - "\t\tprint('Train loss at ', epoch, 'is ', train_loss.item())\n", - "\ttrain_loss.backward()\n", - "\toptimizer.step()\n", - "\n", - "model.eval()\n", - "test_loss = criterion(model(x_tes).squeeze(), y_tes) \n", - "print('After Training, test loss is ', test_loss.item())\n", - "\n", - "torch.save(model.state_dict(), './model.pt')\n", + "`save()` 를 실행하고 나면 학습된 신경망의 가중치를 내포하는 model.pt 라는 파일이 생성됩니다. 아래 코드처럼 새로운 신경망 객체에 model.pt 속의 가중치값을 입력시키는 것 또한 가능합니다." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.9745796918869019\n" + ] + } + ], + "source": [ "new_model = Feed_forward_nn(2, 5)\n", "new_model.load_state_dict(torch.load('./model.pt'))\n", "new_model.eval()\n", - "print(new_model(torch.FloatTensor([-1,1])).item() )\n", - "```\n" + "print(new_model(torch.FloatTensor([-1,1])).item() )" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], - "source": [] + "source": [ + "벡터 [-1,1]을 학습하고 저장된 모델에 입력시켰을 때 레이블이 1일 확률은 90% 이상이 나옵니다.\n", + "우리의 첫번째 신경망 모델은 이제 꽤 믿을만한 분류 작업이 가능하게 된 것입니다.\n", + "\n", + "```python\n", + "벡터 [-1,1]이 레이블 1 을 가질 확률은 0.9745796918869019\n", + "```" + ] } ], "metadata": { @@ -691,7 +324,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.0" + "version": "3.7.0" } }, "nbformat": 4, diff --git a/03-Coding-Neural-Networks-In-PyTorch/01-basic-feed-forward_nn.py b/03-Coding-Neural-Networks-In-PyTorch/01-basic-feed-forward_nn.py new file mode 100644 index 0000000..8fdfdfe --- /dev/null +++ b/03-Coding-Neural-Networks-In-PyTorch/01-basic-feed-forward_nn.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python +# coding: utf-8 + +# # 파이토치로 구현하는 신경망 +# 파이토치를 이용하여 가장 기본적인 신경망을 만들어봅니다. +# ## 프로젝트 2. 신경망 모델 구현하기 +# ### 간단한 분류 모델 구현하기 + +import torch +import numpy +from sklearn.datasets import make_blobs +import matplotlib.pyplot as plot +import torch.nn.functional as F + + +# 인공신경망을 구현하기 전 인공신경망의 학습과 평가를 위한 데이터셋을 만들어 줍니다. +# 밑의 코드에서 x_tra 와 y_tra 라고 정의된 실험데이터는 직접 인공신경망을 학습시키는데 쓰이는 데이터 입니다. 반대로 x_tes 와 y_tes 라고 정의된 데이터는 직접 신경망을 학습시키는데는 쓰이지 않지만 학습이 끝난 신경망의 성능을 평가하고 실험하는데 쓰일 데이터 셋입니다. +# make_blobs() 함수를 이용하여 데이터를 2차원 벡터의 형태로 만들어 주었습니다. +# 학습데이터(Training Data Set)에는 80개, 실험데이터(Test Data Set)에는 20개의 2차원 벡터 형태의 데이터가 있는 것을 확인하실 수 있습니다. +# 데이터를 만든 후, 데이터에 해당하는 정답인 ‘레이블’ 을 달아줍니다. label_map 이라는 간단한 함수를 구현해 데이터가 [-1, -1] 혹은 [1, 1] 주위에 있으면 0 이라는 레이블을 달아 줬습니다. 반대로 [1, -1] 혹은 [-1, 1] 주위에 위치해 있으면 1 이라는 레이블을 달아 줬습니다. + +def label_map(y_, from_, to_): + y = numpy.copy(y_) + for f in from_: + y[y_ == f] = to_ + return y + +n_dim = 2 +x_tra, y_tra = make_blobs(n_samples=80, n_features=n_dim, centers=[[1,1],[-1,-1],[1,-1],[-1,1]], shuffle=True, cluster_std=0.3) +x_tes, y_tes = make_blobs(n_samples=20, n_features=n_dim, centers=[[1,1],[-1,-1],[1,-1],[-1,1]], shuffle=True, cluster_std=0.3) +y_tra = label_map(y_tra, [0, 1], 0) +y_tra = label_map(y_tra, [2, 3], 1) +y_tes = label_map(y_tes, [0, 1], 0) +y_tes = label_map(y_tes, [2, 3], 1) + + +# 데이터가 제대로 만들어 졌는지, 그리고 제대로 레이블링이 되었는지 확인하기 위해 matplotlib 을 이용해 데이터를 시각화 해 보겠습니다. +# 레이블이 0 인 학습 데이터는 점으로, 1인 데이터는 십자가로 표시했습니다. +# + +def vis_data(x,y = None, c = 'r'): + if y is None: + y = [None] * len(x) + for x_, y_ in zip(x,y): + if y_ is None: + plot.plot(x_[0], x_[1], '*',markerfacecolor='none', markeredgecolor=c) + else: + plot.plot(x_[0], x_[1], c+'o' if y_ == 0 else c+'+') + +plot.figure() +vis_data(x_tra, y_tra, c='r') +plot.show() + + +# 마지막으로 신경망을 구현 하기 전, 위에서 정의한 데이터들을 넘파이 리스트가 아닌 파이토치 텐서로 바꿔줍니다. + +x_tra = torch.FloatTensor(x_tra) +x_tes = torch.FloatTensor(x_tes) +y_tra = torch.FloatTensor(y_tra) +y_tes = torch.FloatTensor(y_tes) + + +class Feed_forward_nn(torch.nn.Module): + def __init__(self, input_size, hidden_size): + super(Feed_forward_nn, self).__init__() + self.input_size = input_size + self.hidden_size = hidden_size + self.linear_1 = torch.nn.Linear(self.input_size, self.hidden_size) + self.relu = torch.nn.ReLU() + self.linear_2 = torch.nn.Linear(self.hidden_size, 1) + self.sigmoid = torch.nn.Sigmoid() + def forward(self, input_tensor): + linear1 = self.linear_1(input_tensor) + relu = self.relu(linear1) + linear2 = self.linear_2(relu) + output = self.sigmoid(linear2) + return output + + +# input_size 를 2로, hidden_size 를 5 로 설정한 신경망 객체를 만들었습니다. learning_rate 은 ‘얼마나 급하게 학습하는가’ 를 설정하는 값입니다. 값이 너무 크면 오차함수의 최소점을 찾지 못하고 지나치게 되고, 값이 너무 작으면 학습속도가 느려집니다. +# 러닝레이트를 설정했으면 그 다음으로는 오차함수를 만들어야 합니다. 물론 직접 오차함수를 코딩 할 수도 있지만 이는 매우 까다롭고 귀찮은 일입니다. 다행히도 파이토치는 여러 오차함수를 미리 구현해서 바로 사용 할 수 있도록 해놓았습니다. 이번에 우리는 파이토치가 제공해 주는 이진교차 엔트로피(Binary Cross Entropy) 라는 오차함수를 사용하겠습니다. +# epochs는 학습데이터를 총 몇번 반복 +# 동안 오차를 구하고 그 최소점으로 이동 할지 결정해줍니다. +# 마지막 변수 optimizer 는 최적화 알고리즘입니다. 최적화 알고리즘 에는 여러 종류가 있고 상황에 따라 다른 알고리즘을 사용합니다. 이번 예제를 통해 처음으로 인공신경망을 구현하는 분들을 위해 그중에서도 가장 기본적인 알고리즘인 스토카스틱 경사 하강법(Stochastic Gradient Descent)을 사용하겠습니다. + +model = Feed_forward_nn(2, 5) +learning_rate = 0.03 +criterion = torch.nn.BCELoss() +epochs = 1000 +optimizer = torch.optim.SGD(model.parameters(), lr = learning_rate) + + +model.eval() +test_loss_before = criterion(model(x_tes).squeeze(), y_tes) +print('Before Training, test loss is ', test_loss_before.item()) + + +# 오차값이 0.73 이 나왔습니다. 이정도의 오차를 가진 모델은 사실상 분류하는 능력이 없다고 봐도 무방합니다. +# 자, 이제 드디어 인공신경망을 학습시켜 퍼포먼스를 향상시켜 보겠습니다. + +for epoch in range(epochs): + model.train() + optimizer.zero_grad() + train_output = model(x_tra) + train_loss = criterion(train_output.squeeze(), y_tra) + if epoch % 100 == 0: + print('Train loss at ', epoch, 'is ', train_loss.item()) + train_loss.backward() + optimizer.step() + + +model.eval() +test_loss = criterion(model(x_tes).squeeze(), y_tes) +print('After Training, test loss is ', test_loss.item()) + + +# 학습을 하기 전과 비교했을때 현저하게 줄어든 오차값을 확인 하실 수 있습니다. +# 지금까지 인공신경망을 구현하고 학습시켜 보았습니다. +# 이제 학습된 모델을 .pt 파일로 저장해 보겠습니다. + +torch.save(model.state_dict(), './model.pt') + + +# `save()` 를 실행하고 나면 학습된 신경망의 가중치를 내포하는 model.pt 라는 파일이 생성됩니다. 아래 코드처럼 새로운 신경망 객체에 model.pt 속의 가중치값을 입력시키는 것 또한 가능합니다. + +new_model = Feed_forward_nn(2, 5) +new_model.load_state_dict(torch.load('./model.pt')) +new_model.eval() +print(new_model(torch.FloatTensor([-1,1])).item() ) + + +# 벡터 [-1,1]을 학습하고 저장된 모델에 입력시켰을 때 레이블이 1일 확률은 90% 이상이 나옵니다. +# 우리의 첫번째 신경망 모델은 이제 꽤 믿을만한 분류 작업이 가능하게 된 것입니다. +# ```python +# 벡터 [-1,1]이 레이블 1 을 가질 확률은 0.9745796918869019 +# ``` diff --git a/03-Coding-Neural-Networks-In-PyTorch/01-basic_feed_forward_nn.py b/03-Coding-Neural-Networks-In-PyTorch/01-basic_feed_forward_nn.py deleted file mode 100644 index a55162d..0000000 --- a/03-Coding-Neural-Networks-In-PyTorch/01-basic_feed_forward_nn.py +++ /dev/null @@ -1,122 +0,0 @@ -import torch -import numpy -from sklearn.datasets import make_blobs -import matplotlib.pyplot as plot -import torch.nn.functional as F - -##인공신경망을 이용해 간단한 분류 모델 구현하기 -#분류 설명 - -# #Define Data Set in Numpy------------------------------------------------------------------------------------------------------ -# 인공신경망을 구현하고 학습시키기 전에, 학습에 쓰일 데이터들을 Numpy 라이브러리를 사용해 만들어 보겠습니다. - -def label_map(y_, from_, to_): - y = numpy.copy(y_) - for f in from_: - y[y_ == f] = to_ - return y - -n_dim = 2 -x_tra, y_tra = make_blobs(n_samples=80, n_features=n_dim, centers=[[1,1],[-1,-1],[1,-1],[-1,1]], shuffle=True, cluster_std=0.3) -x_tes, y_tes = make_blobs(n_samples=20, n_features=n_dim, centers=[[1,1],[-1,-1],[1,-1],[-1,1]], shuffle=True, cluster_std=0.3) -y_tra = label_map(y_tra, [0, 1], 0) -y_tra = label_map(y_tra, [2, 3], 1) -y_tes = label_map(y_tes, [0, 1], 0) -y_tes = label_map(y_tes, [2, 3], 1) - -# 위 코드를 통해서 원소가 두개 있는 벡터 형태의 데이터 들을 만들었습니다. 코드에서 볼 수 있듯이, -# 트레이닝 데이터(training data set) 에는 80개의 데이터가 있고, 테스트 데이터(Test Data set) 에는 -# 20개의 데이터가 있는 것을 확인 하실 수 있습니다. 데이터가 [1,1]벡터 혹은 [-1,-1]벡터 와 가까이 있으면 0 이라고 레이블링 해줬고, -# [1,-1]벡터 혹은 [-1,1]벡터 가까이 있으면 반대로 1 이라고 레이블링 해 주었습니다. 이러한 패턴을 보이는 -# 데이터를 통틀어 'XOR 패턴의 데이터'라고 합니다. 이번 장에서 우리의 목표는 XOR 패턴의 데이터를 분류하는 간단한 인공신경망을 구현해 보는 겁니다. -#------------------------------------------------------------------------------------------------------------------------------ -#For Data visualizeation purpose--------------------------------------------------------------------------------------------- -#Matplotlib 라이브러리를 사용해 밑의 코드를 실행시켜 보면 데이터가 어느 패턴을 보이는지 한 눈에 볼 수 있습니다. -def vis_data(x,y = None, c = 'r'): - if y is None: - y = [None] * len(x) - for x_, y_ in zip(x,y): - if y_ is None: - plot.plot(x_[0], x_[1], '*',markerfacecolor='none', markeredgecolor=c) - else: - plot.plot(x_[0], x_[1], c+'o' if y_ == 0 else c+'+') - -plot.figure() -vis_data(x_tra, y_tra, c='r') -plot.show() -#------------------------------------------------------------------------------------------------------------------------------ -#Turn Data Set to Pytorch tensors -#모델을 구현하기 전, 위에서 정의한 데이터들을 넘파이 리스트가 아닌 파이토치 텐서로 재정의 합니다. -x_tra = torch.FloatTensor(x_tra) -x_tes = torch.FloatTensor(x_tes) -y_tra = torch.FloatTensor(y_tra) -y_tes = torch.FloatTensor(y_tes) -#------------------------------------------------------------------------- -#Our first Neural Network Model -# 자, 그럼 매우 간단한 인공신경망을 구현해 보겠습니다. 파이토치에서는 인공신경망을 하나의 파이썬 객체(Object)로 나타낼 수 있습니다. -# __init__()에선 인공신경망 속에 필요한 행렬곱, 활성화 함수, 그리고 그 외 다른 계산식들을 함수로 정의합니다. -# forward()에선 앞의 __init__()에서 정의한 함수들을 호출하여 입력된 데이터에 대한 결과값을 출력합니다. - -class Feed_forward_nn(torch.nn.Module): - def __init__(self, input_size, hidden_size): - super(Feed_forward_nn, self).__init__() - self.input_size = input_size - self.hidden_size = hidden_size - self.linear_1 = torch.nn.Linear(self.input_size, self.hidden_size) - self.relu = torch.nn.ReLU() - self.linear_2 = torch.nn.Linear(self.hidden_size, 1) - self.sigmoid = torch.nn.Sigmoid() - def forward(self, input_tensor): - linear1 = self.linear_1(input_tensor) - relu = self.relu(linear1) - linear2 = self.linear_2(relu) - output = self.sigmoid(linear2) - return output - -#Train our Model -# 자, 이제 학습시킬 인공신경망도 있으니 학습에 필요한 러닝레이트(Learning Rate), -# 오차(Loss), 이포씨(Epoch), 그리고 최적화 알고리즘(Optimizer)을 정의합니다. -model = Feed_forward_nn(2, 5) -learning_rate = 0.03 -criterion = torch.nn.BCELoss() -epochs = 1000 -optimizer = torch.optim.SGD(model.parameters(), lr = learning_rate) -# 러닝레이트는 쉽게 말해 얼마나 급하게 학습을 시키고 싶은지 -# 정해주는 값이라고 할 수 있습니다. 너무 크게 값을 설정해 버리면 모델이 오차의 최소점을 지나치게 되고, 값이 너무 작으면 -# 학습이 느려집니다. -# 알맞는 러닝레이트틀 선택하기 위해선 랜덤서치(Random Search) 와 같은 알고리즘을 사용해야 하며, 최적화된 -# 러닝레이트를 찾아내는 과정은 현재 딥러닝 학계에서도 활발히 연구되고 있는 주제입니다. -# 다음으로 오차 함수(loss function) 를 정의합니다. 다시 한번 말씀드리지만, 오차 함수는 머신러닝 모델의 결과값과 실제답과의 차이를 산술적으로 표현한 함수입니다. -# 이번 예제에서는 예측답과 실제답의 사이의 확률분포의 차이를 나타내 주는 -# 교차 엔트로피(Cross Entrophy) 라는 오차함수를 사용하겠습니다. -# # 이포씨(epoch) -# 는 쉽게 말해 총 몇번 학습을 시키고 싶은지 정해주는 값입니다. 즉, 위의 코드에선 '총 1000 번 오차의 최소값의 방향으로 -# 움직이겠다' 라고 선언한 것입니다. - -#Performance of the model before training -model.eval() - -test_loss_before = criterion(model(x_tes).squeeze(), y_tes) -print('Before Training, test loss is ', test_loss_before.item()) - -for epoch in range(epochs): - model.train() - optimizer.zero_grad() - train_output = model(x_tra) - train_loss = criterion(train_output.squeeze(), y_tra) - if epoch % 100 == 0: - print('Train loss at ', epoch, 'is ', train_loss.item()) - train_loss.backward() - optimizer.step() - -#Performance of the model before training -model.eval() -test_loss = criterion(model(x_tes).squeeze(), y_tes) -print('After Training, test loss is ', test_loss.item()) - -#Save model and use again. -torch.save(model.state_dict(), './model.pt') -new_model = Feed_forward_nn(2, 5) -new_model.load_state_dict(torch.load('./model.pt')) -new_model.eval() -print(new_model(torch.FloatTensor([-1,1])).item() ) diff --git a/03-Coding-Neural-Networks-In-PyTorch/basic-feed-forward_nn.ipynb b/03-Coding-Neural-Networks-In-PyTorch/basic-feed-forward_nn.ipynb deleted file mode 100644 index 3a3bc2e..0000000 --- a/03-Coding-Neural-Networks-In-PyTorch/basic-feed-forward_nn.ipynb +++ /dev/null @@ -1,699 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# 파이토치로 구현하는 신경망\n", - "\n", - "파이토치를 이용하여 가장 기본적인 신경망을 만들어봅니다.\n", - " * [개념] 텐서와 Autograd\n", - " * [프로젝트 1] 텐서와 Autograd\n", - " * [프로젝트 2] 신경망 모델 구현하기\n", - " * [프로젝트 2] 토치비전과 토치텍스트로 데이터셋 다루기\n", - "\n", - "파이토치는 기본적인 수학 계산용 라이브러리를 바탕으로 그 위에 머신러닝에 필요한 그래프 형태의 계산방식을 추가 시킨 라이브러리 입니다. 물론 파이토치의 바탕이 되는 계산 라이브러리에 대한 깊은 지식이 없더라도 파이토치를 이용해 머신러닝 모델을 구현하는데 그리 큰 문제는 없습니다.\n", - "하지만 파이썬 개발자들에게 편리하도록 설계 되었더라도 수리적 계산이 많이 들어가는 머신러닝의 특성 때문에 파이토치의 자료구조는 기존 파이썬의 자료구조와는 사뭇 다릅니다. \n", - "파이토치의 가장 기본적인 자료구조인 텐서(Tensor) 가 그 대표적인 예 인데요,이번 장에선 이 텐서와 텐서를 이용한 연산, 그리고 Autograd 등의 기능을 배워 보겠습니다. 더불어 이들을 이용해 기본적인 신경망 모델을 구현 해 보고 저장, 재사용 하는 방법까지 배워 보겠습니다.\n", - "\n", - "## 프로젝트 1. 텐서와 Autograd\n", - "\n", - "프로그래밍 언어를 배울 때와 마찬가지로, 파이토치 또한 직접 코딩을 하면서 배우는 것이 가장 효율적인 방법이라고 생각합니다. 간단한 파이토치 코드 예제를 같이 코딩하면서 파이토치에 대해 공부 해 보겠습니다.\n", - "\n", - "### 텐서 다루기 기본: 차원(Rank)과 모양(Shpae)\n", - "\n", - "가장 먼저 파이토치를 임포트 합니다.\n", - "\n", - "```python\n", - "import torch\n", - "```\n", - "\n", - "텐서(Tensor)는 파이토치에서 다양한 수식을 계산하기 위한 가장 기본적인 자료구조 입니다. 흔히 수학에서 말하는 벡터나 행렬 과 같은 개념이며, 숫자들을 특정한 모양으로 배열 한 것입니다. 그럼 간단한 텐서를 만들어 보겠습니다. \n", - "\n", - "```python\n", - "x = torch.tensor([[1,2,3], [4,5,6], [7,8,9]])\n", - "print(x)\n", - "```\n", - "\t\n", - "위 코드는 다음과 같은 결과를 출력합니다.\n", - "\n", - "```\n", - "tensor([[1, 2, 3], [ 4, 5, 6], [7, 8, 9]])\n", - "```\n", - "\n", - "즉 x는 1부터 9까지의 숫자를 가로 3줄, 세로 3줄의 모양을 지니도록 배열한 텐서입니다. 그리고 가로와 세로 두 차원으로만 이루어져 있는 2차원 텐서라고 할 수 있습니다.\n", - "이처럼 텐서는 랭크(Rank) 과 모양(Shape) 이라는 개념을 갖고 있습니다. 텐서의 랭크가 0이면 스케일러(Scaler), 1이면 벡터(Vector), 2면 행렬(Matrix), 3이상이면 n 랭크 텐서 라고 부릅니다.\n", - "\n", - "```python\n", - "1 -> 스케일러, 모양은 []\n", - "[1,2,3] -> 벡터, 모양은 [3]\n", - "[[1,2,3]] -> 행렬, 모양은 [1,3]\n", - "```\n", - "\n", - "텐서의 랭크과 모양은 size() 함수 혹은 shape 키워드를 통해 확인 할 수 있습니다.\n", - "\n", - "```python\n", - "print(x.size())\n", - "print(x.shape)\n", - "```\n", - "\n", - "```python\n", - "torch.Size([3, 3])\n", - "torch.Size([3, 3])\n", - "```\n", - " \n", - "unsqueeze(), squeeze(), 그리고 view() 함수를 통해 우리는 인위적으로 텐서의 랭크와 모양을 바꿔 줄 수도 있습니다.\n", - "먼저 unsqueeze() 함수를 통해 텐서 x의 랭크를 늘려 보겠습니다.\n", - "\n", - "```python\n", - "x = torch.unsqueeze(x, 0)\n", - "print(x)\n", - "print(x.shape)\n", - "```\n", - "\n", - "위 코드는 텐서 모양의 첫번째(0 번째) 자리에 1 이라는 차원값을 인위적으로 추가 시켜 [3,3] 모양의 랭크 2 텐서를 [1,3,3] 모양의 랭크 3 텐서로 변경시킵니다. 랭크는 늘어나도, 텐서 속 원소의 수는 유지됩니다.\n", - "\n", - "```python\n", - "tensor([[[ 1, 2, 3], [ 4, 5, 6], [ 7, 8, 9]]])\n", - "torch.Size([1, 3, 3])\n", - "```\n", - "\n", - "squeeze() 함수를 이용하면 텐서의 랭크 중 크기가 1인 랭크를 삭제하여 다시 랭크 2 텐서로 되돌릴 수 있습니다. [1, 3, 3] 모양을 가진 텐서 x 를 다시 [3,3] 모양으로 되돌려 보겠습니다.\n", - "\n", - "```python\n", - "x = torch.squeeze(x)\n", - "print(x)\n", - "print(x.shape)\n", - "```\n", - "\n", - "```python\n", - "tensor([[1, 2, 3], [ 4, 5, 6], [ 7, 8, 9]])\n", - "torch.Size([3, 3])\t#[3,3] 모양의 랭크 2 텐서\n", - "```\n", - "\n", - "x 는 이제 랭크 2의 텐서가 되었지만 이번에도 역시 텐서 속의 총 숫자 수는 계속 9로 영향을 받지 않았습니다.\n", - "view()함수를 이용하면 위와 같은 작업을 더 쉽게 할 수 있을 뿐만 아니라, 직접 텐서의 모양을 바꿔 줄 수도 있습니다. 랭크 2의 [3,3] 모양을 한 x 를 랭크 1의 [1,9] 모양으로 바꿔 보겠습니다.\n", - " \n", - "```python\n", - "x = x.view(9)\n", - "print(x)\n", - "print(x.shape)\n", - "```\n", - "\n", - "```python\n", - "tensor([ 1, 2, 3, 4, 5, 6, 7, 8, 9])\n", - "torch.Size([9])\n", - "```\n", - "\n", - "이제 텐서 x는 [9] 모양을 한 랭크 1 텐서가 되었습니다. \n", - "이처럼 squeeze(), unsqueeze(), view() 함수는 텐서 속 원소의 수를 그대로 유지하면서 텐서의 모양과 차원을 조절합니다. 말인즉슨, view() 함수에 잘못된 모양을 입력하면 함수는 실행 될 수 없습니다.\n", - "예를 들어 view 함수를 이용해 x 의 모양을 [2, 4] 가 되도록 만들어 보겠습니다. \n", - "\n", - "```python\n", - "x = x.view(2,4)\n", - "Print(x)\n", - "```\n", - "\n", - "코드를 실행시키면 다음과 같은 에러 메시지를 보게 됩니다.\n", - "\n", - "```python\n", - "Traceback (most recent call last):\n", - " File \"tensor_autograd.py\", line 12, in \n", - " x = x.view(2,4)\n", - "RuntimeError: invalid argument 2: size '[2 x 4]' is invalid for input with 9 elements at /Users/soumith/minicondabuild3/conda-bld/pytorch_1524590658547/work/aten/src/TH/THStorage.c:41\n", - "```\n", - "\n", - "이처럼 원소가 9 개인 텐서를 2 X 4, 즉 8 개 의 원소를 가진 텐서로 바꿔주는것은 불가능합니다.\n", - "\n", - "### 전체 코드\n", - "```python\n", - "import torch\n", - "\n", - "x = torch.tensor([[1,2,3], [4,5,6], [7,8,9]])\n", - "\n", - "print(x)\n", - "print(x.size())\n", - "print(x.shape)\n", - "\n", - "x = torch.unsqueeze(x, 0)\n", - "print(x)\n", - "print(x.shape)\n", - "\n", - "x = torch.squeeze(x)\n", - "print(x)\n", - "print(x.shape)\n", - "\n", - "x = x.view(9)\n", - "print(x)\n", - "print(x.shape)\n", - "\n", - "x = x.view(2,4)\n", - "Print(x)\n", - "```\n", - "\n", - "### 텐서를 이용한 연산과 행렬곱\n", - "\n", - "딥러닝을 하는데 수준 높은 수학적 지식이 필요하지는 않습니다. 하지만 기본적으로 행렬과 행렬곱은 모든 딥러닝 알고리즘에 사용되므로 꼭 짚고 넘어가면 좋습니다. 앞서 말씀드렸듯이 행렬은 2차원 텐서와 같은 개념입니다. 숫자들을 네모꼴로 배열한 것으로, 네모꼴의 높이를 행, 넓이를 열 이라고 합니다. 만약 A, B 라는 두 행렬을 가지고 행렬곱을 할 시 다음과 같은 조건이 성립해야 합니다.\n", - "\n", - "```\n", - "A 의 열 수와 B 의 행 수는 같아야 한다.\n", - "행렬곱 A X B 를 계산한 행렬은 A의 행 개수, 그리고 B 의 열 개수를 가지게 된다.\n", - "```\n", - "\n", - "\n", - "\n", - "그러면 직접 파이토치를 이용해 행렬곱을 구현해 보겠습니다. 우선 행렬곱에 사용될 두 행렬을 정의합니다.\n", - "\n", - "```python\n", - "w = torch.randn(5,3, dtype = torch.float)\n", - "x = torch.tensor([[1.0,2.0], [3.0,4.0], [5.0,6.0]])\n", - "```\n", - "\n", - "randn() 함수는 정규분포(Normal Distribution)에서 무작위하게 float32 형의 숫자들을 선택해 w 라는 텐서를 채워넣습니다. 그리고 텐서 x에는 직접 float 형의 원소들을 집어넣어 주었습니다.\n", - "행렬곱 외에도 다른 행렬 연산에 쓰일 b 라는 텐서도 추가로 정의해 보겠습니다.\n", - "\n", - "```python\n", - "b = torch.randn(5,2, dtype = torch.float)\n", - "```\n", - "\n", - "행렬곱을 하려면 torch.mm() 함수를 사용하면 됩니다.\n", - "\n", - "```python\n", - "wx = torch.mm(w,x) # w의 행은 5, x의 열은 2 즉 [5,2] 의 형태\n", - "```\n", - "\n", - "이 wx 행렬의 원소들에 b 행렬의 원소들을 더해 보겠습니다.\n", - "\n", - "result = wx + b\n", - "\n", - "위의 텐서들을 출력시켜 보면, x 는 [5, 3], w 는 [3, 2], 그리고 나머지 텐서는 [5, 2] 형태를 띄고 있음을 확인 할 수 있습니다.\n", - "\n", - "#### 전체 코드\n", - "\n", - "```python\n", - "import torch\n", - "\n", - "w = torch.randn(5,3, dtype = torch.float)\n", - "x = torch.tensor([[1.0,2.0], [3.0,4.0], [5.0,6.0]])\n", - "b = torch.randn(5,2, dtype = torch.float)\n", - "wx = torch.mm(w,x)\n", - "result = wx + b\n", - "\n", - "print(x)\n", - "print(w)\n", - "print(b)\n", - "print(wx)\n", - "print(result)\n", - "```\n", - "\n", - "### Autograd \n", - "\n", - "Autograd 는 머신러닝에 필수적인 최적화 알고리즘인 ***경사 하강법(Gradient Descent)*** 에 관련된 기능을 제공합니다. 처음 머신러닝을 접하시는 분들은 이 알고리즘이 무엇인지, 그리고 어떻게 머신러닝에 관련되 있는지 몰라 고개를 갸웃거리실 수도 있습니다. 그런 분들을 위해 이번에는 직접 코드를 짜보기에 앞서 머신러닝의 학습 원리에 대하여 조금 더 깊게 배워보고 이 알고리즘이 어떻게 머신러닝에 사용되는지 알아보겠습니다.\n", - "\n", - "앞 장에서 배웠듯 머신러닝 모델은 입력된 데이터를 기반으로 학습합니다. 다시말해 아직 충분한 데이터를 입력받지 못하거나 학습을 아직 끝내지 않은 모델은 입력된 데이터에 대해 잘못된 결과를 출력하게 됩니다.\n", - "이처럼 입력 데이터에 대해 정해진 답(Ground Truth) 과 머신러닝 모델이 낸 답의 차이를 산술적으로 표현한 것을 ***거리(Distance)*** 라고 합니다. 그리고 학습에 이용되는 데이터들을 가지고 계산된 거리들의 평균을 ***오차(loss)*** 라고 일컫습니다. 즉, 오차 값이 작은 머신러닝 모델일수록 주어진 데이터에 대해 더 정확한 답을 낸다고 볼수 있습니다.\n", - "\n", - "오차값을 최소화 하는데는 여러 알고리즘이 쓰이고 있지만, 가장 유명하고 많이 쓰이는 알고리즘은 바로 전 언급한 경사하강법 이라는 알고리즘입니다. 오차를 수학적 함수로 표현한 후, 오차 함수의 기울기를 구해 오차의 최소값이 있는 곳의 방향을 찾아내는 알고리즘이죠. 간단한 경사하강법은 Numpy와 같은 라이브러리 만으로도 직접 구현이 가능합니다만 복잡한 인공신경망 모델에선 어렵고 머리아픈 미분식의 구현과 계산을 여러번 해 주어야 합니다. 다행히도 파이토치의 Autograd는 이름 그대로 파이토치 라이브러리 내에서 미분과 같은 수학 계산들을 자동화 시켜 우리로부터 직접 경사하강법을 구현하는 수고를 덜어줍니다.\n", - "그럼 Autograd를 어떻게 사용하는지 같이 공부해 보겠습니다.\n", - "\n", - "우선 값이 1인 w 라는 0차원 스케일러 텐서를 만들어 보겠습니다. 방금 전 설명에서 Autograd가 미분 계산을 자동화 해준다고 설명했는데요, 쉽게 말하면 w 가 변수로 들어가는 수식을 w로 미분하고 기울기를 계산해 준다고 이해하면 됩니다. 이를 위해선 텐서 w의 requires_grad 키워드를 True로 설정해야 합니다.\n", - "아주 쉬운 예를 통해 간단한 미분식을 계산 해 보겠습니다.\n", - "\n", - "```python\n", - "w = torch.tensor(1, requires_grad=True)\n", - "a = w*3\n", - "```\n", - "\n", - "a 라는 수식을 w 곱하기 3이라고 정의했습니다. 즉 이 식의 w에 대한 기울기는 3 입니다. backward() 함수를 이용하면 이 수식의 기울기를 구할 수 있습니다.\n", - "\n", - "```python\n", - "a.backward()\n", - "print(w.grad)\n", - "```\n", - "\n", - "예상대로, 위 코드는 다음과 같이 3 이라는 결과를 출력합니다.\n", - "\n", - "```python\n", - "tensor(3)\n", - "```\n", - "\n", - "간단한 미분식과 기울기 계산을 해 봤으니, 이번엔 조금 더 복잡한 미분식 계산을 해 보겠습니다.\n", - "\n", - "```python\n", - "w = torch.tensor(1, requires_grad=True)\n", - "a = w*3\n", - "l = a*2\n", - "```\n", - "\n", - "위의 l은 텐서 a의 모든 값을 제곱한 텐서 입니다.\n", - "텐서 w에 3을 곱해 a를 만들었고, 또 a를 제곱하여 l을 만들었습니다.\n", - "이를 수식으로 표현하면 다음과 같습니다.\n", - "\n", - "```python\n", - "l = 2*a\n", - "a = 3*w\n", - "그러므로\n", - "l = 2*(3*w) = 6w\n", - "```\n", - "\n", - "이러한 l을 w로 미분하려면 연쇄법칙(Chain Rule)을 이용하여 l을 a와 w로 차례대로 미분해 줘야합니다.\n", - "\n", - "```python\n", - "l.backward()\n", - "print('l을 w로 미분한 값은 ', w.grad)\n", - "```\n", - "\n", - "위의 코드를 실행하면 다음과 같은 결과를 확인 하실 수 있습니다.\n", - "\n", - "```python\n", - "l을 w로 미분한 값은 tensor(6)\n", - "```\n", - "\n", - "backward() 함수는 l을 a로 미분한 후, 그 값을 a를 w로 미분한 값에 곱해줘 w.grad 를 계산했습니다.\n", - "여러 겹의 행렬곱을 하는 인공신경망이 경사 하강법을 할때는 위처럼 여러 겹의 미분식을 해야합니다.\n", - "이렇게 연쇄법칙을 사용하여 경사 하강법을 하는 딥러닝 특유의 알고리즘이 바로 그 유명한\n", - "***역전파 알고리즘(Backpropagation Algorithm)*** 입니다.\n", - "\n", - "역전파 알고리즘은 딥러닝에 있어 가장 자주 쓰이는 알고리즘 이지만 직접 구현하는데에는 복잡한 코드와 수학적 지식이 필요합니다.\n", - "다행히 파이토치는 역전파 알고리즘 기법을 제공해주기 때문에 우리가 직접 역전파 알고리즘을 구현할 일은 없습니다만, 아주 중요한 알고리즘이므로\n", - "딥러닝을 좀 더 깊게 공부하고자 하신다면 꼭 자세히 공부하는걸 권하고 싶습니다.\n", - "\n", - "#### 전체 코드\n", - "\n", - "```python\n", - "import torch\n", - "\n", - "w = torch.tensor(1, requires_grad=True)\n", - "a = w*3\n", - "l = a*2\n", - "\n", - "l.backward()\n", - "print('l을 w로 미분한 값은 ', w.grad)\n", - "```\n", - "\n", - "## 프로젝트 2. 신경망 모델 구현하기\n", - "\n", - "이번 장에서는 지금까지 배워 온 개념들을 토대로 간단한 신경망을 함께 구현해 보겠습니다. 지금까지 내용과는 달리, 이번 장에는 딥러닝에 핵심적인 내용을 조금 더 깊게, 그리고 이론적으로 설명하여 처음 딥러닝을 접하는 분들에게는 다소 어려울 수도 있습니다. 하지만 설명을 읽어가며 함께 코딩을 해 보면 어느새 딥러닝을 코딩하는데 익숙해 질 것입니다.\n", - "\n", - "### 딥러닝과 인공신경망\n", - "\n", - "이름에서부터 알 수 있듯이 인공신경망은 인간의 뇌, 혹은 신경계의 작동 방식에서 그 영감을 받았습니다. 신경계가 작동을 하기 위해선 가장 먼저 눈이나 혀 같은 감각 기관을 통해 자극을 입력 받아야 합니다. 이런 자극이 첫번째 신경세포로 전달되고, 이 신경세포는 자극을 처리해 다른 신경세포로 전달합니다. 이러한 자극 처리와 전달 과정을 여러번 반복하다 보면 인간의 신경계는 수많은 자극을 인지하고 그에 따라 다른 반응을 하게됩니다. 그러다 언젠가는 맛을 판별하거나 손가락을 움직이는 등 다양하고 복잡한 작업을 할수 있게 됩니다.\n", - "\n", - "자극을 텐서의 형태로 입력받는 인공신경망에선 이러한 자극의 입력과 전달과정이 행렬곱 과 활성화 함수 라는 수학적 연산으로 표현됩니다.\n", - "실제 인간의 신경세포가 자극을 전달하기 전에 입력받은 자극에 여러 화학적 가공처리를 가하듯 인공신경망도 입력된 텐서에 특정한 수학적 연산을 실행합니다. 바로 ***가중치(Weight)*** 라고 하는 랜덤한 텐서를 행렬곱 시켜주는 것이죠.\n", - "그리고 이 행렬곱의 결과는 ***활성화 함수(Activation Function)*** 를 거쳐 결과값을 산출하게 됩니다. 이 결과값이 곧 인접한 다른 신경세포로 전달되는 자극이라고 보시면 됩니다.\n", - "자극의 처리와 전달, 이러한 과정을 몇겹에 싸여 반복한 후 마지막 결과값 만들어 내는 것이 인공신경망의 기본적인 작동원리입니다.\n", - "\n", - "### 간단한 분류 모델 구현하기\n", - "\n", - "이번 장에서는 인공신경망을 이용해 간단한 분류 모델을 함께 구현해 보겠습니다. 하지만 처음 인공신경망과 머신러닝을 접하는 분들을 위해 이미지 같은 고차원의 복잡한 데이터가 아닌 간단한 2차원의 데이터를 이용하겠습니다. 첫번째 인공신경망을 구현하는 만큼, 이번 프로젝트의 코드는 조금 새롭고 생소한 개념을 다소 포함하고 있습니다. 그러므로 꼭 설명을 자세하게 읽어 보고 코딩해 보시기 바랍니다.\n", - "\n", - "우선 파이토치와 그 외 다른 라이브러리들을 임포트합니다. Numpy는 유명한 수치 해석용 라이브러리 입니다. 행렬과 벡터를 이용한 연산을 하는데 아주 유용한 라이브러리며, 파이토치도 이 넘파이를 기반으로 개발되었을 정도로 긴밀하게 이용됩니다. 이번 프로젝트에서는 인공신경망 학습을 위한 데이터를 만드는데 넘파이와 sklearn 라이브러리를 이용하여 생성하겠습니다. 마지막으로 임포트 되어지는 matplotlib 라이브러리는 데이터를 시각화 하는데 있어 유용한 툴 입니다. 학습데이터가 어떠한 패턴을 보이며 분포되어 있는지 확인하기 위해 matplotlib 을 이용하겠습니다.\n", - "\n", - "```python\n", - "import torch\n", - "import numpy\n", - "from sklearn.datasets import make_blobs\n", - "import matplotlib.pyplot as plot\n", - "import torch.nn.functional as F\n", - "```\n", - "\n", - "인공신경망을 구현하기 전 인공신경망의 학습과 평가를 위한 데이터셋을 만들어 줍니다.\n", - "밑의 코드에서 x_tra 와 y_tra 라고 정의된 실험데이터는 직접 인공신경망을 학습시키는데 쓰이는 데이터 입니다. 반대로 x_tes 와 y_tes 라고 정의된 데이터는 직접 신경망을 학습시키는데는 쓰이지 않지만 학습이 끝난 신경망의 성능을 평가하고 실험하는데 쓰일 데이터 셋입니다.\n", - "\n", - "```python\n", - "n_dim = 2\n", - "x_tra, y_tra = make_blobs(n_samples=80, n_features=n_dim, centers=[[1,1],[-1,-1],[1,-1],[-1,1]], shuffle=True, cluster_std=0.3)\n", - "x_tes, y_tes = make_blobs(n_samples=20, n_features=n_dim, centers=[[1,1],[-1,-1],[1,-1],[-1,1]], shuffle=True, cluster_std=0.3)\n", - "```\n", - "\n", - "make_blobs() 함수를 이용하여 데이터를 2차원 벡터의 형태로 만들어 주었습니다.\n", - "학습데이터(Training Data Set)에는 80개, 실험데이터(Test Data Set)에는 20개의 2차원 벡터 형태의 데이터가 있는 것을 확인하실 수 있습니다.\n", - "데이터를 만든 후, 데이터에 해당하는 정답인 ‘레이블’ 을 달아줍니다. label_map 이라는 간단한 함수를 구현해 데이터가 [-1, -1] 혹은 [1, 1] 주위에 있으면 0 이라는 레이블을 달아 줬습니다. 반대로 [1, -1] 혹은 [-1, 1] 주위에 위치해 있으면 1 이라는 레이블을 달아 줬습니다.\n", - "\n", - "```python\n", - "def label_map(y_, from_, to_):\n", - " y = numpy.copy(y_)\n", - " for f in from_:\n", - " y[y_ == f] = to_\n", - " return y\n", - "\n", - "y_tra = label_map(y_tra, [0, 1], 0)\n", - "y_tra = label_map(y_tra, [2, 3], 1)\n", - "y_tes = label_map(y_tes, [0, 1], 0)\n", - "y_tes = label_map(y_tes, [2, 3], 1)\n", - "```\n", - "\n", - "데이터가 제대로 만들어 졌는지, 그리고 제대로 레이블링이 되었는지 확인하기 위해 matplotlib 을 이용해 데이터를 시각화 해 보겠습니다.\n", - "\n", - "```python\n", - "def vis_data(x,y = None, c = 'r'):\n", - "\tif y is None:\n", - "\t\ty = [None] * len(x)\n", - "\tfor x_, y_ in zip(x,y):\n", - "\t\tif y_ is None:\n", - "\t\t\tplot.plot(x_[0], x_[1], '*',markerfacecolor='none', markeredgecolor=c)\n", - "\t\telse:\n", - "\t\t\tplot.plot(x_[0], x_[1], c+'o' if y_ == 0 else c+'+')\n", - "\n", - "plot.figure()\n", - "vis_data(x_tra, y_tra, c='r')\n", - "plot.show()\n", - "```\n", - "\n", - "레이블이 0 인 학습 데이터는 점으로, 1인 데이터는 십자가로 표시했습니다.\n", - "\n", - "\n", - "\n", - "마지막으로 신경망을 구현 하기 전, 위에서 정의한 데이터들을 넘파이 리스트가 아닌 파이토치 텐서로 바꿔줍니다.\n", - "\n", - "```python\n", - "x_tra = torch.FloatTensor(x_tra)\n", - "x_tes = torch.FloatTensor(x_tes)\n", - "y_tra = torch.FloatTensor(y_tra)\n", - "y_tes = torch.FloatTensor(y_tes)\n", - "```\n", - "\n", - "이제 데이터를 준비했으니 본격적으로 신경망 모델을 구현해 보겠습니다. \n", - "파이토치에서 인공신경망은 아래와 같이 신경망 모듈(Neural Network Module)을 상속받는 파이썬 객체 로 나타낼 수 있습니다.\n", - "\n", - "```python\n", - "class Feed_forward_nn(torch.nn.Module):\n", - "```\n", - "\n", - "인공신경망의 구조와 동작을 정의하는 컨스트럭터/이니셜라이져(Constructor/Initializer) 를 모델 클래스 안에 정의해 보겠습니다.\n", - "\n", - "```python\n", - "\t\tdef __init__(self, input_size, hidden_size):\n", - "```\n", - "\n", - "__init()__ 함수는 파이썬 객체지향 프로그래밍에서 객체가 생성될 때 객체에 내포된 값을 설정 해 주는 함수이며, 객체가 생성 될 때 자동적으로 호출됩니다. 이번 예제에서는 학습/실험 데이터의 차원인 input_size 라는 변수와 hidden_size 라는 변수를 __init()__ 함수를 통해 설정하도록 구현했습니다.\n", - "input_size 는 신경망에 입력되는 데이터들의 차원입니다. \n", - "2차원 데이터를 입력받는 모델을 구현할 것이므로 input_size는 2라고 정의됩니다.\n", - "[1,2] 사이즈의 입력데이터가 [2,5] 모양을 가진 가중치 텐서와 행렬곱 해 [1,5] 모양의 텐서가 만들어지듯이, 신경망에 입력된 데이터는 신경망 속의 가중치와 활성화 함수를 거치며 차원을 변화시킵니다. 이렇게 중간에 변화된 차원값을 hidden_size 라고 부르겠습니다.\n", - "\n", - "```python\n", - "\t\t\tsuper(Feed_forward_nn, self).__init__()\n", - "\t\t\tself.input_size = input_size\n", - "\t\t\tself.hidden_size = hidden_size\n", - "```\n", - "\n", - "다음은 입력된 데이터가 인공신경망을 통과하면서 거치는 연산들을 정의해 주겠습니다.\n", - "\n", - "```python\n", - "\t\t\tself.linear_1 = torch.nn.Linear(self.input_size, self.hidden_size)\n", - "\t\t\tself.relu = torch.nn.ReLU()\n", - "\t\t\tself.linear_2 = torch.nn.Linear(self.hidden_size, 1)\n", - "\t\t\tself.sigmoid = torch.nn.Sigmoid()\n", - "```\n", - "\n", - "linear_1 함수는 앞서 여러번 반복해 설명드렸던 행렬곱을 하는 함수입니다. [input_size, hidden_size] 사이즈의 가중치를 입력 데이터에 행렬곱 시켜 [1,hidden_size] 꼴의 텐서를 리턴합니다. 이 때 리턴된 값은 torch.nn.ReLU() 라는 활성화 함수를 거치게 됩니다. ReLU 는 입력값이 0보다 작으면 0을, 0보다 크면 입력값을 그대로 출력합니다. 예를 들어 텐서 [-1, 1, 3, -5]가 ReLU 를 거치면 텐서 [0, 1, 3, 0]가 리턴됩니다.\n", - "\n", - "\n", - "\n", - "ReLU 를 통과한 텐서는 다시 한번 linear_2 로 정의된 행렬곱을 거쳐 [1,1] 꼴을 지니게 됩니다. 마지막으로 이 텐서는 sigmoid 활성화 함수에 입력됩니다. Sigmoid 는 입력된 학습데이터가 레이블 1에 해당할 확률값을 리턴하는 함수로써, 머신러닝과 딥러닝에서 가장 중요한 활성화 함수 입니다.\n", - "\n", - "\n", - "\n", - "위의 그림처럼 sigmoid 함수는 0과 1 사이의 값을 리턴합니다. \n", - "다음으로 __init__() 함수에서 정의된 동작들을 차례대로 실행하는 forward() 함수를 구현합니다.\n", - "\n", - "```python\n", - "\t\tdef forward(self, input_tensor):\n", - "\t\t\tlinear1 = self.linear_1(input_tensor)\n", - "\t\t\trelu = self.relu(linear1)\n", - "\t\t\tlinear2 = self.linear_2(relu)\n", - "\t\t\toutput = self.sigmoid(linear2)\n", - "\t\t\treturn output\n", - "```\n", - "\n", - "이로써 인공신경망을 구현이 끝났습니다. 이제 실제로 신경망 객체를 생성하고 학습에 필요한 여러 변수와 알고리즘을 정의하겠습니다.\n", - "\n", - "```python\n", - "model = Feed_forward_nn(2, 5)\n", - "learning_rate = 0.03\n", - "criterion = torch.nn.BCELoss()\n", - "```\n", - "\n", - "input_size 를 2로, hidden_size 를 5 로 설정한 신경망 객체를 만들었습니다. learning_rate 은 ‘얼마나 급하게 학습하는가’ 를 설정하는 값입니다. 값이 너무 크면 오차함수의 최소점을 찾지 못하고 지나치게 되고, 값이 너무 작으면 학습속도가 느려집니다.\n", - "러닝레이트를 설정했으면 그 다음으로는 오차함수를 만들어야 합니다. 물론 직접 오차함수를 코딩 할 수도 있지만 이는 매우 까다롭고 귀찮은 일입니다. 다행히도 파이토치는 여러 오차함수를 미리 구현해서 바로 사용 할 수 있도록 해놓았습니다. 이번에 우리는 파이토치가 제공해 주는 이진교차 엔트로피(Binary Cross Entropy) 라는 오차함수를 사용하겠습니다.\n", - "\n", - "epochs는 학습데이터를 총 몇번 반복\n", - "동안 오차를 구하고 그 최소점으로 이동 할지 결정해줍니다. \n", - "마지막 변수 optimizer 는 최적화 알고리즘입니다. 최적화 알고리즘 에는 여러 종류가 있고 상황에 따라 다른 알고리즘을 사용합니다. 이번 예제를 통해 처음으로 인공신경망을 구현하는 분들을 위해 그중에서도 가장 기본적인 알고리즘인 스토카스틱 경사 하강법(Stochastic Gradient Descent)을 사용하겠습니다.\n", - "\n", - "```python\n", - "epochs = 1000\n", - "optimizer = torch.optim.SGD(model.parameters(), lr = learning_rate)\n", - "```\n", - "\n", - "학습을 시작하기 전 정말 마지막으로 아무 학습도 하지 않은 모델의 성능을 시험해 보겠습니다.\n", - "\n", - "```python\n", - "model.eval()\n", - "test_loss_before = criterion(model(x_tes).squeeze(), y_tes)\n", - "print('Before Training, test loss is ', test_loss_before.item())\n", - "```\n", - "\n", - "위 코드는 아래와 같은 결과를 출력합니다.\n", - "\n", - "```\n", - "Before Training, test loss is 0.7301096916198730\n", - "```\n", - "\n", - "오차값이 0.73 이 나왔습니다. 이정도의 오차를 가진 모델은 사실상 분류하는 능력이 없다고 봐도 무방합니다.\n", - "자, 이제 드디어 인공신경망을 학습시켜 퍼포먼스를 향상시켜 보겠습니다.\n", - "\n", - "우선 epoch을 반복해주는 for loop 을 만들어 줍니다.\n", - "\n", - "```python\n", - "for epoch in range(epochs):\n", - "```\n", - "\n", - "모델에 train()함수를 호출시켜 학습 모드로 바꿔 줍니다.\n", - "'경사'라고도 할 수 있는 그레디언트(Gradient)는 오차 함수가 최소점을 가진 곳의 방향 입니다.\n", - "매 epoch 마다 우리는 새로운 그레디언트 값을 계산할 것이기 때문에 zero_grad()함수를 통해 그레디언트 값을 0으로 정의해 주겠습니다.\n", - "\n", - "```python\n", - " model.train()\n", - " optimizer.zero_grad()\n", - "```\n", - "\n", - "이미 생성한 모델에 학습데이터를 입력시켜 결과값을 계산합니다.\n", - "여기서 잠깐, 신경망 객체 속에 정의된 forward() 함수가 곧 신경망의 결과값을 내는 함수인 것은 맞지만, torch.nn.module이 forward() 함수 호출을 대신해줘 우리가 직접 호출할 필요는 없습니다.\n", - "\n", - "```python\n", - " train_output = model(x_tra) #torch.nn.module 을 통해서 forward()호출\n", - "```\n", - "\n", - "신경망의 결과값의 차원을 레이블의 차원과 같도록 만들어 주고 오차를 계산합니다.\n", - "\n", - "```python\n", - " train_loss = criterion(train_output.squeeze(), y_tra)\n", - "```\n", - "\n", - "학습이 잘 되는지 확인하기 위해 100 epoch마다 오차를 출력하도록 설정하겠습니다.\n", - "\n", - "```python\n", - "\tif epoch % 100 == 0:\n", - "\t\tprint('Train loss at ', epoch, 'is ', train_loss.item())\n", - "```\n", - "\n", - "그 다음단계는 오차함수를 가중치 값들로 미분하여 오차함수의 최소점의 방향, 즉 그레디언트(Gradient)를 구하고 그 방향으로 모델을 러닝레이트 만큼 이동시키는 것입니다.\n", - "\n", - "```python\n", - " train_loss.backward()\n", - " optimizer.step()\n", - "```\n", - "\n", - "위 코드를 실행시켜 보면 오차값이 점점 줄어드는 것을 보실 수 있습니다.\n", - "\n", - "```python\n", - "Train loss at 0 is 0.7301096916198730\n", - "Train loss at 100 is 0.6517783403396606\n", - "Train loss at 200 is 0.5854113101959229\n", - "Train loss at 300 is 0.519926130771637\n", - "Train loss at 400 is 0.4684883952140808\n", - "Train loss at 500 is 0.42419689893722534\n", - "Train loss at 600 is 0.3720306158065796\n", - "Train loss at 700 is 0.3115468919277191\n", - "Train loss at 800 is 0.25684845447540283\n", - "Train loss at 900 is 0.2133386880159378\n", - "```\n", - "\n", - "바야흐로 우리의 첫 인공신경망 학습이 끝났습니다. 이제 학습된 신경망의 퍼포먼스를 시험할 차례입니다.\n", - "모델을 평가 모드(evaluation mode)로 바꿔 주고 실험데이터인 x_tes, y_tes를 이용해 오차값을 구해보겠습니다.\n", - "\n", - "```python\n", - "model.eval()\n", - "test_loss_before = criterion(torch.squeeze(model(x_tes) ), y_tes)\n", - "print('Before Training, test loss is ', test_loss_before.item())\n", - "```\n", - "\n", - "학습을 하기 전과 비교했을때 현저하게 줄어든 오차값을 확인 하실 수 있습니다.\n", - "\n", - "```python\n", - "After Training, test loss is 0.20166122913360596\n", - "```\n", - "\n", - "지금까지 인공신경망을 구현하고 학습시켜 보았습니다.\n", - "이제 학습된 모델을 .pt 파일로 저장해 보겠습니다.\n", - "\n", - "```python\n", - "torch.save(model.state_dict(), './model.pt')\n", - "```\n", - "\n", - "위 코드를 실행하고 나면 학습된 신경망의 가중치를 내포하는 model.pt 라는 파일이 생성됩니다. 아래 코드처럼 새로운 신경망 객체에 model.pt 속의 가중치값을 입력시키는 것 또한 가능합니다.\n", - "\n", - "```python\n", - "new_model = Feed_forward_nn(2, 5)\n", - "new_model.load_state_dict(torch.load('./model.pt'))\n", - "new_model.eval()\n", - "print(new_model(torch.FloatTensor([-1,1])).item() )\n", - "```\n", - "\n", - "여담으로 벡터 [-1,1]을 학습하고 저장된 모델에 입력시켰을 때 레이블이 1일 확률은 90% 이상이 나왔습니다.\n", - "우리의 첫번째 신경망 모델은 이제 꽤 믿을만한 분류 작업이 가능하게 된 것입니다.\n", - "\n", - "```python\n", - "벡터 [-1,1]이 레이블 1 을 가질 확률은 0.9407910108566284\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 전체 코드\n", - "\n", - "```python\n", - "import torch\n", - "import numpy\n", - "from sklearn.datasets import make_blobs\n", - "import matplotlib.pyplot as plot\n", - "import torch.nn.functional as F\n", - "\n", - "def label_map(y_, from_, to_):\n", - " y = numpy.copy(y_)\n", - " for f in from_:\n", - " y[y_ == f] = to_\n", - " return y\n", - " \n", - "n_dim = 2\n", - "x_tra, y_tra = make_blobs(n_samples=80, n_features=n_dim, centers=[[1,1],[-1,-1],[1,-1],[-1,1]], shuffle=True, cluster_std=0.3)\n", - "x_tes, y_tes = make_blobs(n_samples=20, n_features=n_dim, centers=[[1,1],[-1,-1],[1,-1],[-1,1]], shuffle=True, cluster_std=0.3)\n", - "y_tra = label_map(y_tra, [0, 1], 0)\n", - "y_tra = label_map(y_tra, [2, 3], 1)\n", - "y_tes = label_map(y_tes, [0, 1], 0)\n", - "y_tes = label_map(y_tes, [2, 3], 1)\n", - "\n", - "def vis_data(x,y = None, c = 'r'):\n", - "\tif y is None:\n", - "\t\ty = [None] * len(x)\n", - "\tfor x_, y_ in zip(x,y):\n", - "\t\tif y_ is None:\n", - "\t\t\tplot.plot(x_[0], x_[1], '*',markerfacecolor='none', markeredgecolor=c)\n", - "\t\telse:\n", - "\t\t\tplot.plot(x_[0], x_[1], c+'o' if y_ == 0 else c+'+')\n", - "\n", - "plot.figure()\n", - "vis_data(x_tra, y_tra, c='r')\n", - "plot.show()\n", - "\n", - "x_tra = torch.FloatTensor(x_tra)\n", - "x_tes = torch.FloatTensor(x_tes)\n", - "y_tra = torch.FloatTensor(y_tra)\n", - "y_tes = torch.FloatTensor(y_tes)\n", - "\n", - "class Feed_forward_nn(torch.nn.Module):\n", - "\t\tdef __init__(self, input_size, hidden_size):\n", - "\t\t\tsuper(Feed_forward_nn, self).__init__()\n", - "\t\t\tself.input_size = input_size\n", - "\t\t\tself.hidden_size = hidden_size\n", - "\t\t\tself.linear_1 = torch.nn.Linear(self.input_size, self.hidden_size)\n", - "\t\t\tself.relu = torch.nn.ReLU()\n", - "\t\t\tself.linear_2 = torch.nn.Linear(self.hidden_size, 1)\n", - "\t\t\tself.sigmoid = torch.nn.Sigmoid()\n", - "\t\tdef forward(self, input_tensor):\n", - "\t\t\tlinear1 = self.linear_1(input_tensor)\n", - "\t\t\trelu = self.relu(linear1)\n", - "\t\t\tlinear2 = self.linear_2(relu)\n", - "\t\t\toutput = self.sigmoid(linear2)\n", - "\t\t\treturn output\n", - "\n", - "model = Feed_forward_nn(2, 5)\n", - "learning_rate = 0.03\n", - "criterion = torch.nn.BCELoss()\n", - "epochs = 1000\n", - "optimizer = torch.optim.SGD(model.parameters(), lr = learning_rate)\n", - "\n", - "model.eval()\n", - "test_loss_before = criterion(model(x_tes).squeeze(), y_tes)\n", - "print('Before Training, test loss is ', test_loss_before.item())\n", - "\n", - "for epoch in range(epochs):\n", - "\tmodel.train()\n", - "\toptimizer.zero_grad()\n", - "\ttrain_output = model(x_tra)\n", - "\ttrain_loss = criterion(train_output.squeeze(), y_tra)\n", - "\tif epoch % 100 == 0:\n", - "\t\tprint('Train loss at ', epoch, 'is ', train_loss.item())\n", - "\ttrain_loss.backward()\n", - "\toptimizer.step()\n", - "\n", - "model.eval()\n", - "test_loss = criterion(model(x_tes).squeeze(), y_tes) \n", - "print('After Training, test loss is ', test_loss.item())\n", - "\n", - "torch.save(model.state_dict(), './model.pt')\n", - "new_model = Feed_forward_nn(2, 5)\n", - "new_model.load_state_dict(torch.load('./model.pt'))\n", - "new_model.eval()\n", - "print(new_model(torch.FloatTensor([-1,1])).item() )\n", - "```\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.0" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/03-Coding-Neural-Networks-In-PyTorch/model.pt b/03-Coding-Neural-Networks-In-PyTorch/model.pt new file mode 100644 index 0000000000000000000000000000000000000000..cd35ee0c7c060a9ab33de3f0281541eb3dd6060e GIT binary patch literal 893 zcmZ8fOKa3n6izakP8;n*ZLPIFTD5iRW1NXKbwFlNbQI#F9R;(IkY+BED`_$(xv3}w zx@j2|S3+^2I~SEgaVz3vB@|p0!G(+N{Rx5#?@eZ;^)4>l@1FCW?|kPh2`?o3&)drH zvBzt(Be^Bv&aV``L*TO70LO6+&n2$u+WI2$eC#^F(*UiP{@9oh8|lcYVZ#BTsYw(h zPa8=0@iXK@Dg-gBr77rJu16rP3R;T3utT6r6*ZA@xK151su%>crb)}#$RxCiFWvKM z$V0X3*d!8gQ&r%zT0hIc4l+Ewkb8n~-69~E#Pv)orw0VvzNC9-KEQTOC&=+#PhXTJ zNM*BHhRL9jMje5eq(~LpHOT!0lpw8j(-=iAD$_IhY3PcznvHMuMwy**{ zs-W^KAU(I3tUUzwR-w1;NL$-HHhk!_XkkmV65>lrUb$Q-%Fu6R)HEd=hzXZrpUN-8 z{)FrRfx#*a?QB%;Gsr68|^aVtWnn4UW9h*UorYHwf zA)JXV?<~z(mG@(RJL7|MA)L2jnC*dw>;Q5hTu2}`iFTw6`49>T91juK7hGHeIfRQb z8u;^ccOJ2QWoiF>=G*gZwsZ5Toh4`Eh;;eYO^&wJn?yLNNIg~^Rl$rck>IM zd~vtYl@{A_cJy0cTeJN)N)HBCqi>aW<;n5)" + "
" ] }, - "metadata": {}, + "metadata": { + "needs_background": "light" + }, "output_type": "display_data" } ], @@ -203,12 +338,14 @@ }, { "data": { - "image/png": "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\n", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAP8AAAEICAYAAACQ6CLfAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvnQurowAAFDxJREFUeJzt3XuMXOV5x/HvE1/ZdcC34izmYi5OUtciDlgIFAgpUQlxiwilIlBEnTbUJE2gURMVlP4R1AoVNeRClCqNUwimApKgBEIlSoJRJUAkcQx2sbnUxqmJb6zBl/geX3j6xxxHg9l53mXOnDnjfX8fabWz85x3zrvHfvacmee872vujojk5x11d0BE6qHkF8mUkl8kU0p+kUwp+UUypeQXyZSSPyNm9pSZfaJF7DQz29XlLkmNlPw9zsx2NX29YWZ7m36+plP7cfdfufuERF+G/ONhZheY2RNmNtrM3MxmdKpfUp3RdXdAYs0JaWZrgevcfXE3+2BmqZPEHwOPdKMv0jk6848wZtZnZveZ2RYz225mS8xsatMmp5rZ02a208weNbPJRbszzMybXucpM/snM/sZsBu4HzgP+LfiquPrTa85j0byP1H8/HyxzRXFa33KzF4u+vSQmQ0Uzx++UrjBzP7PzF43s9uG8cdGOkAHeeT5S6APOBGYAvwNsK8p/ufAfGAa0A/8XfBa1wJ/BRwLXAP8DPiUu09w988BmNlJwER3fw74YNHuD4ptfmhmFwP/CPwZMB3YCNx7xH4uA84Czi62+4s2fm95m5T8I88BYCpwhrsfcvel7t78Qd6d7r7a3fcADwBzgte6y91fdPcD7n6wxTbzgP8KXuMa4N/dfbm77wNuBi40sxObtrnN3be5+yvAN4CrE7+jdICS/yhmZqOO+EDwBOBuYDHwAzPbUFxGN3+282rT4z1A9CHfumF04/AlfysnAK8c/sHddwDbaFwFDLWfV4o2UjEl/1GsOLNPaPra6O773f0Wd/994Hzgchpn37Z2Ef1sZmOLfSxusT00LvNPaWrzTmASsKFpm5OaHp9ctJGKKflHGDO7yMxmFx+a7aDxNuCNDr38IHBa088XAs+4+25o/DECthyxzf3AJ83sTDMbB/wz8KS7r2/a5u/NbKKZnQzcCHy/Q/2VgJJ/5DkB+BGNxH+exln5vg699teBq4sqwlcZusT3JeC+Yps/dfdHaXzg9yCwicaZ/cgrkf8ElgPLiu3u7lB/JWCazEPaZWargD9x91Vtth9N48rkVHdf28m+SZrO/NIWMxtPo3LQVuJL/XTml9rozF8vJb9IpnTZL5Kprg7sab53XIZv/PjxYfzkk09uGdu6dWvYds+ePWE8dWWYih9zzDEtY5MmTQrb7tu3L4wPDg6G8UOHDoXxkcrdbTjblUp+M7sEuAMYReMWztvKvF6dzOLjVefboxkzZoTxb37zmy1jDzzwQNh22bJlYXz//v1h/MCBA2F89uzZLWOXX3552HbNmjVh/Mtf/nIY3759exjPXduX/WY2CvhX4KPALBr131md6piIVKvMe/5zgJeLSSD2A9+jMTpLRI4CZZJ/Om8ekLGeNw/WAMDMFpjZUjNbWmJfItJhlX/g5+4LgYWgD/xEekmZM/8G3jwa60TePFJLRHpYmeT/JTDTzE4thnZeBTzcmW6JSNVK3eFnZvNojPQaRWPWl1sT21d22V9nqW7OnGgyHLjqqqvC+BVXXBHGU/Xq/v7+lrGozg4wZcqUMF6lVaviYQFvvBGPRH7Pe94TxqP7AH7yk5+EbW+//fYwvnLlyjBep67U+d39ETRrq8hRSbf3imRKyS+SKSW/SKaU/CKZUvKLZErJL5Kprs7k08u39x577LFh/J577mkZO/PMM8O273hH/Dd2586dYTw1rj0aVpu6R2DMmDFh/Ljjjgvju3fvDuNRrb7q/3vRPAip+x/Gjh0bxp988skwfu2114bxKg23zq8zv0imlPwimVLyi2RKyS+SKSW/SKaU/CKZUqmvsHjx4jB+yimntIxt2bIlbJsamjp6dDy48uDBg2E8NZw5kipDpmbvHTVqVGX7rlLZIeADAwNh/CMf+UgYf+mll8J4GSr1iUhIyS+SKSW/SKaU/CKZUvKLZErJL5IpJb9Iprq6RHedzj777DAe1fEBXn/99ZaxVJ0+VQtPLcE9ffpbVkF7k76+vpaxVC09tcpu6ndLDRmO6ump4cSp+xtSQ6HXr1/f9munpH7v6667Lox/4QtfKLX/TtCZXyRTSn6RTCn5RTKl5BfJlJJfJFNKfpFMKflFMpXNeP5UXfXGG28M41GdPzVeP1XnT9WMv/3tb4fxjRs3toxFtW6AE044IYxv2rQpjJeZD2DcuHFh2wkTJoTxs846K4zfcMMNLWPRvyek729ITfWeaj9jxowwXkZXlug2s7XATuAQcNDd55Z5PRHpnk7c4feH7h7/GRWRnqP3/CKZKpv8DvzUzJ4xswVDbWBmC8xsqZktLbkvEemgspf957v7BjM7HnjMzF5y9yeaN3D3hcBC6O0JPEVyU+rM7+4biu+bgQeBczrRKRGpXtvJb2b9ZvbOw4+Bi4GVneqYiFSr7Tq/mZ1G42wPjbcP97n7rYk2tV32//znPw/jxx9/fBiPxo6n5rZP1at/85vfhPFzzz03jF988cUtY6m5AL773e+G8euvvz6Mr1wZ/72PlsJO3f8wODgYxpcvXx7GV69e3TKWmgsgNcdCaj6A9773vWF89uzZLWOrVq0K26ZUXud3918B72u3vYjUS6U+kUwp+UUypeQXyZSSXyRTSn6RTGUzdff73hcXJtatWxfGo6GrqaGpKanhoSmPPvpoy9ju3bvDtrNmzQrjqaHQDz74YBi/9NJLW8ZSw16fffbZMJ6ajj0qx/X394dtU8OsU8O4f/3rX4fx8847r2WsbKlvuHTmF8mUkl8kU0p+kUwp+UUypeQXyZSSXyRTSn6RTI2YOn80RBLgtddeC+OpIZrR8NNoGWqIh7UCbNmyJYynRL/7b3/727DtwMBAGL/11nCUdvJ3j5YAT7WNauHDEU1pnhrqXLbOv3fv3jB+wQUXtIwtWrQobNspOvOLZErJL5IpJb9IppT8IplS8otkSskvkiklv0imRkyd/6abbgrjqVr7rl27wnhU90299r59+8J46h6DuXPjxY+nTJnSMjZ58uSw7ZgxY8L4tGnTwnhUx4f4dx87dmzYduLEiWH84x//eBifNGlSy1iqDn/ccceF8VT71O+W+jftBp35RTKl5BfJlJJfJFNKfpFMKflFMqXkF8mUkl8kUyOmzv/000+H8Xe9611h/Iwzzgjj0dz6qTngo6WiIT12PLW8eDS2PDXuPLXv1DLaqbn3ozH7qX1HayVAepntaP77vr6+sG3q9071LZpLAOChhx4K492QPPOb2V1mttnMVjY9N9nMHjOz1cX31ndTiEhPGs5l/93AJUc8dzPwuLvPBB4vfhaRo0gy+d39CWDrEU9fBhyea2gR8LEO90tEKtbue/5p7r6pePwq0PIGcDNbACxocz8iUpHSH/i5u5uZB/GFwEKAaDsR6a52S32DZjYAUHzf3LkuiUg3tJv8DwPzi8fzgR93pjsi0i3mHl+Jm9n9wIeAqcAg8CXgIeAHwMnAK8CV7n7kh4JDvVbPXvZHY78BZs6c2TL26U9/Omx74YUXhvF169aF8dTY8u3bt7eMpcbrp+rZVUrN25+qpafmSYiO24oVK8K211xzTRjvZe4eH9hC8j2/u1/dIvTht9UjEekpur1XJFNKfpFMKflFMqXkF8mUkl8kUyNmSG9Z27ZtC+NLlixpGUstg33RRReF8VS5NTUNdDSkOFXKSw35TUmV66J4at/jxo0L4/v37w/j48ePbxlLDQHPgc78IplS8otkSskvkiklv0imlPwimVLyi2RKyS+SqWzq/Kl6dGroa1RTTtXpd+zYEcZTtfjUFNep/UdSx6XMa1etzHDkaBh0J/aduoehF46rzvwimVLyi2RKyS+SKSW/SKaU/CKZUvKLZErJL5KpbOr8qbrqgQMH2n7tNWvWhPFUnT+1zHVq3HpkGFOzl2qfknr9SOr3Tt2bEUn9m6SkphVP3ZvRC3TmF8mUkl8kU0p+kUwp+UUypeQXyZSSXyRTSn6RTGVT508pU7fdu3dv2DZVr07NT3/w4MEwHt0nULaOX2ZefoiPa2rfqfUQ+vr6wnjUt9QxzUHyzG9md5nZZjNb2fTcLWa2wcyWF1/zqu2miHTacC777wYuGeL5r7n7nOLrkc52S0Sqlkx+d38C2NqFvohIF5X5wO+zZvZc8bZgUquNzGyBmS01s6Ul9iUiHdZu8n8LOB2YA2wCvtJqQ3df6O5z3X1um/sSkQq0lfzuPujuh9z9DeA7wDmd7ZaIVK2t5DezgaYfLwdWttpWRHpTss5vZvcDHwKmmtl64EvAh8xsDuDAWuD6CvvYFWXGrafmaC87734qnrpHIZLqe5m58SGutaf6nfq9U30vc49BSi/Mu19WMvnd/eohnr6zgr6ISBfp9l6RTCn5RTKl5BfJlJJfJFNKfpFMaUhvF0yfPj2Mb9u2LYynym1R2SlVTisztXbVUn1PTbce/W5lS5gjgc78IplS8otkSskvkiklv0imlPwimVLyi2RKyS+SKdX5C1UO0Sw7TfTYsWPDeDRkuOzU21VO/Z0akptagjs1tXfUtzLLe6de+2ihM79IppT8IplS8otkSskvkiklv0imlPwimVLyi2RKdf4uSNWjU2PLU/cJRO1TtfRUvTrVt9Ty49HrR0uLp9oC7NmzJ4xHJk6c2HbbkUJnfpFMKflFMqXkF8mUkl8kU0p+kUwp+UUypeQXydRwlug+CbgHmEZjSe6F7n6HmU0Gvg/MoLFM95XuHk9An6lUrb2saMx82XHnVc77X2YugOG0j+6POOaYY8K2KbmM5z8IfN7dZwHnAp8xs1nAzcDj7j4TeLz4WUSOEsnkd/dN7v5s8Xgn8CIwHbgMWFRstgj4WFWdFJHOe1vv+c1sBvB+4BfANHffVIRepfG2QESOEsO+t9/MJgA/BD7n7jua34+5u5vZkG+CzGwBsKBsR0Wks4Z15jezMTQS/153/1Hx9KCZDRTxAWDzUG3dfaG7z3X3uZ3osIh0RjL5rXGKvxN40d2/2hR6GJhfPJ4P/Ljz3RORqgznsv8DwLXACjNbXjz3ReA24Adm9kngFeDKarp49EuVy8qqsuxUZ6kvte8ypb6+vr6wbQ6Sye/uTwGt/oU/3NnuiEi36A4/kUwp+UUypeQXyZSSXyRTSn6RTCn5RTKlqbsLdQ7RTE2PXUbZYbMpZfpe9XDjaOnyKo/50UJnfpFMKflFMqXkF8mUkl8kU0p+kUwp+UUypeQXyZTq/IWy00RHUstYVzm2PDVteNnlwas8bmVVWefPZepuERmBlPwimVLyi2RKyS+SKSW/SKaU/CKZUvKLZEp1/h5QZlw6xLX21GuXjafuI6hzXv+IxvPrzC+SLSW/SKaU/CKZUvKLZErJL5IpJb9IppT8IplK1vnN7CTgHmAa4MBCd7/DzG4B/hp4rdj0i+7+SFUdrVqV47M3btwYxt/97neH8dSY+qjWnqrDjxkzpu3XHk48Oq6p+xdGjy53G0q0b43nH95NPgeBz7v7s2b2TuAZM3usiH3N3W+vrnsiUpVk8rv7JmBT8Xinmb0ITK+6YyJSrbf1nt/MZgDvB35RPPVZM3vOzO4ys0kt2iwws6VmtrRUT0Wko4ad/GY2Afgh8Dl33wF8CzgdmEPjyuArQ7Vz94XuPtfd53agvyLSIcNKfjMbQyPx73X3HwG4+6C7H3L3N4DvAOdU100R6bRk8ltjWNadwIvu/tWm5weaNrscWNn57olIVYbzaf8HgGuBFWa2vHjui8DVZjaHRvlvLXB9JT0cASZOnBjG+/v7w3iq5DV16tSWsbJDdlOlwDJSpb5UOW7dunVhPJoS/fTTTw/bppQd6twLhvNp/1PAUIOyj9qavojoDj+RbCn5RTKl5BfJlJJfJFNKfpFMKflFMqWpuwtVLjW9bNmyMP7CCy+E8e3bt4fxMrX4VL16165dYTx1XKLjWmaoMqSXPp80acjhJgAsWbIkbJtyNNTxU3TmF8mUkl8kU0p+kUwp+UUypeQXyZSSXyRTSn6RTFk3pyA2s9eAV5qemgq83rUOvD292rde7Reob+3qZN9OcfffG86GXU3+t+zcbGmvzu3Xq33r1X6B+tauuvqmy36RTCn5RTJVd/IvrHn/kV7tW6/2C9S3dtXSt1rf84tIfeo+84tITZT8IpmqJfnN7BIz+18ze9nMbq6jD62Y2VozW2Fmy+teX7BYA3Gzma1sem6ymT1mZquL760HrXe/b7eY2Ybi2C03s3k19e0kM/tvM3vBzJ43s78tnq/12AX9quW4df09v5mNAlYBfwSsB34JXO3u8YwWXWJma4G57l77DSFm9kFgF3CPu88unvsXYKu731b84Zzk7jf1SN9uAXbVvWx7sZrUQPOy8sDHgE9Q47EL+nUlNRy3Os785wAvu/uv3H0/8D3gshr60fPc/Qlg6xFPXwYsKh4vovGfp+ta9K0nuPsmd3+2eLwTOLysfK3HLuhXLepI/ulA8zpL66nxAAzBgZ+a2TNmtqDuzgxhmrtvKh6/CkyrszNDSC7b3k1HLCvfM8euneXuO00f+L3V+e5+FvBR4DPF5W1P8sZ7tl6q1Q5r2fZuGWJZ+d+p89i1u9x9p9WR/BuAk5p+PrF4rie4+4bi+2bgQXpv6fHBwyskF98319yf3+mlZduHWlaeHjh2vbTcfR3J/0tgppmdamZjgauAh2vox1uYWX/xQQxm1g9cTO8tPf4wML94PB/4cY19eZNeWba91bLy1Hzsem65e3fv+hcwj8Yn/muAf6ijDy36dRrwP8XX83X3DbifxmXgARqfjXwSmAI8DqwGFgOTe6hv/wGsAJ6jkWgDNfXtfBqX9M8By4uveXUfu6BftRw33d4rkil94CeSKSW/SKaU/CKZUvKLZErJL5IpJb9IppT8Ipn6fyVSGbXrqlkuAAAAAElFTkSuQmCC\n", "text/plain": [ - "" + "
" ] }, - "metadata": {}, + "metadata": { + "needs_background": "light" + }, "output_type": "display_data" } ], @@ -225,7 +362,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -236,14 +373,26 @@ "torch.Size([60000])\n" ] }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/usr/local/lib/python3.7/dist-packages/torchvision/datasets/mnist.py:53: UserWarning: train_data has been renamed data\n", + " warnings.warn(\"train_data has been renamed data\")\n", + "/usr/local/lib/python3.7/dist-packages/torchvision/datasets/mnist.py:43: UserWarning: train_labels has been renamed targets\n", + " warnings.warn(\"train_labels has been renamed targets\")\n" + ] + }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ - "" + "
" ] }, - "metadata": {}, + "metadata": { + "needs_background": "light" + }, "output_type": "display_data" } ], @@ -304,46 +453,47 @@ { "data": { "text/plain": [ - "tensor([[[ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000],\n", - " [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000],\n", - " [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000],\n", + "tensor([[[0., 0., 0., ..., 0., 0., 0.],\n", + " [0., 0., 0., ..., 0., 0., 0.],\n", + " [0., 0., 0., ..., 0., 0., 0.],\n", " ...,\n", - " [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000],\n", - " [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000],\n", - " [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000]],\n", + " [0., 0., 0., ..., 0., 0., 0.],\n", + " [0., 0., 0., ..., 0., 0., 0.],\n", + " [0., 0., 0., ..., 0., 0., 0.]],\n", "\n", - " [[ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000],\n", - " [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000],\n", - " [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000],\n", + " [[0., 0., 0., ..., 0., 0., 0.],\n", + " [0., 0., 0., ..., 0., 0., 0.],\n", + " [0., 0., 0., ..., 0., 0., 0.],\n", " ...,\n", - " [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000],\n", - " [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000],\n", - " [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000]],\n", + " [0., 0., 0., ..., 0., 0., 0.],\n", + " [0., 0., 0., ..., 0., 0., 0.],\n", + " [0., 0., 0., ..., 0., 0., 0.]],\n", "\n", - " [[ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000],\n", - " [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000],\n", - " [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000],\n", + " [[0., 0., 0., ..., 0., 0., 0.],\n", + " [0., 0., 0., ..., 0., 0., 0.],\n", + " [0., 0., 0., ..., 0., 0., 0.],\n", " ...,\n", - " [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000],\n", - " [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000],\n", - " [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000]]])" + " [0., 0., 0., ..., 0., 0., 0.],\n", + " [0., 0., 0., ..., 0., 0., 0.],\n", + " [0., 0., 0., ..., 0., 0., 0.]]])" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "26427392it [00:40, 1001746.53it/s] \n", + "4423680it [00:21, 1135714.54it/s] \u001b[A" + ] } ], "source": [ "img" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { @@ -362,7 +512,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.5.2" + "version": "3.7.0" } }, "nbformat": 4, diff --git a/04-Neural-Network-For-Fashion/01-fashion-mnist.py b/04-Neural-Network-For-Fashion/01-fashion-mnist.py index 4212b08..4d802e2 100644 --- a/04-Neural-Network-For-Fashion/01-fashion-mnist.py +++ b/04-Neural-Network-For-Fashion/01-fashion-mnist.py @@ -1,4 +1,4 @@ - +#!/usr/bin/env python # coding: utf-8 # # 4.1 Fashion MNIST 데이터셋 알아보기 @@ -11,7 +11,7 @@ import numpy as np -# ## [개념] Fashion MNIST 데이터셋 설명 +# ## [개념] Fashion MNIST 데이터셋 transform = transforms.Compose([ transforms.ToTensor() diff --git a/04-Neural-Network-For-Fashion/02-neural-network.ipynb b/04-Neural-Network-For-Fashion/02-neural-network.ipynb index ea6577d..d56d079 100644 --- a/04-Neural-Network-For-Fashion/02-neural-network.ipynb +++ b/04-Neural-Network-For-Fashion/02-neural-network.ipynb @@ -38,7 +38,7 @@ "metadata": {}, "outputs": [], "source": [ - "EPOCHS = 20\n", + "EPOCHS = 30\n", "BATCH_SIZE = 64" ] }, @@ -102,7 +102,7 @@ "Fashion MNIST에서 이미지의 크기는 28 x 28, 색은 흑백으로 1 가지 입니다.\n", "그러므로 입력 x의 총 특성값 갯수는 28 x 28 x 1, 즉 784개 입니다.\n", "\n", - "우리가 사용할 모델은 3개의 레이어를 가진 뉴럴네트워크 입니다. " + "우리가 사용할 모델은 3개의 레이어를 가진 인공신경망 입니다. " ] }, { @@ -154,7 +154,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## 훈련하기" + "## 학습하기" ] }, { @@ -185,14 +185,14 @@ "source": [ "## 테스트하기\n", "\n", - "아무리 훈련이 잘 되었다고 해도 실제 데이터를 만났을때 성능이 낮다면 쓸모 없는 모델일 것입니다.\n", + "아무리 학습이 잘 되었다고 해도 실제 데이터를 만났을때 성능이 낮다면 쓸모 없는 모델일 것입니다.\n", "우리가 진정 원하는 것은 훈련 데이터에 최적화한 모델이 아니라 모든 데이터에서 높은 성능을 보이는 모델이기 때문입니다.\n", "세상에 존재하는 모든 데이터에 최적화 하는 것을 \"일반화\"라고 부르고\n", "모델이 얼마나 실제 데이터에 적응하는지를 수치로 나타낸 것을 \"일반화 오류\"(Generalization Error) 라고 합니다. \n", "\n", "우리가 만든 모델이 얼마나 일반화를 잘 하는지 알아보기 위해,\n", "그리고 언제 훈련을 멈추어야 할지 알기 위해\n", - "매 이포크가 끝날때 마다 테스트셋으로 모델의 성능을 측정해보겠습니다." + "매 이폭이 끝날때 마다 테스트셋으로 모델의 성능을 측정해보겠습니다." ] }, { @@ -241,126 +241,206 @@ "name": "stdout", "output_type": "stream", "text": [ - "Train Epoch: 1 [0/60000 (0%)]\tLoss: 2.304437\n", - "Train Epoch: 1 [12800/60000 (21%)]\tLoss: 2.227375\n", - "Train Epoch: 1 [25600/60000 (43%)]\tLoss: 1.982917\n", - "Train Epoch: 1 [38400/60000 (64%)]\tLoss: 1.475527\n", - "Train Epoch: 1 [51200/60000 (85%)]\tLoss: 0.894861\n", - "[1] Test Loss: 0.7465, Accuracy: 82.58%\n", - "Train Epoch: 2 [0/60000 (0%)]\tLoss: 0.903040\n", - "Train Epoch: 2 [12800/60000 (21%)]\tLoss: 0.483391\n", - "Train Epoch: 2 [25600/60000 (43%)]\tLoss: 0.611188\n", - "Train Epoch: 2 [38400/60000 (64%)]\tLoss: 0.474139\n", - "Train Epoch: 2 [51200/60000 (85%)]\tLoss: 0.361451\n", - "[2] Test Loss: 0.4155, Accuracy: 88.34%\n", - "Train Epoch: 3 [0/60000 (0%)]\tLoss: 0.729169\n", - "Train Epoch: 3 [12800/60000 (21%)]\tLoss: 0.431383\n", - "Train Epoch: 3 [25600/60000 (43%)]\tLoss: 0.367140\n", - "Train Epoch: 3 [38400/60000 (64%)]\tLoss: 0.289560\n", - "Train Epoch: 3 [51200/60000 (85%)]\tLoss: 0.540734\n", - "[3] Test Loss: 0.3473, Accuracy: 90.00%\n", - "Train Epoch: 4 [0/60000 (0%)]\tLoss: 0.422233\n", - "Train Epoch: 4 [12800/60000 (21%)]\tLoss: 0.222322\n", - "Train Epoch: 4 [25600/60000 (43%)]\tLoss: 0.298619\n", - "Train Epoch: 4 [38400/60000 (64%)]\tLoss: 0.433186\n", - "Train Epoch: 4 [51200/60000 (85%)]\tLoss: 0.589228\n", - "[4] Test Loss: 0.3108, Accuracy: 91.10%\n", - "Train Epoch: 5 [0/60000 (0%)]\tLoss: 0.422999\n", - "Train Epoch: 5 [12800/60000 (21%)]\tLoss: 0.266815\n", - "Train Epoch: 5 [25600/60000 (43%)]\tLoss: 0.311641\n", - "Train Epoch: 5 [38400/60000 (64%)]\tLoss: 0.300602\n", - "Train Epoch: 5 [51200/60000 (85%)]\tLoss: 0.300490\n", - "[5] Test Loss: 0.2894, Accuracy: 91.78%\n", - "Train Epoch: 6 [0/60000 (0%)]\tLoss: 0.235929\n", - "Train Epoch: 6 [12800/60000 (21%)]\tLoss: 0.222314\n", - "Train Epoch: 6 [25600/60000 (43%)]\tLoss: 0.197358\n", - "Train Epoch: 6 [38400/60000 (64%)]\tLoss: 0.244315\n", - "Train Epoch: 6 [51200/60000 (85%)]\tLoss: 0.306562\n", - "[6] Test Loss: 0.2698, Accuracy: 92.14%\n", - "Train Epoch: 7 [0/60000 (0%)]\tLoss: 0.200137\n", - "Train Epoch: 7 [12800/60000 (21%)]\tLoss: 0.258287\n", - "Train Epoch: 7 [25600/60000 (43%)]\tLoss: 0.266378\n", - "Train Epoch: 7 [38400/60000 (64%)]\tLoss: 0.203062\n", - "Train Epoch: 7 [51200/60000 (85%)]\tLoss: 0.150239\n", - "[7] Test Loss: 0.2533, Accuracy: 92.82%\n", - "Train Epoch: 8 [0/60000 (0%)]\tLoss: 0.243388\n", - "Train Epoch: 8 [12800/60000 (21%)]\tLoss: 0.279029\n", - "Train Epoch: 8 [25600/60000 (43%)]\tLoss: 0.365382\n", - "Train Epoch: 8 [38400/60000 (64%)]\tLoss: 0.225877\n", - "Train Epoch: 8 [51200/60000 (85%)]\tLoss: 0.141756\n", - "[8] Test Loss: 0.2380, Accuracy: 93.15%\n", - "Train Epoch: 9 [0/60000 (0%)]\tLoss: 0.149094\n", - "Train Epoch: 9 [12800/60000 (21%)]\tLoss: 0.262485\n", - "Train Epoch: 9 [25600/60000 (43%)]\tLoss: 0.260431\n", - "Train Epoch: 9 [38400/60000 (64%)]\tLoss: 0.195173\n", - "Train Epoch: 9 [51200/60000 (85%)]\tLoss: 0.263135\n", - "[9] Test Loss: 0.2278, Accuracy: 93.50%\n", - "Train Epoch: 10 [0/60000 (0%)]\tLoss: 0.177255\n", - "Train Epoch: 10 [12800/60000 (21%)]\tLoss: 0.201979\n", - "Train Epoch: 10 [25600/60000 (43%)]\tLoss: 0.116959\n", - "Train Epoch: 10 [38400/60000 (64%)]\tLoss: 0.330033\n", - "Train Epoch: 10 [51200/60000 (85%)]\tLoss: 0.353522\n", - "[10] Test Loss: 0.2148, Accuracy: 93.91%\n", - "Train Epoch: 11 [0/60000 (0%)]\tLoss: 0.261565\n", - "Train Epoch: 11 [12800/60000 (21%)]\tLoss: 0.161238\n", - "Train Epoch: 11 [25600/60000 (43%)]\tLoss: 0.263850\n", - "Train Epoch: 11 [38400/60000 (64%)]\tLoss: 0.143608\n", - "Train Epoch: 11 [51200/60000 (85%)]\tLoss: 0.135988\n", - "[11] Test Loss: 0.2013, Accuracy: 94.01%\n", - "Train Epoch: 12 [0/60000 (0%)]\tLoss: 0.224298\n", - "Train Epoch: 12 [12800/60000 (21%)]\tLoss: 0.137181\n", - "Train Epoch: 12 [25600/60000 (43%)]\tLoss: 0.247515\n", - "Train Epoch: 12 [38400/60000 (64%)]\tLoss: 0.341607\n", - "Train Epoch: 12 [51200/60000 (85%)]\tLoss: 0.328232\n", - "[12] Test Loss: 0.1906, Accuracy: 94.32%\n", - "Train Epoch: 13 [0/60000 (0%)]\tLoss: 0.134893\n", - "Train Epoch: 13 [12800/60000 (21%)]\tLoss: 0.134292\n", - "Train Epoch: 13 [25600/60000 (43%)]\tLoss: 0.232267\n", - "Train Epoch: 13 [38400/60000 (64%)]\tLoss: 0.383422\n", - "Train Epoch: 13 [51200/60000 (85%)]\tLoss: 0.145729\n", - "[13] Test Loss: 0.1871, Accuracy: 94.57%\n", - "Train Epoch: 14 [0/60000 (0%)]\tLoss: 0.209971\n", - "Train Epoch: 14 [12800/60000 (21%)]\tLoss: 0.131140\n", - "Train Epoch: 14 [25600/60000 (43%)]\tLoss: 0.218684\n", - "Train Epoch: 14 [38400/60000 (64%)]\tLoss: 0.186877\n", - "Train Epoch: 14 [51200/60000 (85%)]\tLoss: 0.128308\n", - "[14] Test Loss: 0.1756, Accuracy: 94.86%\n", - "Train Epoch: 15 [0/60000 (0%)]\tLoss: 0.223753\n", - "Train Epoch: 15 [12800/60000 (21%)]\tLoss: 0.144715\n", - "Train Epoch: 15 [25600/60000 (43%)]\tLoss: 0.211008\n", - "Train Epoch: 15 [38400/60000 (64%)]\tLoss: 0.247066\n", - "Train Epoch: 15 [51200/60000 (85%)]\tLoss: 0.155979\n", - "[15] Test Loss: 0.1672, Accuracy: 95.05%\n", - "Train Epoch: 16 [0/60000 (0%)]\tLoss: 0.271273\n", - "Train Epoch: 16 [12800/60000 (21%)]\tLoss: 0.143092\n", - "Train Epoch: 16 [25600/60000 (43%)]\tLoss: 0.148368\n", - "Train Epoch: 16 [38400/60000 (64%)]\tLoss: 0.260529\n", - "Train Epoch: 16 [51200/60000 (85%)]\tLoss: 0.118180\n", - "[16] Test Loss: 0.1595, Accuracy: 95.22%\n", - "Train Epoch: 17 [0/60000 (0%)]\tLoss: 0.166883\n", - "Train Epoch: 17 [12800/60000 (21%)]\tLoss: 0.141381\n", - "Train Epoch: 17 [25600/60000 (43%)]\tLoss: 0.224895\n", - "Train Epoch: 17 [38400/60000 (64%)]\tLoss: 0.107485\n", - "Train Epoch: 17 [51200/60000 (85%)]\tLoss: 0.049170\n", - "[17] Test Loss: 0.1539, Accuracy: 95.39%\n", - "Train Epoch: 18 [0/60000 (0%)]\tLoss: 0.111888\n", - "Train Epoch: 18 [12800/60000 (21%)]\tLoss: 0.163464\n", - "Train Epoch: 18 [25600/60000 (43%)]\tLoss: 0.269391\n", - "Train Epoch: 18 [38400/60000 (64%)]\tLoss: 0.056480\n", - "Train Epoch: 18 [51200/60000 (85%)]\tLoss: 0.079581\n", - "[18] Test Loss: 0.1469, Accuracy: 95.60%\n", - "Train Epoch: 19 [0/60000 (0%)]\tLoss: 0.242008\n", - "Train Epoch: 19 [12800/60000 (21%)]\tLoss: 0.196076\n", - "Train Epoch: 19 [25600/60000 (43%)]\tLoss: 0.092570\n", - "Train Epoch: 19 [38400/60000 (64%)]\tLoss: 0.175782\n", - "Train Epoch: 19 [51200/60000 (85%)]\tLoss: 0.089211\n", - "[19] Test Loss: 0.1406, Accuracy: 95.86%\n", - "Train Epoch: 20 [0/60000 (0%)]\tLoss: 0.078100\n", - "Train Epoch: 20 [12800/60000 (21%)]\tLoss: 0.140952\n", - "Train Epoch: 20 [25600/60000 (43%)]\tLoss: 0.093126\n", - "Train Epoch: 20 [38400/60000 (64%)]\tLoss: 0.123385\n", - "Train Epoch: 20 [51200/60000 (85%)]\tLoss: 0.080617\n", - "[20] Test Loss: 0.1364, Accuracy: 95.84%\n" + "Train Epoch: 1 [0/60000 (0%)]\tLoss: 2.295678\n", + "Train Epoch: 1 [12800/60000 (21%)]\tLoss: 2.012621\n", + "Train Epoch: 1 [25600/60000 (43%)]\tLoss: 1.427331\n", + "Train Epoch: 1 [38400/60000 (64%)]\tLoss: 1.079668\n", + "Train Epoch: 1 [51200/60000 (85%)]\tLoss: 0.903088\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/usr/local/lib/python3.7/dist-packages/torch/nn/_reduction.py:49: UserWarning: size_average and reduce args will be deprecated, please use reduction='sum' instead.\n", + " warnings.warn(warning.format(ret))\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1] Test Loss: 0.8554, Accuracy: 65.62%\n", + "Train Epoch: 2 [0/60000 (0%)]\tLoss: 0.795721\n", + "Train Epoch: 2 [12800/60000 (21%)]\tLoss: 0.756177\n", + "Train Epoch: 2 [25600/60000 (43%)]\tLoss: 0.798905\n", + "Train Epoch: 2 [38400/60000 (64%)]\tLoss: 0.778698\n", + "Train Epoch: 2 [51200/60000 (85%)]\tLoss: 0.541811\n", + "[2] Test Loss: 0.6664, Accuracy: 76.38%\n", + "Train Epoch: 3 [0/60000 (0%)]\tLoss: 0.675293\n", + "Train Epoch: 3 [12800/60000 (21%)]\tLoss: 0.877079\n", + "Train Epoch: 3 [25600/60000 (43%)]\tLoss: 0.502244\n", + "Train Epoch: 3 [38400/60000 (64%)]\tLoss: 0.597138\n", + "Train Epoch: 3 [51200/60000 (85%)]\tLoss: 0.703515\n", + "[3] Test Loss: 0.6540, Accuracy: 74.22%\n", + "Train Epoch: 4 [0/60000 (0%)]\tLoss: 0.558876\n", + "Train Epoch: 4 [12800/60000 (21%)]\tLoss: 0.625799\n", + "Train Epoch: 4 [25600/60000 (43%)]\tLoss: 0.730325\n", + "Train Epoch: 4 [38400/60000 (64%)]\tLoss: 0.532876\n", + "Train Epoch: 4 [51200/60000 (85%)]\tLoss: 0.573380\n", + "[4] Test Loss: 0.5896, Accuracy: 78.44%\n", + "Train Epoch: 5 [0/60000 (0%)]\tLoss: 0.855548\n", + "Train Epoch: 5 [12800/60000 (21%)]\tLoss: 0.603643\n", + "Train Epoch: 5 [25600/60000 (43%)]\tLoss: 0.432968\n", + "Train Epoch: 5 [38400/60000 (64%)]\tLoss: 0.507090\n", + "Train Epoch: 5 [51200/60000 (85%)]\tLoss: 0.269888\n", + "[5] Test Loss: 0.5108, Accuracy: 81.80%\n", + "Train Epoch: 6 [0/60000 (0%)]\tLoss: 0.358449\n", + "Train Epoch: 6 [12800/60000 (21%)]\tLoss: 0.636116\n", + "Train Epoch: 6 [25600/60000 (43%)]\tLoss: 0.549211\n", + "Train Epoch: 6 [38400/60000 (64%)]\tLoss: 0.654910\n", + "Train Epoch: 6 [51200/60000 (85%)]\tLoss: 0.473879\n", + "[6] Test Loss: 0.4948, Accuracy: 82.39%\n", + "Train Epoch: 7 [0/60000 (0%)]\tLoss: 0.490334\n", + "Train Epoch: 7 [12800/60000 (21%)]\tLoss: 0.461894\n", + "Train Epoch: 7 [25600/60000 (43%)]\tLoss: 0.275516\n", + "Train Epoch: 7 [38400/60000 (64%)]\tLoss: 0.351789\n", + "Train Epoch: 7 [51200/60000 (85%)]\tLoss: 0.492854\n", + "[7] Test Loss: 0.4815, Accuracy: 82.83%\n", + "Train Epoch: 8 [0/60000 (0%)]\tLoss: 0.583296\n", + "Train Epoch: 8 [12800/60000 (21%)]\tLoss: 0.421291\n", + "Train Epoch: 8 [25600/60000 (43%)]\tLoss: 0.522762\n", + "Train Epoch: 8 [38400/60000 (64%)]\tLoss: 0.484937\n", + "Train Epoch: 8 [51200/60000 (85%)]\tLoss: 0.262815\n", + "[8] Test Loss: 0.5049, Accuracy: 81.47%\n", + "Train Epoch: 9 [0/60000 (0%)]\tLoss: 0.541337\n", + "Train Epoch: 9 [12800/60000 (21%)]\tLoss: 0.337907\n", + "Train Epoch: 9 [25600/60000 (43%)]\tLoss: 0.565217\n", + "Train Epoch: 9 [38400/60000 (64%)]\tLoss: 0.652757\n", + "Train Epoch: 9 [51200/60000 (85%)]\tLoss: 0.558290\n", + "[9] Test Loss: 0.4628, Accuracy: 83.42%\n", + "Train Epoch: 10 [0/60000 (0%)]\tLoss: 0.372100\n", + "Train Epoch: 10 [12800/60000 (21%)]\tLoss: 0.589613\n", + "Train Epoch: 10 [25600/60000 (43%)]\tLoss: 0.431193\n", + "Train Epoch: 10 [38400/60000 (64%)]\tLoss: 0.419246\n", + "Train Epoch: 10 [51200/60000 (85%)]\tLoss: 0.577897\n", + "[10] Test Loss: 0.4628, Accuracy: 83.29%\n", + "Train Epoch: 11 [0/60000 (0%)]\tLoss: 0.363149\n", + "Train Epoch: 11 [12800/60000 (21%)]\tLoss: 0.398224\n", + "Train Epoch: 11 [25600/60000 (43%)]\tLoss: 0.295631\n", + "Train Epoch: 11 [38400/60000 (64%)]\tLoss: 0.369558\n", + "Train Epoch: 11 [51200/60000 (85%)]\tLoss: 0.392698\n", + "[11] Test Loss: 0.4555, Accuracy: 83.88%\n", + "Train Epoch: 12 [0/60000 (0%)]\tLoss: 0.284362\n", + "Train Epoch: 12 [12800/60000 (21%)]\tLoss: 0.555017\n", + "Train Epoch: 12 [25600/60000 (43%)]\tLoss: 0.333733\n", + "Train Epoch: 12 [38400/60000 (64%)]\tLoss: 0.694656\n", + "Train Epoch: 12 [51200/60000 (85%)]\tLoss: 0.520645\n", + "[12] Test Loss: 0.4473, Accuracy: 84.04%\n", + "Train Epoch: 13 [0/60000 (0%)]\tLoss: 0.345179\n", + "Train Epoch: 13 [12800/60000 (21%)]\tLoss: 0.618456\n", + "Train Epoch: 13 [25600/60000 (43%)]\tLoss: 0.379806\n", + "Train Epoch: 13 [38400/60000 (64%)]\tLoss: 0.542192\n", + "Train Epoch: 13 [51200/60000 (85%)]\tLoss: 0.397154\n", + "[13] Test Loss: 0.4513, Accuracy: 84.06%\n", + "Train Epoch: 14 [0/60000 (0%)]\tLoss: 0.556780\n", + "Train Epoch: 14 [12800/60000 (21%)]\tLoss: 0.412385\n", + "Train Epoch: 14 [25600/60000 (43%)]\tLoss: 0.414974\n", + "Train Epoch: 14 [38400/60000 (64%)]\tLoss: 0.311251\n", + "Train Epoch: 14 [51200/60000 (85%)]\tLoss: 0.337588\n", + "[14] Test Loss: 0.4407, Accuracy: 84.12%\n", + "Train Epoch: 15 [0/60000 (0%)]\tLoss: 0.410217\n", + "Train Epoch: 15 [12800/60000 (21%)]\tLoss: 0.410661\n", + "Train Epoch: 15 [25600/60000 (43%)]\tLoss: 0.288363\n", + "Train Epoch: 15 [38400/60000 (64%)]\tLoss: 0.298956\n", + "Train Epoch: 15 [51200/60000 (85%)]\tLoss: 0.341082\n", + "[15] Test Loss: 0.4473, Accuracy: 83.60%\n", + "Train Epoch: 16 [0/60000 (0%)]\tLoss: 0.530110\n", + "Train Epoch: 16 [12800/60000 (21%)]\tLoss: 0.288140\n", + "Train Epoch: 16 [25600/60000 (43%)]\tLoss: 0.295319\n", + "Train Epoch: 16 [38400/60000 (64%)]\tLoss: 0.330036\n", + "Train Epoch: 16 [51200/60000 (85%)]\tLoss: 0.390008\n", + "[16] Test Loss: 0.4185, Accuracy: 85.34%\n", + "Train Epoch: 17 [0/60000 (0%)]\tLoss: 0.552437\n", + "Train Epoch: 17 [12800/60000 (21%)]\tLoss: 0.451838\n", + "Train Epoch: 17 [25600/60000 (43%)]\tLoss: 0.623034\n", + "Train Epoch: 17 [38400/60000 (64%)]\tLoss: 0.476145\n", + "Train Epoch: 17 [51200/60000 (85%)]\tLoss: 0.338539\n", + "[17] Test Loss: 0.4112, Accuracy: 85.56%\n", + "Train Epoch: 18 [0/60000 (0%)]\tLoss: 0.226492\n", + "Train Epoch: 18 [12800/60000 (21%)]\tLoss: 0.271587\n", + "Train Epoch: 18 [25600/60000 (43%)]\tLoss: 0.611406\n", + "Train Epoch: 18 [38400/60000 (64%)]\tLoss: 0.194023\n", + "Train Epoch: 18 [51200/60000 (85%)]\tLoss: 0.265219\n", + "[18] Test Loss: 0.4203, Accuracy: 84.96%\n", + "Train Epoch: 19 [0/60000 (0%)]\tLoss: 0.352621\n", + "Train Epoch: 19 [12800/60000 (21%)]\tLoss: 0.287578\n", + "Train Epoch: 19 [25600/60000 (43%)]\tLoss: 0.221895\n", + "Train Epoch: 19 [38400/60000 (64%)]\tLoss: 0.358851\n", + "Train Epoch: 19 [51200/60000 (85%)]\tLoss: 0.268449\n", + "[19] Test Loss: 0.4321, Accuracy: 84.43%\n", + "Train Epoch: 20 [0/60000 (0%)]\tLoss: 0.630308\n", + "Train Epoch: 20 [12800/60000 (21%)]\tLoss: 0.274588\n", + "Train Epoch: 20 [25600/60000 (43%)]\tLoss: 0.320827\n", + "Train Epoch: 20 [38400/60000 (64%)]\tLoss: 0.387005\n", + "Train Epoch: 20 [51200/60000 (85%)]\tLoss: 0.386735\n", + "[20] Test Loss: 0.4134, Accuracy: 85.65%\n", + "Train Epoch: 21 [0/60000 (0%)]\tLoss: 0.532505\n", + "Train Epoch: 21 [12800/60000 (21%)]\tLoss: 0.266865\n", + "Train Epoch: 21 [25600/60000 (43%)]\tLoss: 0.253141\n", + "Train Epoch: 21 [38400/60000 (64%)]\tLoss: 0.436258\n", + "Train Epoch: 21 [51200/60000 (85%)]\tLoss: 0.386696\n", + "[21] Test Loss: 0.4222, Accuracy: 85.25%\n", + "Train Epoch: 22 [0/60000 (0%)]\tLoss: 0.446268\n", + "Train Epoch: 22 [12800/60000 (21%)]\tLoss: 0.478635\n", + "Train Epoch: 22 [25600/60000 (43%)]\tLoss: 0.260758\n", + "Train Epoch: 22 [38400/60000 (64%)]\tLoss: 0.292733\n", + "Train Epoch: 22 [51200/60000 (85%)]\tLoss: 0.128355\n", + "[22] Test Loss: 0.4066, Accuracy: 85.36%\n", + "Train Epoch: 23 [0/60000 (0%)]\tLoss: 0.245655\n", + "Train Epoch: 23 [12800/60000 (21%)]\tLoss: 0.556162\n", + "Train Epoch: 23 [25600/60000 (43%)]\tLoss: 0.511000\n", + "Train Epoch: 23 [38400/60000 (64%)]\tLoss: 0.347455\n", + "Train Epoch: 23 [51200/60000 (85%)]\tLoss: 0.443265\n", + "[23] Test Loss: 0.4019, Accuracy: 85.67%\n", + "Train Epoch: 24 [0/60000 (0%)]\tLoss: 0.195539\n", + "Train Epoch: 24 [12800/60000 (21%)]\tLoss: 0.258744\n", + "Train Epoch: 24 [25600/60000 (43%)]\tLoss: 0.308132\n", + "Train Epoch: 24 [38400/60000 (64%)]\tLoss: 0.370229\n", + "Train Epoch: 24 [51200/60000 (85%)]\tLoss: 0.198481\n", + "[24] Test Loss: 0.4009, Accuracy: 85.47%\n", + "Train Epoch: 25 [0/60000 (0%)]\tLoss: 0.493723\n", + "Train Epoch: 25 [12800/60000 (21%)]\tLoss: 0.509313\n", + "Train Epoch: 25 [25600/60000 (43%)]\tLoss: 0.367207\n", + "Train Epoch: 25 [38400/60000 (64%)]\tLoss: 0.371157\n", + "Train Epoch: 25 [51200/60000 (85%)]\tLoss: 0.312315\n", + "[25] Test Loss: 0.3871, Accuracy: 86.23%\n", + "Train Epoch: 26 [0/60000 (0%)]\tLoss: 0.480394\n", + "Train Epoch: 26 [12800/60000 (21%)]\tLoss: 0.216126\n", + "Train Epoch: 26 [25600/60000 (43%)]\tLoss: 0.305258\n", + "Train Epoch: 26 [38400/60000 (64%)]\tLoss: 0.312277\n", + "Train Epoch: 26 [51200/60000 (85%)]\tLoss: 0.292373\n", + "[26] Test Loss: 0.3834, Accuracy: 86.42%\n", + "Train Epoch: 27 [0/60000 (0%)]\tLoss: 0.299468\n", + "Train Epoch: 27 [12800/60000 (21%)]\tLoss: 0.268669\n", + "Train Epoch: 27 [25600/60000 (43%)]\tLoss: 0.283542\n", + "Train Epoch: 27 [38400/60000 (64%)]\tLoss: 0.349247\n", + "Train Epoch: 27 [51200/60000 (85%)]\tLoss: 0.464154\n", + "[27] Test Loss: 0.4025, Accuracy: 85.46%\n", + "Train Epoch: 28 [0/60000 (0%)]\tLoss: 0.264294\n", + "Train Epoch: 28 [12800/60000 (21%)]\tLoss: 0.309099\n", + "Train Epoch: 28 [25600/60000 (43%)]\tLoss: 0.289983\n", + "Train Epoch: 28 [38400/60000 (64%)]\tLoss: 0.362376\n", + "Train Epoch: 28 [51200/60000 (85%)]\tLoss: 0.289647\n", + "[28] Test Loss: 0.3897, Accuracy: 86.16%\n", + "Train Epoch: 29 [0/60000 (0%)]\tLoss: 0.398753\n", + "Train Epoch: 29 [12800/60000 (21%)]\tLoss: 0.333156\n", + "Train Epoch: 29 [25600/60000 (43%)]\tLoss: 0.305585\n", + "Train Epoch: 29 [38400/60000 (64%)]\tLoss: 0.226775\n", + "Train Epoch: 29 [51200/60000 (85%)]\tLoss: 0.305694\n", + "[29] Test Loss: 0.3750, Accuracy: 86.65%\n", + "Train Epoch: 30 [0/60000 (0%)]\tLoss: 0.266521\n", + "Train Epoch: 30 [12800/60000 (21%)]\tLoss: 0.360401\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Epoch: 30 [25600/60000 (43%)]\tLoss: 0.394718\n", + "Train Epoch: 30 [38400/60000 (64%)]\tLoss: 0.394041\n", + "Train Epoch: 30 [51200/60000 (85%)]\tLoss: 0.247221\n", + "[30] Test Loss: 0.3720, Accuracy: 86.86%\n" ] } ], @@ -372,20 +452,6 @@ " print('[{}] Test Loss: {:.4f}, Accuracy: {:.2f}%'.format(\n", " epoch, test_loss, test_accuracy))" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { @@ -404,7 +470,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.5.2" + "version": "3.7.0" } }, "nbformat": 4, diff --git a/04-Neural-Network-For-Fashion/02-neural-network.py b/04-Neural-Network-For-Fashion/02-neural-network.py index 49a71fe..38606e2 100644 --- a/04-Neural-Network-For-Fashion/02-neural-network.py +++ b/04-Neural-Network-For-Fashion/02-neural-network.py @@ -1,4 +1,4 @@ - +#!/usr/bin/env python # coding: utf-8 # # 4.2 뉴럴넷으로 패션 아이템 구분하기 @@ -16,7 +16,7 @@ DEVICE = torch.device("cuda" if USE_CUDA else "cpu") -EPOCHS = 20 +EPOCHS = 30 BATCH_SIZE = 64 @@ -57,7 +57,7 @@ # `x.size()`를 해보면 `[64, 1, 28, 28]`이라고 표시되는 것을 보실 수 있습니다. # Fashion MNIST에서 이미지의 크기는 28 x 28, 색은 흑백으로 1 가지 입니다. # 그러므로 입력 x의 총 특성값 갯수는 28 x 28 x 1, 즉 784개 입니다. -# 우리가 사용할 모델은 3개의 레이어를 가진 뉴럴네트워크 입니다. +# 우리가 사용할 모델은 3개의 레이어를 가진 인공신경망 입니다. class Net(nn.Module): def __init__(self): @@ -85,7 +85,7 @@ def forward(self, x): optimizer = optim.SGD(model.parameters(), lr=0.01) -# ## 훈련하기 +# ## 학습하기 def train(model, train_loader, optimizer, epoch): model.train() @@ -104,13 +104,13 @@ def train(model, train_loader, optimizer, epoch): # ## 테스트하기 -# 아무리 훈련이 잘 되었다고 해도 실제 데이터를 만났을때 성능이 낮다면 쓸모 없는 모델일 것입니다. +# 아무리 학습이 잘 되었다고 해도 실제 데이터를 만났을때 성능이 낮다면 쓸모 없는 모델일 것입니다. # 우리가 진정 원하는 것은 훈련 데이터에 최적화한 모델이 아니라 모든 데이터에서 높은 성능을 보이는 모델이기 때문입니다. # 세상에 존재하는 모든 데이터에 최적화 하는 것을 "일반화"라고 부르고 # 모델이 얼마나 실제 데이터에 적응하는지를 수치로 나타낸 것을 "일반화 오류"(Generalization Error) 라고 합니다. # 우리가 만든 모델이 얼마나 일반화를 잘 하는지 알아보기 위해, # 그리고 언제 훈련을 멈추어야 할지 알기 위해 -# 매 이포크가 끝날때 마다 테스트셋으로 모델의 성능을 측정해보겠습니다. +# 매 이폭이 끝날때 마다 테스트셋으로 모델의 성능을 측정해보겠습니다. def test(model, test_loader): model.eval() diff --git a/04-Neural-Network-For-Fashion/03-overfitting-and-regularization.ipynb b/04-Neural-Network-For-Fashion/03-overfitting-and-regularization.ipynb index 2031b5b..138c119 100644 --- a/04-Neural-Network-For-Fashion/03-overfitting-and-regularization.ipynb +++ b/04-Neural-Network-For-Fashion/03-overfitting-and-regularization.ipynb @@ -149,7 +149,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## 훈련하기" + "## 학습하기" ] }, { @@ -160,7 +160,7 @@ "source": [ "def train(model, train_loader, optimizer):\n", " model.train()\n", - " for data, target in enumerate(train_loader):\n", + " for batch_idx, (data, target) in enumerate(train_loader):\n", " data, target = data.to(DEVICE), target.to(DEVICE)\n", " optimizer.zero_grad()\n", " output = model(data)\n", @@ -225,60 +225,68 @@ "execution_count": 9, "metadata": {}, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/usr/local/lib/python3.7/dist-packages/torch/nn/_reduction.py:49: UserWarning: size_average and reduce args will be deprecated, please use reduction='sum' instead.\n", + " warnings.warn(warning.format(ret))\n" + ] + }, { "name": "stdout", "output_type": "stream", "text": [ - "[1] Test Loss: 0.5499, Accuracy: 82.68%\n", - "[2] Test Loss: 0.4284, Accuracy: 86.36%\n", - "[3] Test Loss: 0.3552, Accuracy: 89.13%\n", - "[4] Test Loss: 0.3043, Accuracy: 90.44%\n", - "[5] Test Loss: 0.2602, Accuracy: 92.07%\n", - "[6] Test Loss: 0.2321, Accuracy: 92.82%\n", - "[7] Test Loss: 0.2132, Accuracy: 93.41%\n", - "[8] Test Loss: 0.1986, Accuracy: 93.85%\n", - "[9] Test Loss: 0.1824, Accuracy: 94.46%\n", - "[10] Test Loss: 0.1756, Accuracy: 94.56%\n", - "[11] Test Loss: 0.1665, Accuracy: 94.87%\n", - "[12] Test Loss: 0.1555, Accuracy: 95.30%\n", - "[13] Test Loss: 0.1524, Accuracy: 95.39%\n", - "[14] Test Loss: 0.1451, Accuracy: 95.58%\n", - "[15] Test Loss: 0.1399, Accuracy: 95.78%\n", - "[16] Test Loss: 0.1370, Accuracy: 95.82%\n", - "[17] Test Loss: 0.1339, Accuracy: 95.93%\n", - "[18] Test Loss: 0.1289, Accuracy: 96.08%\n", - "[19] Test Loss: 0.1239, Accuracy: 96.17%\n", - "[20] Test Loss: 0.1194, Accuracy: 96.27%\n", - "[21] Test Loss: 0.1153, Accuracy: 96.47%\n", - "[22] Test Loss: 0.1183, Accuracy: 96.20%\n", - "[23] Test Loss: 0.1140, Accuracy: 96.49%\n", - "[24] Test Loss: 0.1101, Accuracy: 96.60%\n", - "[25] Test Loss: 0.1088, Accuracy: 96.62%\n", - "[26] Test Loss: 0.1064, Accuracy: 96.66%\n", - "[27] Test Loss: 0.1028, Accuracy: 96.82%\n", - "[28] Test Loss: 0.1022, Accuracy: 96.90%\n", - "[29] Test Loss: 0.1018, Accuracy: 96.70%\n", - "[30] Test Loss: 0.1002, Accuracy: 96.88%\n", - "[31] Test Loss: 0.0984, Accuracy: 96.89%\n", - "[32] Test Loss: 0.0966, Accuracy: 97.04%\n", - "[33] Test Loss: 0.0979, Accuracy: 96.85%\n", - "[34] Test Loss: 0.0954, Accuracy: 97.04%\n", - "[35] Test Loss: 0.0963, Accuracy: 97.00%\n", - "[36] Test Loss: 0.0944, Accuracy: 97.06%\n", - "[37] Test Loss: 0.0925, Accuracy: 97.00%\n", - "[38] Test Loss: 0.0933, Accuracy: 97.13%\n", - "[39] Test Loss: 0.0918, Accuracy: 97.12%\n", - "[40] Test Loss: 0.0898, Accuracy: 97.16%\n", - "[41] Test Loss: 0.0888, Accuracy: 97.24%\n", - "[42] Test Loss: 0.0884, Accuracy: 97.16%\n", - "[43] Test Loss: 0.0889, Accuracy: 97.34%\n", - "[44] Test Loss: 0.0876, Accuracy: 97.22%\n", - "[45] Test Loss: 0.0860, Accuracy: 97.35%\n", - "[46] Test Loss: 0.0852, Accuracy: 97.37%\n", - "[47] Test Loss: 0.0860, Accuracy: 97.29%\n", - "[48] Test Loss: 0.0866, Accuracy: 97.32%\n", - "[49] Test Loss: 0.0851, Accuracy: 97.36%\n", - "[50] Test Loss: 0.0837, Accuracy: 97.26%\n" + "[1] Test Loss: 0.5479, Accuracy: 82.90%\n", + "[2] Test Loss: 0.4317, Accuracy: 86.49%\n", + "[3] Test Loss: 0.3601, Accuracy: 88.81%\n", + "[4] Test Loss: 0.3016, Accuracy: 90.57%\n", + "[5] Test Loss: 0.2617, Accuracy: 91.97%\n", + "[6] Test Loss: 0.2281, Accuracy: 93.02%\n", + "[7] Test Loss: 0.2089, Accuracy: 93.50%\n", + "[8] Test Loss: 0.1939, Accuracy: 94.03%\n", + "[9] Test Loss: 0.1802, Accuracy: 94.54%\n", + "[10] Test Loss: 0.1753, Accuracy: 94.60%\n", + "[11] Test Loss: 0.1654, Accuracy: 94.92%\n", + "[12] Test Loss: 0.1554, Accuracy: 95.38%\n", + "[13] Test Loss: 0.1513, Accuracy: 95.46%\n", + "[14] Test Loss: 0.1456, Accuracy: 95.42%\n", + "[15] Test Loss: 0.1405, Accuracy: 95.61%\n", + "[16] Test Loss: 0.1376, Accuracy: 95.72%\n", + "[17] Test Loss: 0.1331, Accuracy: 95.82%\n", + "[18] Test Loss: 0.1265, Accuracy: 95.93%\n", + "[19] Test Loss: 0.1234, Accuracy: 96.12%\n", + "[20] Test Loss: 0.1225, Accuracy: 96.16%\n", + "[21] Test Loss: 0.1179, Accuracy: 96.28%\n", + "[22] Test Loss: 0.1143, Accuracy: 96.25%\n", + "[23] Test Loss: 0.1147, Accuracy: 96.25%\n", + "[24] Test Loss: 0.1122, Accuracy: 96.48%\n", + "[25] Test Loss: 0.1084, Accuracy: 96.59%\n", + "[26] Test Loss: 0.1098, Accuracy: 96.59%\n", + "[27] Test Loss: 0.1083, Accuracy: 96.65%\n", + "[28] Test Loss: 0.1052, Accuracy: 96.58%\n", + "[29] Test Loss: 0.1011, Accuracy: 96.79%\n", + "[30] Test Loss: 0.1020, Accuracy: 96.69%\n", + "[31] Test Loss: 0.1013, Accuracy: 96.80%\n", + "[32] Test Loss: 0.0977, Accuracy: 96.90%\n", + "[33] Test Loss: 0.0978, Accuracy: 96.84%\n", + "[34] Test Loss: 0.0973, Accuracy: 96.88%\n", + "[35] Test Loss: 0.0943, Accuracy: 97.02%\n", + "[36] Test Loss: 0.0948, Accuracy: 96.97%\n", + "[37] Test Loss: 0.0937, Accuracy: 96.94%\n", + "[38] Test Loss: 0.0919, Accuracy: 97.09%\n", + "[39] Test Loss: 0.0916, Accuracy: 96.96%\n", + "[40] Test Loss: 0.0890, Accuracy: 97.16%\n", + "[41] Test Loss: 0.0888, Accuracy: 97.14%\n", + "[42] Test Loss: 0.0884, Accuracy: 97.32%\n", + "[43] Test Loss: 0.0912, Accuracy: 97.24%\n", + "[44] Test Loss: 0.0893, Accuracy: 97.24%\n", + "[45] Test Loss: 0.0873, Accuracy: 97.36%\n", + "[46] Test Loss: 0.0860, Accuracy: 97.34%\n", + "[47] Test Loss: 0.0854, Accuracy: 97.32%\n", + "[48] Test Loss: 0.0846, Accuracy: 97.38%\n", + "[49] Test Loss: 0.0840, Accuracy: 97.37%\n", + "[50] Test Loss: 0.0858, Accuracy: 97.23%\n" ] } ], @@ -290,13 +298,6 @@ " print('[{}] Test Loss: {:.4f}, Accuracy: {:.2f}%'.format(\n", " epoch, test_loss, test_accuracy))" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { @@ -315,7 +316,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.5.2" + "version": "3.7.0" } }, "nbformat": 4, diff --git a/04-Neural-Network-For-Fashion/03-overfitting-and-regularization.py b/04-Neural-Network-For-Fashion/03-overfitting-and-regularization.py index 0e3338c..2e19acd 100644 --- a/04-Neural-Network-For-Fashion/03-overfitting-and-regularization.py +++ b/04-Neural-Network-For-Fashion/03-overfitting-and-regularization.py @@ -1,4 +1,4 @@ - +#!/usr/bin/env python # coding: utf-8 # # 4.3 오버피팅과 정규화 (Overfitting and Regularization) @@ -83,11 +83,11 @@ def forward(self, x): optimizer = optim.SGD(model.parameters(), lr=0.01) -# ## 훈련하기 +# ## 학습하기 def train(model, train_loader, optimizer): model.train() - for data, target in enumerate(train_loader): + for batch_idx, (data, target) in enumerate(train_loader): data, target = data.to(DEVICE), target.to(DEVICE) optimizer.zero_grad() output = model(data) diff --git a/05-CNN-For-Image-Classification/01-cnn.ipynb b/05-CNN-For-Image-Classification/01-cnn.ipynb index 8240a41..8dba2ae 100644 --- a/05-CNN-For-Image-Classification/01-cnn.ipynb +++ b/05-CNN-For-Image-Classification/01-cnn.ipynb @@ -53,7 +53,106 @@ "cell_type": "code", "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\r", + "0it [00:00, ?it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz to ./.data/MNIST/raw/train-images-idx3-ubyte.gz\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "9920512it [00:05, 1866240.63it/s] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Extracting ./.data/MNIST/raw/train-images-idx3-ubyte.gz\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + " 0%| | 0/28881 [00:00 [b, i, e] +# ``` +# 보통의 신경망이라면 이제 바로 신경망 모듈의 forward 함수를 호출해도 되겠지만 +# LSTM과 같은 RNN 계열의 신경망은 입력 데이터 말고도 밑의 코드처럼 은닉 벡터(Hidden Vector)라는 텐서를 정의하고 신경망에 입력해 줘야 합니다. +# ```python +# h_0 = self._init_state(batch_size=x.size(0)) +# x, _ = self.lstm(x, h_0) # [i, b, h] +# ``` +# 첫번째 은닉 벡터(Hidden Vector) 인 h_0을 생성하는 _init_state 함수를 구현합니다. 꼭 그럴 필요는 없으나, 첫번째 은닉 벡터는 아래의 코드처럼 모든 특성값이 0인 벡터로 설정해 주는 것이 보통입니다. +# ```python +# def _init_state(self, batch_size=1): +# weight = next(self.parameters()).data +# return ( +# weight.new(self.n_layers, batch_size, self.hidden_dim).zero_(), +# weight.new(self.n_layers, batch_size, self.hidden_dim).zero_() +# ) +# ``` +# next(self.parameters()).data 를 통해 모델 속 가중치 텐서를 weight 이라는 변수에 대입시킵니다. +# 그리고 new() 함수를 이용해 weight 텐서와 같은 자료형을 갖고 있지만 (n_layers, batch_size, hidden_dim)꼴의 텐서 두개를 정의합니다. 그리고 이 두 텐서에 zero_() 함수를 호출함으로써 텐서 속 모든 원소값을 0으로 바꿔줍니다. 대부분의 RNN 계열의 신경망은 은닉 벡터를 하나만을 요구하지만, 좀 더 복잡한 구조를 가진 LSTM 은 이렇게 같은 모양의 텐서 두 개를 정의해 줘야 합니다. +# RNN이 만들어낸 마지막 은닉 벡터를 h_t 라고 정의하겠습니다. +# ```python +# h_t = x[:,-1,:] +# ``` +# 이제 영화 리뷰속 모든 내용을 압축한 h_t를 다층신경망에 입력시켜 결과를 출력해야 합니다. +# ```python +# logit = self.out(h_t) # [b, h] -> [b, o] +# return logit +# ``` +# 모델 구현과 신경망 학습에 필요한 함수를 구현했으면 본격적으로 IMDB 데이터셋을 가져와 보겠습니다. +# 사실 아무 가공처리를 가하지 않은 텍스트 형태의 데이터셋을 신경망에 입력하는데까지는 매우 번거로운 작업을 필요로합니다. +# 그러므로 우리는 이러한 전처리 작업들을 대신 해주는 Torch Text라이브러리를 사용해 IMDB 데이터셋을 가져오겠습니다. +# 가장 먼저 텍스트 형태의 영화 리뷰들과 그에 해당하는 레이블을 텐서로 바꿔줄 때 필요한 설정사항들을 정해줘야 합니다. +# 그러기 위해 이러한 설정정보를 담고있는 TEXT 와 LABEL 이라는 객체를 생성합니다. +# ```python +# TEXT = data.Field(sequential=True, batch_first=True, lower=True) +# LABEL = data.Field(sequential=False, batch_first=True) +# ``` +# sequential 이라는 파라미터를 이용해 데이터셋이 순차적 데이터셋이라고 명시해 주고 batch_first 파라미터로 신경망에 입력되는 텐서의 첫번째 차원값이 batch_size 가 되도록 정해줍니다. +# 마지막으로 lower 변수를 이용해 텍스트 데이터 속 모든 영문 알파벳이 소문자가 되도록 설정해 줍니다. +# 그 다음으로는 datasets 객체의 splits 함수를 이용해 모델에 입력되는 데이터셋을 만들어줍니다. +# ```python +# train_data, test_data = datasets.IMDB.splits(TEXT, LABEL) +# ``` +# 이제 만들어진 데이터셋을 이용해 전에 설명한 워드 임베딩에 필요한 워드 사전(Word Vocabulary)를 만들어줍니다. +# ```python +# TEXT.build_vocab(train_data, min_freq=5) +# LABEL.build_vocab(train_data) +# ``` +# min_freq 은 학습데이터 속에서 최소한 5번 이상 등장한 단어들만을 사전속에 정의하겠다는 뜻입니다. 즉 학습 데이터 속에서 드물게 출현하는 단어는 'unk'(Unknown) 이라는 토큰으로 정의됩니다. +# 그 다음으로는 train_data 와 test_data 에서 batch tensor 를 generate 할 수 있는 iterator 를 만들어 줍니다. +# ```python +# train_iter, test_iter = data.BucketIterator.splits( +# (train_data, test_data), batch_size=BATCH_SIZE, +# shuffle=True, repeat=False) +# ``` +# 마지막으로 사전 속 단어들의 숫자와 레이블의 수를 정해주는 변수를 만들어 줍니다. +# ```python +# vocab_size = len(TEXT.vocab) +# n_classes = 2 +# ``` + +# 그 다음은 train() 함수와 evaluate() 함수를 구현할 차례입니다. +# ```python +# def train(model, optimizer, train_iter): +# model.train() +# for b, batch in enumerate(train_iter): +# x, y = batch.text.to(DEVICE), batch.label.to(DEVICE) +# y.data.sub_(1) # index align +# optimizer.zero_grad() +# logit = model(x) +# loss = F.cross_entropy(logit, y) +# loss.backward() +# optimizer.step() +# if b % 100 == 0: +# corrects = (logit.max(1)[1].view(y.size()).data == y.data).sum() +# accuracy = 100.0 * corrects / batch.batch_size +# sys.stdout.write( +# '\rBatch[%d] - loss: %.6f acc: %.2f' % +# (b, loss.item(), accuracy)) +# ``` +# ```python +# def evaluate(model, val_iter): +# """evaluate model""" +# model.eval() +# corrects, avg_loss = 0, 0 +# for batch in val_iter: +# x, y = batch.text.to(DEVICE), batch.label.to(DEVICE) +# # x, y = batch.text, batch.label +# y.data.sub_(1) # index align +# logit = model(x) +# loss = F.cross_entropy(logit, y, size_average=False) +# avg_loss += loss.item() +# corrects += (logit.max(1)[1].view(y.size()).data == y.data).sum() +# size = len(val_iter.dataset) +# avg_loss = avg_loss / size +# accuracy = 100.0 * corrects / size +# return avg_loss, accuracy +# ``` +# 본격적으로 학습을 시작하기 전, 모델 객체와 최적화 알고리즘을 정의합니다. +# ```python +# model = BasicLSTM(1, 256, vocab_size, 128, n_classes, 0.5).to(DEVICE) +# optimizer = torch.optim.Adam(model.parameters(), lr=lr) +# ``` +# 이제 학습에 필요한 모든 준비는 되었습니다. 마지막으로 학습을 하는 loop을 구현합니다. +# ```python +# best_val_loss = None +# for e in range(1, EPOCHS+1): +# train(model, optimizer, train_iter) +# val_loss, val_accuracy = evaluate(model, test_iter) +# print("\n[Epoch: %d] val_loss:%5.2f | acc:%5.2f" % (e, val_loss, val_accuracy)) +# ``` +# 4장에서 배워 봤듯이, 우리가 원하는 최종 모델은 Training Loss가 아닌 Validation Loss가 최소화된 모델입니다. 다음과 같이 Validation Loss가 가장 작은 모델을 저장하는 로직을 구현합니다. +# ```python +# # Save the model if the validation loss is the best we've seen so far. +# if not best_val_loss or val_loss < best_val_loss: +# if not os.path.isdir("snapshot"): +# os.makedirs("snapshot") +# torch.save(model.state_dict(), './snapshot/convcnn.pt') +# best_val_loss = val_loss +# ``` + +# ### 전체 코드 +# ```python +# import os +# import sys +# import argparse +# import torch +# import torch.nn as nn +# import torch.nn.functional as F +# from torchtext import data, datasets +# BATCH_SIZE = 64 +# lr = 0.001 +# EPOCHS = 40 +# torch.manual_seed(42) +# USE_CUDA = torch.cuda.is_available() +# DEVICE = torch.device("cuda" if USE_CUDA else "cpu") +# class BasicLSTM(nn.Module): +# def __init__(self, n_layers, hidden_dim, n_vocab, embed_dim, n_classes, dropout_p=0.2): +# super(BasicLSTM, self).__init__() +# print("Building Basic LSTM model...") +# self.n_layers = n_layers +# self.embed = nn.Embedding(n_vocab, embed_dim) +# self.hidden_dim = hidden_dim +# self.dropout = nn.Dropout(dropout_p) +# self.lstm = nn.LSTM(embed_dim, self.hidden_dim, +# num_layers=self.n_layers, +# dropout=dropout_p, +# batch_first=True) +# self.out = nn.Linear(self.hidden_dim, n_classes) +# def forward(self, x): +# x = self.embed(x) # [b, i] -> [b, i, e] +# h_0 = self._init_state(batch_size=x.size(0)) +# x, _ = self.lstm(x, h_0) # [i, b, h] +# h_t = x[:,-1,:] +# self.dropout(h_t) +# logit = self.out(h_t) # [b, h] -> [b, o] +# return logit +# def _init_state(self, batch_size=1): +# weight = next(self.parameters()).data +# return ( +# weight.new(self.n_layers, batch_size, self.hidden_dim).zero_(), +# weight.new(self.n_layers, batch_size, self.hidden_dim).zero_() +# ) +# print("\nLoading data...") +# TEXT = data.Field(sequential=True, batch_first=True, lower=True) +# LABEL = data.Field(sequential=False, batch_first=True) +# train_data, test_data = datasets.IMDB.splits(TEXT, LABEL) +# TEXT.build_vocab(train_data, min_freq=5) +# LABEL.build_vocab(train_data) +# train_iter, test_iter = data.BucketIterator.splits( +# (train_data, test_data), batch_size=BATCH_SIZE, +# shuffle=True, repeat=False) +# vocab_size = len(TEXT.vocab) +# n_classes = 2 +# def train(model, optimizer, train_iter): +# model.train() +# for b, batch in enumerate(train_iter): +# x, y = batch.text.to(DEVICE), batch.label.to(DEVICE) +# # x, y = batch.text, batch.label +# y.data.sub_(1) # index align +# optimizer.zero_grad() +# logit = model(x) +# loss = F.cross_entropy(logit, y) +# loss.backward() +# optimizer.step() +# if b % 100 == 0: +# corrects = (logit.max(1)[1].view(y.size()).data == y.data).sum() +# accuracy = 100.0 * corrects / batch.batch_size +# sys.stdout.write( +# '\rBatch[%d] - loss: %.6f acc: %.2f' % +# (b, loss.item(), accuracy)) +# def evaluate(model, val_iter): +# """evaluate model""" +# model.eval() +# corrects, avg_loss = 0, 0 +# for batch in val_iter: +# x, y = batch.text.to(DEVICE), batch.label.to(DEVICE) +# # x, y = batch.text, batch.label +# y.data.sub_(1) # index align +# logit = model(x) +# loss = F.cross_entropy(logit, y, size_average=False) +# avg_loss += loss.item() +# corrects += (logit.max(1)[1].view(y.size()).data == y.data).sum() +# size = len(val_iter.dataset) +# avg_loss = avg_loss / size +# accuracy = 100.0 * corrects / size +# return avg_loss, accuracy +# print("[TRAIN]: %d \t [TEST]: %d \t [VOCAB] %d \t [CLASSES] %d" +# % (len(train_iter),len(test_iter), vocab_size, n_classes)) +# model = BasicLSTM(1, 256, vocab_size, 128, n_classes, 0.5).to(DEVICE) +# optimizer = torch.optim.Adam(model.parameters(), lr=lr) +# print(model) +# best_val_loss = None +# for e in range(1, EPOCHS+1): +# train(model, optimizer, train_iter) +# val_loss, val_accuracy = evaluate(model, test_iter) +# print("\n[Epoch: %d] val_loss:%5.2f | acc:%5.2f" % (e, val_loss, val_accuracy)) +# # Save the model if the validation loss is the best we've seen so far. +# if not best_val_loss or val_loss < best_val_loss: +# if not os.path.isdir("snapshot"): +# os.makedirs("snapshot") +# torch.save(model.state_dict(), './snapshot/convcnn.pt') +# best_val_loss = val_loss +# ``` + +# ## 원본 코드 + import os import sys import argparse @@ -19,49 +324,55 @@ DEVICE = torch.device("cuda" if USE_CUDA else "cpu") -# class BasicRNN(nn.Module): -# """ -# Basic RNN -# """ -# def __init__(self, n_layers, hidden_dim, n_vocab, embed_dim, n_classes, dropout_p=0.2): -# super(BasicRNN, self).__init__() -# print("Building Basic RNN model...") -# self.n_layers = n_layers -# self.hidden_dim = hidden_dim +# load data +print("\nLoading data...") +TEXT = data.Field(sequential=True, batch_first=True, lower=True) +LABEL = data.Field(sequential=False, batch_first=True) +train_data, test_data = datasets.IMDB.splits(TEXT, LABEL) +TEXT.build_vocab(train_data, min_freq=5) +LABEL.build_vocab(train_data) + +# train_iter, test_iter = data.BucketIterator.splits( +# (train_data, test_data), batch_size=BATCH_SIZE, +# shuffle=True, repeat=False,device=-1) +train_iter, test_iter = data.BucketIterator.splits( + (train_data, test_data), batch_size=BATCH_SIZE, + shuffle=True, repeat=False) + + +vocab_size = len(TEXT.vocab) +n_classes = 2 +#len(LABEL.vocab) - 1 + + +print("[TRAIN]: %d \t [TEST]: %d \t [VOCAB] %d \t [CLASSES] %d" + % (len(train_iter),len(test_iter), vocab_size, n_classes)) + -# self.embed = nn.Embedding(n_vocab, embed_dim) -# self.dropout = nn.Dropout(dropout_p) -# self.rnn = nn.RNN(embed_dim, hidden_dim, n_layers, -# dropout=dropout_p, batch_first=True) -# self.out = nn.Linear(self.hidden_dim, n_classes) -# def forward(self, x): -# embedded = self.embed(x) # [b, i] -> [b, i, e] -# _, hidden = self.rnn(embedded) -# self.dropout(hidden) -# hidden = hidden.squeeze() -# logit = self.out(hidden) # [b, h] -> [b, o] -# return logit -class BasicLSTM(nn.Module): + + + + +class BasicGRU(nn.Module): def __init__(self, n_layers, hidden_dim, n_vocab, embed_dim, n_classes, dropout_p=0.2): - super(BasicLSTM, self).__init__() - print("Building Basic LSTM model...") + super(BasicGRU, self).__init__() + print("Building Basic GRU model...") self.n_layers = n_layers - self.hidden_dim = hidden_dim - self.embed = nn.Embedding(n_vocab, embed_dim) + self.hidden_dim = hidden_dim self.dropout = nn.Dropout(dropout_p) - self.lstm = nn.LSTM(embed_dim, self.hidden_dim, + self.gru = nn.GRU(embed_dim, self.hidden_dim, num_layers=self.n_layers, dropout=dropout_p, batch_first=True) self.out = nn.Linear(self.hidden_dim, n_classes) def forward(self, x): - x = self.embed(x) # [b, i] -> [b, i, e] + x = self.embed(x) h_0 = self._init_state(batch_size=x.size(0)) - x, _ = self.lstm(x, h_0) # [i, b, h] + x, _ = self.gru(x, h_0) # [i, b, h] h_t = x[:,-1,:] self.dropout(h_t) logit = self.out(h_t) # [b, h] -> [b, o] @@ -69,28 +380,27 @@ def forward(self, x): def _init_state(self, batch_size=1): weight = next(self.parameters()).data - return ( - weight.new(self.n_layers, batch_size, self.hidden_dim).zero_(), - weight.new(self.n_layers, batch_size, self.hidden_dim).zero_() - ) + return weight.new(self.n_layers, batch_size, self.hidden_dim).zero_() def train(model, optimizer, train_iter): model.train() for b, batch in enumerate(train_iter): x, y = batch.text.to(DEVICE), batch.label.to(DEVICE) +# x, y = batch.text, batch.label y.data.sub_(1) # index align optimizer.zero_grad() + logit = model(x) loss = F.cross_entropy(logit, y) loss.backward() optimizer.step() -# if b % 100 == 0: -# corrects = (logit.max(1)[1].view(y.size()).data == y.data).sum() -# accuracy = 100.0 * corrects / batch.batch_size -# sys.stdout.write( -# '\rBatch[%d] - loss: %.6f acc: %.2f' % -# (b, loss.item(), accuracy)) + if b % 100 == 0: + corrects = (logit.max(1)[1].view(y.size()).data == y.data).sum() + accuracy = 100.0 * corrects / batch.batch_size + sys.stdout.write( + '\rBatch[%d] - loss: %.6f acc: %.2f' % + (b, loss.item(), accuracy)) def evaluate(model, val_iter): @@ -99,6 +409,7 @@ def evaluate(model, val_iter): corrects, avg_loss = 0, 0 for batch in val_iter: x, y = batch.text.to(DEVICE), batch.label.to(DEVICE) +# x, y = batch.text, batch.label y.data.sub_(1) # index align logit = model(x) loss = F.cross_entropy(logit, y, size_average=False) @@ -110,31 +421,8 @@ def evaluate(model, val_iter): return avg_loss, accuracy -# # IMDB 데이터셋 가져오기 - -# load data -print("\nLoading data...") -TEXT = data.Field(sequential=True, batch_first=True, lower=True) -LABEL = data.Field(sequential=False, batch_first=True) -train_data, test_data = datasets.IMDB.splits(TEXT, LABEL) -TEXT.build_vocab(train_data, min_freq=5) -LABEL.build_vocab(train_data) - -train_iter, test_iter = data.BucketIterator.splits( - (train_data, test_data), batch_size=BATCH_SIZE, - shuffle=True, repeat=False) - -vocab_size = len(TEXT.vocab) -n_classes = len(LABEL.vocab) - 1 - - -print("[TRAIN]: %d \t [TEST]: %d \t [VOCAB] %d \t [CLASSES] %d" - % (len(train_iter),len(test_iter), vocab_size, n_classes)) - - -model = BasicLSTM(1, 256, vocab_size, 128, n_classes, 0.5).to(DEVICE) +model = BasicGRU(1, 256, vocab_size, 128, n_classes, 0.5).to(DEVICE) optimizer = torch.optim.Adam(model.parameters(), lr=lr) - print(model) @@ -146,9 +434,34 @@ def evaluate(model, val_iter): print("\n[Epoch: %d] val_loss:%5.2f | acc:%5.2f" % (e, val_loss, val_accuracy)) # Save the model if the validation loss is the best we've seen so far. -# if not best_val_loss or val_loss < best_val_loss: -# if not os.path.isdir("snapshot"): -# os.makedirs("snapshot") -# torch.save(model.state_dict(), './snapshot/convcnn.pt') -# best_val_loss = val_loss + if not best_val_loss or val_loss < best_val_loss: + if not os.path.isdir("snapshot"): + os.makedirs("snapshot") + torch.save(model.state_dict(), './snapshot/txtclassification.pt') + best_val_loss = val_loss + + +# class BasicRNN(nn.Module): +# """ +# Basic RNN +# """ +# def __init__(self, n_layers, hidden_dim, n_vocab, embed_dim, n_classes, dropout_p=0.2): +# super(BasicRNN, self).__init__() +# print("Building Basic RNN model...") +# self.n_layers = n_layers +# self.hidden_dim = hidden_dim + +# self.embed = nn.Embedding(n_vocab, embed_dim) +# self.dropout = nn.Dropout(dropout_p) +# self.rnn = nn.RNN(embed_dim, hidden_dim, n_layers, +# dropout=dropout_p, batch_first=True) +# self.out = nn.Linear(self.hidden_dim, n_classes) + +# def forward(self, x): +# embedded = self.embed(x) # [b, i] -> [b, i, e] +# _, hidden = self.rnn(embedded) +# self.dropout(hidden) +# hidden = hidden.squeeze() +# logit = self.out(hidden) # [b, h] -> [b, o] +# return logit diff --git a/07-RNN-For-Sequential-Data/02-sequence-to-sequence.py b/07-RNN-For-Sequential-Data/02-sequence-to-sequence.py index d04d236..d517b67 100644 --- a/07-RNN-For-Sequential-Data/02-sequence-to-sequence.py +++ b/07-RNN-For-Sequential-Data/02-sequence-to-sequence.py @@ -1,9 +1,168 @@ - +#!/usr/bin/env python # coding: utf-8 -# # Seq2Seq (Encoder-Decoder) Model -# this model is the basic encoder decoder model without attention mechanism. +# # Seq2Seq 기계 번역 +# 2010년 이후 가장 큰 관심을 받은건 역시 알파고 였지만, 그와 더불어 크게 화제가 된 또다른 머신러닝 모델이 있었습니다. +# 바로 한 언어를 다른 언어로 해석시켜주는 **뉴럴 기계번역(Neural Machine Translation)** 모델입니다. +# 항상 RNN이 기계번역에 쓰였던 것은 아니지만, RNN 기반의 번역모델인 **Sequence to Sequence**(줄여서 Seq2Seq 이라고도 합니다) 모델은 기계번역의 새로운 패러다임을 열었다고 할 정도로 기존 번역모델의 성능을 아득히 뛰어넘었습니다. +# 이름에서 알 수 있듯이 Seq2Seq 모델은 순차적인 형태의 배열 혹은 문장을 다른 문장으로 바꾸거나 번역해주는 모델입니다. +# 일반적으로 Seq2Seq와 같은 기계번역 모델이 이러한 능력을 학습하려면 원문과 번역문이 쌍을 이루는 형태의 다량의 텍스트 데이터셋이 필요합니다. +# 당연히 이런 데이터를 가지고 학습하는 모델들은 고용량 GPU와 복잡한 텍스트 전처리 과정, 그리고 긴 학습시간 등 꽤 많은 리소스를 필요로 합니다. +# 그래서 이번 프로젝트에선 임의로 Seq2Seq 모델을 아주 간단화 시켰습니다. +# 한 언어로 된 문장을 다른 언어로 된 문장으로 번역하는 덩치가 큰 모델이 아닌 +# 영어 알파벳 문자열("hello")을 스페인어 알파벳 문자열("hola")로 번역하는 Mini Seq2Seq 모델을 같이 구현해 보겠습니다. +# ## Seq2Seq 개요 +# 지금까지 이 책을 읽으면서 이미 눈치를 채셨을 수도 있겠지만, 복잡한 일을 처리하는 딥러닝 모델이 단 하나의 신경망으로 이루어진 경우는 매우 드뭅니다. +# 우리가 앞 프로젝트에서 같이 구현한 비교적 간단한 모델인 감정분석(Sentiment Analysis) 모델도 RNN 과 다층신경망, 이 두 신경망이 연결된 형태였습니다. +# 이번 프로젝트의 메인 토픽인 Seq2Seq모델 또한 마찬가지입니다. 엄밀히 말하자면 Seq2Seq 모델은 서로 다른 역할을 하는 두개의 RNN을 이어붙인 신경망입니다 +# 두개의 RNN이 연결되어 있다는 점에서 Seq2Seq 모델이 매우 어렵고 복잡하게 느껴지실 수도 있습니다. +# 하지만 실제 우리가 번역을 할때 거치는 생각과 과정을 곱씹어보면 Seq2Seq가 왜 이런 구조로 구현되었는지 쉽게 이해가 되실겁니다. +# 일반적으로 우리가 영어와 같은 외국어를 한국어로 번역하는 과정은 다음과 같습니다. +# 먼저 외국어 문장을 읽고 그 내용을 이해합니다. +# 그다음 이러한 이해를 바탕으로 한국어 단어들을 하나 하나 문맥에 맞게 써내려갑니다. +# 이처럼 번역은 원문의 이해와 번역문 작성, 이렇게 크게 두가지 동작을 필요로 합니다. +# Seq2Seq 모델에선 이 두가지 동작을 **인코더(Encoder)** 와 **디코더(Decoder)** 라고 하는 각자 다른 RNN에 부여하므로써 기계번역을 실행합니다. +# 첫번째 RNN인 **인코더(Encoder)** 는 원문을 입력받고 그 뜻을 학습합니다. 인코더를 통해 학습된 내용을 이어받는 **디코더(Decoder)** 는 원문의 내용을 바탕으로 번역문을 차례대로 출력합니다. +# ### 인코더 +# 인코더는 원문의 내용을 학습하는 RNN 입니다. +# 한 마디로 원문 속의 모든 단어들을 입력받아 문장의 뜻을 내포하는 하나의 고정된 크기의 텐서로 압축시킵니다. +# 이렇게 압축된 텐서는 원문의 뜻과 내용을 담고 있다고 하여 **Context Vector(내용 벡터)** 라고 부릅니다. +# ### 디코더 +# 다시 말씀드리지만, 번역을 할 때에는 항상 '원문이 말하는 바가 무엇인가', 그리고 '번역문과 원문이 전하는 뜻이 같은가'라는 생각을 하고 있어야합니다. +# 이는 곧 번역문의 단어 하나, 글자 한 자를 작성할 때도 원문이 주는 정보에 입각하여야 한다는 뜻입니다. +# 즉 디코더가 번역문의 단어나 토큰을 출력할 때 마다 인코더의 Context Vector를 어느 형태로든 전달받아야 합니다. +# ![rl](./assets/encoder_decoder.png) +# 사실 인코더의 Context Vector 를 디코더에 전해주는데는 여러 방법이 있습니다. +# 원본 Sequence to Sequence 모델에선 인코더의 Context Vector 가 디코더에 입력되는 모든 번역문 토큰 벡터에 이어붙였습니다. 이렇게 구현함으로써 디코더가 다음 번역문 토큰을 예상할 때 원문의 내용을 고려할 수 있도록 말이죠. +# 우리가 구현해 볼 Mini Seq2Seq은 이러한 동작은 생략하고 단순히 디코더 RNN 의 첫번쨰 Hidden State 을 인코더의 Context Vector 로 정의함으로써 원문의 내용을 디코더에 입력합니다. +# Context Vector를 입력받은 디코더는 번역문 속의 토큰을 입력받아 번역문 속 다음 토큰을 예상합니다. +# 디코더가 예상한 토큰과 실제 토큰을 비교하여 오차를 줄여나가는 것이 Seq2Seq 모델이 학습하는 기본원리입니다. + +# ## Seq2Seq 모델을 구현하고 기계번역을 해 봅시다. +# 여느때와 마찬가지로 구현에 필요한 라이브러리들을 임포트합니다. +# ```python +# import numpy as np +# import torch as th +# import torch.nn as nn +# import torch.nn.functional as F +# from torch.autograd import Variable +# from torch import optim +# ``` + +# 이번 프로젝트에선 워드 임베딩(Word Embedding)이 아닌 캐릭터 임베딩(Character Embedding)을 사용하겠습니다. +# 즉 단어가 아닌 알파벳들을 벡터로 표현하여 알파벳의 배열인 단어를 벡터의 배열로 표현하겠습니다. +# 앞의 프로젝트에서 했던것과 마찬가지로, 임베딩을 하기 위해선 '사전'을 정의해야 합니다. ascii 코드엔 총 256개의 캐릭터가 속해 있으므로, 모든 캐릭터를 사전에 담아내기 위해 vocab_size 를 ascii 코드의 총 갯수인 256으로 정의하겠습니다. +# ```python +# vocab_size = 256 # ascii size +# ``` +# Seq2Seq 모델에 입력될 원문과 번역문 ascii 코드의 배열로 정의하고 파이토치 텐서로 바꿔줍니다. +# ```python +# x_ = list(map(ord, "hello")) # convert to list of ascii codes +# y_ = list(map(ord, "hola")) # convert to list of ascii codes +# x = Variable(th.LongTensor(x_)) +# y = Variable(th.LongTensor(y_)) +# ``` + +# Seq2Seq 모델 클래스를 정의합니다. +# 전 프로젝트와 마찬가지로 n_layer는 1로 정의해 주고 RNN 의 Hidden Size를 입력받도록 설정합니다. +# ```python +# class Seq2Seq(nn.Module): +# def __init__(self, vocab_size, hidden_size): +# super(Seq2Seq, self).__init__() +# self.n_layers = 1 +# self.hidden_size = hidden_size +# ``` +# 임베딩 사이즈를 설정하고 인코더와 디코더를 LSTM 객체로 정의해줍니다. +# 원래는 원문을 위한 임베딩과 번역문을 위한 임베딩을 따로 정의해 줘야 하지만 간단한 Seq2Seq 모델인 만큼 임베딩을 하나만 정의해 주겠습니다. +# ```python +# self.embedding = nn.Embedding(vocab_size, hidden_size) +# self.encoder = nn.LSTM(hidden_size, hidden_size) +# self.decoder = nn.LSTM(hidden_size, hidden_size) +# ``` +# 디코더가 번역문 속 다음 토큰을 예상하기 위해선 다음과 같이 작은 신경망을 하나 더 만들어 줘야합니다. +# ```python +# self.project = nn.Linear(hidden_size, vocab_size) +# ``` +# forward 함수를 구현하면서 위에 정의된 신경망 모듈과 객체들이 어떻게 서로 이어붙여 지는지 알아보겠습니다. +# 인코더의 첫번째 Hidden State을 정의하고 인코더에 입력되는 원문인 'hello' 속의 모든 캐릭터를 임베딩시킵니다. +# ```python +# def forward(self, inputs, targets): +# initial_state = self._init_state() +# embedding = self.embedding(inputs).unsqueeze(1) +# ``` +# 'hello'를 인코더에 입력시켜 encoder_state 이라는 텐서로 압축시킵니다. +# 원문의 Context Vector인 encoder_state를 디코더의 첫번째 Hidden State 로 설정합니다. +# 디코더가 번멱문 'hola'의 첫번째 토큰인 'h'를 예상하려면 null character 혹은 문장 시작 토큰(Start of Sentence Tocken)을 첫번째 입력데이터로써 받아야 합니다. 이번 예제에서는 ascii 번호 0을 문장 시작 토큰으로 설정하겠습니다. +# ```python +# encoder_output, encoder_state = self.encoder(embedding, initial_state) +# decoder_state = encoder_state +# decoder_input = Variable(th.LongTensor([[0]])) +# ``` +# 디코더의 동작에 필요한 for loop 을 구현합니다. +# 디코더는 인코더와는 달리 번역문 속의 토큰을 입력받을 때 마다 loss를 계산하는데 쓰일 결과값을 출력해야합니다. +# 위에 정의한 decoder_input 과 encoder의 Context Vector인 decoder_state을 디코더에 입력합니다. +# ```python +# outputs = [] +# for i in range(targets.size()[0]): +# decoder_input = self.embedding(decoder_input) +# decoder_output, decoder_state = self.decoder(decoder_input, decoder_state) +# ``` +# decoder를 통해 나온 결과값은 다시 작은 신경망에 입력됩니다. +# 이렇게 해서 원문의 내용과 현재의 번역문 토큰을 기반으로 추론해 본 번역문의 다음 토큰을 예상하는 결과값을 구합니다. +# 이 결과값을 outputs라는 배열 속에 저장해 loss 를 계산할 때 사용하겠습니다. +# ```python +# # Project to the vocabulary size +# projection = self.project(decoder_output.view(1, -1)) # batch x vocab_size +# # Make prediction +# prediction = F.softmax(projection) # batch x vocab_size +# outputs.append(prediction) +# ``` +# 마지막으로 디코더에 입력되는 데이터를 번역문의 토큰을 업데이트합니다. +# ```python +# # update decoder input +# _, top_i = prediction.data.topk(1) # 1 x 1 +# decoder_input = Variable(top_i) +# ``` +# 번역문의 모든 토큰에 대한 결과값들을 배열이라 할 수 있는 outputs을 리턴합니다. +# ```python +# outputs = th.stack(outputs).squeeze() +# return outputs +# ``` +# 이렇게 모델의 구현이 끝났습니다. +# 이제 vocab_size를 256으로, hidden_size를 16으로 설정해 모델을 생성하고 loss 함수와 optimizer를 정의합니다. +# ```python +# seq2seq = Seq2Seq(vocab_size, 16) +# criterion = nn.CrossEntropyLoss() +# optimizer = th.optim.Adam(seq2seq.parameters(), lr=1e-3) +# ``` + +# 1000번의 epoch에 걸쳐 모델을 학습시킵니다. +# ```python +# log = [] +# for i in range(1000): +# prediction = seq2seq(x, y) +# loss = criterion(prediction, y) +# optimizer.zero_grad() +# loss.backward() +# optimizer.step() +# loss_val = loss.data[0] +# log.append(loss_val) +# if i % 100 == 0: +# print("%d loss: %s" % (i, loss_val)) +# _, top1 = prediction.data.topk(1, 1) +# for c in top1.squeeze().numpy().tolist(): +# print(chr(c), end=" ") +# print() +# ``` +# matplotlib 라이브러리를 이용해서 loss 가 줄어드는 것을 한 눈에 확인하실 수 있습니다. +# ```python +# import matplotlib.pyplot as plt +# plt.plot(log) +# plt.ylabel('cross entropy loss') +# plt.show() +# ``` +# ### 전체 코드 import numpy as np import torch as th import torch.nn as nn @@ -79,14 +238,20 @@ def _init_state(self, batch_size=1): ) + + + seq2seq = Seq2Seq(vocab_size, 16) print(seq2seq) pred = seq2seq(x, y) print(pred) + + + criterion = nn.CrossEntropyLoss() -optimizer = optim.Adam(seq2seq.parameters(), lr=1e-3) +optimizer = th.optim.Adam(seq2seq.parameters(), lr=1e-3) log = [] @@ -111,3 +276,6 @@ def _init_state(self, batch_size=1): plt.ylabel('cross entropy loss') plt.show() + + + diff --git a/07-RNN-For-Sequential-Data/03-Seq2Seq_gru.py b/07-RNN-For-Sequential-Data/03-Seq2Seq_gru.py new file mode 100644 index 0000000..65695b5 --- /dev/null +++ b/07-RNN-For-Sequential-Data/03-Seq2Seq_gru.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python +# coding: utf-8 + +import numpy as np +import torch as th +import torch.nn as nn +import torch.nn.functional as F +from torch.autograd import Variable +from torch import optim + + +vocab_size = 256 # ascii size +x_ = list(map(ord, "hello")) # convert to list of ascii codes +y_ = list(map(ord, "hola")) # convert to list of ascii codes +print("hello -> ", x_) +print("hola -> ", y_) + + +x = Variable(th.LongTensor(x_)) +y = Variable(th.LongTensor(y_)) + + +print(x) + + +''' +Model using GRU and conventional concatenating motion. +''' +class Seq2Seq_GRU(nn.Module): + def __init__(self, vocab_size, hidden_size): + super(Seq2Seq_GRU, self).__init__() + + self.n_layers = 1 + self.hidden_size = hidden_size + self.embedding = nn.Embedding(vocab_size, hidden_size) + self.encoder = nn.GRU(hidden_size, hidden_size) + self.decoder = nn.GRU(hidden_size * 2, hidden_size) + self.project = nn.Linear(hidden_size, vocab_size) + + def forward(self, inputs, targets): + # Encoder inputs and states + initial_state = self._init_state() + embedding = self.embedding(inputs).unsqueeze(1) + encoder_output, encoder_state = self.encoder(embedding, initial_state) + outputs = [] + + decoder_state = encoder_state + for i in range(targets.size()[0]): + decoder_input = self.embedding(targets)[i].view(1,-1, self.hidden_size) + decoder_input = th.cat((decoder_input, encoder_state), 2) + decoder_output, decoder_state = self.decoder(decoder_input, decoder_state) + projection = self.project(decoder_output)#.unsqueeze(0)) + outputs.append(projection) + + #_, top_i = prediction.data.topk(1) + + outputs = th.stack(outputs, 1).squeeze() + + return outputs + + def _init_state(self, batch_size=1): + weight = next(self.parameters()).data + return Variable(weight.new(self.n_layers, batch_size, self.hidden_size).zero_()) + + +model = Seq2Seq_GRU(vocab_size, 16) +pred = model(x, y) + + +criterion = nn.CrossEntropyLoss() +optimizer = th.optim.Adam(model.parameters(), lr=1e-3) + + +y_.append(3) +y_label = Variable(th.LongTensor(y_[1:])) + + +print(y_label.shape) +print(y_label) + + +log = [] +for i in range(10000): + prediction = model(x, y) + loss = criterion(prediction, y) + optimizer.zero_grad() + loss.backward() + optimizer.step() + loss_val = loss.data[0] + log.append(loss_val) + if i % 100 == 0: + print("%d loss: %s" % (i, loss_val)) + _, top1 = prediction.data.topk(1, 1) + for c in top1.squeeze().numpy().tolist(): + print(chr(c), end=" ") + print() + + +import matplotlib.pyplot as plt +plt.plot(log) +plt.xlim(0,150) +plt.ylim(0,15) +plt.ylabel('cross entropy loss') +plt.show() + + +# l = nn.CrossEntropyLoss() +# i = th.randn(3, 5, requires_grad=True) +# t = th.empty(3, dtype=th.long).random_(5) +# print(i.shape, t.shape) +# o = l(i, t) + diff --git a/08-Hacking-Deep-Learning/01-fgsm-attack.py b/08-Hacking-Deep-Learning/01-fgsm-attack.py new file mode 100644 index 0000000..269e73d --- /dev/null +++ b/08-Hacking-Deep-Learning/01-fgsm-attack.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python +# coding: utf-8 + +# # 8.1 FGSM 공격 +# 정상 이미지와 노이즈를 더해 머신러닝 모델을 헷갈리게 하는 이미지가 +# 바로 적대적 예제(Adversarial Example) 입니다. +# 이 프로젝트에선 Fast Gradient Sign Method, 즉 줄여서 FGSM이라는 방식으로 +# 적대적 예제를 생성해 미리 학습이 완료된 딥러닝 모델을 공격해보도록 하겠습니다. +# FGSM 학습이 필요 없지만 공격 목표를 정할 수 없는 Non-Targeted 방식의 공격입니다. +# 또, 공격하고자 하는 모델의 정보가 필요한 White Box 방식입니다. +# 공격이 어떻게 진행되는지 단계별로 설명하도록 하겠습니다. + +import torch +import torchvision.models as models +import torchvision.transforms as transforms + +import numpy as np +from PIL import Image +import json + + +get_ipython().run_line_magic('matplotlib', 'inline') +import matplotlib.pyplot as plt + +torch.manual_seed(1) + + +# ## 학습된 모델 불러오기 +# `torchvison`은 `AlexNet`, `VGG`, `ResNet`, `SqueezeNet`, `DenseNet`, `Inception`등 여러가지 학습된 모델들을 제공합니다. +# 대부분 ImageNet이라는 데이터셋으로 학습된 모델이며, +# 컬러 이미지를 다루는 컴퓨터 비전 분야의 대표적인 데이터셋입니다. +# 간단하게 사용하고자 하는 모델을 고르고, +# 함수 내에 `pretrained=True`를 명시하면 +# 학습된 모델을 가져옵니다. +# 이미 학습된 모델이므로 재학습을 시킬 필요 없이 우리가 원하는 +# 이미지를 분류하게 할 수 있습니다. +# 본 예제에선 `ResNet101`이라는 모델을 사용하고 있습니다. +# 너무 복잡하지도 않고, 너무 간단하지도 않은 적당한 모델이라 생각하여 채택하게 되었습니다. +# ImageNet 테스트 데이터셋을 돌려보았을때 +# Top-1 error 성능은 22.63, +# Top-5 error는 6.44로 성능도 좋게 나오는 편입니다. +# 모델을 바꾸고 싶다면 이름만 바꾸면 됩니다. +# 성능을 더 끌어올리고 싶다면 `DenseNet`이나 `Inception v3`같은 모델을 사용하고, +# 노트북 같은 컴퓨터를 사용해야된다면 `SqueezeNet`같이 가벼운 모델을 사용하면 됩니다. +model = models.resnet50(pretrained=True) +model.eval() +print(model) + + +# ## 데이터셋 불러오기 +# 방금 불러온 모델을 그대로 사용할 수 있지만, +# 실제 예측값을 보면 0부터 1000까지의 숫자를 내뱉을 뿐입니다. +# 이건 ImageNet 데이터셋의 클래스들의 지정 숫자(인덱스) 입니다. +# 사람이 각 클래스 숫자가 무엇을 의미하는지 알아보기 위해선 +# 숫자와 클래스 이름을 이어주는 작업이 필요합니다. +# 미리 준비해둔 `imagenet_classes.json`이라는 파일에 각 숫자가 어떤 클래스 제목을 의미하는지에 대한 정보가 담겨있습니다. +# `json`파일을 파이썬 사용자들에게 좀더 친숙한 +# 딕셔너리 자료형으로 만들어 언제든 사용할 수 있도록 +# 인덱스에서 클래스로 매핑해주는 `idx2class`와 +# 반대로 클래스 이름을 숫자로 변환해주는`class2idx`을 만들어보겠습니다. + +CLASSES = json.load(open('./imagenet_samples/imagenet_classes.json')) +idx2class = [CLASSES[str(i)] for i in range(1000)] +class2idx = {v:i for i,v in enumerate(idx2class)} + + +# ## 공격용 이미지 불러오기 +# 모델이 준비되었으니 공격하고자 하는 이미지를 불러오겠습니다. +# 실제 공격에 사용될 데이터는 학습용 데이터에 존재하지 않을 것이므로 +# 우리도 데이터셋에 존재하지 않는 이미지를 새로 준비해야 합니다. +# 인터넷에 존재하는 이미지는 다양한 사이즈가 있으므로 +# 새로운 입력은 `torchvision`의 `transforms`를 이용하여 +# 이미지넷과 같은 사이즈인 224 x 224로 바꿔주도록 하겠습니다. +# 그리고 파이토치 텐서로 변환하고, 노말라이즈를 하는 기능을 추가하여 +# `img_transforms`를 통과시키면 어떤 이미지던 입력으로 사용할 수 있도록 합니다. + +img_transforms = transforms.Compose( + [transforms.Resize((224, 224), Image.BICUBIC), + transforms.ToTensor(), + transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) + + +# 이미지넷 데이터셋에는 치와와(Chihuahua)라는 클래스가 존재합니다. +# 그래서 약간 부담스럽지만 귀여운 치와와 사진을 준비해보았습니다. + +img = Image.open('imagenet_samples/chihuahua.jpg') +img_tensor = img_transforms(img) + +plt.figure(figsize=(10,5)) +plt.imshow(np.asarray(img)) + + +# ## 공격 전 성능 확인하기 +# 공격을 하기 전에 우리가 준비한 학습용 데이터에 없는 +# 이미지를 얼마나 잘 분류하나 확인하겠습니다. +# 분류하는 것은 매우 간단한데, +# 아까 준비한 모델에 이미지를 통과시키기만 하면 됩니다. +# 모델에서 나온 값에 `Softmax`를 씌우면 +# 각각의 레이블에 대한 확률 예측값으로 환산됩니다. +# ```python +# out = model(img_tensor.unsqueeze(0)) +# probs = softmax(out) +# ``` +# 그리고 `argmax`를 이용하여 가장 큰 확률을 갖고 있는 인덱스, +# 즉, 모델이 가장 확신하는 예측값을 가져올 수 있습니다. +# 우리가 준비한 ResNet101 모델은 정확하게 치와와라고 분류하는 것을 볼 수 있습니다. +# 신뢰도도 99.87%로 매우 치와와라고 확신하고 있네요. +# ``` +# 151:Chihuahua:18.289345:0.9987244 +# ``` + +softmax = torch.nn.Softmax() + + +img_tensor.requires_grad_(True) +out = model(img_tensor.unsqueeze(0)) +probs = softmax(out) +cls_idx = np.argmax(out.data.numpy()) +print(str(cls_idx) + ":" + idx2class[cls_idx] + ":" + str(out.data.numpy()[0][cls_idx]) + ":" + str(probs.data.numpy()[0][cls_idx])) + + +# ### 이미지 변환하기 +# 입력에 사용되는 이미지는 노말라이즈되어 있으므로, +# 다시 사람의 눈에 보이게 하기 위해서는 반대로 변환시켜주는 작업이 필요합니다. +# `norm`함수는 Normalize를, `unnorm`함수는 다시 사람의 눈에 보이게 +# 복원시켜주는 역활을 합니다. + +def norm(x): + return 2.*(x/255.-0.5) + +def unnorm(x): + un_x = 255*(x*0.5+0.5) + un_x[un_x > 255] = 255 + un_x[un_x < 0] = 0 + un_x = un_x.astype(np.uint8) + return un_x + + +# ## 적대적 예제 시각화 하기 +# 적대적 예제의 목적중에 하나가 바로 사람의 눈에는 다름이 없어야 함으로 +# 시각화를 하여 결과물을 확인하는 것도 중요합니다. + +def draw_result(img, noise, adv_img): + fig, ax = plt.subplots(1, 3, figsize=(15, 10)) + orig_class, attack_class = get_class(img), get_class(adv_img) + ax[0].imshow(reverse_trans(img[0])) + ax[0].set_title('Original image: {}'.format(orig_class.split(',')[0])) + ax[1].imshow(noise[0].cpu().numpy().transpose(1, 2, 0)) + ax[1].set_title('Attacking noise') + ax[2].imshow(reverse_trans(adv_img[0])) + ax[2].set_title('Adversarial example: {}'.format(attack_class)) + for i in range(3): + ax[i].set_axis_off() + plt.tight_layout() + plt.show() + + +# ## 모델 정보 추출하기 + +criterion = F.cross_entropy +def fgsm_attack(model, x, y, eps): + x_adv = x.clone().requires_grad_() + h_adv = model(x_adv) + cost = F.cross_entropy(h_adv, y) + model.zero_grad() + + + +out[0,class2idx['wooden spoon']].backward() + + +img_grad = img_tensor.grad +img_tensor = img_tensor.detach() + +grad_sign = np.sign(img_grad.numpy()).astype(np.uint8) +epsilon = 0.05 +new_img_array = np.asarray(unnorm(img_tensor.numpy()))+epsilon*grad_sign +new_img_array[new_img_array>255] = 255 +new_img_array[new_img_array<0] = 0 +new_img_array = new_img_array.astype(np.uint8) + +plt.figure(figsize=(10,5)) +plt.subplot(1,2,1) +plt.imshow(unnorm(img_tensor.numpy()).transpose(1,2,0)) +plt.subplot(1,2,2) +plt.imshow(new_img_array.transpose(1,2,0)) + +new_img_array = norm(new_img_array) +new_img_var = torch.FloatTensor(new_img_array) +new_img_var.requires_grad_(True) +new_out = model(new_img_var.unsqueeze(0)) +new_out_np = new_out.data.numpy() +new_probs = softmax(new_out) +new_cls_idx = np.argmax(new_out_np) +print(str(new_cls_idx) + ":" + idx2class[new_cls_idx] + ":" + str(new_probs.data.numpy()[0][new_cls_idx])) + + + + diff --git a/08-Hacking-Deep-Learning/02-iterative-target-attack.py b/08-Hacking-Deep-Learning/02-iterative-target-attack.py new file mode 100644 index 0000000..1e0e8aa --- /dev/null +++ b/08-Hacking-Deep-Learning/02-iterative-target-attack.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python +# coding: utf-8 + +# # 8.2 목표를 정해 공격하기 +import torch +import torchvision.models as models +import torchvision.transforms as transforms + +import numpy as np +from PIL import Image +import json + + +get_ipython().run_line_magic('matplotlib', 'inline') +import matplotlib.pyplot as plt + +torch.manual_seed(1) + + +CLASSES = json.load(open('./imagenet_samples/imagenet_classes.json')) +idx2class = [CLASSES[str(i)] for i in range(1000)] +class2idx = {v:i for i,v in enumerate(idx2class)} + + +vgg16 = models.vgg16(pretrained=True) +vgg16.eval() +print(vgg16) + + +softmax = torch.nn.Softmax() + + +img_transforms = transforms.Compose([transforms.Scale((224, 224), Image.BICUBIC), + transforms.ToTensor(), + transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) +def norm(x): + return 2.*(x/255.-0.5) + +def unnorm(x): + un_x = 255*(x*0.5+0.5) + un_x[un_x > 255] = 255 + un_x[un_x < 0] = 0 + un_x = un_x.astype(np.uint8) + return un_x + + +img = Image.open('imagenet_samples/chihuahua.jpg') +img_tensor = img_transforms(img) + +plt.figure(figsize=(10,5)) +plt.imshow(np.asarray(img)) + + +img_tensor.requires_grad_(True) +out = vgg16(img_tensor.unsqueeze(0)) +probs = softmax(out) +cls_idx = np.argmax(out.data.numpy()) +print(str(cls_idx) + ":" + idx2class[cls_idx] + ":" + str(out.data.numpy()[0][cls_idx]) + ":" + str(probs.data.numpy()[0][cls_idx])) + + +learning_rate = 1 +img = Image.open('imagenet_samples/chihuahua.jpg') +fake_img_tensor = img_transforms(img) +img_var_fake = torch.autograd.Variable(fake_img_tensor.unsqueeze(0), requires_grad=True) +fake_class_idx = class2idx['street sign'] +for i in range(100): + out_fake = vgg16(img_var_fake) + _, out_idx = out_fake.data.max(dim=1) + if out_idx.numpy() == fake_class_idx: + print('Fake generated in ' + str(i) + ' iterations') + break + out_fake[0,fake_class_idx].backward() + img_var_fake_grad = img_var_fake.grad.data + img_var_fake.data += learning_rate*img_var_fake_grad/img_var_fake_grad.norm() + img_var_fake.grad.data.zero_() +probs_fake = softmax(out_fake) +print(str(fake_class_idx) + ":" + idx2class[fake_class_idx] + ":" + str(out_fake.data.numpy()[0][fake_class_idx]) + ":" + str(probs_fake.data.numpy()[0][fake_class_idx])) + + +plt.figure(figsize=(10,5)) +plt.subplot(1,3,1) +plt.imshow(unnorm(img_tensor.detach().numpy()).transpose(1,2,0)) +plt.subplot(1,3,2) +plt.imshow(unnorm(img_var_fake.data.detach().numpy()[0]).transpose(1,2,0)) +plt.subplot(1,3,3) +plt.imshow(unnorm(img_var_fake.data.detach().numpy()[0] - img_tensor.detach().numpy()).transpose(1,2,0)) + diff --git a/09-Generative-Adversarial-Networks/01-gan-explanation.py b/09-Generative-Adversarial-Networks/01-gan-explanation.py new file mode 100644 index 0000000..c6f0a77 --- /dev/null +++ b/09-Generative-Adversarial-Networks/01-gan-explanation.py @@ -0,0 +1,323 @@ +#!/usr/bin/env python +# coding: utf-8 + +# # 9.1 GAN으로 새로운 패션아이템 생성하기 +# *GAN을 이용하여 새로운 패션 아이템을 만들어봅니다* +# GAN을 구현하기 위해 그 구조를 더 자세히 알아보겠습니다. +# GAN은 생성자(Generator)와 판별자(Discriminator) 2개의 신경망으로 이루어져 있습니다. +# 생성자는 실제 데이터와 비슷한 가짜 데이터를 만들어냅니다. 생성자가 만든 가짜 데이터는 '가짜' 라는 레이블을 부여받고 +# Fashion MNIST의 이미지와 같은 '진짜' 데이터와 함께 판별자에 입력됩니다. +# 그러면 판별자는 진짜와 가짜 데이터를 구분하는 능력을 학습합니다. 여기서 재밌는점은 판별자가 가짜와 진짜를 제대로 분류할 때 마다 생성자에 대한 페널티는 늘어난다는 것입니다. +# 그러므로 생성자는 판별자가 좋은 퍼포먼스를 내는것을 방해하기 위해 더 진짜 데이터와 비슷한 데이터를 생성하게 됩니다. +# 이처럼 GAN은 이름 그대로 판별자와 생성자의 경쟁을 통해서 학습하는 모델입니다. + +# ## GAN 구현하기 + +# 지금까지 해온 것 처럼 구현에 필요한 라이브러리들을 임포트합니다. + +import os +import torch +import torchvision +import torch.nn as nn +import torch.optim as optim +from torchvision import transforms, datasets +from torchvision.utils import save_image +import matplotlib.pyplot as plt + + +# 생성자는 랜덤한 텐서를 입력받아 기존 데이터와 비슷한 데이터를 창작하는 '신경망' 입니다. 그러므로 생성자에 입력되는 랜덤 텐서가 어떻게 설정되느냐에 따라 같은 코드라도 결과물과 퍼포먼스 근소하게 달라질 여지가 있습니다. 그러므로 여러분들이 직접 이 책의 GAN 코드를 보면서 구현한 결과와 책에서 보여주는 결과를 최대한 비슷하게 만들어주기 위해 학습 도중 생성되는 모든 랜덤한 값을 동일하게 설정해 주겠습니다. + +torch.manual_seed(1) # reproducible + + +# EPOCHS 과 BATCH_SIZE 등 학습에 필요한 하이퍼 파라미터 들을 설정해 줍니다. + +# Hyper Parameters +EPOCHS = 100 +BATCH_SIZE = 100 +USE_CDA = torch.cuda.is_available() +DEVICE = -1#torch.device("cuda" if USE_CUDA else "cpu") +print("Using Device:", DEVICE) + + +# 학습에 필요한 데이터셋을 로딩합니다. + +# Fashion MNIST digits dataset +trainset = datasets.FashionMNIST('./.data', + train=True, + download=True, + transform=transforms.Compose([ + transforms.ToTensor(), + transforms.Normalize((0.5,), (0.5,)) + ])) +train_loader = torch.utils.data.DataLoader( + dataset = trainset, + batch_size = BATCH_SIZE, + shuffle = True) + + +# 데이터의 로딩이 끝났으면 GAN의 생성자와 판별자를 구현합니다. +# 지금까지는 신경망 모델들을 파이썬의 객체로써 정의해 주었습니다. 그렇게 함으로써 신경망의 복잡한 기능과 동작들을 함수의 형태로 편리하게 정의해 줄 수 있었습니다. +# 그러나 이번 예제에서 구현할 생성자와 판별자는 비교적 단순한 신경망이므로, 좀 더 간소한 방법을 이용해 정의해 보겠습니다. +# Pytorch가 제공하는 Sequential 자료구조는 신경망의 forward() 동작에 필요한 동작들을 입력받아 이들을 차례대로 실행시키는 신경망 구조체를 만들어 줍니다. +# 생성자는 64차원의 랜덤한 텐서를 입력받아 이에 행렬곱(Linear)과 활성화 함수(ReLU, Tanh) 연산을 실행합니다. 생성자의 결과값은 784차원, 즉 Fashion MNIST 속의 이미지와 같은 차원의 텐서입니다. + +# Generator +G = nn.Sequential( + nn.Linear(64, 256), + nn.ReLU(), + nn.Linear(256, 256), + nn.ReLU(), + nn.Linear(256, 784), + nn.Tanh()) + + +# 판별자는 784차원의 텐서를 입력받습니다. 판별자 역시 입력된 데이터에 행렬곱과 활성화 함수를 실행시키지만, 생성자와 달리 판별자의 결과값은 입력받은 텐서가 진짜 Fashion MNIST 데이터일 확률값입니다. + +# Discriminator +D = nn.Sequential( + nn.Linear(784, 256), + nn.LeakyReLU(0.2), + nn.Linear(256, 256), + nn.LeakyReLU(0.2), + nn.Linear(256, 1), + nn.Sigmoid()) + + +# 생성자와 판별자 학습에 쓰일 오차 함수와 최적화 알고리즘도 정의해 줍니다. + + +# Device setting +# D = D.to(DEVICE) +# G = G.to(DEVICE) + +# Binary cross entropy loss and optimizer +criterion = nn.BCELoss() +d_optimizer = optim.Adam(D.parameters(), lr=0.0002) +g_optimizer = optim.Adam(G.parameters(), lr=0.0002) + + +# 모델 학습에 필요한 준비는 끝났습니다. 그럼 본격적으로 GAN을 학습시키는 loop을 만들어 보겠습니다. + +total_step = len(train_loader) +for epoch in range(EPOCHS): + for i, (images, _) in enumerate(train_loader): + images = images.reshape(BATCH_SIZE, -1)#.to(-1) + + +# 데이터셋 속의 진짜 이미지에는 '진짜' 라는 레이블을, 반대로 생성자가 만든 이미지에는 '가짜'라는 레이블링을 해 줘야 합니다. 이 두 레이블을 나타내는 레이블 텐서를 정의해 줍니다. + +real_labels = torch.ones(BATCH_SIZE, 1)#.to(-1) +fake_labels = torch.zeros(BATCH_SIZE, 1)#.to(-1) + + +# 판별자는 실제 이미지를 보고 '진짜'라고 구분짓는 능력을 학습해야 합니다. 그러기 위해선 실제 이미지를 판별자 신경망에 입력시켜 얻어낸 결과값과 '진짜' 레이블 간의 오차값을 계산해야 합니다. + +outputs = D(images) +d_loss_real = criterion(outputs, real_labels) +real_score = outputs + + +# 다음으로는 생성자의 동작을 정의합니다. 생성자는 무작위한 텐서를 입력받아 실제 이미지와 같은 차원의 텐서를 배출해야합니다. + +z = torch.randn(BATCH_SIZE, 64)#.to(-1) +fake_images = G(z) + + +# 생성자가 만들어낸 fake_images를 판별자에 입력합니다. 이번엔 결과값과 '가짜' 레이블 간의 오차를 계산해야 합니다. + +outputs = D(fake_images) +d_loss_fake = criterion(outputs, fake_labels) +fake_score = outputs + + +# 실제 데이터와 가짜 데이터를 가지고 낸 오차를 더해줌으로써 판별자 신경망의 전체 오차가 계산됩니다. +# 그 다음 과정은 역전파 알고리즘과 경사 하강법을 통하여 판별자 신경망을 학습시키는 겁니다. + +d_loss = d_loss_real + d_loss_fake +d_optimizer.zero_grad() +d_loss.backward() +d_optimizer.step() + + +# 판별자를 학습시키는 코드를 모두 작성했으면 이제 생성자를 학습시킬 차례입니다. +# 생성자가 더 진짜같은 데이터셋을 만들어내려면, 생성자가 만들어낸 가짜 이미지를 판별자가 진짜 라고 착각하게 만들어야 합니다. +# 즉, 생성자의 결과물을 다시 판별자에 입력시켜, 그 결과물과 real_labels간의 오차를 최소화 시키는 식으로 학습을 진행해야 합니다. + +fake_images = G(z) +outputs = D(fake_images) +g_loss = criterion(outputs, real_labels) + + +# 그리고 마찬가지로 경사 하강법과 역전파 알고리즘을 사용해서 모델의 학습을 완료합니다. + +d_optimizer.zero_grad() +g_optimizer.zero_grad() +g_loss.backward() +g_optimizer.step() + + +# 학습을 진행하는 동안 오차를 확인하고 생성자의 결과물을 시각화하는 코드 또한 추가시켰습니다. + +if (i+1) % 200 == 0: + print('Epoch [{}/{}], Step [{}/{}], d_loss: {:.4f}, g_loss: {:.4f}, D(x): {:.2f}, D(G(z)): {:.2f}' + .format(epoch, EPOCHS, i+1, total_step, d_loss.item(), g_loss.item(), + real_score.mean().item(), fake_score.mean().item())) + +if (epoch+1) % 10 == 0 and (i+1) % 100 == 0 : + fake_images = np.reshape(fake_images.data.numpy()[0],(28, 28)) + plt.imshow(fake_images, cmap = 'gray') + plt.show() + + +# 학습이 끝난 생성자의 결과물을 한번 확인해 보겠습니다. + +# ![generated_image0](./assets/generated_image0.png) +# ![generated_image1](./assets/generated_image1.png) +# ![generated_image2](./assets/generated_image2.png) +# ![generated_image3](./assets/generated_image3.png) +# ![generated_image4](./assets/generated_image4.png) + + + + + + + + + + + + + +# 전체 코드 + +import os +import torch +import torchvision +import torch.nn as nn +import torch.optim as optim +from torchvision import transforms, datasets +from torchvision.utils import save_image +import matplotlib.pyplot as plt +import numpy as np + + +torch.manual_seed(1) # reproducible + + +# Hyper Parameters +EPOCHS = 100 +BATCH_SIZE = 100 +USE_CDA = torch.cuda.is_available() +DEVICE = torch.device("cuda" if USE_CUDA else "cpu") + +print("Using Device:", DEVICE) + + +# Fashion MNIST digits dataset +trainset = datasets.FashionMNIST('./.data', + train=True, + download=True, + transform=transforms.Compose([ + transforms.ToTensor(), + transforms.Normalize((0.5,), (0.5,)) + ])) +train_loader = torch.utils.data.DataLoader( + dataset = trainset, + batch_size = BATCH_SIZE, + shuffle = True) + + +# Discriminator +D = nn.Sequential( + nn.Linear(784, 256), + nn.LeakyReLU(0.2), + nn.Linear(256, 256), + nn.LeakyReLU(0.2), + nn.Linear(256, 1), + nn.Sigmoid()) + + +# Generator +G = nn.Sequential( + nn.Linear(64, 256), + nn.ReLU(), + nn.Linear(256, 256), + nn.ReLU(), + nn.Linear(256, 784), + nn.Tanh()) + + + +# Device setting +D = D.to(DEVICE) +G = G.to(DEVICE) + +# Binary cross entropy loss and optimizer +criterion = nn.BCELoss() +d_optimizer = optim.Adam(D.parameters(), lr=0.0002) +g_optimizer = optim.Adam(G.parameters(), lr=0.0002) + + +total_step = len(train_loader) +for epoch in range(EPOCHS): + for i, (images, _) in enumerate(train_loader): + images = images.reshape(BATCH_SIZE, -1).to(DEVICE) + + # Create the labels which are later used as input for the BCE loss + real_labels = torch.ones(BATCH_SIZE, 1).to(DEVICE) + fake_labels = torch.zeros(BATCH_SIZE, 1).to(DEVICE) + + # Train Discriminator + + # Compute BCE_Loss using real images where BCE_Loss(x, y): - y * log(D(x)) - (1-y) * log(1 - D(x)) + # Second term of the loss is always zero since real_labels == 1 + outputs = D(images) + d_loss_real = criterion(outputs, real_labels) + real_score = outputs + + # Compute BCELoss using fake images + # First term of the loss is always zero since fake_labels == 0 + z = torch.randn(BATCH_SIZE, 64).to(DEVICE) + fake_images = G(z) + outputs = D(fake_images) + d_loss_fake = criterion(outputs, fake_labels) + fake_score = outputs + + # Backprop and optimize + d_loss = d_loss_real + d_loss_fake + d_optimizer.zero_grad() + d_loss.backward() + d_optimizer.step() + + # Train Generator + + # Compute loss with fake images + z = torch.randn(BATCH_SIZE, 64).to(DEVICE) + fake_images = G(z) + outputs = D(fake_images) + + # We train G to maximize log(D(G(z)) instead of minimizing log(1-D(G(z))) + # For the reason, see the last paragraph of section 3. https://arxiv.org/pdf/1406.2661.pdf + g_loss = criterion(outputs, real_labels) + + # Backprop and optimize + d_optimizer.zero_grad() + g_optimizer.zero_grad() + g_loss.backward() + g_optimizer.step() + + if (i+1) % 200 == 0: + print('Epoch [{}/{}], Step [{}/{}], d_loss: {:.4f}, g_loss: {:.4f}, D(x): {:.2f}, D(G(z)): {:.2f}' + .format(epoch, EPOCHS, i+1, total_step, d_loss.item(), g_loss.item(), + real_score.mean().item(), fake_score.mean().item())) + if (epoch+1) % 10 == 0 and (i+1) % 100 == 0 : + fake_images = np.reshape(fake_images.data.numpy()[0],(28, 28)) + plt.imshow(fake_images, cmap = 'gray') + plt.show() + + +# ## 참고 +# 본 튜토리얼은 다음 자료를 참고하여 만들어졌습니다. +# * [yunjey/pytorch-tutorial](https://github.com/yunjey/pytorch-tutorial) - MIT License diff --git a/09-Generative-Adversarial-Networks/01-gan.py b/09-Generative-Adversarial-Networks/01-gan.py new file mode 100644 index 0000000..3d2a977 --- /dev/null +++ b/09-Generative-Adversarial-Networks/01-gan.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python +# coding: utf-8 + +# # 9.1 GAN으로 새로운 패션아이템 생성하기 +# *GAN을 이용하여 새로운 패션 아이템을 만들어봅니다* +# GAN을 구현하기 위해 그 구조를 더 자세히 알아보겠습니다. +# GAN은 생성자(Generator)와 판별자(Discriminator) 2개의 신경망으로 +# 이루어져 있습니다. +# ## GAN 구현하기 + +import os +import torch +import torchvision +import torch.nn as nn +import torch.optim as optim +from torchvision import transforms, datasets +from torchvision.utils import save_image + + +torch.manual_seed(1) # reproducible + + +# Hyper Parameters +EPOCHS = 100 +BATCH_SIZE = 100 +USE_CUDA = torch.cuda.is_available() +DEVICE = torch.device("cuda" if USE_CUDA else "cpu") +print("Using Device:", DEVICE) + + +# Fashion MNIST digits dataset +trainset = datasets.FashionMNIST('./.data', + train=True, + download=True, + transform=transforms.Compose([ + transforms.ToTensor(), + transforms.Normalize((0.5,), (0.5,)) + ])) +train_loader = torch.utils.data.DataLoader( + dataset = trainset, + batch_size = BATCH_SIZE, + shuffle = True) + + +# Discriminator +D = nn.Sequential( + nn.Linear(784, 256), + nn.LeakyReLU(0.2), + nn.Linear(256, 256), + nn.LeakyReLU(0.2), + nn.Linear(256, 1), + nn.Sigmoid()) + + +# Generator +G = nn.Sequential( + nn.Linear(64, 256), + nn.ReLU(), + nn.Linear(256, 256), + nn.ReLU(), + nn.Linear(256, 784), + nn.Tanh()) + + + +# Device setting +D = D.to(DEVICE) +G = G.to(DEVICE) + +# Binary cross entropy loss and optimizer +criterion = nn.BCELoss() +d_optimizer = optim.Adam(D.parameters(), lr=0.0002) +g_optimizer = optim.Adam(G.parameters(), lr=0.0002) + + +total_step = len(train_loader) +for epoch in range(EPOCHS): + for i, (images, _) in enumerate(train_loader): + images = images.reshape(BATCH_SIZE, -1).to(DEVICE) + + # Create the labels which are later used as input for the BCE loss + real_labels = torch.ones(BATCH_SIZE, 1).to(DEVICE) + fake_labels = torch.zeros(BATCH_SIZE, 1).to(DEVICE) + + # Train Discriminator + + # Compute BCE_Loss using real images where BCE_Loss(x, y): - y * log(D(x)) - (1-y) * log(1 - D(x)) + # Second term of the loss is always zero since real_labels == 1 + outputs = D(images) + d_loss_real = criterion(outputs, real_labels) + real_score = outputs + + # Compute BCELoss using fake images + # First term of the loss is always zero since fake_labels == 0 + z = torch.randn(BATCH_SIZE, 64).to(DEVICE) + fake_images = G(z) + outputs = D(fake_images) + d_loss_fake = criterion(outputs, fake_labels) + fake_score = outputs + + # Backprop and optimize + d_loss = d_loss_real + d_loss_fake + d_optimizer.zero_grad() + d_loss.backward() + d_optimizer.step() + + # Train Generator + + # Compute loss with fake images + z = torch.randn(BATCH_SIZE, 64).to(DEVICE) + fake_images = G(z) + outputs = D(fake_images) + + # We train G to maximize log(D(G(z)) instead of minimizing log(1-D(G(z))) + # For the reason, see the last paragraph of section 3. https://arxiv.org/pdf/1406.2661.pdf + g_loss = criterion(outputs, real_labels) + + # Backprop and optimize + d_optimizer.zero_grad() + g_optimizer.zero_grad() + g_loss.backward() + g_optimizer.step() + + if (i+1) % 200 == 0: + print('Epoch [{}/{}], Step [{}/{}], d_loss: {:.4f}, g_loss: {:.4f}, D(x): {:.2f}, D(G(z)): {:.2f}' + .format(epoch, EPOCHS, i+1, total_step, d_loss.item(), g_loss.item(), + real_score.mean().item(), fake_score.mean().item())) + + +# ## 참고 +# 본 튜토리얼은 다음 자료를 참고하여 만들어졌습니다. +# * [yunjey/pytorch-tutorial](https://github.com/yunjey/pytorch-tutorial) - MIT License diff --git a/09-Generative-Adversarial-Networks/02-conditional-gan.py b/09-Generative-Adversarial-Networks/02-conditional-gan.py new file mode 100644 index 0000000..c05f55f --- /dev/null +++ b/09-Generative-Adversarial-Networks/02-conditional-gan.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python +# coding: utf-8 + +# # Conditional GAN으로 생성 컨트롤하기 + +import os +import torch +import torchvision +import torch.nn as nn +import torch.optim as optim +from torchvision import transforms, datasets +from torchvision.utils import save_image +import matplotlib.pyplot as plt + + +torch.manual_seed(1) # reproducible + + +# Hyper Parameters +EPOCHS = 100 +BATCH_SIZE = 100 +USE_CDA = torch.cuda.is_available() +DEVICE = -1#torch.device("cuda" if USE_CUDA else "cpu") +print("Using Device:", DEVICE) + + +# Fashion MNIST digits dataset +trainset = datasets.FashionMNIST('./.data', + train=True, + download=True, + transform=transforms.Compose([ + transforms.ToTensor(), + transforms.Normalize((0.5,), (0.5,)) + ])) +train_loader = torch.utils.data.DataLoader( + dataset = trainset, + batch_size = BATCH_SIZE, + shuffle = True) + + + + + +def one_hot_embedding(labels, num_classes): + y = torch.eye(num_classes) + return y[labels] + + +# Discriminator +D = nn.Sequential( + nn.Linear(784, 256), + nn.LeakyReLU(0.2), + nn.Linear(256, 256), + nn.LeakyReLU(0.2), + nn.Linear(256, 1), + nn.Sigmoid()) + + +# Generator +G = nn.Sequential( + nn.Linear(64 + 10, 256), + nn.ReLU(), + nn.Linear(256, 256), + nn.ReLU(), + nn.Linear(256, 784), + nn.Tanh()) + + + +# Device setting +# D = D.to(DEVICE) +# G = G.to(DEVICE) + +# Binary cross entropy loss and optimizer +criterion = nn.BCELoss() +d_optimizer = optim.Adam(D.parameters(), lr=0.0002) +g_optimizer = optim.Adam(G.parameters(), lr=0.0002) + + + + + +total_step = len(train_loader) +for epoch in range(EPOCHS): + for i, (images, label) in enumerate(train_loader): + images = images.reshape(BATCH_SIZE, -1)#.to(DEVICE) + + real_labels = torch.ones(BATCH_SIZE, 1)#.to(DEVICE) + fake_labels = torch.zeros(BATCH_SIZE, 1)#.to(DEVICE) + + outputs = D(images) + d_loss_real = criterion(outputs, real_labels) + real_score = outputs + + class_label = one_hot_embedding(label, 10) + z = torch.randn(BATCH_SIZE, 64)#.to(DEVICE) + + generator_input = torch.cat([z, class_label], 1) + + fake_images= G(generator_input) + + outputs = D(fake_images) + d_loss_fake = criterion(outputs, fake_labels) + fake_score = outputs + + # Backprop and optimize + d_loss = d_loss_real + d_loss_fake + d_optimizer.zero_grad() + d_loss.backward() + d_optimizer.step() + + # Train Generator + + # Compute loss with fake images + fake_images = G(generator_input) + outputs = D(fake_images) + + g_loss = criterion(outputs, real_labels) + + # Backprop and optimize + d_optimizer.zero_grad() + g_optimizer.zero_grad() + g_loss.backward() + g_optimizer.step() + + if (i+1) % 200 == 0: + print('Epoch [{}/{}], Step [{}/{}], d_loss: {:.4f}, g_loss: {:.4f}, D(x): {:.2f}, D(G(z)): {:.2f}' + .format(epoch, EPOCHS, i+1, total_step, d_loss.item(), g_loss.item(), + real_score.mean().item(), fake_score.mean().item())) + if (epoch+1) % 10 == 0 and (i+1) % 100 == 0 : + fake_images = np.reshape(fake_images.data.numpy()[0],(28, 28)) + plt.imshow(fake_images, cmap = 'gray') + plt.show() + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/10-DQN-Learns-From-Environment/01-cartpole-dqn.py b/10-DQN-Learns-From-Environment/01-cartpole-dqn.py index 326f036..b49cbb2 100644 --- a/10-DQN-Learns-From-Environment/01-cartpole-dqn.py +++ b/10-DQN-Learns-From-Environment/01-cartpole-dqn.py @@ -1,6 +1,25 @@ - +#!/usr/bin/env python # coding: utf-8 +# # 카트폴 게임 마스터하기 +# 어떤 게임을 마스터한다는 뜻은 최고의 점수를 받는다는 뜻이기도 합니다. +# 그러므로 게임의 점수를 리워드로 취급하면 될 것 같습니다. +# 우리가 만들 에이전트는 리워드를 예측하고, +# 리워드를 최대로 만드는 쪽으로 학습하게 할 것입니다. +# 예를들어 카트폴 게임에서는 막대기를 세우고 오래 버틸수록 점수가 증가합니다. +# 카트폴 게임에서 막대가 오른쪽으로 기울었을때, +# 어느 동작이 가장 큰 리워드를 준다고 예측할 수 있을까요? +# 오른쪽으로 가서 중심을 다시 맞춰야 하니 +# 오른쪽 버튼을 누르는 쪽이 왼쪽 버튼보다 리워드가 클 것이라고 예측 할 수 있습니다. +# 이것을 한줄로 요약하자면 아래 한줄의 코드가 됩니다. +# ``` +# target = reward + gamma * np.amax(model.predict(next_state)) +# ``` +# DQN은 가장 중요한 특징 2가지로 요약될 수 있습니다. +# 바로 기억하기(Remember)와 다시 보기(Replay)입니다. +# 둘다 간단한 아이디어이지만 신경망이 강화학습에 이용될 수 있게 만든 혁명적인 방법들입니다. +# 순서대로 개념과 구현법을 알아보도록 하겠습니다. + import gym from gym import wrappers import random @@ -15,9 +34,15 @@ import numpy as np +# ## OpenAI Gym을 이용하여 게임환경 구축하기 +# 강화학습 예제들을 보면 항상 게임과 연관되어 있습니다. 원래 우리가 궁극적으로 원하는 목표는 어디서든 적응할 수 있는 인공지능이지만, 너무 복잡한 문제이기도 하고 가상 환경을 설계하기도 어렵기 때문에 일단 게임이라는 환경을 사용해 하는 것입니다. +# 대부분의 게임은 점수 혹은 목표가 있습니다. 점수가 오르거나 목표에 도달하면 일종의 리워드를 받고 원치 않은 행동을 할때는 마이너스 리워드를 주는 경우도 있습니다. 아까 비유를 들었던 달리기를 배울때의 경우를 예로 들면 총 나아간 길이 혹은 목표 도착지 도착 여부로 리워드를 주고 넘어질때 패널티를 줄 수 있을 것입니다. +# 게임중에서도 가장 간단한 카트폴이라는 환경을 구축하여 강화학습을 배울 토대를 마련해보겠습니다. + env = gym.make('CartPole-v1') +# ### 하이퍼파라미터 # hyper parameters EPISODES = 50 # number of episodes EPS_START = 0.9 # e-greedy threshold start value @@ -25,16 +50,99 @@ EPS_DECAY = 200 # e-greedy threshold decay GAMMA = 0.8 # Q-learning discount factor LR = 0.001 # NN optimizer learning rate -HIDDEN_LAYER = 256 # NN hidden layer size BATCH_SIZE = 64 # Q-learning batch size +# ## DQN 에이전트 +# DQNAgent라는 클래스를 만들어 +# ```python +# class DQNAgent +# ``` +# ### DQN 에이전트의 뇌, 뉴럴넷 +# ![dqn_net](./assets/dqn_net.png) +# ```python +# self.model = nn.Sequential( +# nn.Linear(4, 256), +# nn.ReLU(), +# nn.Linear(256, 2) +# ) +# ``` +# ### 행동하기 (Act) +# ### 전 경험 기억하기 (Remember) +# 신경망을 Q-learning학습에 처음 적용하면서 맞닥뜨린 문제는 +# 바로 신경망이 새로운 경험을 전 경험에 겹쳐쓰며 쉽게 잊어버린다는 것이었습니다. +# 그래서 나온 해결책이 바로 기억하기(Remember)라는 기능인데요, +# 바로 이전 경험들을 배열에 담아 계속 재학습 시키며 신경망이 까먹지 않게 하는 아이디어 입니다. +# 각 경험은 상태, 행동, 보상등을 담아야 합니다. +# 이전 경험들을 담을 배열을 `memory`라고 부르고 아래와 같이 만들어봅시다. +# ```python +# self.memory = [(상태, 행동, 보상, 다음 상태)...] +# ``` +# 이를 구현하기 위해 복잡한 모델을 만들때는 Memory클래스를 구현하기도 하지만, +# 이번 예제에서는 사용하기 가장 간단한 deque (double ended queue), +# 즉 큐(queue) 자료구조를 이용할 것입니다. +# 파이썬에서 `deque`의 `maxlen`을 지정해주었을때 큐가 가득 찼을 경우 +# 제일 오래된 요소부터 없어지므로 +# 자연스레 오래된 기억을 까먹게 해주는 역할을 할 수 있습니다. +# ```python +# self.memory = deque(maxlen=10000) +# ``` +# 그리고 memory 배열에 새로운 경험을 덧붙일 remember() 함수를 만들어보겠습니다. +# ```python +# def memorize(self, state, action, reward, next_state): +# self.memory.append((state, +# action, +# torch.FloatTensor([reward]), +# torch.FloatTensor([next_state]))) +# ``` +# ### 경험으로부터 배우기 (Experience Replay) +# 이전 경험들을 모아놨으면 반복적으로 학습해야합니다. +# 사람도 수면중일때 자동차 운전, 농구 슈팅, +# 등 운동과 관련된 정보를 정리하며, +# 단기 기억을 운동피질에서 측두엽으로 전달하여 장기 기억으로 변환시킨다고 합니다. +# 우연하게도 DQN에이전트가 기억하고 다시 상기하는 과정도 비슷한 개념입니다. +# `learn`함수는 바로 이런 개념으로 방금 만들어둔 뉴럴넷인 `model`을 +# `memory`에 쌓인 경험을 토대로 학습시키는 역할을 합니다. +# ```python +# def learn(self): +# """Experience Replay""" +# if len(self.memory) < BATCH_SIZE: +# return +# batch = random.sample(self.memory, BATCH_SIZE) +# states, actions, rewards, next_states = zip(*batch) +# ``` +# `self.memory`에서 무작위로 배치 크기만큼의 "경험"들을 가져옵니다. +# 이 예제에선 배치사이즈를 64개로 정했습니다. +# ```python +# states = torch.cat(states) +# actions = torch.cat(actions) +# rewards = torch.cat(rewards) +# next_states = torch.cat(next_states) +# ``` +# 각각의 경험들은 상태(`states`), 행동(`actions`), 행동에 따른 보상(`rewards`), +# 그리고 다음 상태(`next_states`)를 담고있습니다. +# 모두 리스트의 리스트 형태이므로 `torch.cat()`을 이용하여 하나의 리스트로 만듭니다. +# `cat`은 concatenate의 준말로 결합하다, 혹은 연결하다라는 뜻입니다. +# ```python +# current_q = self.model(states).gather(1, actions) +# max_next_q = self.model(next_states).detach().max(1)[0] +# expected_q = rewards + (GAMMA * max_next_q) +# ``` +# Q값을 구합니다. +# ```python +# loss = F.mse_loss(current_q.squeeze(), expected_q) +# self.optimizer.zero_grad() +# loss.backward() +# self.optimizer.step() +# ``` +# 학습시킵니다. + class DQNAgent: def __init__(self): self.model = nn.Sequential( - nn.Linear(4, HIDDEN_LAYER), + nn.Linear(4, 256), nn.ReLU(), - nn.Linear(HIDDEN_LAYER, 2) + nn.Linear(256, 2) ) self.memory = deque(maxlen=10000) self.optimizer = optim.Adam(self.model.parameters(), LR) @@ -76,11 +184,54 @@ def learn(self): self.optimizer.step() +# ## 학습 준비하기 +# 드디어 만들어둔 DQNAgent를 인스턴스화 합니다. +# 그리고 `gym`을 이용하여 `CartPole-v0`환경도 준비합니다. +# 자, 이제 `agent` 객체를 이용하여 `CartPole-v0` 환경과 상호작용을 통해 게임을 배우도록 하겠습니다. +# 학습 진행을 기록하기 위해 `score_history` 리스트를 이용하여 점수를 저장하겠습니다. + agent = DQNAgent() +env = gym.make('CartPole-v0') +score_history = [] -env = gym.make('CartPole-v0') -episode_durations = [] +# ## 학습 시작하기 +# EPISODES는 얼마나 많은 게임을 진행하느냐를 나타내는 하이퍼파라미터입니다. +# ``` +# for e in range(1, EPISODES+1): +# state = env.reset() +# steps = 0 +# ``` +# `done`변수에는 게임이 끝났는지의 여부가 참(True), 거짓(False)로 표현됩니다. +# ``` +# while True: +# env.render() +# state = torch.FloatTensor([state]) +# action = agent.act(state) +# next_state, reward, done, _ = env.step(action.item()) +# ``` +# 우리의 에이전트가 한 행동의 결과가 나왔습니다! +# 이 경험을 기억(memorize)하고 배우도록합니다. +# ``` +# # negative reward when attempt ends +# if done: +# reward = -1 +# agent.memorize(state, action, reward, next_state) +# agent.learn() +# state = next_state +# steps += 1 +# ``` +# 게임이 끝났을 경우 `done`이 `True`가 되며 아래 코드가 실행되게 됩니다. +# 보통 게임 분석을 위해 복잡한 도구와 코드가 사용되는 경우가 많으나 +# 여기서는 간단하게 에피소드 숫자와 점수만 표기하도록 하겠습니다. +# 또 앞서 만들어둔 `score_history` 리스트에 점수를 담도록 합니다. +# 마지막으로 게임이 더 이상 진행되지 않으므로 `break` 문으로 무한루프를 나옵니다. +# ``` +# if done: +# print("에피소드:{0} 점수: {1}".format(e, steps)) +# score_history.append(steps) +# break +# ``` for e in range(1, EPISODES+1): state = env.reset() @@ -102,8 +253,10 @@ def learn(self): steps += 1 if done: - print("{2} Episode {0} finished after {1} steps" - .format(e, steps, '\033[92m' if steps >= 195 else '\033[99m')) - episode_durations.append(steps) + print("에피소드:{0} 점수: {1}".format(e, steps)) + score_history.append(steps) break + + +