Skip to content

Instantly share code, notes, and snippets.

@nicewook
Created June 7, 2017 03:05
Show Gist options
  • Save nicewook/03e8846f84295f9f08b77de91749d385 to your computer and use it in GitHub Desktop.
Save nicewook/03e8846f84295f9f08b77de91749d385 to your computer and use it in GitHub Desktop.
rnn toy code - korean comment
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Anyone Can Learn To Code an LSTM-RNN in Python (Part 1: RNN)\n",
"\n",
"원문링크: https://iamtrask.github.io/2015/11/15/anyone-can-code-lstm/ \n",
"- 간단한 toy code를 통해 RNN을 이해해본다.\n",
"- 바이트 연산 - 더하기를 구하는 것이다. \n",
"\n",
"바이트 연산으로 3 + 4 = 7 이 되는 것을 RNN으로 해볼 것이다.\n",
"각 2진수의 자리 연산마다 **carry** 값이 있을 것인데, carry는 결국 이전의 연산 결과들 정보들 묶음이다. \n",
"이것이 바로 **hidden memory**라 보면 된다. \n",
"\n",
" 0000 0011(b) + 0000 0100(b) = 0000 0111(b) 이다. \n",
"\n",
" 각 time step에 input은 두개이다. 가장 낮은 자리수 부터 1개씩\n",
"\t가장 낮은 자리수인 맨 오른쪽부터 더해갈 것이며, carry 값을 기억할 것이다. \n",
"\t\n",
"\t두개의 input은 forward propagation 되어 hidden layer로 들어갈 것이고,\n",
"\thidden layer는 carry 값이 있는지 없는지를 기억할 것이다. \n",
"\t\n",
" prediction은 이 모든 정보들을 바탕으로 각 자릿수 (= time step)의 값을 예측할 것이다. \n"
]
},
{
"cell_type": "code",
"execution_count": 46,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# numpy와 copy를 import 하고 random 값의 seed를 심는다\n",
"# copy는 object를 복제하는데 쓰인다 https://pymotw.com/2/copy/\n",
"import copy, numpy as np\n",
"import matplotlib.pyplot as plt\n",
"np.random.seed(0)\n",
"\n",
"%matplotlib inline"
]
},
{
"cell_type": "code",
"execution_count": 47,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# utility 함수들 구현\n",
"# 시그모이드 함수 구현\n",
"def sigmoid(x):\n",
" output = 1 / (1 + np.exp(-x))\n",
" return output\n",
"\n",
"# convert output of sigmoid function to its derivative\n",
"# 시그모이드 함수의 미분\n",
"# http://math.stackexchange.com/questions/78575/derivative-of-sigmoid-function-sigma-x-frac11e-x\n",
"def sigmoid_output_to_derivative(output):\n",
" return output * (1 - output)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## train set을 만들자\n",
"\n",
"- 랜덤한 값 2개를 만들고 더한 값을 train set으로 한다. \n",
"- 이렇게 계속 랜덤하게 생성하도록 한다"
]
},
{
"cell_type": "code",
"execution_count": 48,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# train set을 위한 lookup table을 만든다. dictionary로 1:00000001 이런 식으로 미리 만들어두는 것\n",
"int2binary = {} # lookup table 이다.\n",
"binary_dim = 8 # 이진수는 최대 8자리. 이걸 넘어가진 않는다고 제한해두자. toy code 잖아?\n",
"largest_number = pow(2, binary_dim) # 따라서 최대값은 2^8 -1 이 되겠지? 1 0000 0000 - 1 = 1111 1111\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 이제 lookup table을 채워넣자\n",
"\n",
"우선 binary에 2진수 값을 만들어서 넣어주자\n",
" - numpy.unpackbits 는 숫자를 2진수 리스트로 만들어준다. 예를 들어 [7] 을 [ 0,0,0,0,1,1,1,1]로 바꿔준다.\n",
" - https://docs.scipy.org/doc/numpy/reference/generated/numpy.unpackbits.html\n"
]
},
{
"cell_type": "code",
"execution_count": 49,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[0 0 1 1 1 1 1 1]\n"
]
}
],
"source": [
"# numpy.unpackbits 테스트\n",
"print(np.unpackbits(np.array([63], dtype=np.uint8)))"
]
},
{
"cell_type": "code",
"execution_count": 50,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1. Just range()\n",
"(256,) [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17\n",
" 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35\n",
" 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53\n",
" 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71\n",
" 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89\n",
" 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107\n",
" 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125\n",
" 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143\n",
" 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161\n",
" 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179\n",
" 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197\n",
" 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215\n",
" 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233\n",
" 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251\n",
" 252 253 254 255]\n",
"2. 2-d list\n",
"(1, 256) [[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17\n",
" 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35\n",
" 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53\n",
" 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71\n",
" 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89\n",
" 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107\n",
" 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125\n",
" 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143\n",
" 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161\n",
" 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179\n",
" 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197\n",
" 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215\n",
" 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233\n",
" 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251\n",
" 252 253 254 255]]\n",
"transpose\n",
"(256, 1) [[ 0]\n",
" [ 1]\n",
" [ 2]\n",
" [ 3]\n",
" [ 4]\n",
" [ 5]\n",
" [ 6]\n",
" [ 7]\n",
" [ 8]\n",
" [ 9]\n",
" [ 10]\n",
" [ 11]\n",
" [ 12]\n",
" [ 13]\n",
" [ 14]\n",
" [ 15]\n",
" [ 16]\n",
" [ 17]\n",
" [ 18]\n",
" [ 19]\n",
" [ 20]\n",
" [ 21]\n",
" [ 22]\n",
" [ 23]\n",
" [ 24]\n",
" [ 25]\n",
" [ 26]\n",
" [ 27]\n",
" [ 28]\n",
" [ 29]\n",
" [ 30]\n",
" [ 31]\n",
" [ 32]\n",
" [ 33]\n",
" [ 34]\n",
" [ 35]\n",
" [ 36]\n",
" [ 37]\n",
" [ 38]\n",
" [ 39]\n",
" [ 40]\n",
" [ 41]\n",
" [ 42]\n",
" [ 43]\n",
" [ 44]\n",
" [ 45]\n",
" [ 46]\n",
" [ 47]\n",
" [ 48]\n",
" [ 49]\n",
" [ 50]\n",
" [ 51]\n",
" [ 52]\n",
" [ 53]\n",
" [ 54]\n",
" [ 55]\n",
" [ 56]\n",
" [ 57]\n",
" [ 58]\n",
" [ 59]\n",
" [ 60]\n",
" [ 61]\n",
" [ 62]\n",
" [ 63]\n",
" [ 64]\n",
" [ 65]\n",
" [ 66]\n",
" [ 67]\n",
" [ 68]\n",
" [ 69]\n",
" [ 70]\n",
" [ 71]\n",
" [ 72]\n",
" [ 73]\n",
" [ 74]\n",
" [ 75]\n",
" [ 76]\n",
" [ 77]\n",
" [ 78]\n",
" [ 79]\n",
" [ 80]\n",
" [ 81]\n",
" [ 82]\n",
" [ 83]\n",
" [ 84]\n",
" [ 85]\n",
" [ 86]\n",
" [ 87]\n",
" [ 88]\n",
" [ 89]\n",
" [ 90]\n",
" [ 91]\n",
" [ 92]\n",
" [ 93]\n",
" [ 94]\n",
" [ 95]\n",
" [ 96]\n",
" [ 97]\n",
" [ 98]\n",
" [ 99]\n",
" [100]\n",
" [101]\n",
" [102]\n",
" [103]\n",
" [104]\n",
" [105]\n",
" [106]\n",
" [107]\n",
" [108]\n",
" [109]\n",
" [110]\n",
" [111]\n",
" [112]\n",
" [113]\n",
" [114]\n",
" [115]\n",
" [116]\n",
" [117]\n",
" [118]\n",
" [119]\n",
" [120]\n",
" [121]\n",
" [122]\n",
" [123]\n",
" [124]\n",
" [125]\n",
" [126]\n",
" [127]\n",
" [128]\n",
" [129]\n",
" [130]\n",
" [131]\n",
" [132]\n",
" [133]\n",
" [134]\n",
" [135]\n",
" [136]\n",
" [137]\n",
" [138]\n",
" [139]\n",
" [140]\n",
" [141]\n",
" [142]\n",
" [143]\n",
" [144]\n",
" [145]\n",
" [146]\n",
" [147]\n",
" [148]\n",
" [149]\n",
" [150]\n",
" [151]\n",
" [152]\n",
" [153]\n",
" [154]\n",
" [155]\n",
" [156]\n",
" [157]\n",
" [158]\n",
" [159]\n",
" [160]\n",
" [161]\n",
" [162]\n",
" [163]\n",
" [164]\n",
" [165]\n",
" [166]\n",
" [167]\n",
" [168]\n",
" [169]\n",
" [170]\n",
" [171]\n",
" [172]\n",
" [173]\n",
" [174]\n",
" [175]\n",
" [176]\n",
" [177]\n",
" [178]\n",
" [179]\n",
" [180]\n",
" [181]\n",
" [182]\n",
" [183]\n",
" [184]\n",
" [185]\n",
" [186]\n",
" [187]\n",
" [188]\n",
" [189]\n",
" [190]\n",
" [191]\n",
" [192]\n",
" [193]\n",
" [194]\n",
" [195]\n",
" [196]\n",
" [197]\n",
" [198]\n",
" [199]\n",
" [200]\n",
" [201]\n",
" [202]\n",
" [203]\n",
" [204]\n",
" [205]\n",
" [206]\n",
" [207]\n",
" [208]\n",
" [209]\n",
" [210]\n",
" [211]\n",
" [212]\n",
" [213]\n",
" [214]\n",
" [215]\n",
" [216]\n",
" [217]\n",
" [218]\n",
" [219]\n",
" [220]\n",
" [221]\n",
" [222]\n",
" [223]\n",
" [224]\n",
" [225]\n",
" [226]\n",
" [227]\n",
" [228]\n",
" [229]\n",
" [230]\n",
" [231]\n",
" [232]\n",
" [233]\n",
" [234]\n",
" [235]\n",
" [236]\n",
" [237]\n",
" [238]\n",
" [239]\n",
" [240]\n",
" [241]\n",
" [242]\n",
" [243]\n",
" [244]\n",
" [245]\n",
" [246]\n",
" [247]\n",
" [248]\n",
" [249]\n",
" [250]\n",
" [251]\n",
" [252]\n",
" [253]\n",
" [254]\n",
" [255]]\n",
"np.unpackbits\n",
"(256, 8) [[0 0 0 ..., 0 0 0]\n",
" [0 0 0 ..., 0 0 1]\n",
" [0 0 0 ..., 0 1 0]\n",
" ..., \n",
" [1 1 1 ..., 1 0 1]\n",
" [1 1 1 ..., 1 1 0]\n",
" [1 1 1 ..., 1 1 1]]\n"
]
}
],
"source": [
"# 0 부터 우리가 다룰 최대값(largest_number-1)까지의 숫자를 2진수의 리스트로 변환해두자\n",
"# 눈으로 알아보기 쉽게 단계별로 본다\n",
"\n",
"# range만 해도 리스트를 만든다\n",
"binary = np.array(range(largest_number), dtype = np.uint8)\n",
"print(\"1. Just range()\")\n",
"print(binary.shape, binary)\n",
"\n",
"# range만 해도 리스트를 만드는데 다시 []를 먹였으니 리스트가 2개인게 보인다.\n",
"binary = np.array([range(largest_number)], dtype = np.uint8)\n",
"print(\"2. 2-d list\")\n",
"print(binary.shape, binary)\n",
"\n",
"# 이걸 transpose 해준다. \n",
"binary = binary.T\n",
"print('transpose')\n",
"print(binary.shape, binary)\n",
"\n",
"# 마지막으로 np.unpackbits 를 먹인다. \n",
"print('np.unpackbits')\n",
"binary = np.unpackbits(np.array([range(largest_number)], dtype = np.uint8).T, axis=1)\n",
"print(binary.shape, binary)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 이제 딕셔너리에 넣어주자\n",
"\n",
" - 이러면 int2binary[2] 라고 하면 2의 binary 모양인 [0,0,0,0,0,0,1,0] 를 토해내게 된다.\n",
" - 애써 이렇게 할 필요는 없는데 편의, 잘 보여지게 하려고 이렇게 구현함\n",
" \n",
" - 위에서 (256,8) 형태의 list를 만들었고 이걸 딕셔너리에 넣어보자"
]
},
{
"cell_type": "code",
"execution_count": 51,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{}\n",
"[0 0 0 0 0 0 1 1]\n",
"[0 0 0 0 0 0 1 1]\n"
]
}
],
"source": [
"int2binary = {}\n",
"print(int2binary) # 초기에는 아무것도 없는 딕셔너리\n",
"print(binary[3])\n",
"\n",
"# 값을 채우고\n",
"for i in range(largest_number):\n",
" int2binary[i] = binary[i]\n",
"\n",
"print(int2binary[3]) # 테스트 삼아 출력해보자"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 하이퍼 파라미터들\n"
]
},
{
"cell_type": "code",
"execution_count": 52,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(2, 32) (32, 1) (32, 32)\n"
]
}
],
"source": [
"alpha = 0.1 # learning_rate\n",
"input_node_num = 2 # 우리는 이진수 두 개의 같은 자리 값을 입력으로 넣는다\n",
"\n",
"# hidden 계층의 노드는 16개로 하였다. 이것이 carry 정보를 저장할 것이다. \n",
"# 현재 8자리의 이진수를 다루니 hidden 계층이 16개면 충분할듯 하다. 이 값을 변경해가며 \n",
"# 얼마나 빨리 최종값을 찾아가는지 테스트 해보자\n",
"\n",
"hidden_node_num = 32\n",
"output_node_num = 1 # 각 자리수의 결과값이 여기에 온다\n",
"\n",
"# ------------------------------------------------------------\n",
"# initialize neural network weights\n",
"# weight 값들을 정해 준다. \n",
"# [0.0, 1.0) 사이의 랜덤으로 만든 값들에 2를 곱해주고 1을 빼준다. 따라서 [-1, 1.0) 값이 생성된다. \n",
"# http://terms.naver.com/entry.nhn?docId=3404970&cid=47324&categoryId=47324\n",
"# np.random.random은 [0,0, 1.0) 의 값을 생성해준다. half open interval 이라 하는데 간단히 0.0 <= y < 1.0 이다.\n",
"# 값들을 여러개 생성하면 continueous uniform하게 만들어준다.\n",
"w0 = 2 * np.random.random((input_node_num, hidden_node_num)) - 1\n",
"w1 = 2 * np.random.random((hidden_node_num, output_node_num)) - 1\n",
"wh = 2 * np.random.random((hidden_node_num, hidden_node_num)) - 1\n",
"\n",
"print (w0.shape, w1.shape, wh.shape )\n"
]
},
{
"cell_type": "code",
"execution_count": 53,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# wieght를 업데이트 하기 위한 용도의 공간도 잡아둔다. shape는 똑같겠지?\n",
"w0_update = np.zeros_like(w0)\n",
"w1_update = np.zeros_like(w1)\n",
"wh_update = np.zeros_like(wh)\n",
"\n",
"# display 용도\n",
"overallError_history = list()\n",
"accuracy = list()\n",
"accuracy_history = list()\n",
"accuracy_count = 0\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 드디어 train 시작\n",
"\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 54,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"image/png": "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\nAABJRU5ErkJggg==\n",
"text/plain": [
"<matplotlib.figure.Figure at 0x7bace10>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"image/png": "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\nHY8AAAAASUVORK5CYII=\n",
"text/plain": [
"<matplotlib.figure.Figure at 0x7fb4c88>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"w0\n",
"[[ -5.97219869e-01 -6.93597312e-02 -2.57455491e+00 -3.38378563e-01\n",
" -1.75749040e-01 -5.82257908e-01 3.88239564e-01 2.03845153e+00\n",
" 1.33880856e+00 -2.08366535e-01 8.32917253e-01 -4.08539232e-01\n",
" 2.82378048e-01 1.51573543e+00 -2.43849466e+00 -1.23134703e+00\n",
" -1.92579965e+00 6.65879843e-01 4.87520755e-01 7.37217805e-01\n",
" 4.03825849e+00 6.34330343e-01 5.43585756e-01 6.94832806e+00\n",
" -1.27649075e+00 6.18754173e-01 -1.65014761e+00 8.48479058e-01\n",
" -1.09173990e-01 3.31809878e+00 -1.01095355e+00 3.79923857e-01]\n",
" [ -4.92614010e-01 -6.17747884e-01 -3.10070668e+00 6.71369599e-02\n",
" -1.69272036e-02 -1.14028929e-02 6.83258110e-01 2.37409066e+00\n",
" -1.59791426e+00 -5.19008430e-01 7.41306391e-01 -1.58697401e+00\n",
" 1.34308990e+00 1.89399687e+00 -2.12704082e+00 -1.15843902e+00\n",
" -1.39411472e+00 -7.44535016e-01 4.69992808e-03 -8.94550615e-01\n",
" 3.68999419e+00 -1.52946971e+00 4.12465184e-01 -4.77673016e+00\n",
" 4.79399958e-01 -1.22110097e+00 -4.46814352e-01 -1.07264996e+00\n",
" -7.78655194e-02 -5.34434513e+00 9.94657729e-01 -5.35011563e-01]]\n",
"w1\n",
"[[-0.40203081]\n",
" [-0.20858175]\n",
" [ 2.00193935]\n",
" [-0.64166139]\n",
" [ 0.14068508]\n",
" [-0.92960608]\n",
" [ 0.90596415]\n",
" [-1.30554002]\n",
" [ 1.44947462]\n",
" [-0.023537 ]\n",
" [ 0.87768472]\n",
" [-0.87518944]\n",
" [-0.79981158]\n",
" [-1.36386235]\n",
" [-3.4657257 ]\n",
" [-0.90771114]\n",
" [-2.18094468]\n",
" [ 0.35250633]\n",
" [-0.61857497]\n",
" [ 0.98407444]\n",
" [ 7.28277928]\n",
" [ 0.22230544]\n",
" [ 0.02060729]\n",
" [-8.7460741 ]\n",
" [-0.18035806]\n",
" [ 0.56899046]\n",
" [-0.71097144]\n",
" [ 0.27254687]\n",
" [-0.63771425]\n",
" [ 5.7176397 ]\n",
" [-1.24998507]\n",
" [ 0.01120062]]\n",
"wh\n",
"[[ 0.00430491 -1.0803049 0.96438529 ..., -0.26030016 -1.07330931\n",
" -0.43713244]\n",
" [ 0.16542268 -0.58333964 0.6072552 ..., -0.38021943 -0.28907004\n",
" -0.91605346]\n",
" [ 0.32570138 -0.0763639 1.10606754 ..., -1.36006557 -0.22646215\n",
" -0.58308475]\n",
" ..., \n",
" [ 0.5573387 0.71178216 0.97842055 ..., -0.85758885 0.61467915\n",
" -0.55362447]\n",
" [-0.57556552 0.28414698 0.98473092 ..., -0.04423211 -0.85713938\n",
" 0.3047269 ]\n",
" [ 0.76408938 0.01895369 0.12118621 ..., -1.27924919 0.64584885\n",
" 0.62476619]]\n"
]
}
],
"source": [
"for j in range(10000): # 10000번 돌린다\n",
" \n",
" # a, b를 생성하고 두 값을 더한 c를 계산한다.\n",
" # 복잡성을 줄이기 위해(=우리는 toy code를 짜고 있잖아?) \n",
" # a, b는 largest_number 의 절반 - 1 크기의 랜덤 정수값이다. 따라서 a + b < largest_number\n",
"\n",
" a_int = np.random.randint(largest_number / 2)\n",
" a = int2binary[a_int]\n",
"\n",
" b_int = np.random.randint(largest_number / 2)\n",
" b = int2binary[b_int]\n",
"\n",
" # 입력값 a, b를 임의 생성했으니 정답인 c도 확보해두자(=label. 따라서 우리는 supervised learning 중이다)\n",
" c_int = a_int + b_int\n",
" c = int2binary[c_int]\n",
"\n",
" # c에는 정답이 들어있고, predict_binary에는 우리 네트워크가 예측한 값을 넣어두자.\n",
" d = np.zeros_like(c)\n",
"\n",
" overallError = 0 # 에러값을 저장할 공간\n",
"\n",
" # 각 time step마다 output_layer의 미분값과 hidden_layer의 값을 추적할 리스트이다.\n",
" output_layer_deltas = list()\n",
" hidden_layer_values = list()\n",
"\n",
" # output_layer_deltas 에는 생성한 값을 넣으면 되지만\n",
" # hidden_layer_values 처음에는 이전 hidden이 없으니 0으로 한다\n",
" hidden_layer_values.append(np.zeros(hidden_node_num))\n",
"\n",
" # forward propagation 부터\n",
" #---------------------------------------------------------------\n",
" # 여기서부터 각 자리수별로 NN을 돌린다\n",
" #---------------------------------------------------------------\n",
" # 낮은 자리수부터 각 자리수별로 for 문을 돌면서 수행한다.\n",
" for position in range(binary_dim):\n",
"\n",
" # 입력값과 정답(=label)을 각각 X와 y에 써넣자\n",
" X = np.array([[a[binary_dim - position-1], b[binary_dim - position-1]]])\n",
" y = np.array([[c[binary_dim - position-1]]]).T\n",
" \n",
" \n",
" # hidden layer 연산부 (input ~+ prev_hidden) 이 부분이 RNN의 매우 중요한 부분이다\n",
" # hidden layer 연산이다. input X와 weight_input_hidden(=W0) 을 곱해주고,\n",
" # 이전의(= -1 = 가장 마지막의) hidden layer 값과 Wweight_hidden_hidden(=Wh)를 곱해준다.\n",
" # 그리고 두 값을 더한뒤 sigmoid 먹여준다. 더해준다는 것이 중요하다\n",
" hidden_layer = sigmoid(np.dot(X, w0) + np.dot(hidden_layer_values[-1], wh))\n",
"\n",
" # output layer (new binary representation)\n",
" # output layer는 이렇게 나온 hidden_layer 값과 weight_hidden_output(= W1) 값을 곱해준뒤 sigmoid 해준다\n",
" output_layer = sigmoid(np.dot(hidden_layer, w1))\n",
"\n",
" # 정답과 예측값을 비교해서 loss를 계산해보자\n",
" # 일치하면 0, 틀리면 절대값이 1일 것이다. \n",
" output_layer_error = y - output_layer \n",
"\n",
" # 우선 sigmoid에 대한 미분값만을 계산하고 저장하자. \n",
" output_layer_deltas.append((output_layer_error) * sigmoid_output_to_derivative(output_layer))\n",
"\n",
" # 에러율 저장\n",
" overallError += np.abs(output_layer_error[0])\n",
"\n",
" # 예측값 저장하기. sigmoid 결과값이니 \n",
" d[binary_dim - position - 1] = np.round(output_layer[0][0])\n",
"\n",
" '''\n",
" if (j == 9999):\n",
" print (\"hidden_layer.shape: \", hidden_layer.shape)\n",
" print (\"output_layer_error.shape: \", output_layer_error.shape)\n",
" print (\"output_layer.shape: \", output_layer.shape)\n",
" print (\"output_layer: \", output_layer)\n",
" print (\"-------------------------------\")\n",
" '''\n",
" \n",
" # 계산한 hidden_layer는 다음 자리수 계산을 위해 보관해두자\n",
" hidden_layer_values.append(copy.deepcopy(hidden_layer))\n",
"\n",
" # 루프를 다 돌면 모든 자리수에 대한 forward propagation 완료된 것\n",
"\n",
" # 미분을 위해서는 그 다음 상위 hidden_layer에서의 미분값이 필요한데 없으므로 0을 채워놓음\n",
" future_hidden_layer_delta = np.zeros(hidden_node_num)\n",
" \n",
" # display 용도\n",
" if (j % 100 == 0):\n",
" overallError_history.append(overallError[0])\n",
" \n",
" # 이제 backward propagation\n",
" \n",
" for position in range(binary_dim):\n",
"\n",
" ##################################################################################\n",
" #일단 보관해뒀던 값들을 다 꺼낸다\n",
"\n",
" X = np.array([[a[position], b[position]]]) # 이번엔 맨 왼쪽의 가장 높은 자리수부터 계산해야겠지?\n",
" hidden_layer = hidden_layer_values[-position - 1] # 가장 나중 time step의 hidden layer의 값을 꺼내고\n",
" prev_hidden_layer = hidden_layer_values[-position - 2] # 그 하나전의 prev hidden layer의 값도 꺼낸다\n",
"\n",
" # error at output layer\n",
" # 현재 자릿수의 output layer의 미분한 값값\n",
" output_layer_delta = output_layer_deltas[-position - 1]\n",
"\n",
" ##################################################################################\n",
" # error at hidden layer\n",
" # 이제 현재 자리수의(=현재 time step)의 hidden layer의 미분값을 계산한다\n",
"\n",
" # 다음 layer에서 내려오는 값과 weight_hidden_(=Wh) 값을 곱하고\n",
" # output layer의 미분값과 synapse_1(=W1)값을 곱한 값을 다시 layer_1의 미분값과 곱해준다.\n",
" # 이부분은 어렵다. 차근히 봐야겠다\n",
" hidden_layer_delta = (future_hidden_layer_delta.dot(wh.T) \\\n",
" + output_layer_delta.dot(w1.T)) * sigmoid_output_to_derivative(hidden_layer)\n",
"\n",
" # let's update all our weights so we can try again\n",
" # 이제 weight 값들을 업데이트 해줄 시간이다.\n",
" w1_update += np.atleast_2d(hidden_layer).T.dot(output_layer_delta)\n",
" wh_update += np.atleast_2d(prev_hidden_layer).T.dot(hidden_layer_delta)\n",
" w0_update += X.T.dot(hidden_layer_delta)\n",
"\n",
" future_hidden_layer_delta = hidden_layer_delta\n",
"\n",
" # 모두 다 업데이트 해준다. alpha 라는 learning_rate에 맞춰서 update\n",
" # 이렇게 모든 자리수의 backpropagation이 끝난뒤에 update 하는 것이다.\n",
"\n",
" w1 += w1_update * alpha\n",
" w0 += w0_update * alpha\n",
" wh += wh_update * alpha\n",
"\n",
" # 그리고 다시 초기화\n",
" w1_update *= 0\n",
" w0_update *= 0\n",
" wh_update *= 0\n",
"\n",
" ##################################################################################\n",
" # 진행사항 저장\n",
" # 참인지 저깃인지를 계속 체크\n",
" check = np.equal(d,c)\n",
" #print(\"check:\", np.sum(check), binary_dim)\n",
" if (np.sum(check) == binary_dim): # 같은 것 개수가 8개면 참이다\n",
" accuracy_count += 1\n",
" \n",
" # 100번마다 accuracy에서 값을 찾는다. \n",
" if (j % 100 == 0):\n",
" #print(\"accuracy_count\", accuracy_count)\n",
" accuracy_history.append(accuracy_count)\n",
" accuracy_count = 0\n",
" \n",
" ##################################################################################\n",
" # print out progress\n",
" # 1000번마다 중간 진행 사항을 update 하자\n",
" '''\n",
" if (j % 1000 == 0):\n",
" print (\"Error:\" + str(overallError))\n",
" print (\"Pred:\" + str(d)) # 예측값\n",
" print (\"True:\" + str(c)) # 실제값\n",
"\n",
" final_check = np.equal(d,c)\n",
" print (np.sum(final_check) == binary_dim)\n",
"\n",
" out = 0\n",
"\n",
" for index, x in enumerate(reversed(d)):\n",
" out += x * pow(2, index)\n",
" print (str(a_int) + \" + \" + str(b_int) + \" = \" + str(out))\n",
" print (\"------------\")\n",
" '''\n",
" \n",
" \n",
"x_range = range(100)\n",
"plt.plot(x_range, overallError_history, 'r-')\n",
"plt.ylabel('overallError')\n",
"plt.show()\n",
"\n",
"\n",
"plt.plot(x_range, accuracy_history, 'b-')\n",
"plt.ylabel('accuracy_history')\n",
"plt.show()\n",
"\n",
"print(\"w0\")\n",
"print(w0)\n",
"print(\"w1\")\n",
"print(w1)\n",
"print(\"wh\")\n",
"print(wh)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": []
}
],
"metadata": {
"anaconda-cloud": {},
"kernelspec": {
"display_name": "Python [conda root]",
"language": "python",
"name": "conda-root-py"
},
"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": 1
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment