├── README.md ├── 2_1_선형회귀_with_My_data.ipynb ├── 3. 로지스틱_회귀(logistic_Regression).ipynb ├── 9. 순환 신경망(Recurrent Neural Network).ipynb ├── 3_1_로지스틱_회귀_with_My_data.ipynb └── 4. 소프트맥스 회귀(Softmax_Regression).ipynb /README.md: -------------------------------------------------------------------------------- 1 | # pytorch_basic 2 | 딥러닝 기초 및 딥러닝 프레임워크 pytorch에 관한 내용을 담고있습니다.(seq2seq 실습 보완 및 attention 기법 추가 예정 ) 3 | -------------------------------------------------------------------------------- /2_1_선형회귀_with_My_data.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "name": "2-1. 선형회귀 with My data.ipynb", 7 | "provenance": [], 8 | "private_outputs": true, 9 | "authorship_tag": "ABX9TyNJIzALXLVleUgbXVnSJmRd", 10 | "include_colab_link": true 11 | }, 12 | "kernelspec": { 13 | "name": "python3", 14 | "display_name": "Python 3" 15 | } 16 | }, 17 | "cells": [ 18 | { 19 | "cell_type": "markdown", 20 | "metadata": { 21 | "id": "view-in-github", 22 | "colab_type": "text" 23 | }, 24 | "source": [ 25 | "\"Open" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": { 31 | "id": "IWZf_fuG4Im6", 32 | "colab_type": "text" 33 | }, 34 | "source": [ 35 | "pytorch 기초에서 사용한 boston 데이터셋을 바탕으로 선형회귀 모델 학습" 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "metadata": { 41 | "id": "0uCNgNsL4c-R", 42 | "colab_type": "text" 43 | }, 44 | "source": [ 45 | "# Data Load" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "metadata": { 51 | "id": "Mg4iL4my4Z0x", 52 | "colab_type": "code", 53 | "colab": {} 54 | }, 55 | "source": [ 56 | "import pandas as pd\n", 57 | "from sklearn.datasets import load_boston\n", 58 | "\n", 59 | "boston = load_boston()" 60 | ], 61 | "execution_count": 0, 62 | "outputs": [] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "metadata": { 67 | "id": "bexL3vne4hKB", 68 | "colab_type": "code", 69 | "colab": {} 70 | }, 71 | "source": [ 72 | "boston_df = pd.DataFrame(data=boston['data'],columns=boston['feature_names'])\n", 73 | "boston_df" 74 | ], 75 | "execution_count": 0, 76 | "outputs": [] 77 | }, 78 | { 79 | "cell_type": "markdown", 80 | "metadata": { 81 | "id": "hQO7aS0x7A6o", 82 | "colab_type": "text" 83 | }, 84 | "source": [ 85 | "scikit-learn이 제공하는 회귀 분석용 예제 데이터에 대해 소개한다. 먼저, 보스턴 주택 가격 데이터는 다음과 같이 구성되어 있다.\n", 86 | "\n", 87 | "\n", 88 | "\n", 89 | "* 타겟 데이터\n", 90 | " * 1978 보스턴 주택 가격\n", 91 | " * 506개 타운의 주택 가격 중앙값 (단위 1,000 달러)\n", 92 | "* 특징 데이터\n", 93 | " * CRIM: 범죄율\n", 94 | " * ZN: 25,000 평방피트를 초과 거주지역 비율\n", 95 | " * INDUS: 비소매상업지역 면적 비율\n", 96 | " * CHAS: 찰스강의 경계에 위치한 경우는 1, 아니면 0\n", 97 | " * NOX: 일산화질소 농도\n", 98 | " * RM: 주택당 방 수\n", 99 | " * AGE: 1940년 이전에 건축된 주택의 비율\n", 100 | " * DIS: 직업센터의 거리\n", 101 | " * RAD: 방사형 고속도로까지의 거리\n", 102 | " * TAX: 재산세율\n", 103 | " * B: 인구 중 흑인 비율\n", 104 | " * PTRATIO: 학생/교사 비율\n", 105 | " * LSTAT: 인구 중 하위 계층 비율\n", 106 | "\n" 107 | ] 108 | }, 109 | { 110 | "cell_type": "code", 111 | "metadata": { 112 | "id": "o1EKa4_YGNjM", 113 | "colab_type": "code", 114 | "colab": {} 115 | }, 116 | "source": [ 117 | "data = boston_df\n", 118 | "data = data.apply(\n", 119 | " lambda x: (x - x.mean()) / x.std()\n", 120 | ")\n", 121 | "data['Price'] = boston['target']\n", 122 | "data" 123 | ], 124 | "execution_count": 0, 125 | "outputs": [] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "metadata": { 130 | "id": "OtiYAHiiG8RL", 131 | "colab_type": "code", 132 | "colab": {} 133 | }, 134 | "source": [ 135 | "X,y = data.values[:,:-1],data.values[:,-1:]\n", 136 | "print(X.shape,y.shape)" 137 | ], 138 | "execution_count": 0, 139 | "outputs": [] 140 | }, 141 | { 142 | "cell_type": "markdown", 143 | "metadata": { 144 | "id": "M8a7CNcn8Mnr", 145 | "colab_type": "text" 146 | }, 147 | "source": [ 148 | "# Set Custom Dataset" 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "metadata": { 154 | "id": "TmnUZdjj7auK", 155 | "colab_type": "code", 156 | "colab": {} 157 | }, 158 | "source": [ 159 | "import torch\n", 160 | "import torch.nn as nn\n", 161 | "import torch.nn.functional as F\n", 162 | "import torch.optim as optim\n", 163 | "\n", 164 | "from torch.utils.data import Dataset\n", 165 | "from torch.utils.data import DataLoader" 166 | ], 167 | "execution_count": 0, 168 | "outputs": [] 169 | }, 170 | { 171 | "cell_type": "code", 172 | "metadata": { 173 | "id": "kgBikoFJ8Qdm", 174 | "colab_type": "code", 175 | "colab": {} 176 | }, 177 | "source": [ 178 | "class MyDataset(Dataset):\n", 179 | " def __init__(self):\n", 180 | " self.x_data = torch.tensor(X,dtype=torch.float)\n", 181 | " self.y_data = torch.tensor(y,dtype=torch.float)\n", 182 | " def __len__(self):\n", 183 | " return len(self.x_data)\n", 184 | "\n", 185 | " def __getitem__(self,idx):\n", 186 | " x = self.x_data[idx]\n", 187 | " y = self.y_data[idx]\n", 188 | " return x,y" 189 | ], 190 | "execution_count": 0, 191 | "outputs": [] 192 | }, 193 | { 194 | "cell_type": "code", 195 | "metadata": { 196 | "id": "3WIhn_gd9PLs", 197 | "colab_type": "code", 198 | "colab": {} 199 | }, 200 | "source": [ 201 | "dataset = MyDataset()\n", 202 | "dataloader = DataLoader(dataset, batch_size=len(dataset), shuffle=True)" 203 | ], 204 | "execution_count": 0, 205 | "outputs": [] 206 | }, 207 | { 208 | "cell_type": "code", 209 | "metadata": { 210 | "id": "9Zu2nE929RQp", 211 | "colab_type": "code", 212 | "colab": {} 213 | }, 214 | "source": [ 215 | "model = nn.Linear(13,1)\n", 216 | "optimizer = optim.SGD(model.parameters(), lr=0.05)" 217 | ], 218 | "execution_count": 0, 219 | "outputs": [] 220 | }, 221 | { 222 | "cell_type": "code", 223 | "metadata": { 224 | "id": "pocJgvu39cEv", 225 | "colab_type": "code", 226 | "colab": {} 227 | }, 228 | "source": [ 229 | "nb_epochs = 50\n", 230 | "for epoch in range(nb_epochs + 1):\n", 231 | " for samples in dataloader:\n", 232 | " # print(batch_idx)\n", 233 | " # print(samples)\n", 234 | " x_train, y_train = samples\n", 235 | " # H(x) 계산\n", 236 | " prediction = model(x_train)\n", 237 | "\n", 238 | " # cost 계산\n", 239 | " cost = F.mse_loss(prediction, y_train)\n", 240 | "\n", 241 | " # cost로 H(x) 계산\n", 242 | " optimizer.zero_grad()\n", 243 | " cost.backward()\n", 244 | " optimizer.step()\n", 245 | "\n", 246 | " print('Epoch {:4d}/{} Cost: {:.6f}'.format(\n", 247 | " epoch, nb_epochs,cost.item()))" 248 | ], 249 | "execution_count": 0, 250 | "outputs": [] 251 | }, 252 | { 253 | "cell_type": "code", 254 | "metadata": { 255 | "id": "PvQvjq2S9hWy", 256 | "colab_type": "code", 257 | "colab": {} 258 | }, 259 | "source": [ 260 | "from sklearn.linear_model import LinearRegression\n", 261 | "from sklearn.metrics import mean_squared_error" 262 | ], 263 | "execution_count": 0, 264 | "outputs": [] 265 | }, 266 | { 267 | "cell_type": "code", 268 | "metadata": { 269 | "id": "jJUpb8oIC6-5", 270 | "colab_type": "code", 271 | "colab": {} 272 | }, 273 | "source": [ 274 | "reg_model = LinearRegression()\n", 275 | "reg_model.fit(boston['data'],boston['target'])\n", 276 | "pred_y = reg_model.predict(boston['data'])" 277 | ], 278 | "execution_count": 0, 279 | "outputs": [] 280 | }, 281 | { 282 | "cell_type": "code", 283 | "metadata": { 284 | "id": "mu30W1_4DK3v", 285 | "colab_type": "code", 286 | "colab": {} 287 | }, 288 | "source": [ 289 | "mean_squared_error(boston['target'],pred_y )" 290 | ], 291 | "execution_count": 0, 292 | "outputs": [] 293 | }, 294 | { 295 | "cell_type": "code", 296 | "metadata": { 297 | "id": "MNLWz0JaDgGY", 298 | "colab_type": "code", 299 | "colab": {} 300 | }, 301 | "source": [ 302 | "" 303 | ], 304 | "execution_count": 0, 305 | "outputs": [] 306 | } 307 | ] 308 | } 309 | -------------------------------------------------------------------------------- /3. 로지스틱_회귀(logistic_Regression).ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "name": "3. 로지스틱 회귀(logistic Regression).ipynb", 7 | "private_outputs": true, 8 | "provenance": [], 9 | "toc_visible": true, 10 | "authorship_tag": "ABX9TyNoxNbYBBIE2wqtMPHhPmWp", 11 | "include_colab_link": true 12 | }, 13 | "kernelspec": { 14 | "name": "python3", 15 | "display_name": "Python 3" 16 | } 17 | }, 18 | "cells": [ 19 | { 20 | "cell_type": "markdown", 21 | "metadata": { 22 | "id": "view-in-github", 23 | "colab_type": "text" 24 | }, 25 | "source": [ 26 | "\"Open" 27 | ] 28 | }, 29 | { 30 | "cell_type": "markdown", 31 | "metadata": { 32 | "id": "n0qodsM0uaxg" 33 | }, 34 | "source": [ 35 | "# 3. 로지스틱 회귀(Logistic Regression)\n", 36 | "\n", 37 | "일상 속 풀고자하는 많은 문제 중에서는 두 개의 선택지 중에서 정답을 고르는 문제가 많습니다. 예를 들어 시험을 봤는데 이 시험 점수가 합격인지 불합격인지가 궁금할 수도 있고, 어떤 메일을 받았을 때 이게 정상 메일인지 스팸 메일인지를 분류하는 문제도 그렇습니다. 이렇게 둘 중 하나를 결정하는 문제를 이진 분류(Binary Classification)라고 합니다. 그리고 이진 분류를 풀기 위한 대표적인 알고리즘으로 로지스틱 회귀(Logistic Regression)가 있습니다.\n", 38 | "\n", 39 | "* 로지스틱 회귀는 알고리즘의 이름은 회귀이지만 실제로는 분류(Classification) 작업에 사용할 수 있습니다.\n", 40 | "\n" 41 | ] 42 | }, 43 | { 44 | "cell_type": "markdown", 45 | "metadata": { 46 | "id": "8N4QFwpj2iQr" 47 | }, 48 | "source": [ 49 | "## 3.1 로지스틱 회귀(Logistic Regression)" 50 | ] 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "metadata": { 55 | "id": "iW0XVJBLumkU" 56 | }, 57 | "source": [ 58 | "### 3.1.1 이진 분류(Binary Classification)\n", 59 | "\n", 60 | "학생들이 시험 성적에 따라서 합격, 불합격이 기재된 데이터가 있다고 가정해봅시다. 시험 성적이 $x$라면, 합불 결과는 $y$입니다. 이 시험의 커트라인은 공개되지 않았는데 이 데이터로부터 특정 점수를 얻었을 때의 합격, 불합격 여부를 판정하는 모델을 만들고자 합시다.\n", 61 | "\n", 62 | "|score$(x)$|result$(y)$|\n", 63 | "|:----|:----|\n", 64 | "|45|불합격|\n", 65 | "|50|불합격|\n", 66 | "|55|불합격|\n", 67 | "|60|합격|\n", 68 | "|65|합격|\n", 69 | "|70|합격|\n", 70 | "\n", 71 | "위의 데이터에서 합격을 1, 불합격을 0이라고 하였을 때 그래프를 그려보면 아래와 같습니다.\n", 72 | "\n", 73 | "![대체 텍스트](https://wikidocs.net/images/page/22881/%EB%A1%9C%EC%A7%80%EC%8A%A4%ED%8B%B1%ED%9A%8C%EA%B7%80.PNG)\n", 74 | "\n", 75 | "이러한 점들을 표현하는 그래프는 알파벳의 S자 형태로 표현됩니다. 이러한 $x$와 $y$의 관계를 표현하기 위해서는 $Wx+b$와 같은 직선 함수가 아니라 S자 형태로 표현할 수 있는 함수가 필요합니다. 이런 문제에 직선을 사용할 경우 분류 작업이 잘 동작하지 않습니다.\n", 76 | "\n", 77 | "그래서 이번 로지스틱 회귀의 가설은 선형 회귀 때의 $H(x)=Wx+b$가 아니라, 위와 같이 S자 모양의 그래프를 만들 수 있는 어떤 특정 함수 $f$를 추가적으로 사용하여 $H(x)=f(Wx+b)$의 가설을 사용할 겁니다. 그리고 위와 같이 S자 모양의 그래프를 그릴 수 있는 어떤 함수 $f$가 이미 널리 알려져있습니다. 바로 시그모이드 함수입니다." 78 | ] 79 | }, 80 | { 81 | "cell_type": "markdown", 82 | "metadata": { 83 | "id": "DI3fTHE3wCyE" 84 | }, 85 | "source": [ 86 | "### 3.1.2 시그모이드 함수(Sigmoid function)\n", 87 | "\n", 88 | "위와 같이 S자 형태로 그래프를 그려주는 시그모이드 함수의 방정식은 아래와 같습니다.\n", 89 | "\n", 90 | "$H(x) = sigmoid(Wx + b) = \\frac{1}{1 + e^{-(Wx + b)}} = \\sigma(Wx + b)$\n", 91 | "\n", 92 | "선형 회귀에서는 최적의 $W$와 $b$를 찾는 것이 목표였습니다. 여기서도 마찬가지입니다. 선형 회귀에서는 $W$가 직선의 기울기, $b$가 y절편을 의미했습니다. 그렇다면 여기에서는 $W$와 $b$가 함수의 그래프에 어떤 영향을 주는지 직접 그래프를 그려서 알아보겠습니다.\n", 93 | "\n", 94 | "\n", 95 | "* 파이썬에서는 그래프를 그릴 수 있는 도구로서 Matplotlib을 사용할 수 있습니다.\n", 96 | "\n", 97 | "우선 Matplotlib과 Numpy를 임포트합니다.\n" 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "metadata": { 103 | "id": "uG5VRXqyuly-" 104 | }, 105 | "source": [ 106 | "%matplotlib inline\n", 107 | "import numpy as np # 넘파이 사용\n", 108 | "import matplotlib.pyplot as plt # 맷플롯립사용" 109 | ], 110 | "execution_count": null, 111 | "outputs": [] 112 | }, 113 | { 114 | "cell_type": "markdown", 115 | "metadata": { 116 | "id": "JTAXHGCzwXvt" 117 | }, 118 | "source": [ 119 | "Numpy를 사용하여 시그모이드 함수를 정의합니다." 120 | ] 121 | }, 122 | { 123 | "cell_type": "code", 124 | "metadata": { 125 | "id": "sgmw2m5MwWgN" 126 | }, 127 | "source": [ 128 | "def sigmoid(x): # 시그모이드 함수 정의\n", 129 | " return 1/(1+np.exp(-x))" 130 | ], 131 | "execution_count": null, 132 | "outputs": [] 133 | }, 134 | { 135 | "cell_type": "markdown", 136 | "metadata": { 137 | "id": "SMj2mF1wwceM" 138 | }, 139 | "source": [ 140 | "1) W가 1이고 b가 0인 그래프\n", 141 | "\n", 142 | "가장 먼저 $W$가 1이고, $b$가 0인 그래프를 그려봅시다." 143 | ] 144 | }, 145 | { 146 | "cell_type": "code", 147 | "metadata": { 148 | "id": "hKzWOynEwaCt" 149 | }, 150 | "source": [ 151 | "x = np.arange(-5.0, 5.0, 0.1)\n", 152 | "y = sigmoid(x)\n", 153 | "\n", 154 | "plt.plot(x, y, 'g')\n", 155 | "plt.plot([0,0],[1.0,0.0], ':') # 가운데 점선 추가\n", 156 | "plt.title('Sigmoid Function')\n", 157 | "plt.show()" 158 | ], 159 | "execution_count": null, 160 | "outputs": [] 161 | }, 162 | { 163 | "cell_type": "markdown", 164 | "metadata": { 165 | "id": "4waNup3qw2xb" 166 | }, 167 | "source": [ 168 | "위의 그래프를 통해시그모이드 함수는 출력값을 0과 1사이의 값으로 조정하여 반환함을 알 수 있습니다. $x$가 0일 때 0.5의 값을 가집니다. $x$가 매우 커지면 1에 수렴합니다. 반면, $x$가 매우 작아지면 0에 수렴합니다." 169 | ] 170 | }, 171 | { 172 | "cell_type": "markdown", 173 | "metadata": { 174 | "id": "lG0KwFbkw67z" 175 | }, 176 | "source": [ 177 | "2) W값의 변화에 따른 경사도의 변화\n", 178 | "\n", 179 | "이제 $W$의 값을 변화시키고 이에 따른 그래프를 확인해보겠습니다." 180 | ] 181 | }, 182 | { 183 | "cell_type": "code", 184 | "metadata": { 185 | "id": "MpuzxZt2w05d" 186 | }, 187 | "source": [ 188 | "x = np.arange(-5.0, 5.0, 0.1)\n", 189 | "y1 = sigmoid(0.5*x)\n", 190 | "y2 = sigmoid(x)\n", 191 | "y3 = sigmoid(2*x)\n", 192 | "\n", 193 | "plt.plot(x, y1, 'r', linestyle='--') # W의 값이 0.5일때\n", 194 | "plt.plot(x, y2, 'g') # W의 값이 1일때\n", 195 | "plt.plot(x, y3, 'b', linestyle='--') # W의 값이 2일때\n", 196 | "plt.plot([0,0],[1.0,0.0], ':') # 가운데 점선 추가\n", 197 | "plt.title('Sigmoid Function')\n", 198 | "plt.show()" 199 | ], 200 | "execution_count": null, 201 | "outputs": [] 202 | }, 203 | { 204 | "cell_type": "markdown", 205 | "metadata": { 206 | "id": "XM0KYb0dxBqO" 207 | }, 208 | "source": [ 209 | "위의 그래프는 $W$의 값이 0.5일때 빨간색선, $W$의 값이 1일때는 초록색선, $W$의 값이 2일때 파란색선이 나오도록 하였습니다. 자세히 보면 $W$의 값에 따라 그래프의 경사도가 변하는 것을 볼 수 있습니다. 앞서 선형 회귀에서 가중치 $W$는 직선의 기울기를 의미했지만, 여기서는 그래프의 경사도를 결정합니다. $W$의 값이 커지면 경사가 커지고 $W$의 값이 작아지면 경사가 작아집니다." 210 | ] 211 | }, 212 | { 213 | "cell_type": "markdown", 214 | "metadata": { 215 | "id": "ov-Fo4PmxIMt" 216 | }, 217 | "source": [ 218 | "3) b값의 변화에 따른 좌, 우 이동\n", 219 | "\n", 220 | "이제 $b$의 값에 따라서 그래프가 어떻게 변하는지 확인해보겠습니다." 221 | ] 222 | }, 223 | { 224 | "cell_type": "code", 225 | "metadata": { 226 | "id": "K2xKdHY7w_8k" 227 | }, 228 | "source": [ 229 | "x = np.arange(-5.0, 5.0, 0.1)\n", 230 | "y1 = sigmoid(x+0.5)\n", 231 | "y2 = sigmoid(x+1)\n", 232 | "y3 = sigmoid(x+1.5)\n", 233 | "\n", 234 | "plt.plot(x, y1, 'r', linestyle='--') # x + 0.5\n", 235 | "plt.plot(x, y2, 'g') # x + 1\n", 236 | "plt.plot(x, y3, 'b', linestyle='--') # x + 1.5\n", 237 | "plt.plot([0,0],[1.0,0.0], ':') # 가운데 점선 추가\n", 238 | "plt.title('Sigmoid Function')\n", 239 | "plt.show()" 240 | ], 241 | "execution_count": null, 242 | "outputs": [] 243 | }, 244 | { 245 | "cell_type": "markdown", 246 | "metadata": { 247 | "id": "y9BHhfGRxOsW" 248 | }, 249 | "source": [ 250 | "위의 그래프는 $b$의 값에 따라서 그래프가 좌, 우로 이동하는 것을 보여줍니다." 251 | ] 252 | }, 253 | { 254 | "cell_type": "markdown", 255 | "metadata": { 256 | "id": "y66KS3NwxdbO" 257 | }, 258 | "source": [ 259 | "4) 시그모이드 함수를 이용한 분류\n", 260 | "\n", 261 | "시그모이드 함수는 입력값이 한없이 커지면 1에 수렴하고, 입력값이 한없이 작아지면 0에 수렴합니다. 시그모이드 함수의 출력값은 0과 1 사이의 값을 가지는데 이 특성을 이용하여 분류 작업에 사용할 수 있습니다. 예를 들어 임계값을 0.5라고 정해보겠습니다. 출력값이 0.5 이상이면 1(True), 0.5이하면 0(False)으로 판단하도록 할 수 있습니다. 이를 확률이라고 생각하면 해당 레이블에 속할 확률이 50%가 넘으면 해당 레이블로 판단하고, 해당 레이블에 속할 확률이 50%보다 낮으면 아니라고 판단하는 것으로 볼 수 있습니다." 262 | ] 263 | }, 264 | { 265 | "cell_type": "markdown", 266 | "metadata": { 267 | "id": "R_D3wwQ1xj2G" 268 | }, 269 | "source": [ 270 | "### 3.1.3 비용 함수(Cost function)" 271 | ] 272 | }, 273 | { 274 | "cell_type": "markdown", 275 | "metadata": { 276 | "id": "CP5Y9zRPxmh3" 277 | }, 278 | "source": [ 279 | "이제 로지스틱 회귀의 가설이 $H(x)=sigmoid(Wx+b)$인 것은 알았습니다. 이제 최적의 $W$와 $b$를 찾을 수 있는 비용 함수(cost function)를 정의해야 합니다. 그런데 혹시 앞서 선형 회귀에서 배운 비용 함수인 평균 제곱 오차(Mean Square Error, MSE)를 로지스틱 회귀의 비용 함수로 그냥 사용하면 안 될까요?\n", 280 | "\n", 281 | "다음은 선형 회귀에서 사용했던 평균 제곱 오차의 수식입니다.\n", 282 | "\n", 283 | "$cost(W, b) = \\frac{1}{n} \\sum_{i=1}^{n} \\left[y^{(i)} - H(x^{(i)})\\right]^2$\n", 284 | "\n", 285 | "위의 비용 함수 수식에서 가설은 이제 $H(x)=Wx+b$가 아니라 $H(x)=sigmoid(Wx+b)$입니다. 그리고 이 비용 함수를 미분하면 선형 회귀때와 달리 다음과 같이 비볼록(non-convex) 형태의 그래프가 나옵니다.\n", 286 | "\n", 287 | "![대체 텍스트](https://wikidocs.net/images/page/22881/%EB%A1%9C%EC%BB%AC%EB%AF%B8%EB%8B%88%EB%A9%88.PNG)\n", 288 | "\n", 289 | "위와 같은 그래프에 경사 하강법을 사용할 경우의 문제점은 경사 하강법이 오차가 최소값이 되는 구간에 도착했다고 판단한 그 구간이 실제 오차가 완전히 최소값이 되는 구간이 아닐 수 있다는 점입니다. 사람이 등산 후에 산을 내려올 때도, 가파른 경사를 내려오다가 넓은 평지가 나오면 순간적으로 다 내려왔다고 착각할 수 있습니다. 하지만 실제로는 그곳이 다 내려온 것이 아니라 잠깐 평지가 나왔을 뿐이라면 길을 더 찾아서 더 내려가야 할 겁니다. 모델도 마찬가지로 실제 오차가 최소가 되는 구간을 찾을 수 있도록 도와주어야 합니다. 만약, 실제 최소가 되는 구간을 잘못 판단하면 최적의 가중치 $W$가 아닌 다른 값을 택해 모델의 성능이 더 오르지 않습니다.\n", 290 | "\n", 291 | "이를 전체 함수에 걸쳐 최소값인 글로벌 미니멈(Global Minimum)이 아닌 특정 구역에서의 최소값인 로컬 미니멈(Local Minimum)에 도달했다고 합니다. 이는 cost가 최소가 되는 가중치 $W$를 찾는다는 비용 함수의 목적에 맞지 않습니다.\n", 292 | "\n", 293 | "시그모이드 함수의 특징은 함수의 출력값이 0과 1사이의 값이라는 점입니다. 즉, 실제값이 1일 때 예측값이 0에 가까워지면 오차가 커져야 하며, 실제값이 0일 때, 예측값이 1에 가까워지면 오차가 커져야 합니다. 그리고 이를 충족하는 함수가 바로 로그 함수입니다. 다음은 $y=0.5$에 대칭하는 두 개의 로그 함수 그래프입니다.\n", 294 | "\n", 295 | "![대체 텍스트](https://wikidocs.net/images/page/57805/%EA%B7%B8%EB%9E%98%ED%94%84.PNG)\n", 296 | "\n", 297 | "실제값이 1일 때의 그래프를 주황색 선으로 표현하였으며, 실제값이 0일 때의 그래프를 초록색 선으로 표현하였습니다. 실제값이 1이라고 해봅시다. 이 경우, 예측값인 $H(x)$의 값이 1이면 오차가 0이므로 당연히 cost는 0이 됩니다. 반면, $H(x)$가 0으로 수렴하면 cost는 무한대로 발산합니다. 실제값이 0인 경우는 그 반대로 이해하면 됩니다. 이 두 개의 로그 함수를 식으로 표현하면 다음과 같습니다.\n", 298 | "\n", 299 | "$\\text{if } y=1 , \\text{cost}\\left( H(x), y \\right) = -\\log(H(x))$\n", 300 | "\n", 301 | "$\\text{if } y=0 , \\text{cost}\\left( H(x), y \\right) = -\\log(1-H(x))$\n", 302 | "\n", 303 | "$y$의 실제값이 1일 때 −logH(x) 그래프를 사용하고 y의 실제값이 0일 때 −log(1−H(X)) 그래프를 사용해야 합니다.\n", 304 | "이는 다음과 같이 하나의 식으로 통합할 수 있습니다.\n", 305 | "\n", 306 | "$\\text{cost}\\left( H(x), y \\right) = -[ylogH(x) + (1-y)log(1-H(x))]$\n", 307 | "\n", 308 | "왜 위 식이 두 개의 식을 통합한 식이라고 볼 수 있을까요? 실제값 $y$가 1이라고하면 덧셈 기호를 기준으로 우측의 항이 없어집니다. 반대로 실제값 $y$가 0이라고 하면 덧셈 기호를 기준으로 좌측의 항이 없어집니다. 선형 회귀에서는 모든 오차의 평균을 구해 평균 제곱 오차를 사용했었습니다. 마찬가지로 여기에서도 모든 오차의 평균을 구합니다.\n", 309 | "\n", 310 | "$cost(W) = -\\frac{1}{n} \\sum_{i=1}^{n} [y^{(i)}logH(x^{(i)}) + (1-y^{(i)})log(1-H(x^{(i)}))]$\n", 311 | "\n", 312 | "정리하면, 위 비용 함수는 실제값 $y$와 예측값 $H(x)$의 차이가 커지면 cost가 커지고, 실제값 $y$와 예측값 $H(x)$의 차이가 작아지면 cost는 작아집니다. 이제 위 비용 함수에 대해서 경사 하강법을 수행하면서 최적의 가중치 $W$를 찾아갑니다.\n", 313 | "\n", 314 | "$W := W - \\alpha\\frac{\\partial}{\\partial W}cost(W)$\n", 315 | "\n", 316 | "\n", 317 | "\n" 318 | ] 319 | }, 320 | { 321 | "cell_type": "markdown", 322 | "metadata": { 323 | "id": "jozI9345zAnj" 324 | }, 325 | "source": [ 326 | "### 3.1.4 파이토치로 로지스틱 회귀 구현하기\n", 327 | "\n", 328 | "이제 파이토치로 로지스틱 회귀 중에서도 다수의 x로 부터 y를 예측하는 다중 로지스틱 회귀를 구현해봅시다.\n", 329 | "\n", 330 | "우선 필요한 도구들을 임포트합니다." 331 | ] 332 | }, 333 | { 334 | "cell_type": "code", 335 | "metadata": { 336 | "id": "zSRHxYETxNF7" 337 | }, 338 | "source": [ 339 | "import torch\n", 340 | "import torch.nn as nn\n", 341 | "import torch.nn.functional as F\n", 342 | "import torch.optim as optim" 343 | ], 344 | "execution_count": null, 345 | "outputs": [] 346 | }, 347 | { 348 | "cell_type": "code", 349 | "metadata": { 350 | "id": "v6l1tDWazF0z" 351 | }, 352 | "source": [ 353 | "torch.manual_seed(1)" 354 | ], 355 | "execution_count": null, 356 | "outputs": [] 357 | }, 358 | { 359 | "cell_type": "markdown", 360 | "metadata": { 361 | "id": "tgPi3VFbzHgj" 362 | }, 363 | "source": [ 364 | "x_train과 y_train을 텐서로 선언합니다." 365 | ] 366 | }, 367 | { 368 | "cell_type": "code", 369 | "metadata": { 370 | "id": "5l43IJ7azGqH" 371 | }, 372 | "source": [ 373 | "x_data = [[1, 2], [2, 3], [3, 1], [4, 3], [5, 3], [6, 2]]\n", 374 | "y_data = [[0], [0], [0], [1], [1], [1]]\n", 375 | "x_train = torch.FloatTensor(x_data)\n", 376 | "y_train = torch.FloatTensor(y_data)" 377 | ], 378 | "execution_count": null, 379 | "outputs": [] 380 | }, 381 | { 382 | "cell_type": "markdown", 383 | "metadata": { 384 | "id": "lRD1OJ9szJ5g" 385 | }, 386 | "source": [ 387 | "앞서 훈련 데이터를 행렬로 선언하고, 행렬 연산으로 가설을 세우는 방법을 배웠습니다.\n", 388 | "여기서도 마찬가지로 행렬 연산을 사용하여 가설식을 세울겁니다. x_train과 y_train의 크기를 확인해봅시다." 389 | ] 390 | }, 391 | { 392 | "cell_type": "code", 393 | "metadata": { 394 | "id": "POwspkDczJHH" 395 | }, 396 | "source": [ 397 | "print(x_train.shape)\n", 398 | "print(y_train.shape)" 399 | ], 400 | "execution_count": null, 401 | "outputs": [] 402 | }, 403 | { 404 | "cell_type": "markdown", 405 | "metadata": { 406 | "id": "8rxd42SwzNTZ" 407 | }, 408 | "source": [ 409 | "현재 x_train은 6 × 2의 크기(shape)를 가지는 행렬이며, y_train은 6 × 1의 크기를 가지는 벡터입니다. x_train을 $X$라고 하고, 이와 곱해지는 가중치 벡터를 $W$라고 하였을 때, $XW$가 성립되기 위해서는 $W$ 벡터의 크기는 2 × 1이어야 합니다. 이제 W와 b를 선언합니다." 410 | ] 411 | }, 412 | { 413 | "cell_type": "code", 414 | "metadata": { 415 | "id": "_H4XZWXszLqS" 416 | }, 417 | "source": [ 418 | "W = torch.zeros((2, 1), requires_grad=True) # 크기는 2 x 1\n", 419 | "b = torch.zeros(1, requires_grad=True)" 420 | ], 421 | "execution_count": null, 422 | "outputs": [] 423 | }, 424 | { 425 | "cell_type": "markdown", 426 | "metadata": { 427 | "id": "I4DE77AQzWUL" 428 | }, 429 | "source": [ 430 | "이제 가설식을 세워보겠습니다. 파이토치에서는 $e^{x}$를 구현하기 위해서 torch.exp(x)를 사용합니다.\n", 431 | "이에 따라 행렬 연산을 사용한 가설식은 다음과 같습니다." 432 | ] 433 | }, 434 | { 435 | "cell_type": "code", 436 | "metadata": { 437 | "id": "Y98UotqGzUGf" 438 | }, 439 | "source": [ 440 | "hypothesis = 1 / (1 + torch.exp(-(x_train.matmul(W) + b)))" 441 | ], 442 | "execution_count": null, 443 | "outputs": [] 444 | }, 445 | { 446 | "cell_type": "markdown", 447 | "metadata": { 448 | "id": "5JkZEJjLzjI0" 449 | }, 450 | "source": [ 451 | "앞서 W와 b는 torch.zeros를 통해 전부 0으로 초기화 된 상태입니다. 이 상태에서 예측값을 출력해봅시다." 452 | ] 453 | }, 454 | { 455 | "cell_type": "code", 456 | "metadata": { 457 | "id": "PgdzBCO-zhgs" 458 | }, 459 | "source": [ 460 | "print(hypothesis) # 예측값인 H(x) 출력" 461 | ], 462 | "execution_count": null, 463 | "outputs": [] 464 | }, 465 | { 466 | "cell_type": "markdown", 467 | "metadata": { 468 | "id": "r7L0-BztzqNe" 469 | }, 470 | "source": [ 471 | "실제값 y_train과 크기가 동일한 6 × 1의 크기를 가지는 예측값 벡터가 나오는데 모든 값이 0.5입니다.\n", 472 | "\n", 473 | "사실 가설식을 좀 더 간단하게도 구현할 수 있습니다. 이미 파이토치에서는 시그모이드 함수를 이미 구현하여 제공하고 있기 때문입니다. 다음은 torch.sigmoid를 사용하여 좀 더 간단히 구현한 가설식입니다." 474 | ] 475 | }, 476 | { 477 | "cell_type": "code", 478 | "metadata": { 479 | "id": "ekhAuQk2zk14" 480 | }, 481 | "source": [ 482 | "hypothesis = torch.sigmoid(x_train.matmul(W) + b)" 483 | ], 484 | "execution_count": null, 485 | "outputs": [] 486 | }, 487 | { 488 | "cell_type": "markdown", 489 | "metadata": { 490 | "id": "zhgRELmrzwzn" 491 | }, 492 | "source": [ 493 | "앞서 구현한 식과 본질적으로 동일한 식입니다. 마찬가지로 W와 b가 0으로 초기화 된 상태에서 예측값을 출력해봅시다." 494 | ] 495 | }, 496 | { 497 | "cell_type": "code", 498 | "metadata": { 499 | "id": "J033OiRKztXs" 500 | }, 501 | "source": [ 502 | "print(hypothesis)" 503 | ], 504 | "execution_count": null, 505 | "outputs": [] 506 | }, 507 | { 508 | "cell_type": "markdown", 509 | "metadata": { 510 | "id": "jQUrPL_bz9It" 511 | }, 512 | "source": [ 513 | "앞선 결과와 동일하게 y_train과 크기가 동일한 6 × 1의 크기를 가지는 예측값 벡터가 나오는데 모든 값이 0.5입니다.\n", 514 | "\n", 515 | "이제 아래의 비용 함수값. 즉, 현재 예측값과 실제값 사이의 cost를 구해보겠습니다.\n", 516 | "\n", 517 | "$cost(W) = -\\frac{1}{n} \\sum_{i=1}^{n} [y^{(i)}logH(x^{(i)}) + (1-y^{(i)})log(1-H(x^{(i)}))]$\n", 518 | "\n", 519 | "우선, 현재 예측값과 실제값을 출력해보겠습니다." 520 | ] 521 | }, 522 | { 523 | "cell_type": "code", 524 | "metadata": { 525 | "id": "e-LO2N_-z0t2" 526 | }, 527 | "source": [ 528 | "print(hypothesis)\n", 529 | "print(y_train)" 530 | ], 531 | "execution_count": null, 532 | "outputs": [] 533 | }, 534 | { 535 | "cell_type": "markdown", 536 | "metadata": { 537 | "id": "tvBF-Rkn0SLk" 538 | }, 539 | "source": [ 540 | "현재 총 6개의 원소가 존재하지만 하나의 샘플. 즉, 하나의 원소에 대해서만 오차를 구하는 식을 작성해보겠습니다." 541 | ] 542 | }, 543 | { 544 | "cell_type": "code", 545 | "metadata": { 546 | "id": "emSqCltv0Ho8" 547 | }, 548 | "source": [ 549 | "-(y_train[0] * torch.log(hypothesis[0]) + \n", 550 | " (1 - y_train[0]) * torch.log(1 - hypothesis[0]))" 551 | ], 552 | "execution_count": null, 553 | "outputs": [] 554 | }, 555 | { 556 | "cell_type": "markdown", 557 | "metadata": { 558 | "id": "D1N62E380WV_" 559 | }, 560 | "source": [ 561 | "이제 모든 원소에 대해서 오차를 구해보겠습니다." 562 | ] 563 | }, 564 | { 565 | "cell_type": "code", 566 | "metadata": { 567 | "id": "lNmeK2dC0UQS" 568 | }, 569 | "source": [ 570 | "losses = -(y_train * torch.log(hypothesis) + \n", 571 | " (1 - y_train) * torch.log(1 - hypothesis))\n", 572 | "print(losses)" 573 | ], 574 | "execution_count": null, 575 | "outputs": [] 576 | }, 577 | { 578 | "cell_type": "markdown", 579 | "metadata": { 580 | "id": "mjqPUKqG0gLD" 581 | }, 582 | "source": [ 583 | "그리고 이 전체 오차에 대한 평균을 구합니다." 584 | ] 585 | }, 586 | { 587 | "cell_type": "code", 588 | "metadata": { 589 | "id": "0Yw__vVR0XnL" 590 | }, 591 | "source": [ 592 | "cost = losses.mean()\n", 593 | "print(cost)" 594 | ], 595 | "execution_count": null, 596 | "outputs": [] 597 | }, 598 | { 599 | "cell_type": "markdown", 600 | "metadata": { 601 | "id": "zZdFawoB0j5-" 602 | }, 603 | "source": [ 604 | "결과적으로 얻은 cost는 0.6931입니다.\n", 605 | "\n", 606 | "지금까지 비용 함수의 값을 직접 구현하였는데, 사실 파이토치에서는 로지스틱 회귀의 비용 함수를 이미 구현해서 제공하고 있습니다.\n", 607 | "사용 방법은 torch.nn.functional as F와 같이 임포트 한 후에 F.binary_cross_entropy(예측값, 실제값)과 같이 사용하면 됩니다." 608 | ] 609 | }, 610 | { 611 | "cell_type": "code", 612 | "metadata": { 613 | "id": "t2EsLVd90kY4" 614 | }, 615 | "source": [ 616 | "F.binary_cross_entropy(hypothesis, y_train)" 617 | ], 618 | "execution_count": null, 619 | "outputs": [] 620 | }, 621 | { 622 | "cell_type": "markdown", 623 | "metadata": { 624 | "id": "Y5XzdaXX0oP3" 625 | }, 626 | "source": [ 627 | "동일하게 cost가 0.6931이 출력되는 것을 볼 수 있습니다. 모델의 훈련 과정까지 추가한 전체 코드는 아래와 같습니다." 628 | ] 629 | }, 630 | { 631 | "cell_type": "code", 632 | "metadata": { 633 | "id": "y58AL-fu0mC5" 634 | }, 635 | "source": [ 636 | "x_data = [[1, 2], [2, 3], [3, 1], [4, 3], [5, 3], [6, 2]]\n", 637 | "y_data = [[0], [0], [0], [1], [1], [1]]\n", 638 | "x_train = torch.FloatTensor(x_data)\n", 639 | "y_train = torch.FloatTensor(y_data)" 640 | ], 641 | "execution_count": null, 642 | "outputs": [] 643 | }, 644 | { 645 | "cell_type": "code", 646 | "metadata": { 647 | "id": "lNHAMROJ0pfa" 648 | }, 649 | "source": [ 650 | "# 모델 초기화\n", 651 | "W = torch.zeros((2, 1), requires_grad=True)\n", 652 | "b = torch.zeros(1, requires_grad=True)\n", 653 | "# optimizer 설정\n", 654 | "optimizer = optim.SGD([W, b], lr=1)\n", 655 | "\n", 656 | "nb_epochs = 1000\n", 657 | "for epoch in range(nb_epochs + 1):\n", 658 | "\n", 659 | " # Cost 계산\n", 660 | " hypothesis = torch.sigmoid(x_train.matmul(W) + b)\n", 661 | " cost = -(y_train * torch.log(hypothesis) + \n", 662 | " (1 - y_train) * torch.log(1 - hypothesis)).mean()\n", 663 | "\n", 664 | " # cost로 H(x) 개선\n", 665 | " optimizer.zero_grad()\n", 666 | " cost.backward()\n", 667 | " optimizer.step()\n", 668 | "\n", 669 | " # 100번마다 로그 출력\n", 670 | " if epoch % 100 == 0:\n", 671 | " print('Epoch {:4d}/{} Cost: {:.6f}'.format(\n", 672 | " epoch, nb_epochs, cost.item()\n", 673 | " ))" 674 | ], 675 | "execution_count": null, 676 | "outputs": [] 677 | }, 678 | { 679 | "cell_type": "code", 680 | "metadata": { 681 | "id": "BwrsnzWr0sCh" 682 | }, 683 | "source": [ 684 | "hypothesis = torch.sigmoid(x_train.matmul(W) + b)\n", 685 | "print(hypothesis)" 686 | ], 687 | "execution_count": null, 688 | "outputs": [] 689 | }, 690 | { 691 | "cell_type": "markdown", 692 | "metadata": { 693 | "id": "3t1QoKyk0ve4" 694 | }, 695 | "source": [ 696 | "현재 위 값들은 0과 1 사이의 값을 가지고 있습니다. 이제 0.5를 넘으면 True, 넘지 않으면 False로 값을 정하여 출력해보겠습니다." 697 | ] 698 | }, 699 | { 700 | "cell_type": "code", 701 | "metadata": { 702 | "id": "gKx4t0Iw0uH4" 703 | }, 704 | "source": [ 705 | "prediction = hypothesis >= torch.FloatTensor([0.5])\n", 706 | "print(prediction)" 707 | ], 708 | "execution_count": null, 709 | "outputs": [] 710 | }, 711 | { 712 | "cell_type": "markdown", 713 | "metadata": { 714 | "id": "LgTsCU9j07Tr" 715 | }, 716 | "source": [ 717 | "실제값은 [[0], [0], [0], [1], [1], [1]]이므로, 이는 결과적으로 False, False, False, True, True, True와 동일합니다. 즉, 기존의 실제값과 동일하게 예측한 것을 볼 수 있습니다. 훈련이 된 후의 W와 b의 값을 출력해보겠습니다." 718 | ] 719 | }, 720 | { 721 | "cell_type": "code", 722 | "metadata": { 723 | "id": "zR_FzSg70xFJ" 724 | }, 725 | "source": [ 726 | "print(W)\n", 727 | "print(b)" 728 | ], 729 | "execution_count": null, 730 | "outputs": [] 731 | }, 732 | { 733 | "cell_type": "markdown", 734 | "metadata": { 735 | "id": "Y0s2IIrw0-Yz" 736 | }, 737 | "source": [ 738 | "## 3.2 nn.Module로 구현하는 로지스틱 회귀\n", 739 | "\n", 740 | "잠깐만 복습을 해보면 선형 회귀 모델의 가설식은 $H(x)=Wx+b$이었습니다. 그리고 이 가설식을 구현하기 위해서 파이토치의 nn.Linear()를 사용했습니다. 그리고 로지스틱 회귀의 가설식은 $H(x)=sigmoid(Wx+b)$입니다. 파이토치에서는 nn.Sigmoid()를 통해서 시그모이드 함수를 구현하므로 결과적으로 nn.Linear()의 결과를 nn.Sigmoid()를 거치게하면 로지스틱 회귀의 가설식이 됩니다.\n", 741 | "\n", 742 | "파이토치를 통해 이를 구현해봅시다." 743 | ] 744 | }, 745 | { 746 | "cell_type": "markdown", 747 | "metadata": { 748 | "id": "PW3Fv4t_232o" 749 | }, 750 | "source": [ 751 | "### 3.2.1 파이토치의 nn.Linear와 nn.Sigmoid로 로지스틱 회귀 구현하기" 752 | ] 753 | }, 754 | { 755 | "cell_type": "code", 756 | "metadata": { 757 | "id": "dRwmewSJ08wO" 758 | }, 759 | "source": [ 760 | "x_data = [[1, 2], [2, 3], [3, 1], [4, 3], [5, 3], [6, 2]]\n", 761 | "y_data = [[0], [0], [0], [1], [1], [1]]\n", 762 | "x_train = torch.FloatTensor(x_data)\n", 763 | "y_train = torch.FloatTensor(y_data)" 764 | ], 765 | "execution_count": null, 766 | "outputs": [] 767 | }, 768 | { 769 | "cell_type": "markdown", 770 | "metadata": { 771 | "id": "V7T3VgNT1KSX" 772 | }, 773 | "source": [ 774 | "nn.Sequential()은 nn.Module 층을 차례로 쌓을 수 있도록 합니다. 뒤에서 이를 이용해서 인공 신경망을 구현하게 되므로 기억하고 있으면 좋습니다. 조금 쉽게 말해서 nn.Sequential()은 $Wx+b$와 같은 수식과 시그모이드 함수 등과 같은 여러 함수들을 연결해주는 역할을 합니다. 이를 이용해서 로지스틱 회귀를 구현해봅시다." 775 | ] 776 | }, 777 | { 778 | "cell_type": "code", 779 | "metadata": { 780 | "id": "APWII4oI1JDz" 781 | }, 782 | "source": [ 783 | "model = nn.Sequential(\n", 784 | " nn.Linear(2, 1), # input_dim = 2, output_dim = 1\n", 785 | " nn.Sigmoid() # 출력은 시그모이드 함수를 거친다\n", 786 | ")" 787 | ], 788 | "execution_count": null, 789 | "outputs": [] 790 | }, 791 | { 792 | "cell_type": "markdown", 793 | "metadata": { 794 | "id": "gxg6AoQ81VVa" 795 | }, 796 | "source": [ 797 | "현재 W와 b는 랜덤 초기화가 된 상태입니다. 훈련 데이터를 넣어 예측값을 확인해봅시다." 798 | ] 799 | }, 800 | { 801 | "cell_type": "code", 802 | "metadata": { 803 | "id": "AGMKsq_M1Txv" 804 | }, 805 | "source": [ 806 | "model(x_train)" 807 | ], 808 | "execution_count": null, 809 | "outputs": [] 810 | }, 811 | { 812 | "cell_type": "markdown", 813 | "metadata": { 814 | "id": "m97rQE9s1ZJl" 815 | }, 816 | "source": [ 817 | "6 × 1 크기의 예측값 텐서가 출력됩니다. 그러나 현재 W와 b는 임의의 값을 가지므로 현재의 예측은 의미가 없습니다.\n", 818 | "이제 경사 하강법을 사용하여 훈련해보겠습니다. 총 100번의 에포크를 수행합니다." 819 | ] 820 | }, 821 | { 822 | "cell_type": "code", 823 | "metadata": { 824 | "id": "c-_ZL2UX1Wac" 825 | }, 826 | "source": [ 827 | "# optimizer 설정\n", 828 | "optimizer = optim.SGD(model.parameters(), lr=1)\n", 829 | "\n", 830 | "nb_epochs = 1000\n", 831 | "for epoch in range(nb_epochs + 1):\n", 832 | "\n", 833 | " # H(x) 계산\n", 834 | " hypothesis = model(x_train)\n", 835 | "\n", 836 | " # cost 계산\n", 837 | " cost = F.binary_cross_entropy(hypothesis, y_train)\n", 838 | "\n", 839 | " # cost로 H(x) 개선\n", 840 | " optimizer.zero_grad()\n", 841 | " cost.backward()\n", 842 | " optimizer.step()\n", 843 | "\n", 844 | " # 20번마다 로그 출력\n", 845 | " if epoch % 10 == 0:\n", 846 | " prediction = hypothesis >= torch.FloatTensor([0.5]) # 예측값이 0.5를 넘으면 True로 간주\n", 847 | " correct_prediction = prediction.float() == y_train # 실제값과 일치하는 경우만 True로 간주\n", 848 | " accuracy = correct_prediction.sum().item() / len(correct_prediction) # 정확도를 계산\n", 849 | " print('Epoch {:4d}/{} Cost: {:.6f} Accuracy {:2.2f}%'.format( # 각 에포크마다 정확도를 출력\n", 850 | " epoch, nb_epochs, cost.item(), accuracy * 100,\n", 851 | " ))" 852 | ], 853 | "execution_count": null, 854 | "outputs": [] 855 | }, 856 | { 857 | "cell_type": "markdown", 858 | "metadata": { 859 | "id": "SOSJZN2R1gBZ" 860 | }, 861 | "source": [ 862 | "중간부터 정확도는 100%가 나오기 시작합니다. 기존의 훈련 데이터를 입력하여 예측값을 확인해보겠습니다." 863 | ] 864 | }, 865 | { 866 | "cell_type": "code", 867 | "metadata": { 868 | "id": "Z0TUeYmh1bDS" 869 | }, 870 | "source": [ 871 | "model(x_train)" 872 | ], 873 | "execution_count": null, 874 | "outputs": [] 875 | }, 876 | { 877 | "cell_type": "markdown", 878 | "metadata": { 879 | "id": "YEvgY9z51jei" 880 | }, 881 | "source": [ 882 | "0.5를 넘으면 True, 그보다 낮으면 False로 간주합니다. 실제값은 [[0], [0], [0], [1], [1], [1]]입니다. 이는 False, False, False, True, True, True에 해당되므로 전부 실제값과 일치하도록 예측한 것을 확인할 수 있습니다.\n", 883 | "\n", 884 | "훈련 후의 W와 b의 값을 출력해보겠습니다." 885 | ] 886 | }, 887 | { 888 | "cell_type": "code", 889 | "metadata": { 890 | "id": "n1T7oIM81h32" 891 | }, 892 | "source": [ 893 | "print(list(model.parameters()))" 894 | ], 895 | "execution_count": null, 896 | "outputs": [] 897 | }, 898 | { 899 | "cell_type": "markdown", 900 | "metadata": { 901 | "id": "j98eODQR1nTl" 902 | }, 903 | "source": [ 904 | "출력값이 앞 챕터에서 nn.Module을 사용하지 않고 로지스틱 회귀를 구현한 실습에서 얻었던 W와 b와 거의 일치합니다." 905 | ] 906 | }, 907 | { 908 | "cell_type": "markdown", 909 | "metadata": { 910 | "id": "_8l1hxvK1omE" 911 | }, 912 | "source": [ 913 | "### 3.2.2 인공 신경망으로 표현되는 로지스틱 회귀" 914 | ] 915 | }, 916 | { 917 | "cell_type": "markdown", 918 | "metadata": { 919 | "id": "3lXKktmX1sWj" 920 | }, 921 | "source": [ 922 | "사실 로지스틱 회귀는 인공 신경망으로 간주할 수 있습니다.\n", 923 | "\n", 924 | "![대체 텍스트](https://wikidocs.net/images/page/58686/logistic_regression.PNG)\n", 925 | "\n", 926 | "위의 인공 신경망 그림에서 각 화살표는 입력과 곱해지는 가중치 또는 편향입니다. 각 입력에 대해서 검은색 화살표는 가중치, 회색 화살표는 편향이 곱해집니다. 각 입력 $x$는 각 입력의 가중치 $w$와 곱해지고, 편향 $b$는 상수 1과 곱해지는 것으로 표현되었습니다. 그리고 출력하기 전에 시그모이드 함수를 지나게 됩니다.\n", 927 | "\n", 928 | "결과적으로 위의 인공 신경망은 다음과 같은 다중 로지스틱 회귀를 표현하고 있습니다.\n", 929 | "\n", 930 | "$H(x)=sigmoid(x_{1}w_{1} + x_{2}w_{2} + b)$\n", 931 | "\n", 932 | "뒤에서 인공 신경망을 배우면서 언급하겠지만, 시그모이드 함수는 인공 신경망의 은닉층에서는 거의 사용되지 않습니다.\n", 933 | "\n", 934 | "로지스틱 회귀와 소프트맥스 회귀 : https://hyeonnii.tistory.com/239\n" 935 | ] 936 | }, 937 | { 938 | "cell_type": "markdown", 939 | "metadata": { 940 | "id": "9wyQwkOm2biX" 941 | }, 942 | "source": [ 943 | "## 3.3 클래스로 파이토치 모델 구현하기\n", 944 | "\n", 945 | "파이토치의 대부분의 구현체들은 대부분 모델을 생성할 때 클래스(Class)를 사용하고 있습니다. 앞서 배운 선형 회귀를 클래스로 구현해보겠습니다. 앞서 구현한 코드와 다른 점은 오직 클래스로 모델을 구현했다는 점입니다." 946 | ] 947 | }, 948 | { 949 | "cell_type": "markdown", 950 | "metadata": { 951 | "id": "f2JQwgCA3FAN" 952 | }, 953 | "source": [ 954 | "### 3.3.1 모델을 클래스로 구현하기\n", 955 | "\n", 956 | "앞서 로지스틱 회귀 모델은 다음과 같이 구현했었습니다." 957 | ] 958 | }, 959 | { 960 | "cell_type": "code", 961 | "metadata": { 962 | "id": "3BRw0ELj1k_S" 963 | }, 964 | "source": [ 965 | "model = nn.Sequential(\n", 966 | " nn.Linear(2, 1), # input_dim = 2, output_dim = 1\n", 967 | " nn.Sigmoid() # 출력은 시그모이드 함수를 거친다\n", 968 | ")" 969 | ], 970 | "execution_count": null, 971 | "outputs": [] 972 | }, 973 | { 974 | "cell_type": "markdown", 975 | "metadata": { 976 | "id": "_rvqTfFn3LO6" 977 | }, 978 | "source": [ 979 | "이를 클래스로 구현하면 다음과 같습니다." 980 | ] 981 | }, 982 | { 983 | "cell_type": "code", 984 | "metadata": { 985 | "id": "2639i8fG3KBq" 986 | }, 987 | "source": [ 988 | "class BinaryClassifier(nn.Module):\n", 989 | " def __init__(self):\n", 990 | " super().__init__()\n", 991 | " self.linear = nn.Linear(2, 1)\n", 992 | " self.sigmoid = nn.Sigmoid()\n", 993 | "\n", 994 | " def forward(self, x):\n", 995 | " return self.sigmoid(self.linear(x))" 996 | ], 997 | "execution_count": null, 998 | "outputs": [] 999 | }, 1000 | { 1001 | "cell_type": "markdown", 1002 | "metadata": { 1003 | "id": "aHhjPJ5o3Nnc" 1004 | }, 1005 | "source": [ 1006 | "위와 같은 클래스를 사용한 모델 구현 형식은 대부분의 파이토치 구현체에서 사용하고 있는 방식으로 반드시 숙지할 필요가 있습니다.\n", 1007 | "\n", 1008 | "클래스(class) 형태의 모델은 nn.Module 을 상속받습니다. 그리고 __init__()에서 모델의 구조와 동적을 정의하는 생성자를 정의합니다. 이는 파이썬에서 객체가 갖는 속성값을 초기화하는 역할로, 객체가 생성될 때 자동으호 호출됩니다. super() 함수를 부르면 여기서 만든 클래스는 nn.Module 클래스의 속성들을 가지고 초기화 됩니다. foward() 함수는 모델이 학습데이터를 입력받아서 forward 연산을 진행시키는 함수입니다. 이 forward() 함수는 model 객체를 데이터와 함께 호출하면 자동으로 실행이됩니다. 예를 들어 model이란 이름의 객체를 생성 후, model(입력 데이터)와 같은 형식으로 객체를 호출하면 자동으로 forward 연산이 수행됩니다.\n", 1009 | "\n", 1010 | "\n", 1011 | "\n", 1012 | "* $H(x)$ 식에 입력 $x$로부터 예측된 $y$를 얻는 것을 forward 연산이라고 합니다.\n", 1013 | "\n" 1014 | ] 1015 | }, 1016 | { 1017 | "cell_type": "markdown", 1018 | "metadata": { 1019 | "id": "YJDVdTeC3WJ7" 1020 | }, 1021 | "source": [ 1022 | "### 3.3.2 로지스틱 회귀 클래스로 구현하기\n", 1023 | "\n", 1024 | "이제 모델을 클래스로 구현한 코드를 보겠습니다. 달라진 점은 모델을 클래스로 구현했다는 점 뿐입니다. 다른 코드는 전부 동일합니다." 1025 | ] 1026 | }, 1027 | { 1028 | "cell_type": "code", 1029 | "metadata": { 1030 | "id": "gtbhI0ne3Mus" 1031 | }, 1032 | "source": [ 1033 | "x_data = [[1, 2], [2, 3], [3, 1], [4, 3], [5, 3], [6, 2]]\n", 1034 | "y_data = [[0], [0], [0], [1], [1], [1]]\n", 1035 | "x_train = torch.FloatTensor(x_data)\n", 1036 | "y_train = torch.FloatTensor(y_data)" 1037 | ], 1038 | "execution_count": null, 1039 | "outputs": [] 1040 | }, 1041 | { 1042 | "cell_type": "code", 1043 | "metadata": { 1044 | "id": "dcypMQ0A3bTy" 1045 | }, 1046 | "source": [ 1047 | "class BinaryClassifier(nn.Module):\n", 1048 | " def __init__(self):\n", 1049 | " super().__init__()\n", 1050 | " self.linear = nn.Linear(2, 1)\n", 1051 | " self.sigmoid = nn.Sigmoid()\n", 1052 | "\n", 1053 | " def forward(self, x):\n", 1054 | " return self.sigmoid(self.linear(x))" 1055 | ], 1056 | "execution_count": null, 1057 | "outputs": [] 1058 | }, 1059 | { 1060 | "cell_type": "code", 1061 | "metadata": { 1062 | "id": "m97lD0N33cRp" 1063 | }, 1064 | "source": [ 1065 | "model = BinaryClassifier()" 1066 | ], 1067 | "execution_count": null, 1068 | "outputs": [] 1069 | }, 1070 | { 1071 | "cell_type": "code", 1072 | "metadata": { 1073 | "id": "m6-YZhlw3dOp" 1074 | }, 1075 | "source": [ 1076 | "# optimizer 설정\n", 1077 | "optimizer = optim.SGD(model.parameters(), lr=1)\n", 1078 | "\n", 1079 | "nb_epochs = 1000\n", 1080 | "for epoch in range(nb_epochs + 1):\n", 1081 | "\n", 1082 | " # H(x) 계산\n", 1083 | " hypothesis = model(x_train)\n", 1084 | "\n", 1085 | " # cost 계산\n", 1086 | " cost = F.binary_cross_entropy(hypothesis, y_train)\n", 1087 | "\n", 1088 | " # cost로 H(x) 개선\n", 1089 | " optimizer.zero_grad()\n", 1090 | " cost.backward()\n", 1091 | " optimizer.step()\n", 1092 | "\n", 1093 | " # 20번마다 로그 출력\n", 1094 | " if epoch % 10 == 0:\n", 1095 | " prediction = hypothesis >= torch.FloatTensor([0.5]) # 예측값이 0.5를 넘으면 True로 간주\n", 1096 | " correct_prediction = prediction.float() == y_train # 실제값과 일치하는 경우만 True로 간주\n", 1097 | " accuracy = correct_prediction.sum().item() / len(correct_prediction) # 정확도를 계산\n", 1098 | " print('Epoch {:4d}/{} Cost: {:.6f} Accuracy {:2.2f}%'.format( # 각 에포크마다 정확도를 출력\n", 1099 | " epoch, nb_epochs, cost.item(), accuracy * 100,\n", 1100 | " ))" 1101 | ], 1102 | "execution_count": null, 1103 | "outputs": [] 1104 | }, 1105 | { 1106 | "cell_type": "code", 1107 | "metadata": { 1108 | "id": "Hv0fIT2b3eeO" 1109 | }, 1110 | "source": [ 1111 | "" 1112 | ], 1113 | "execution_count": null, 1114 | "outputs": [] 1115 | } 1116 | ] 1117 | } -------------------------------------------------------------------------------- /9. 순환 신경망(Recurrent Neural Network).ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "name": "Untitled33.ipynb", 7 | "provenance": [], 8 | "toc_visible": true, 9 | "authorship_tag": "ABX9TyNv3lR1U1bqz1X7KHEwNOAb", 10 | "include_colab_link": true 11 | }, 12 | "kernelspec": { 13 | "name": "python3", 14 | "display_name": "Python 3" 15 | } 16 | }, 17 | "cells": [ 18 | { 19 | "cell_type": "markdown", 20 | "metadata": { 21 | "id": "view-in-github", 22 | "colab_type": "text" 23 | }, 24 | "source": [ 25 | "\"Open" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": { 31 | "id": "S13CA9CF2BME", 32 | "colab_type": "text" 33 | }, 34 | "source": [ 35 | "# 9. 순환 신경망(Recurrent Neural Network)\n", 36 | "\n", 37 | "이번 챕터에서는 RNN(순환 신경망)에 대해서 이해하고, RNN의 장기 의존성 문제를 보완한 LSTM, 그리고 RNN과 LSTM을 이용하여 각종 딥 러닝 실습을 진행해보겠습니다." 38 | ] 39 | }, 40 | { 41 | "cell_type": "markdown", 42 | "metadata": { 43 | "id": "3iz01GoF2Crz", 44 | "colab_type": "text" 45 | }, 46 | "source": [ 47 | "## 9.1 순환 신경망(Recurrent Neural Network, RNN)\n", 48 | "\n", 49 | "RNN(Recurrent Neural Network)은 시퀀스(Sequence) 모델입니다. 입력과 출력을 시퀀스 단위로 처리하는 모델입니다. 번역기를 생각해보면 입력은 번역하고자 하는 문장. 즉, 단어 시퀀스입니다. 출력에 해당되는 번역된 문장 또한 단어 시퀀스입니다. 이러한 시퀀스들을 처리하기 위해 고안된 모델들을 시퀀스 모델이라고 합니다. 그 중에서도 RNN은 딥 러닝에 있어 가장 기본적인 시퀀스 모델입니다.\n", 50 | "\n", 51 | "\n", 52 | "\n", 53 | "```\n", 54 | "용어는 비슷하지만 순환 신경망과 재귀 신경망(Recursive Neural Network)은 전혀 다른 개념입니다. \n", 55 | "```\n", 56 | "\n" 57 | ] 58 | }, 59 | { 60 | "cell_type": "markdown", 61 | "metadata": { 62 | "id": "7M0kSuvD2JzL", 63 | "colab_type": "text" 64 | }, 65 | "source": [ 66 | "### 9.1.1 순환 신경망(Recurrent Neural Network, RNN)" 67 | ] 68 | }, 69 | { 70 | "cell_type": "markdown", 71 | "metadata": { 72 | "id": "DWXWKaxL2Msc", 73 | "colab_type": "text" 74 | }, 75 | "source": [ 76 | "앞서 배운 신경망들은 전부 은닉층에서 활성화 함수를 지난 값은 오직 출력층 방향으로만 향했습니다. 이와 같은 신경망들을 피드 포워드 신경망(Feed Forward Neural Network)이라고 합니다. 그런데 그렇지 않은 신경망들이 있습니다. RNN(Recurrent Neural Network) 또한 그 중 하나입니다. RNN은 은닉층의 노드에서 활성화 함수를 통해 나온 결과값을 출력층 방향으로도 보내면서, 다시 은닉층 노드의 다음 계산의 입력으로 보내는 특징을 갖고있습니다.\n", 77 | "\n", 78 | "![대체 텍스트](https://wikidocs.net/images/page/22886/rnn_image1_ver2.PNG)\n", 79 | "\n", 80 | "이를 그림으로 표현하면 위와 같습니다. $x$는 입력층의 입력 벡터, $y$는 출력층의 출력 벡터입니다. 실제로는 편향 $b$도 입력으로 존재할 수 있지만 앞으로의 그림에서는 생략합니다. RNN에서 은닉층에서 활성화 함수를 통해 결과를 내보내는 역할을 하는 노드를 셀(cell)이라고 합니다. 이 셀은 이전의 값을 기억하려고 하는 일종의 메모리 역할을 수행하므로 이를 메모리 셀 또는 RNN 셀이라고 표현합니다.\n", 81 | "\n", 82 | "은닉층의 메모리 셀은 각각의 시점(time step)에서 바로 이전 시점에서의 은닉층의 메모리 셀에서 나온 값을 자신의 입력으로 사용하는 재귀적 활동을 하고 있습니다. 앞으로는 현재 시점을 변수 t로 표현하겠습니다. 이는 현재 시점 t에서의 메모리 셀이 갖고있는 값은 과거의 메모리 셀들의 값에 영향을 받은 것임을 의미합니다. 그렇다면 메모리 셀이 갖고 있는 이 값은 뭐라고 부를까요?\n", 83 | "\n", 84 | "메모리 셀이 출력층 방향으로 또는 다음 시점 t+1의 자신에게 보내는 값을 은닉 상태(hidden state)라고 합니다. 다시 말해 t 시점의 메모리 셀은 t-1 시점의 메모리 셀이 보낸 은닉 상태값을 t 시점의 은닉 상태 계산을 위한 입력값으로 사용합니다.\n", 85 | "\n", 86 | "![대체 텍스트](https://wikidocs.net/images/page/22886/rnn_image2_ver3.PNG)\n", 87 | "\n", 88 | "RNN을 표현할 때는 일반적으로 위의 그림에서 좌측과 같이 화살표로 사이클을 그려서 재귀 형태로 표현하기도 하지만, 우측과 같이 사이클을 그리는 화살표 대신 여러 시점으로 펼쳐서 표현하기도 합니다. 두 그림은 동일한 그림으로 단지 사이클을 그리는 화살표를 사용하여 표현하였느냐, 시점의 흐름에 따라서 표현하였느냐의 차이일 뿐 둘 다 동일한 RNN을 표현하고 있습니다.\n", 89 | "\n", 90 | "피드 포워드 신경망에서는 뉴런이라는 단위를 사용했지만, RNN에서는 뉴런이라는 단위보다는 입력층과 출력층에서는 각각 입력 벡터와 출력 벡터, 은닉층에서는 은닉 상태라는 표현을 주로 사용합니다. 그래서 사실 위의 그림에서 회색과 초록색으로 표현한 각 네모들은 기본적으로 벡터 단위를 가정하고 있습니다. 피드 포워드 신경망과의 차이를 비교하기 위해서 RNN을 뉴런 단위로 시각화해보겠습니다.\n", 91 | "\n", 92 | "![대체 텍스트](https://wikidocs.net/images/page/22886/rnn_image2.5.PNG)\n", 93 | "\n", 94 | "위의 그림은 입력 벡터의 차원이 4, 은닉 상태의 크기가 2, 출력층의 출력 벡터의 차원이 2인 RNN이 시점이 2일 때의 모습을 보여줍니다. 다시 말해 뉴런 단위로 해석하면 입력층의 뉴런 수는 4, 은닉층의 뉴런 수는 2, 출력층의 뉴런 수는 2입니다.\n", 95 | "\n", 96 | "![대체 텍스트](https://wikidocs.net/images/page/22886/rnn_image3_ver2.PNG)\n", 97 | "\n", 98 | "RNN은 입력과 출력의 길이를 다르게 설계 할 수 있으므로 다양한 용도로 사용할 수 있습니다. 위 그림은 입력과 출력의 길이에 따라서 달라지는 RNN의 다양한 형태를 보여줍니다. 위 구조가 자연어 처리에서 어떻게 사용될 수 있는지 예를 들어봅시다. RNN 셀의 각 시점 별 입, 출력의 단위는 사용자가 정의하기 나름이지만 가장 보편적인 단위는 '단어 벡터'입니다.\n", 99 | "\n", 100 | "예를 들어 하나의 입력에 대해서 여러개의 출력(one-to-many)의 모델은 하나의 이미지 입력에 대해서 사진의 제목을 출력하는 이미지 캡셔닝(Image Captioning) 작업에 사용할 수 있습니다. 사진의 제목은 단어들의 나열이므로 시퀀스 출력입니다.\n", 101 | "\n", 102 | "![대체 텍스트](https://wikidocs.net/images/page/22886/rnn_image3.5.PNG)\n", 103 | "\n", 104 | "또한 단어 시퀀스에 대해서 하나의 출력(many-to-one)을 하는 모델은 입력 문서가 긍정적인지 부정적인지를 판별하는 감성 분류(sentiment classification), 또는 메일이 정상 메일인지 스팸 메일인지 판별하는 스팸 메일 분류(spam detection)에 사용할 수 있습니다. 위 그림은 RNN으로 스팸 메일을 분류할 때의 아키텍처를 보여줍니다.\n", 105 | "\n", 106 | "![대체 텍스트](https://wikidocs.net/images/page/22886/rnn_image3.7.PNG)\n", 107 | "\n", 108 | "다 대 다(many-to-many)의 모델의 경우에는 입력 문장으로 부터 대답 문장을 출력하는 챗봇과 입력 문장으로부터 번역된 문장을 출력하는 번역기, 개체명 인식이나 품사 태깅과 같은 작업이 속합니다. 위 그림은 개체명 인식을 수행할 때의 RNN 아키텍처를 보여줍니다.\n", 109 | "\n", 110 | "이제 RNN에 대한 수식을 정의해보겠습니다.\n", 111 | "\n", 112 | "![대체 텍스트](https://wikidocs.net/images/page/22886/rnn_image4_ver2.PNG)\n", 113 | "\n", 114 | "현재 시점 $t$에서의 은닉 상태값을 $h_t$라고 정의하겠습니다. 은닉층의 메모리 셀은 $h_t$를 계산하기 위해서 총 두 개의 가중치를 갖게 됩니다. 하나는 입력층에서 입력값을 위한 가중치 $W_x$이고, 하나는 이전 시점 t-1의 은닉 상태값인 $h_{t−1}$을 위한 가중치 $W_h$입니다.\n", 115 | "\n", 116 | "이를 식으로 표현하면 다음과 같습니다.\n", 117 | "\n", 118 | "은닉층 : $h_{t} = tanh(W_{x} x_{t} + W_{h}h_{t−1} + b)$\n", 119 | "\n", 120 | "출력층 : $y_{t} = f(W_{y}h_{t} + b)$\n", 121 | "\n", 122 | "단, $f$는 비선형 활성화 함수 중 하나.\n", 123 | "\n", 124 | "RNN의 은닉층 연산을 벡터와 행렬 연산으로 이해할 수 있습니다. 자연어 처리에서 RNN의 입력 $x_t$는 대부분의 경우에서 단어 벡터로 간주할 수 있는데, 단어 벡터의 차원을 $d$라고 하고, 은닉 상태의 크기를 $D_h$라고 하였을 때 각 벡터와 행렬의 크기는 다음과 같습니다.\n", 125 | "\n", 126 | "$x_t : (d * 1)$\n", 127 | "\n", 128 | "$W_x : (D_h * d)$\n", 129 | "\n", 130 | "$W_h : (D_h * D_h)$\n", 131 | "\n", 132 | "$h_{t-1} : (D_h * 1)$\n", 133 | "\n", 134 | "$b : (D_h * 1)$\n", 135 | "\n", 136 | "배치 크기가 1이고, $d$와 $D_h$ 두 값 모두를 4로 가정하였을 때, RNN의 은닉층 연산을 그림으로 표현하면 아래와 같습니다.\n", 137 | "\n", 138 | "![대체 텍스트](https://wikidocs.net/images/page/22886/rnn_images4-5.PNG)\n", 139 | "\n", 140 | "이때 $h_t$를 계산하기 위한 활성화 함수로는 주로 하이퍼볼릭탄젠트 함수(tanh)가 사용되지만, ReLU로 바꿔 사용하는 시도도 있습니다.\n", 141 | "\n", 142 | "위의 식에서 각각의 가중치 $W_x, W_h, W_y$의 값은 모든 시점에서 값을 동일하게 공유합니다. 만약, 은닉층이 2개 이상일 경우에는 은닉층 2개의 가중치는 서로 다릅니다.\n", 143 | "\n", 144 | "출력층은 결과값인 $y_t$를 계산하기 위한 활성화 함수로는 상황에 따라 다를텐데, 예를 들어서 이진 분류를 해야하는 경우라면 시그모이드 함수를 사용할 수 있고 다양한 카테고리 중에서 선택해야하는 문제라면 소프트맥스 함수를 사용하게 될 것입니다." 145 | ] 146 | }, 147 | { 148 | "cell_type": "markdown", 149 | "metadata": { 150 | "id": "YAvF8mNp3zO9", 151 | "colab_type": "text" 152 | }, 153 | "source": [ 154 | "### 9.1.2 파이썬으로 RNN 구현하기" 155 | ] 156 | }, 157 | { 158 | "cell_type": "markdown", 159 | "metadata": { 160 | "id": "-zIiWb7334Bq", 161 | "colab_type": "text" 162 | }, 163 | "source": [ 164 | "직접 Numpy로 RNN 층을 구현해보겠습니다. 앞서 메모리 셀에서 은닉 상태를 계산하는 식을 다음과 같이 정의하였습니다.\n", 165 | "\n", 166 | "$h_{t} = tanh(W_{x}X_{t} + W_{h}h_{t−1} + b)$\n", 167 | "\n", 168 | "실제 구현에 앞서 간단히 의사 코드(pseudocode)를 작성해보겠습니다.\n", 169 | "\n", 170 | "\n", 171 | "\n", 172 | "```\n", 173 | "# 아래의 코드는 의사 코드(pseudocode)로 실제 동작하는 코드가 아님. \n", 174 | "\n", 175 | "hidden_state_t = 0 # 초기 은닉 상태를 0(벡터)로 초기화\n", 176 | "for input_t in input_length: # 각 시점마다 입력을 받는다.\n", 177 | " output_t = tanh(input_t, hidden_state_t) # 각 시점에 대해서 입력과 은닉 상태를 가지고 연산\n", 178 | " hidden_state_t = output_t # 계산 결과는 현재 시점의 은닉 상태가 된다.\n", 179 | "```\n", 180 | "\n", 181 | "우선 t 시점의 은닉 상태를 hidden_state_t라는 변수로 선언하였고, 입력 데이터의 길이를 input_length로 선언하였습니다. 이 경우, 입력 데이터의 길이는 곧 총 시점의 수(timesteps)가 됩니다. 그리고 t 시점의 입력값을 input_t로 선언하였습니다. 각 메모리 셀은 각 시점마다 input_t와 hidden_sate_t(이전 상태의 은닉 상태)를 입력으로 활성화 함수인 하이퍼볼릭탄젠트 함수를 통해 현 시점의 hidden_state_t를 계산합니다.\n", 182 | "\n", 183 | "의사 코드를 통해 간단히 개념 정립을 해보았습니다. 이제 RNN 층을 실제 동작되는 코드로 구현해보겠습니다. 아래의 코드는 이해를 돕기 위해 (timesteps, input_size) 크기의 2D 텐서를 입력으로 받았다고 가정하였으나, 실제로 파이토치에서는 (batch_size, timesteps, input_size)의 크기의 3D 텐서를 입력으로 받는 것을 기억합시다.\n" 184 | ] 185 | }, 186 | { 187 | "cell_type": "code", 188 | "metadata": { 189 | "id": "3yJih81T2AbD", 190 | "colab_type": "code", 191 | "colab": {} 192 | }, 193 | "source": [ 194 | "import numpy as np\n", 195 | "\n", 196 | "timesteps = 10 # 시점의 수. NLP에서는 보통 문장의 길이가 된다.\n", 197 | "input_size = 4 # 입력의 차원. NLP에서는 보통 단어 벡터의 차원이 된다.\n", 198 | "hidden_size = 8 # 은닉 상태의 크기. 메모리 셀의 용량이다.\n", 199 | "\n", 200 | "inputs = np.random.random((timesteps, input_size)) # 입력에 해당되는 2D 텐서\n", 201 | "\n", 202 | "hidden_state_t = np.zeros((hidden_size,)) # 초기 은닉 상태는 0(벡터)로 초기화\n", 203 | "# 은닉 상태의 크기 hidden_size로 은닉 상태를 만듬." 204 | ], 205 | "execution_count": 0, 206 | "outputs": [] 207 | }, 208 | { 209 | "cell_type": "markdown", 210 | "metadata": { 211 | "id": "ogVW9BUe4E5o", 212 | "colab_type": "text" 213 | }, 214 | "source": [ 215 | "우선 시점, 입력의 차원, 은닉 상태의 크기, 그리고 초기 은닉 상태를 정의하였습니다. 현재 초기 은닉 상태는 0의 값을 가지는 벡터로 초기화가 된 상태입니다. 초기 은닉 상태를 출력해보겠습니다." 216 | ] 217 | }, 218 | { 219 | "cell_type": "code", 220 | "metadata": { 221 | "id": "0TURyP-D4D9d", 222 | "colab_type": "code", 223 | "colab": { 224 | "base_uri": "https://localhost:8080/", 225 | "height": 35 226 | }, 227 | "outputId": "c0479f59-6803-42cc-fec7-00d49efdc1cc" 228 | }, 229 | "source": [ 230 | "print(hidden_state_t) # 8의 크기를 가지는 은닉 상태. 현재는 초기 은닉 상태로 모든 차원이 0의 값을 가짐." 231 | ], 232 | "execution_count": 2, 233 | "outputs": [ 234 | { 235 | "output_type": "stream", 236 | "text": [ 237 | "[0. 0. 0. 0. 0. 0. 0. 0.]\n" 238 | ], 239 | "name": "stdout" 240 | } 241 | ] 242 | }, 243 | { 244 | "cell_type": "markdown", 245 | "metadata": { 246 | "id": "8vV3EvDR4Heu", 247 | "colab_type": "text" 248 | }, 249 | "source": [ 250 | "은닉 상태의 크기를 8로 정의하였으므로 8의 차원을 가지는 0의 값으로 구성된 벡터가 출력됩니다. 이제 가중치와 편향을 정의합니다." 251 | ] 252 | }, 253 | { 254 | "cell_type": "code", 255 | "metadata": { 256 | "id": "0H4JwE704GRs", 257 | "colab_type": "code", 258 | "colab": {} 259 | }, 260 | "source": [ 261 | "Wx = np.random.random((hidden_size, input_size)) # (8, 4)크기의 2D 텐서 생성. 입력에 대한 가중치.\n", 262 | "Wh = np.random.random((hidden_size, hidden_size)) # (8, 8)크기의 2D 텐서 생성. 은닉 상태에 대한 가중치.\n", 263 | "b = np.random.random((hidden_size,)) # (8,)크기의 1D 텐서 생성. 이 값은 편향(bias)." 264 | ], 265 | "execution_count": 0, 266 | "outputs": [] 267 | }, 268 | { 269 | "cell_type": "markdown", 270 | "metadata": { 271 | "id": "yxQMulZl4KCl", 272 | "colab_type": "text" 273 | }, 274 | "source": [ 275 | "가중치와 편향을 각 크기에 맞게 정의하였습니다. 가중치와 편향의 크기를 출력해보겠습니다." 276 | ] 277 | }, 278 | { 279 | "cell_type": "code", 280 | "metadata": { 281 | "id": "vClVCoUN4Iyx", 282 | "colab_type": "code", 283 | "colab": { 284 | "base_uri": "https://localhost:8080/", 285 | "height": 71 286 | }, 287 | "outputId": "b5823163-01be-4183-8fee-e18686e022cf" 288 | }, 289 | "source": [ 290 | "print(np.shape(Wx))\n", 291 | "print(np.shape(Wh))\n", 292 | "print(np.shape(b))" 293 | ], 294 | "execution_count": 4, 295 | "outputs": [ 296 | { 297 | "output_type": "stream", 298 | "text": [ 299 | "(8, 4)\n", 300 | "(8, 8)\n", 301 | "(8,)\n" 302 | ], 303 | "name": "stdout" 304 | } 305 | ] 306 | }, 307 | { 308 | "cell_type": "markdown", 309 | "metadata": { 310 | "id": "NlfvbqgI4Mer", 311 | "colab_type": "text" 312 | }, 313 | "source": [ 314 | "각 가중치와 편향의 크기는 다음과 같습니다. Wx는 (은닉 상태의 크기 × 입력의 차원), Wh는 (은닉 상태의 크기 × 은닉 상태의 크기), b는 (은닉 상태의 크기)의 크기를 가집니다. 이제 모든 시점의 은닉 상태를 출력한다고 가정하고, RNN 층을 동작시켜봅시다." 315 | ] 316 | }, 317 | { 318 | "cell_type": "code", 319 | "metadata": { 320 | "id": "fWLYplS34LL1", 321 | "colab_type": "code", 322 | "colab": { 323 | "base_uri": "https://localhost:8080/", 324 | "height": 557 325 | }, 326 | "outputId": "1cd4aca1-c5e6-43c3-ecd6-ad23705f0f49" 327 | }, 328 | "source": [ 329 | "total_hidden_states = []\n", 330 | "\n", 331 | "# 메모리 셀 동작\n", 332 | "for input_t in inputs: # 각 시점에 따라서 입력값이 입력됨.\n", 333 | " output_t = np.tanh(np.dot(Wx,input_t) + np.dot(Wh,hidden_state_t) + b) # Wx * Xt + Wh * Ht-1 + b(bias)\n", 334 | " total_hidden_states.append(list(output_t)) # 각 시점의 은닉 상태의 값을 계속해서 축적\n", 335 | " print(np.shape(total_hidden_states)) # 각 시점 t별 메모리 셀의 출력의 크기는 (timestep, output_dim)\n", 336 | " hidden_state_t = output_t\n", 337 | "\n", 338 | "total_hidden_states = np.stack(total_hidden_states, axis = 0) \n", 339 | "# 출력 시 값을 깔끔하게 해준다.\n", 340 | "\n", 341 | "print(total_hidden_states) # (timesteps, output_dim)의 크기. 이 경우 (10, 8)의 크기를 가지는 메모리 셀의 2D 텐서를 출력." 342 | ], 343 | "execution_count": 5, 344 | "outputs": [ 345 | { 346 | "output_type": "stream", 347 | "text": [ 348 | "(1, 8)\n", 349 | "(2, 8)\n", 350 | "(3, 8)\n", 351 | "(4, 8)\n", 352 | "(5, 8)\n", 353 | "(6, 8)\n", 354 | "(7, 8)\n", 355 | "(8, 8)\n", 356 | "(9, 8)\n", 357 | "(10, 8)\n", 358 | "[[0.95272294 0.95851312 0.74888029 0.91389373 0.87315741 0.48624976\n", 359 | " 0.84211511 0.82927802]\n", 360 | " [0.99998568 0.99995339 0.99993613 0.99995415 0.99998828 0.9998739\n", 361 | " 0.99992139 0.99991067]\n", 362 | " [0.99996632 0.99989076 0.9999563 0.99997857 0.99999583 0.99994522\n", 363 | " 0.99986711 0.99988731]\n", 364 | " [0.9999844 0.99994245 0.99997659 0.99998231 0.99999657 0.99996015\n", 365 | " 0.99992883 0.99993496]\n", 366 | " [0.99997159 0.99992672 0.99996021 0.99997457 0.99999638 0.99992305\n", 367 | " 0.99982672 0.99988251]\n", 368 | " [0.99997887 0.99994229 0.99997119 0.99997219 0.99999551 0.99994762\n", 369 | " 0.99986293 0.99991178]\n", 370 | " [0.99998924 0.99996119 0.99998257 0.99997947 0.99999704 0.99993177\n", 371 | " 0.99991111 0.99992294]\n", 372 | " [0.99998869 0.99997684 0.99998058 0.99997419 0.99999704 0.99995112\n", 373 | " 0.99989694 0.99994866]\n", 374 | " [0.99999139 0.99996488 0.99998524 0.99998372 0.99999736 0.99994964\n", 375 | " 0.9999423 0.99994283]\n", 376 | " [0.99998497 0.99993321 0.99997422 0.99999015 0.99999785 0.99996731\n", 377 | " 0.99996091 0.99994974]]\n" 378 | ], 379 | "name": "stdout" 380 | } 381 | ] 382 | }, 383 | { 384 | "cell_type": "markdown", 385 | "metadata": { 386 | "id": "gR1EvhIu4PPM", 387 | "colab_type": "text" 388 | }, 389 | "source": [ 390 | "### 9.1.3 파이토치의 nn.RNN()" 391 | ] 392 | }, 393 | { 394 | "cell_type": "markdown", 395 | "metadata": { 396 | "id": "BJQVI3Q34SKg", 397 | "colab_type": "text" 398 | }, 399 | "source": [ 400 | "파이토치에서는 nn.RNN()을 통해서 RNN 셀을 구현합니다. 실습을 통해 이해해봅시다. 우선 필요한 파이토치의 도구들을 임포트합니다." 401 | ] 402 | }, 403 | { 404 | "cell_type": "code", 405 | "metadata": { 406 | "id": "D8D8KH0d4OBf", 407 | "colab_type": "code", 408 | "colab": {} 409 | }, 410 | "source": [ 411 | "import torch\n", 412 | "import torch.nn as nn" 413 | ], 414 | "execution_count": 0, 415 | "outputs": [] 416 | }, 417 | { 418 | "cell_type": "markdown", 419 | "metadata": { 420 | "id": "CqdQrc4A4Uf5", 421 | "colab_type": "text" 422 | }, 423 | "source": [ 424 | "이제 입력의 크기와 은닉 상태의 크기를 정의합니다. 은닉 상태의 크기는 대표적인 RNN의 하이퍼파라미터입니다. 여기서 입력의 크기는 매 시점마다 들어가는 입력의 크기를 의미합니다." 425 | ] 426 | }, 427 | { 428 | "cell_type": "code", 429 | "metadata": { 430 | "id": "XdE-ym5m4TQI", 431 | "colab_type": "code", 432 | "colab": {} 433 | }, 434 | "source": [ 435 | "input_size = 5 # 입력의 크기\n", 436 | "hidden_size = 8 # 은닉 상태의 크기" 437 | ], 438 | "execution_count": 0, 439 | "outputs": [] 440 | }, 441 | { 442 | "cell_type": "markdown", 443 | "metadata": { 444 | "id": "kJR5eCNl4W0q", 445 | "colab_type": "text" 446 | }, 447 | "source": [ 448 | "이제 입력 텐서를 정의합니다. 입력 텐서는 (배치 크기 × 시점의 수 × 매 시점마다 들어가는 입력)의 크기를 가집니다. 여기서는 배치 크기는 1, 10번의 시점동안 5차원의 입력 벡터가 들어가도록 텐서를 정의합니다." 449 | ] 450 | }, 451 | { 452 | "cell_type": "code", 453 | "metadata": { 454 | "id": "DMLaBrfm4V5j", 455 | "colab_type": "code", 456 | "colab": {} 457 | }, 458 | "source": [ 459 | "# (batch_size, time_steps, input_size)\n", 460 | "inputs = torch.Tensor(1, 10, 5)" 461 | ], 462 | "execution_count": 0, 463 | "outputs": [] 464 | }, 465 | { 466 | "cell_type": "markdown", 467 | "metadata": { 468 | "id": "Z9YWo4x-4Y1S", 469 | "colab_type": "text" 470 | }, 471 | "source": [ 472 | "이제 nn.RNN()을 사용하여 RNN의 셀을 만듭니다. 인자로 입력의 크기, 은닉 상태의 크기를 정의해주고, batch_first=True를 통해서 입력 텐서의 첫번째 차원이 배치 크기임을 알려줍니다." 473 | ] 474 | }, 475 | { 476 | "cell_type": "code", 477 | "metadata": { 478 | "id": "juwzwwGW4YDS", 479 | "colab_type": "code", 480 | "colab": {} 481 | }, 482 | "source": [ 483 | "cell = nn.RNN(input_size, hidden_size, batch_first=True)" 484 | ], 485 | "execution_count": 0, 486 | "outputs": [] 487 | }, 488 | { 489 | "cell_type": "markdown", 490 | "metadata": { 491 | "id": "I_q9-fNV4a6W", 492 | "colab_type": "text" 493 | }, 494 | "source": [ 495 | "입력 텐서를 RNN 셀에 입력하여 출력을 확인해봅시다." 496 | ] 497 | }, 498 | { 499 | "cell_type": "code", 500 | "metadata": { 501 | "id": "vkIUVoaU4aOH", 502 | "colab_type": "code", 503 | "colab": {} 504 | }, 505 | "source": [ 506 | "outputs, _status = cell(inputs)" 507 | ], 508 | "execution_count": 0, 509 | "outputs": [] 510 | }, 511 | { 512 | "cell_type": "markdown", 513 | "metadata": { 514 | "id": "5gprf3yA4cxW", 515 | "colab_type": "text" 516 | }, 517 | "source": [ 518 | "RNN 셀은 두 개의 입력을 리턴하는데, 첫번째 리턴값은 모든 시점(timesteps)의 은닉 상태들이며, 두번째 리턴값은 마지막 시점(timestep)의 은닉 상태입니다. 우선 첫번째 리턴값에 대해서 크기를 확인해봅시다." 519 | ] 520 | }, 521 | { 522 | "cell_type": "code", 523 | "metadata": { 524 | "id": "4Zs373OY4cUq", 525 | "colab_type": "code", 526 | "colab": { 527 | "base_uri": "https://localhost:8080/", 528 | "height": 35 529 | }, 530 | "outputId": "0aad6d62-c17d-4369-f5e4-e0c0b0f5e218" 531 | }, 532 | "source": [ 533 | "print(outputs.shape) # 모든 time-step의 hidden_state" 534 | ], 535 | "execution_count": 11, 536 | "outputs": [ 537 | { 538 | "output_type": "stream", 539 | "text": [ 540 | "torch.Size([1, 10, 8])\n" 541 | ], 542 | "name": "stdout" 543 | } 544 | ] 545 | }, 546 | { 547 | "cell_type": "markdown", 548 | "metadata": { 549 | "id": "DDJ7A86_4fhv", 550 | "colab_type": "text" 551 | }, 552 | "source": [ 553 | "첫번째 리턴값의 은닉 상태들은 (1, 10, 8)의 크기를 가집니다. 이는 10번의 시점동안 8차원의 은닉상태가 출력되었다는 의미입니다. 두번째 리턴값. 다시 말해 마지막 시점의 은닉 상태의 크기를 확인해보겠습니다." 554 | ] 555 | }, 556 | { 557 | "cell_type": "code", 558 | "metadata": { 559 | "id": "YbQhxqu44erl", 560 | "colab_type": "code", 561 | "colab": { 562 | "base_uri": "https://localhost:8080/", 563 | "height": 35 564 | }, 565 | "outputId": "0164dedc-8b67-4a53-9cb1-ef34b108bf8b" 566 | }, 567 | "source": [ 568 | "print(_status.shape) # 최종 time-step의 hidden_state" 569 | ], 570 | "execution_count": 12, 571 | "outputs": [ 572 | { 573 | "output_type": "stream", 574 | "text": [ 575 | "torch.Size([1, 1, 8])\n" 576 | ], 577 | "name": "stdout" 578 | } 579 | ] 580 | }, 581 | { 582 | "cell_type": "markdown", 583 | "metadata": { 584 | "id": "UDVyaeaQ4hxo", 585 | "colab_type": "text" 586 | }, 587 | "source": [ 588 | "마지막 시점의 은닉 상태는 (1, 1, 8)의 크기를 가집니다." 589 | ] 590 | }, 591 | { 592 | "cell_type": "markdown", 593 | "metadata": { 594 | "id": "c_pspzVe4jA3", 595 | "colab_type": "text" 596 | }, 597 | "source": [ 598 | "### 9.1.4 깊은 순환 신경망(Deep Recurrent Neural Network)" 599 | ] 600 | }, 601 | { 602 | "cell_type": "markdown", 603 | "metadata": { 604 | "id": "WtoTsbUQ4m0P", 605 | "colab_type": "text" 606 | }, 607 | "source": [ 608 | "![대체 텍스트](https://wikidocs.net/images/page/22886/rnn_image4.5_finalPNG.PNG)\n", 609 | "\n", 610 | "앞서 RNN도 다수의 은닉층을 가질 수 있다고 언급한 바 있습니다. 위의 그림은 순환 신경망에서 은닉층이 1개 더 추가되어 은닉층이 2개인 깊은(deep) 순환 신경망의 모습을 보여줍니다. 위의 코드에서 첫번째 은닉층은 다음 은닉층에 모든 시점에 대해서 은닉 상태 값을 다음 은닉층으로 보내주고 있습니다.\n", 611 | "\n", 612 | "깊은 순환 신경망을 파이토치로 구현할 때는 nn.RNN()의 인자인 num_layers에 값을 전달하여 층을 쌓습니다. 층이 2개인 깊은 순환 신경망의 경우, 앞서 실습했던 임의의 입력에 대해서 출력이 어떻게 달라지는지 확인해봅시다." 613 | ] 614 | }, 615 | { 616 | "cell_type": "code", 617 | "metadata": { 618 | "id": "VmOA8KX54hV9", 619 | "colab_type": "code", 620 | "colab": {} 621 | }, 622 | "source": [ 623 | "# (batch_size, time_steps, input_size)\n", 624 | "inputs = torch.Tensor(1, 10, 5)" 625 | ], 626 | "execution_count": 0, 627 | "outputs": [] 628 | }, 629 | { 630 | "cell_type": "code", 631 | "metadata": { 632 | "id": "q64uTkcJ4rJg", 633 | "colab_type": "code", 634 | "colab": {} 635 | }, 636 | "source": [ 637 | "cell = nn.RNN(input_size = 5, hidden_size = 8, num_layers = 2, batch_first=True)" 638 | ], 639 | "execution_count": 0, 640 | "outputs": [] 641 | }, 642 | { 643 | "cell_type": "code", 644 | "metadata": { 645 | "id": "ha9s0zSN4sGg", 646 | "colab_type": "code", 647 | "colab": { 648 | "base_uri": "https://localhost:8080/", 649 | "height": 35 650 | }, 651 | "outputId": "646ae279-1f73-4b86-fe26-a98b4b1cbf03" 652 | }, 653 | "source": [ 654 | "print(outputs.shape) # 모든 time-step의 hidden_state" 655 | ], 656 | "execution_count": 15, 657 | "outputs": [ 658 | { 659 | "output_type": "stream", 660 | "text": [ 661 | "torch.Size([1, 10, 8])\n" 662 | ], 663 | "name": "stdout" 664 | } 665 | ] 666 | }, 667 | { 668 | "cell_type": "markdown", 669 | "metadata": { 670 | "id": "razPiRz_4tzw", 671 | "colab_type": "text" 672 | }, 673 | "source": [ 674 | "첫번째 리턴값의 크기는 층이 1개였던 RNN 셀 때와 달라지지 않았습니다. 여기서는 마지막 층의 모든 시점의 은닉 상태들입니다." 675 | ] 676 | }, 677 | { 678 | "cell_type": "code", 679 | "metadata": { 680 | "id": "19cex2Qr4s6l", 681 | "colab_type": "code", 682 | "colab": { 683 | "base_uri": "https://localhost:8080/", 684 | "height": 35 685 | }, 686 | "outputId": "ca4c808e-fb56-44b1-cdd6-dcc3b500d36f" 687 | }, 688 | "source": [ 689 | "print(_status.shape) # (층의 개수, 배치 크기, 은닉 상태의 크기)" 690 | ], 691 | "execution_count": 16, 692 | "outputs": [ 693 | { 694 | "output_type": "stream", 695 | "text": [ 696 | "torch.Size([1, 1, 8])\n" 697 | ], 698 | "name": "stdout" 699 | } 700 | ] 701 | }, 702 | { 703 | "cell_type": "markdown", 704 | "metadata": { 705 | "id": "fXGjfNBB4wVm", 706 | "colab_type": "text" 707 | }, 708 | "source": [ 709 | "두번째 리턴값의 크기는 층이 1개였던 RNN 셀 때와 달라졌는데, 여기서 크기는 (층의 개수, 배치 크기, 은닉 상태의 크기)에 해당됩니다." 710 | ] 711 | }, 712 | { 713 | "cell_type": "markdown", 714 | "metadata": { 715 | "id": "vq8_o9YT4xUZ", 716 | "colab_type": "text" 717 | }, 718 | "source": [ 719 | "### 9.1.5 양방향 순환 신경망(Bidirectional Recurrent Neural Network)" 720 | ] 721 | }, 722 | { 723 | "cell_type": "markdown", 724 | "metadata": { 725 | "id": "a5RqBBss4zy1", 726 | "colab_type": "text" 727 | }, 728 | "source": [ 729 | "양방향 순환 신경망은 시점 t에서의 출력값을 예측할 때 이전 시점의 데이터뿐만 아니라, 이후 데이터로도 예측할 수 있다는 아이디어에 기반합니다.\n", 730 | "\n", 731 | "영어 빈칸 채우기 문제에 비유하여 보겠습니다.\n", 732 | "\n", 733 | "\n", 734 | "\n", 735 | "```\n", 736 | "Exercise is very effective at [ ] belly fat.\n", 737 | "\n", 738 | "1) reducing\n", 739 | "2) increasing\n", 740 | "3) multiplying\n", 741 | "```\n", 742 | "\n", 743 | "'운동은 복부 지방을 [ ] 효과적이다'라는 영어 문장이고, 정답은 reducing(줄이는 것)입니다. 그런데 위의 영어 빈 칸 채우기 문제를 잘 생각해보면 정답을 찾기 위해서는 이전에 나온 단어들만으로는 부족합니다. 목적어인 belly fat(복부 지방)를 모르는 상태라면 정답을 결정하기가 어렵습니다.\n", 744 | "\n", 745 | "즉, RNN이 과거 시점(time step)의 데이터들을 참고해서, 찾고자하는 정답을 예측하지만 실제 문제에서는 과거 시점의 데이터만 고려하는 것이 아니라 향후 시점의 데이터에 힌트가 있는 경우도 많습니다. 그래서 이전 시점의 데이터뿐만 아니라, 이후 시점의 데이터도 힌트로 활용하기 위해서 고안된 것이 양방향 RNN입니다.\n", 746 | "\n", 747 | "\n", 748 | "![대체 텍스트](https://wikidocs.net/images/page/22886/rnn_image5_ver2.PNG)\n", 749 | "\n", 750 | "양방향 RNN은 하나의 출력값을 예측하기 위해 기본적으로 두 개의 메모리 셀을 사용합니다. 첫번째 메모리 셀은 앞에서 배운 것처럼 앞 시점의 은닉 상태(Forward States)를 전달받아 현재의 은닉 상태를 계산합니다. 위의 그림에서는 주황색 메모리 셀에 해당됩니다. 두번째 메모리 셀은 앞에서 배운 것과는 다릅니다. 앞 시점의 은닉 상태가 아니라 뒤 시점의 은닉 상태(Backward States)를 전달 받아 현재의 은닉 상태를 계산합니다. 위의 그림에서는 초록색 메모리 셀에 해당됩니다. 그리고 이 두 개의 값 모두가 출력층에서 출력값을 예측하기 위해 사용됩니다.\n", 751 | "\n", 752 | "물론, 양방향 RNN도 다수의 은닉층을 가질 수 있습니다. 아래의 그림은 양방향 순환 신경망에서 은닉층이 1개 더 추가되어 은닉층이 2개인 깊은(deep) 양방향 순환 신경망의 모습을 보여줍니다.\n", 753 | "\n", 754 | "![대체 텍스트](https://wikidocs.net/images/page/22886/rnn_image6_ver3.PNG)\n", 755 | "\n", 756 | "다른 인공 신경망 모델들도 마찬가지이지만, 은닉층을 무조건 추가한다고 해서 모델의 성능이 좋아지는 것은 아닙니다. 은닉층을 추가하면, 학습할 수 있는 양이 많아지지만 또한 반대로 훈련 데이터 또한 그만큼 많이 필요합니다.\n", 757 | "\n", 758 | "양방향 순환 신경망을 파이토치로 구현할 때는 nn.RNN()의 인자인 bidirectional에 값을 True로 전달하면 됩니다. 이번에는 층이 2개인 깊은 순환 신경망이면서 양방향인 경우, 앞서 실습했던 임의의 입력에 대해서 출력이 어떻게 달라지는지 확인해봅시다." 759 | ] 760 | }, 761 | { 762 | "cell_type": "code", 763 | "metadata": { 764 | "id": "RcL88CV64vLE", 765 | "colab_type": "code", 766 | "colab": {} 767 | }, 768 | "source": [ 769 | "# (batch_size, time_steps, input_size)\n", 770 | "inputs = torch.Tensor(1, 10, 5)" 771 | ], 772 | "execution_count": 0, 773 | "outputs": [] 774 | }, 775 | { 776 | "cell_type": "code", 777 | "metadata": { 778 | "id": "anKFXWxb5COi", 779 | "colab_type": "code", 780 | "colab": {} 781 | }, 782 | "source": [ 783 | "cell = nn.RNN(input_size = 5, hidden_size = 8, num_layers = 2, batch_first=True, bidirectional = True)" 784 | ], 785 | "execution_count": 0, 786 | "outputs": [] 787 | }, 788 | { 789 | "cell_type": "code", 790 | "metadata": { 791 | "id": "rjJOKJSF5DPD", 792 | "colab_type": "code", 793 | "colab": {} 794 | }, 795 | "source": [ 796 | "outputs, _status = cell(inputs)" 797 | ], 798 | "execution_count": 0, 799 | "outputs": [] 800 | }, 801 | { 802 | "cell_type": "code", 803 | "metadata": { 804 | "id": "WK3OqhO55EF2", 805 | "colab_type": "code", 806 | "colab": { 807 | "base_uri": "https://localhost:8080/", 808 | "height": 35 809 | }, 810 | "outputId": "8d2ce695-f6fc-462c-ec8b-e4b3abf9d842" 811 | }, 812 | "source": [ 813 | "print(outputs.shape) # (배치 크기, 시퀀스 길이, 은닉 상태의 크기 x 2)" 814 | ], 815 | "execution_count": 20, 816 | "outputs": [ 817 | { 818 | "output_type": "stream", 819 | "text": [ 820 | "torch.Size([1, 10, 16])\n" 821 | ], 822 | "name": "stdout" 823 | } 824 | ] 825 | }, 826 | { 827 | "cell_type": "markdown", 828 | "metadata": { 829 | "id": "ucvNPRYl5GSy", 830 | "colab_type": "text" 831 | }, 832 | "source": [ 833 | "첫번째 리턴값의 크기는 단뱡 RNN 셀 때보다 은닉 상태의 크기의 값이 두 배가 되었습니다. 여기서는 (배치 크기, 시퀀스 길이, 은닉 상태의 크기 x 2)의 크기를 가집니다. 이는 양방향의 은닉 상태 값들이 연결(concatenate)되었기 때문입니다." 834 | ] 835 | }, 836 | { 837 | "cell_type": "code", 838 | "metadata": { 839 | "id": "KXcAtiKX5FPp", 840 | "colab_type": "code", 841 | "colab": { 842 | "base_uri": "https://localhost:8080/", 843 | "height": 35 844 | }, 845 | "outputId": "9aa99095-f409-48b7-bcf2-c1f3290a9768" 846 | }, 847 | "source": [ 848 | "print(_status.shape) # (층의 개수 x 2, 배치 크기, 은닉 상태의 크기)" 849 | ], 850 | "execution_count": 21, 851 | "outputs": [ 852 | { 853 | "output_type": "stream", 854 | "text": [ 855 | "torch.Size([4, 1, 8])\n" 856 | ], 857 | "name": "stdout" 858 | } 859 | ] 860 | }, 861 | { 862 | "cell_type": "markdown", 863 | "metadata": { 864 | "id": "GXLVRdAU5IJw", 865 | "colab_type": "text" 866 | }, 867 | "source": [ 868 | "두번째 리턴값의 크기는 (층의 개수 x 2, 배치 크기, 은닉 상태의 크기)를 가집니다. 이는 정방향 기준으로는 마지막 시점에 해당되면서, 역방향 기준에서는 첫번째 시점에 해당되는 시점의 출력값을 층의 개수만큼 쌓아 올린 결과값입니다." 869 | ] 870 | }, 871 | { 872 | "cell_type": "markdown", 873 | "metadata": { 874 | "id": "2ALEDjOq5UtY", 875 | "colab_type": "text" 876 | }, 877 | "source": [ 878 | "## 9.2 장단기 메모리(Long Short-Term Memory, LSTM)\n", 879 | "\n", 880 | "바닐라 아이스크림이 가장 기본적인 맛을 가진 아이스크림인 것처럼, 앞서 배운 RNN을 가장 단순한 형태의 RNN이라고 하여 바닐라 RNN(Vanilla RNN)이라고 합니다. (케라스에서는 SimpleRNN) 바닐라 RNN 이후 바닐라 RNN의 한계를 극복하기 위한 다양한 RNN의 변형이 나왔습니다. 이번 챕터에서 배우게 될 LSTM도 그 중 하나입니다. 앞으로의 설명에서 LSTM과 비교하여 RNN을 언급하는 것은 전부 바닐라 RNN을 말합니다." 881 | ] 882 | }, 883 | { 884 | "cell_type": "markdown", 885 | "metadata": { 886 | "id": "Q14h2J2t5YWw", 887 | "colab_type": "text" 888 | }, 889 | "source": [ 890 | "### 9.2.1 바닐라 RNN의 한계\n", 891 | "\n", 892 | "![대체 텍스트](https://wikidocs.net/images/page/22888/lstm_image1_ver2.PNG)\n", 893 | "\n", 894 | "앞 챕터에서 바닐라 RNN은 출력 결과가 이전의 계산 결과에 의존한다는 것을 언급한 바 있습니다. 하지만 바닐라 RNN은 비교적 짧은 시퀀스(sequence)에 대해서만 효과를 보이는 단점이 있습니다. 바닐라 RNN의 시점(time step)이 길어질 수록 앞의 정보가 뒤로 충분히 전달되지 못하는 현상이 발생합니다. 위의 그림은 첫번째 입력값인 $x_1$의 정보량을 짙은 남색으로 표현했을 때, 색이 점차 얕아지는 것으로 시점이 지날수록 $x_1$의 정보량이 손실되어가는 과정을 표현하였습니다. 뒤로 갈수록 $x_1$의 정보량은 손실되고, 시점이 충분히 긴 상황에서는 $x_1$의 전체 정보에 대한 영향력은 거의 의미가 없을 수도 있습니다.\n", 895 | "\n", 896 | "어쩌면 가장 중요한 정보가 시점의 앞 쪽에 위치할 수도 있습니다. RNN으로 만든 언어 모델이 다음 단어를 예측하는 과정을 생각해봅시다. 예를 들어 ''모스크바에 여행을 왔는데 건물도 예쁘고 먹을 것도 맛있었어. 그런데 글쎄 직장 상사한테 전화가 왔어. 어디냐고 묻더라구 그래서 나는 말했지. 저 여행왔는데요. 여기 ___'' 다음 단어를 예측하기 위해서는 장소 정보가 필요합니다. 그런데 장소 정보에 해당되는 단어인 '모스크바'는 앞에 위치하고 있고, RNN이 충분한 기억력을 가지고 있지 못한다면 다음 단어를 엉뚱하게 예측합니다.\n", 897 | "\n", 898 | "이를 장기 의존성 문제(the problem of Long-Term Dependencies)라고 합니다." 899 | ] 900 | }, 901 | { 902 | "cell_type": "markdown", 903 | "metadata": { 904 | "id": "YBXSdNIp5lCI", 905 | "colab_type": "text" 906 | }, 907 | "source": [ 908 | "### 9.2.2 바닐라 RNN 내부 열어보기\n", 909 | "\n", 910 | "![대체 텍스트](https://wikidocs.net/images/page/22888/vanilla_rnn_ver2.PNG)\n", 911 | "\n", 912 | "LSTM에 대해서 이해해보기 전에 바닐라 RNN의 뚜껑을 열어보겠습니다. 위의 그림은 바닐라 RNN의 내부 구조를 보여줍니다. 이 책에서는 RNN 계열의 인공 신경망의 그림에서는 편향 $b$를 생략합니다. 위의 그림에 편향 $b$를 그린다면 $x_t$ 옆에 tanh로 향하는 또 하나의 입력선을 그리면 됩니다.\n", 913 | "\n", 914 | "$h_{t} = tanh(W_{x}x_{t} + W_{h}h_{t−1} + b)$\n", 915 | "\n", 916 | "바닐라 RNN은 $x_t$와 $h_{t−1}$이라는 두 개의 입력이 각각의 가중치와 곱해져서 메모리 셀의 입력이 됩니다. 그리고 이를 하이퍼볼릭탄젠트 함수의 입력으로 사용하고 이 값은 은닉층의 출력인 은닉 상태가 됩니다." 917 | ] 918 | }, 919 | { 920 | "cell_type": "markdown", 921 | "metadata": { 922 | "id": "79CIxO9P6BF2", 923 | "colab_type": "text" 924 | }, 925 | "source": [ 926 | "### 9.2.3 LSTM(Long Short-Term Memory)\n", 927 | "\n", 928 | "![대체 텍스트](https://wikidocs.net/images/page/22888/vaniila_rnn_and_different_lstm_ver2.PNG)\n", 929 | "\n", 930 | "위의 그림은 LSTM의 전체적인 내부의 모습을 보여줍니다. 전통적인 RNN의 이러한 단점을 보완한 RNN의 일종을 장단기 메모리(Long Short-Term Memory)라고 하며, 줄여서 LSTM이라고 합니다. LSTM은 은닉층의 메모리 셀에 입력 게이트, 망각 게이트, 출력 게이트를 추가하여 불필요한 기억을 지우고, 기억해야할 것들을 정합니다. 요약하면 LSTM은 은닉 상태(hidden state)를 계산하는 식이 전통적인 RNN보다 조금 더 복잡해졌으며 셀 상태(cell state)라는 값을 추가하였습니다. 위의 그림에서는 t시점의 셀 상태를 $C_t$로 표현하고 있습니다. LSTM은 RNN과 비교하여 긴 시퀀스의 입력을 처리하는데 탁월한 성능을 보입니다.\n", 931 | "\n", 932 | "![대체 텍스트](https://wikidocs.net/images/page/22888/cellstate.PNG)\n", 933 | "\n", 934 | "셀 상태는 위의 그림에서 왼쪽에서 오른쪽으로 가는 굵은 선입니다. 셀 상태 또한 이전에 배운 은닉 상태처럼 이전 시점의 셀 상태가 다음 시점의 셀 상태를 구하기 위한 입력으로서 사용됩니다.\n", 935 | "\n", 936 | "은닉 상태값과 셀 상태값을 구하기 위해서 새로 추가 된 3개의 게이트를 사용합니다. 각 게이트는 삭제 게이트, 입력 게이트, 출력 게이트라고 부르며 이 3개의 게이트에는 공통적으로 시그모이드 함수가 존재합니다. 시그모이드 함수를 지나면 0과 1사이의 값이 나오게 되는데 이 값들을 가지고 게이트를 조절합니다. 아래의 내용을 먼저 이해하고 각 게이트에 대해서 알아보도록 하겠습니다.\n", 937 | "\n", 938 | "\n", 939 | "\n", 940 | "* 이하 식에서 σ는 시그모이드 함수를 의미합니다.\n", 941 | "* 이하 식에서 tanh는 하이퍼볼릭탄젠트 함수를 의미합니다.\n", 942 | "* $W_{xi}, W_{xg}, W_{xf}, W_{xo}$는 $x_t$와 함께 각 게이트에서 사용되는 4개의 가중치입니다.\n", 943 | "* $W_{hi}, W_{hg}, W_{hf}, W_{ho}$는 $h_{t−1}$와 함께 각 게이트에서 사용되는 4개의 가중치입니다.\n", 944 | "* $b_{i}, b_{g}, b_{f}, b_{o}$는 각 게이트에서 사용되는 4개의 편향입니다.\n", 945 | "\n", 946 | "(1) 입력게이트\n", 947 | "\n", 948 | "![대체 텍스트](https://wikidocs.net/images/page/22888/inputgate.PNG)\n", 949 | "\n", 950 | "$i_{t}=\\sigma(W_{xi}x_{t}+W_{hi}h_{t-1}+b_{i})$\n", 951 | "\n", 952 | "$g_{t}=tanh(W_{xg}x_{t}+W_{hg}h_{t-1}+b_{g})$\n", 953 | "\n", 954 | "\n", 955 | "입력 게이트는 현재 정보를 기억하기 위한 게이트입니다. 우선 현재 시점 t의 $x$값과 입력 게이트로 이어지는 가중치 $W_{xi}$를 곱한 값과 이전 시점 t-1의 은닉 상태가 입력 게이트로 이어지는 가중치 $W_{hi}$를 곱한 값을 더하여 시그모이드 함수를 지납니다. 이를 $i_t$라고 합니다.\n", 956 | "\n", 957 | "그리고 현재 시점 t의 $x$값과 입력 게이트로 이어지는 가중치 $W_{xi}$를 곱한 값과 이전 시점 t-1의 은닉 상태가 입력 게이트로 이어지는 가중치 $W_{hg}$를 곱한 값을 더하여 하이퍼볼릭탄젠트 함수를 지납니다. 이를 $g_t$라고 합니다.\n", 958 | "\n", 959 | "시그모이드 함수를 지나 0과 1 사이의 값과 하이퍼볼릭탄젠트 함수를 지나 -1과 1사이의 값 두 개가 나오게 됩니다. 이 두 개의 값을 가지고 이번에 선택된 기억할 정보의 양을 정하는데, 구체적으로 어떻게 결정하는지는 아래에서 배우게 될 셀 상태 수식을 보면 됩니다.\n", 960 | "\n", 961 | "(2) 삭제 게이트\n", 962 | "\n", 963 | "![대체 텍스트](https://wikidocs.net/images/page/22888/forgetgate.PNG)\n", 964 | "\n", 965 | "$f_{t}=\\sigma(W_{xf}x_{t}+W_{hf}h_{t-1}+b_{f})$\n", 966 | "\n", 967 | "삭제 게이트는 기억을 삭제하기 위한 게이트입니다. 현재 시점 t의 $x$값과 이전 시점 t-1의 은닉 상태가 시그모이드 함수를 지나게 됩니다. 시그모이드 함수를 지나면 0과 1 사이의 값이 나오게 되는데, 이 값이 곧 삭제 과정을 거친 정보의 양입니다. 0에 가까울수록 정보가 많이 삭제된 것이고 1에 가까울수록 정보를 온전히 기억한 것입니다. 이를 가지고 셀 상태를 구하게 되는데, 구체적으로는 아래의 셀 상태 수식을 보면 됩니다.\n", 968 | "\n", 969 | "(3) 셀 상태(장기 상태)\n", 970 | "\n", 971 | "![대체 텍스트](https://wikidocs.net/images/page/22888/cellstate2.PNG)\n", 972 | "\n", 973 | "$C_{t}=f_{t}∘C_{t-1}+i_{t}∘g_{t}$\n", 974 | "\n", 975 | "셀 상태 $C_t$를 LSTM에서는 장기 상태라고 부르기도 합니다. 그렇다면 셀 상태를 구하는 방법을 알아보겠습니다. 삭제 게이트에서 일부 기억을 잃은 상태입니다.\n", 976 | "\n", 977 | "입력 게이트에서 구한 $i_t$, $g_t$ 이 두 개의 값에 대해서 원소별 곱(entrywise product)을 진행합니다. 다시 말해 같은 크기의 두 행렬이 있을 때 같은 위치의 성분끼리 곱하는 것을 말합니다. 여기서는 식으로 ∘ 로 표현합니다. 이것이 이번에 선택된 기억할 값입니다.\n", 978 | "\n", 979 | "입력 게이트에서 선택된 기억을 삭제 게이트의 결과값과 더합니다. 이 값을 현재 시점 t의 셀 상태라고 하며, 이 값은 다음 t+1 시점의 LSTM 셀로 넘겨집니다.\n", 980 | "\n", 981 | "삭제 게이트와 입력 게이트의 영향력을 이해해봅시다. 만약 삭제 게이트의 출력값인 $f_t$가 0이 된다면, 이전 시점의 셀 상태값인 $C_{t−1}$은 현재 시점의 셀 상태값을 결정하기 위한 영향력이 0이 되면서, 오직 입력 게이트의 결과만이 현재 시점의 셀 상태값 $C_t$을 결정할 수 있습니다. 이는 삭제 게이트가 완전히 닫히고 입력 게이트를 연 상태를 의미합니다. 반대로 입력 게이트의 $i_t$값을 0이라고 한다면, 현재 시점의 셀 상태값 $C_t$는 오직 이전 시점의 셀 상태값 $C_{t−1}$의 값에만 의존합니다. 이는 입력 게이트를 완전히 닫고 삭제 게이트만을 연 상태를 의미합니다. 결과적으로 삭제 게이트는 이전 시점의 입력을 얼마나 반영할지를 의미하고, 입력 게이트는 현재 시점의 입력을 얼마나 반영할지를 결정합니다.\n", 982 | "\n", 983 | "(4) 출력 게이트와 은닉 상태(단기 상태)\n", 984 | "\n", 985 | "![대체 텍스트](https://wikidocs.net/images/page/22888/outputgateandhiddenstate.PNG)\n", 986 | "\n", 987 | "$o_{t}=\\sigma(W_{xo}x_{t}+W_{ho}h_{t-1}+b_{o})$\n", 988 | "\n", 989 | "$h_{t}=o_{t}∘tanh(c_{t})$\n", 990 | "\n", 991 | "출력 게이트는 현재 시점 t의 $x$값과 이전 시점 t-1의 은닉 상태가 시그모이드 함수를 지난 값입니다. 해당 값은 현재 시점 t의 은닉 상태를 결정하는 일에 쓰이게 됩니다.\n", 992 | "\n", 993 | "은닉 상태를 단기 상태라고 하기도 합니다. 은닉 상태는 장기 상태의 값이 하이퍼볼릭탄젠트 함수를 지나 -1과 1사이의 값입니다. 해당 값은 출력 게이트의 값과 연산되면서, 값이 걸러지는 효과가 발생합니다. 단기 상태의 값은 또한 출력층으로도 향합니다." 994 | ] 995 | }, 996 | { 997 | "cell_type": "markdown", 998 | "metadata": { 999 | "id": "dTnfV8Ii8SzP", 1000 | "colab_type": "text" 1001 | }, 1002 | "source": [ 1003 | "### 9.2.4 파이토치의 nn.LSTM()\n", 1004 | "\n", 1005 | "파이토치에서 LSTM 셀을 사용하는 방법은 매우 간단합니다. 기존에 RNN 셀을 사용하려고 했을 때는 다음과 같이 사용했었습니다.\n", 1006 | "\n", 1007 | "\n", 1008 | "\n", 1009 | "```\n", 1010 | "nn.RNN(input_dim, hidden_size, batch_fisrt=True)\n", 1011 | "```\n", 1012 | "\n", 1013 | "LSTM 셀은 이와 유사하게 다음과 같이 사용합니다.\n", 1014 | "\n", 1015 | "```\n", 1016 | "nn.LSTM(input_dim, hidden_size, batch_fisrt=True) \n", 1017 | "```\n", 1018 | "\n" 1019 | ] 1020 | }, 1021 | { 1022 | "cell_type": "code", 1023 | "metadata": { 1024 | "id": "qXzVDkSE5HjJ", 1025 | "colab_type": "code", 1026 | "colab": {} 1027 | }, 1028 | "source": [ 1029 | "" 1030 | ], 1031 | "execution_count": 0, 1032 | "outputs": [] 1033 | } 1034 | ] 1035 | } -------------------------------------------------------------------------------- /3_1_로지스틱_회귀_with_My_data.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "name": "Untitled4.ipynb", 7 | "provenance": [], 8 | "authorship_tag": "ABX9TyOnwFgkw4IBTd5F/RUH+j1B", 9 | "include_colab_link": true 10 | }, 11 | "kernelspec": { 12 | "name": "python3", 13 | "display_name": "Python 3" 14 | } 15 | }, 16 | "cells": [ 17 | { 18 | "cell_type": "markdown", 19 | "metadata": { 20 | "id": "view-in-github", 21 | "colab_type": "text" 22 | }, 23 | "source": [ 24 | "\"Open" 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "metadata": { 30 | "id": "zxbhNiKAFkcp", 31 | "colab_type": "text" 32 | }, 33 | "source": [ 34 | "sklearn에서 제공하는 유방암 데이터를 바탕으로 로지스틱 회귀모델 학습" 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "metadata": { 40 | "id": "b2H7G1KdFvTq", 41 | "colab_type": "text" 42 | }, 43 | "source": [ 44 | "# Data Load" 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "metadata": { 50 | "id": "O5IJnH5hFr3k", 51 | "colab_type": "code", 52 | "colab": {} 53 | }, 54 | "source": [ 55 | "import pandas as pd\n", 56 | "from sklearn.datasets import load_breast_cancer\n", 57 | "\n", 58 | "breast_cancer = load_breast_cancer()" 59 | ], 60 | "execution_count": 0, 61 | "outputs": [] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "metadata": { 66 | "id": "awhKhxzuGih6", 67 | "colab_type": "code", 68 | "outputId": "b985abe1-1a3d-4f46-bd80-b13be6eba019", 69 | "colab": { 70 | "base_uri": "https://localhost:8080/", 71 | "height": 1000 72 | } 73 | }, 74 | "source": [ 75 | "print(breast_cancer.DESCR)" 76 | ], 77 | "execution_count": 16, 78 | "outputs": [ 79 | { 80 | "output_type": "stream", 81 | "text": [ 82 | ".. _breast_cancer_dataset:\n", 83 | "\n", 84 | "Breast cancer wisconsin (diagnostic) dataset\n", 85 | "--------------------------------------------\n", 86 | "\n", 87 | "**Data Set Characteristics:**\n", 88 | "\n", 89 | " :Number of Instances: 569\n", 90 | "\n", 91 | " :Number of Attributes: 30 numeric, predictive attributes and the class\n", 92 | "\n", 93 | " :Attribute Information:\n", 94 | " - radius (mean of distances from center to points on the perimeter)\n", 95 | " - texture (standard deviation of gray-scale values)\n", 96 | " - perimeter\n", 97 | " - area\n", 98 | " - smoothness (local variation in radius lengths)\n", 99 | " - compactness (perimeter^2 / area - 1.0)\n", 100 | " - concavity (severity of concave portions of the contour)\n", 101 | " - concave points (number of concave portions of the contour)\n", 102 | " - symmetry \n", 103 | " - fractal dimension (\"coastline approximation\" - 1)\n", 104 | "\n", 105 | " The mean, standard error, and \"worst\" or largest (mean of the three\n", 106 | " largest values) of these features were computed for each image,\n", 107 | " resulting in 30 features. For instance, field 3 is Mean Radius, field\n", 108 | " 13 is Radius SE, field 23 is Worst Radius.\n", 109 | "\n", 110 | " - class:\n", 111 | " - WDBC-Malignant\n", 112 | " - WDBC-Benign\n", 113 | "\n", 114 | " :Summary Statistics:\n", 115 | "\n", 116 | " ===================================== ====== ======\n", 117 | " Min Max\n", 118 | " ===================================== ====== ======\n", 119 | " radius (mean): 6.981 28.11\n", 120 | " texture (mean): 9.71 39.28\n", 121 | " perimeter (mean): 43.79 188.5\n", 122 | " area (mean): 143.5 2501.0\n", 123 | " smoothness (mean): 0.053 0.163\n", 124 | " compactness (mean): 0.019 0.345\n", 125 | " concavity (mean): 0.0 0.427\n", 126 | " concave points (mean): 0.0 0.201\n", 127 | " symmetry (mean): 0.106 0.304\n", 128 | " fractal dimension (mean): 0.05 0.097\n", 129 | " radius (standard error): 0.112 2.873\n", 130 | " texture (standard error): 0.36 4.885\n", 131 | " perimeter (standard error): 0.757 21.98\n", 132 | " area (standard error): 6.802 542.2\n", 133 | " smoothness (standard error): 0.002 0.031\n", 134 | " compactness (standard error): 0.002 0.135\n", 135 | " concavity (standard error): 0.0 0.396\n", 136 | " concave points (standard error): 0.0 0.053\n", 137 | " symmetry (standard error): 0.008 0.079\n", 138 | " fractal dimension (standard error): 0.001 0.03\n", 139 | " radius (worst): 7.93 36.04\n", 140 | " texture (worst): 12.02 49.54\n", 141 | " perimeter (worst): 50.41 251.2\n", 142 | " area (worst): 185.2 4254.0\n", 143 | " smoothness (worst): 0.071 0.223\n", 144 | " compactness (worst): 0.027 1.058\n", 145 | " concavity (worst): 0.0 1.252\n", 146 | " concave points (worst): 0.0 0.291\n", 147 | " symmetry (worst): 0.156 0.664\n", 148 | " fractal dimension (worst): 0.055 0.208\n", 149 | " ===================================== ====== ======\n", 150 | "\n", 151 | " :Missing Attribute Values: None\n", 152 | "\n", 153 | " :Class Distribution: 212 - Malignant, 357 - Benign\n", 154 | "\n", 155 | " :Creator: Dr. William H. Wolberg, W. Nick Street, Olvi L. Mangasarian\n", 156 | "\n", 157 | " :Donor: Nick Street\n", 158 | "\n", 159 | " :Date: November, 1995\n", 160 | "\n", 161 | "This is a copy of UCI ML Breast Cancer Wisconsin (Diagnostic) datasets.\n", 162 | "https://goo.gl/U2Uwz2\n", 163 | "\n", 164 | "Features are computed from a digitized image of a fine needle\n", 165 | "aspirate (FNA) of a breast mass. They describe\n", 166 | "characteristics of the cell nuclei present in the image.\n", 167 | "\n", 168 | "Separating plane described above was obtained using\n", 169 | "Multisurface Method-Tree (MSM-T) [K. P. Bennett, \"Decision Tree\n", 170 | "Construction Via Linear Programming.\" Proceedings of the 4th\n", 171 | "Midwest Artificial Intelligence and Cognitive Science Society,\n", 172 | "pp. 97-101, 1992], a classification method which uses linear\n", 173 | "programming to construct a decision tree. Relevant features\n", 174 | "were selected using an exhaustive search in the space of 1-4\n", 175 | "features and 1-3 separating planes.\n", 176 | "\n", 177 | "The actual linear program used to obtain the separating plane\n", 178 | "in the 3-dimensional space is that described in:\n", 179 | "[K. P. Bennett and O. L. Mangasarian: \"Robust Linear\n", 180 | "Programming Discrimination of Two Linearly Inseparable Sets\",\n", 181 | "Optimization Methods and Software 1, 1992, 23-34].\n", 182 | "\n", 183 | "This database is also available through the UW CS ftp server:\n", 184 | "\n", 185 | "ftp ftp.cs.wisc.edu\n", 186 | "cd math-prog/cpo-dataset/machine-learn/WDBC/\n", 187 | "\n", 188 | ".. topic:: References\n", 189 | "\n", 190 | " - W.N. Street, W.H. Wolberg and O.L. Mangasarian. Nuclear feature extraction \n", 191 | " for breast tumor diagnosis. IS&T/SPIE 1993 International Symposium on \n", 192 | " Electronic Imaging: Science and Technology, volume 1905, pages 861-870,\n", 193 | " San Jose, CA, 1993.\n", 194 | " - O.L. Mangasarian, W.N. Street and W.H. Wolberg. Breast cancer diagnosis and \n", 195 | " prognosis via linear programming. Operations Research, 43(4), pages 570-577, \n", 196 | " July-August 1995.\n", 197 | " - W.H. Wolberg, W.N. Street, and O.L. Mangasarian. Machine learning techniques\n", 198 | " to diagnose breast cancer from fine-needle aspirates. Cancer Letters 77 (1994) \n", 199 | " 163-171.\n" 200 | ], 201 | "name": "stdout" 202 | } 203 | ] 204 | }, 205 | { 206 | "cell_type": "code", 207 | "metadata": { 208 | "id": "a2rLBCdbGEWp", 209 | "colab_type": "code", 210 | "outputId": "ad2d7afb-b717-4dea-da59-95fa8f2f88d8", 211 | "colab": { 212 | "base_uri": "https://localhost:8080/", 213 | "height": 455 214 | } 215 | }, 216 | "source": [ 217 | "cancer_df = pd.DataFrame(data=breast_cancer['data'],columns=breast_cancer['feature_names'])\n", 218 | "cancer_df" 219 | ], 220 | "execution_count": 17, 221 | "outputs": [ 222 | { 223 | "output_type": "execute_result", 224 | "data": { 225 | "text/html": [ 226 | "
\n", 227 | "\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", 256 | " \n", 257 | " \n", 258 | " \n", 259 | " \n", 260 | " \n", 261 | " \n", 262 | " \n", 263 | " \n", 264 | " \n", 265 | " \n", 266 | " \n", 267 | " \n", 268 | " \n", 269 | " \n", 270 | " \n", 271 | " \n", 272 | " \n", 273 | " \n", 274 | " \n", 275 | " \n", 276 | " \n", 277 | " \n", 278 | " \n", 279 | " \n", 280 | " \n", 281 | " \n", 282 | " \n", 283 | " \n", 284 | " \n", 285 | " \n", 286 | " \n", 287 | " \n", 288 | " \n", 289 | " \n", 290 | " \n", 291 | " \n", 292 | " \n", 293 | " \n", 294 | " \n", 295 | " \n", 296 | " \n", 297 | " \n", 298 | " \n", 299 | " \n", 300 | " \n", 301 | " \n", 302 | " \n", 303 | " \n", 304 | " \n", 305 | " \n", 306 | " \n", 307 | " \n", 308 | " \n", 309 | " \n", 310 | " \n", 311 | " \n", 312 | " \n", 313 | " \n", 314 | " \n", 315 | " \n", 316 | " \n", 317 | " \n", 318 | " \n", 319 | " \n", 320 | " \n", 321 | " \n", 322 | " \n", 323 | " \n", 324 | " \n", 325 | " \n", 326 | " \n", 327 | " \n", 328 | " \n", 329 | " \n", 330 | " \n", 331 | " \n", 332 | " \n", 333 | " \n", 334 | " \n", 335 | " \n", 336 | " \n", 337 | " \n", 338 | " \n", 339 | " \n", 340 | " \n", 341 | " \n", 342 | " \n", 343 | " \n", 344 | " \n", 345 | " \n", 346 | " \n", 347 | " \n", 348 | " \n", 349 | " \n", 350 | " \n", 351 | " \n", 352 | " \n", 353 | " \n", 354 | " \n", 355 | " \n", 356 | " \n", 357 | " \n", 358 | " \n", 359 | " \n", 360 | " \n", 361 | " \n", 362 | " \n", 363 | " \n", 364 | " \n", 365 | " \n", 366 | " \n", 367 | " \n", 368 | " \n", 369 | " \n", 370 | " \n", 371 | " \n", 372 | " \n", 373 | " \n", 374 | " \n", 375 | " \n", 376 | " \n", 377 | " \n", 378 | " \n", 379 | " \n", 380 | " \n", 381 | " \n", 382 | " \n", 383 | " \n", 384 | " \n", 385 | " \n", 386 | " \n", 387 | " \n", 388 | " \n", 389 | " \n", 390 | " \n", 391 | " \n", 392 | " \n", 393 | " \n", 394 | " \n", 395 | " \n", 396 | " \n", 397 | " \n", 398 | " \n", 399 | " \n", 400 | " \n", 401 | " \n", 402 | " \n", 403 | " \n", 404 | " \n", 405 | " \n", 406 | " \n", 407 | " \n", 408 | " \n", 409 | " \n", 410 | " \n", 411 | " \n", 412 | " \n", 413 | " \n", 414 | " \n", 415 | " \n", 416 | " \n", 417 | " \n", 418 | " \n", 419 | " \n", 420 | " \n", 421 | " \n", 422 | " \n", 423 | " \n", 424 | " \n", 425 | " \n", 426 | " \n", 427 | " \n", 428 | " \n", 429 | " \n", 430 | " \n", 431 | " \n", 432 | " \n", 433 | " \n", 434 | " \n", 435 | " \n", 436 | " \n", 437 | " \n", 438 | " \n", 439 | " \n", 440 | " \n", 441 | " \n", 442 | " \n", 443 | " \n", 444 | " \n", 445 | " \n", 446 | " \n", 447 | " \n", 448 | " \n", 449 | " \n", 450 | " \n", 451 | " \n", 452 | " \n", 453 | " \n", 454 | " \n", 455 | " \n", 456 | " \n", 457 | " \n", 458 | " \n", 459 | " \n", 460 | " \n", 461 | " \n", 462 | " \n", 463 | " \n", 464 | " \n", 465 | " \n", 466 | " \n", 467 | " \n", 468 | " \n", 469 | " \n", 470 | " \n", 471 | " \n", 472 | " \n", 473 | " \n", 474 | " \n", 475 | " \n", 476 | " \n", 477 | " \n", 478 | " \n", 479 | " \n", 480 | " \n", 481 | " \n", 482 | " \n", 483 | " \n", 484 | " \n", 485 | " \n", 486 | " \n", 487 | " \n", 488 | " \n", 489 | " \n", 490 | " \n", 491 | " \n", 492 | " \n", 493 | " \n", 494 | " \n", 495 | " \n", 496 | " \n", 497 | " \n", 498 | " \n", 499 | " \n", 500 | " \n", 501 | " \n", 502 | " \n", 503 | " \n", 504 | " \n", 505 | " \n", 506 | " \n", 507 | " \n", 508 | " \n", 509 | " \n", 510 | " \n", 511 | " \n", 512 | " \n", 513 | " \n", 514 | " \n", 515 | " \n", 516 | " \n", 517 | " \n", 518 | " \n", 519 | " \n", 520 | " \n", 521 | " \n", 522 | " \n", 523 | " \n", 524 | " \n", 525 | " \n", 526 | " \n", 527 | " \n", 528 | " \n", 529 | " \n", 530 | " \n", 531 | " \n", 532 | " \n", 533 | " \n", 534 | " \n", 535 | " \n", 536 | " \n", 537 | " \n", 538 | " \n", 539 | " \n", 540 | " \n", 541 | " \n", 542 | " \n", 543 | " \n", 544 | " \n", 545 | " \n", 546 | " \n", 547 | " \n", 548 | " \n", 549 | " \n", 550 | " \n", 551 | " \n", 552 | " \n", 553 | " \n", 554 | " \n", 555 | " \n", 556 | " \n", 557 | " \n", 558 | " \n", 559 | " \n", 560 | " \n", 561 | " \n", 562 | " \n", 563 | " \n", 564 | " \n", 565 | " \n", 566 | " \n", 567 | " \n", 568 | " \n", 569 | " \n", 570 | " \n", 571 | " \n", 572 | " \n", 573 | " \n", 574 | " \n", 575 | " \n", 576 | " \n", 577 | " \n", 578 | " \n", 579 | " \n", 580 | " \n", 581 | " \n", 582 | " \n", 583 | " \n", 584 | " \n", 585 | " \n", 586 | " \n", 587 | " \n", 588 | " \n", 589 | " \n", 590 | " \n", 591 | " \n", 592 | " \n", 593 | " \n", 594 | " \n", 595 | " \n", 596 | " \n", 597 | " \n", 598 | " \n", 599 | " \n", 600 | " \n", 601 | " \n", 602 | " \n", 603 | " \n", 604 | " \n", 605 | " \n", 606 | " \n", 607 | " \n", 608 | " \n", 609 | " \n", 610 | " \n", 611 | " \n", 612 | " \n", 613 | " \n", 614 | " \n", 615 | " \n", 616 | " \n", 617 | " \n", 618 | " \n", 619 | " \n", 620 | " \n", 621 | " \n", 622 | " \n", 623 | " \n", 624 | " \n", 625 | " \n", 626 | " \n", 627 | " \n", 628 | " \n", 629 | " \n", 630 | " \n", 631 | " \n", 632 | " \n", 633 | " \n", 634 | " \n", 635 | " \n", 636 | " \n", 637 | " \n", 638 | " \n", 639 | " \n", 640 | " \n", 641 | "
mean radiusmean texturemean perimetermean areamean smoothnessmean compactnessmean concavitymean concave pointsmean symmetrymean fractal dimensionradius errortexture errorperimeter errorarea errorsmoothness errorcompactness errorconcavity errorconcave points errorsymmetry errorfractal dimension errorworst radiusworst textureworst perimeterworst areaworst smoothnessworst compactnessworst concavityworst concave pointsworst symmetryworst fractal dimension
017.9910.38122.801001.00.118400.277600.300100.147100.24190.078711.09500.90538.589153.400.0063990.049040.053730.015870.030030.00619325.38017.33184.602019.00.162200.665600.71190.26540.46010.11890
120.5717.77132.901326.00.084740.078640.086900.070170.18120.056670.54350.73393.39874.080.0052250.013080.018600.013400.013890.00353224.99023.41158.801956.00.123800.186600.24160.18600.27500.08902
219.6921.25130.001203.00.109600.159900.197400.127900.20690.059990.74560.78694.58594.030.0061500.040060.038320.020580.022500.00457123.57025.53152.501709.00.144400.424500.45040.24300.36130.08758
311.4220.3877.58386.10.142500.283900.241400.105200.25970.097440.49561.15603.44527.230.0091100.074580.056610.018670.059630.00920814.91026.5098.87567.70.209800.866300.68690.25750.66380.17300
420.2914.34135.101297.00.100300.132800.198000.104300.18090.058830.75720.78135.43894.440.0114900.024610.056880.018850.017560.00511522.54016.67152.201575.00.137400.205000.40000.16250.23640.07678
.............................................................................................
56421.5622.39142.001479.00.111000.115900.243900.138900.17260.056231.17601.25607.673158.700.0103000.028910.051980.024540.011140.00423925.45026.40166.102027.00.141000.211300.41070.22160.20600.07115
56520.1328.25131.201261.00.097800.103400.144000.097910.17520.055330.76552.46305.20399.040.0057690.024230.039500.016780.018980.00249823.69038.25155.001731.00.116600.192200.32150.16280.25720.06637
56616.6028.08108.30858.10.084550.102300.092510.053020.15900.056480.45641.07503.42548.550.0059030.037310.047300.015570.013180.00389218.98034.12126.701124.00.113900.309400.34030.14180.22180.07820
56720.6029.33140.101265.00.117800.277000.351400.152000.23970.070160.72601.59505.77286.220.0065220.061580.071170.016640.023240.00618525.74039.42184.601821.00.165000.868100.93870.26500.40870.12400
5687.7624.5447.92181.00.052630.043620.000000.000000.15870.058840.38571.42802.54819.150.0071890.004660.000000.000000.026760.0027839.45630.3759.16268.60.089960.064440.00000.00000.28710.07039
\n", 642 | "

569 rows × 30 columns

\n", 643 | "
" 644 | ], 645 | "text/plain": [ 646 | " mean radius mean texture ... worst symmetry worst fractal dimension\n", 647 | "0 17.99 10.38 ... 0.4601 0.11890\n", 648 | "1 20.57 17.77 ... 0.2750 0.08902\n", 649 | "2 19.69 21.25 ... 0.3613 0.08758\n", 650 | "3 11.42 20.38 ... 0.6638 0.17300\n", 651 | "4 20.29 14.34 ... 0.2364 0.07678\n", 652 | ".. ... ... ... ... ...\n", 653 | "564 21.56 22.39 ... 0.2060 0.07115\n", 654 | "565 20.13 28.25 ... 0.2572 0.06637\n", 655 | "566 16.60 28.08 ... 0.2218 0.07820\n", 656 | "567 20.60 29.33 ... 0.4087 0.12400\n", 657 | "568 7.76 24.54 ... 0.2871 0.07039\n", 658 | "\n", 659 | "[569 rows x 30 columns]" 660 | ] 661 | }, 662 | "metadata": { 663 | "tags": [] 664 | }, 665 | "execution_count": 17 666 | } 667 | ] 668 | }, 669 | { 670 | "cell_type": "code", 671 | "metadata": { 672 | "id": "oH7t18MaGmB4", 673 | "colab_type": "code", 674 | "outputId": "411061d7-0b02-4b06-9ea5-77fd2759a1e9", 675 | "colab": { 676 | "base_uri": "https://localhost:8080/", 677 | "height": 455 678 | } 679 | }, 680 | "source": [ 681 | "data = cancer_df\n", 682 | "data = data.apply(\n", 683 | " lambda x: (x - x.mean()) / x.std()\n", 684 | ")\n", 685 | "data['target'] = breast_cancer['target']\n", 686 | "data" 687 | ], 688 | "execution_count": 18, 689 | "outputs": [ 690 | { 691 | "output_type": "execute_result", 692 | "data": { 693 | "text/html": [ 694 | "
\n", 695 | "\n", 708 | "\n", 709 | " \n", 710 | " \n", 711 | " \n", 712 | " \n", 713 | " \n", 714 | " \n", 715 | " \n", 716 | " \n", 717 | " \n", 718 | " \n", 719 | " \n", 720 | " \n", 721 | " \n", 722 | " \n", 723 | " \n", 724 | " \n", 725 | " \n", 726 | " \n", 727 | " \n", 728 | " \n", 729 | " \n", 730 | " \n", 731 | " \n", 732 | " \n", 733 | " \n", 734 | " \n", 735 | " \n", 736 | " \n", 737 | " \n", 738 | " \n", 739 | " \n", 740 | " \n", 741 | " \n", 742 | " \n", 743 | " \n", 744 | " \n", 745 | " \n", 746 | " \n", 747 | " \n", 748 | " \n", 749 | " \n", 750 | " \n", 751 | " \n", 752 | " \n", 753 | " \n", 754 | " \n", 755 | " \n", 756 | " \n", 757 | " \n", 758 | " \n", 759 | " \n", 760 | " \n", 761 | " \n", 762 | " \n", 763 | " \n", 764 | " \n", 765 | " \n", 766 | " \n", 767 | " \n", 768 | " \n", 769 | " \n", 770 | " \n", 771 | " \n", 772 | " \n", 773 | " \n", 774 | " \n", 775 | " \n", 776 | " \n", 777 | " \n", 778 | " \n", 779 | " \n", 780 | " \n", 781 | " \n", 782 | " \n", 783 | " \n", 784 | " \n", 785 | " \n", 786 | " \n", 787 | " \n", 788 | " \n", 789 | " \n", 790 | " \n", 791 | " \n", 792 | " \n", 793 | " \n", 794 | " \n", 795 | " \n", 796 | " \n", 797 | " \n", 798 | " \n", 799 | " \n", 800 | " \n", 801 | " \n", 802 | " \n", 803 | " \n", 804 | " \n", 805 | " \n", 806 | " \n", 807 | " \n", 808 | " \n", 809 | " \n", 810 | " \n", 811 | " \n", 812 | " \n", 813 | " \n", 814 | " \n", 815 | " \n", 816 | " \n", 817 | " \n", 818 | " \n", 819 | " \n", 820 | " \n", 821 | " \n", 822 | " \n", 823 | " \n", 824 | " \n", 825 | " \n", 826 | " \n", 827 | " \n", 828 | " \n", 829 | " \n", 830 | " \n", 831 | " \n", 832 | " \n", 833 | " \n", 834 | " \n", 835 | " \n", 836 | " \n", 837 | " \n", 838 | " \n", 839 | " \n", 840 | " \n", 841 | " \n", 842 | " \n", 843 | " \n", 844 | " \n", 845 | " \n", 846 | " \n", 847 | " \n", 848 | " \n", 849 | " \n", 850 | " \n", 851 | " \n", 852 | " \n", 853 | " \n", 854 | " \n", 855 | " \n", 856 | " \n", 857 | " \n", 858 | " \n", 859 | " \n", 860 | " \n", 861 | " \n", 862 | " \n", 863 | " \n", 864 | " \n", 865 | " \n", 866 | " \n", 867 | " \n", 868 | " \n", 869 | " \n", 870 | " \n", 871 | " \n", 872 | " \n", 873 | " \n", 874 | " \n", 875 | " \n", 876 | " \n", 877 | " \n", 878 | " \n", 879 | " \n", 880 | " \n", 881 | " \n", 882 | " \n", 883 | " \n", 884 | " \n", 885 | " \n", 886 | " \n", 887 | " \n", 888 | " \n", 889 | " \n", 890 | " \n", 891 | " \n", 892 | " \n", 893 | " \n", 894 | " \n", 895 | " \n", 896 | " \n", 897 | " \n", 898 | " \n", 899 | " \n", 900 | " \n", 901 | " \n", 902 | " \n", 903 | " \n", 904 | " \n", 905 | " \n", 906 | " \n", 907 | " \n", 908 | " \n", 909 | " \n", 910 | " \n", 911 | " \n", 912 | " \n", 913 | " \n", 914 | " \n", 915 | " \n", 916 | " \n", 917 | " \n", 918 | " \n", 919 | " \n", 920 | " \n", 921 | " \n", 922 | " \n", 923 | " \n", 924 | " \n", 925 | " \n", 926 | " \n", 927 | " \n", 928 | " \n", 929 | " \n", 930 | " \n", 931 | " \n", 932 | " \n", 933 | " \n", 934 | " \n", 935 | " \n", 936 | " \n", 937 | " \n", 938 | " \n", 939 | " \n", 940 | " \n", 941 | " \n", 942 | " \n", 943 | " \n", 944 | " \n", 945 | " \n", 946 | " \n", 947 | " \n", 948 | " \n", 949 | " \n", 950 | " \n", 951 | " \n", 952 | " \n", 953 | " \n", 954 | " \n", 955 | " \n", 956 | " \n", 957 | " \n", 958 | " \n", 959 | " \n", 960 | " \n", 961 | " \n", 962 | " \n", 963 | " \n", 964 | " \n", 965 | " \n", 966 | " \n", 967 | " \n", 968 | " \n", 969 | " \n", 970 | " \n", 971 | " \n", 972 | " \n", 973 | " \n", 974 | " \n", 975 | " \n", 976 | " \n", 977 | " \n", 978 | " \n", 979 | " \n", 980 | " \n", 981 | " \n", 982 | " \n", 983 | " \n", 984 | " \n", 985 | " \n", 986 | " \n", 987 | " \n", 988 | " \n", 989 | " \n", 990 | " \n", 991 | " \n", 992 | " \n", 993 | " \n", 994 | " \n", 995 | " \n", 996 | " \n", 997 | " \n", 998 | " \n", 999 | " \n", 1000 | " \n", 1001 | " \n", 1002 | " \n", 1003 | " \n", 1004 | " \n", 1005 | " \n", 1006 | " \n", 1007 | " \n", 1008 | " \n", 1009 | " \n", 1010 | " \n", 1011 | " \n", 1012 | " \n", 1013 | " \n", 1014 | " \n", 1015 | " \n", 1016 | " \n", 1017 | " \n", 1018 | " \n", 1019 | " \n", 1020 | " \n", 1021 | " \n", 1022 | " \n", 1023 | " \n", 1024 | " \n", 1025 | " \n", 1026 | " \n", 1027 | " \n", 1028 | " \n", 1029 | " \n", 1030 | " \n", 1031 | " \n", 1032 | " \n", 1033 | " \n", 1034 | " \n", 1035 | " \n", 1036 | " \n", 1037 | " \n", 1038 | " \n", 1039 | " \n", 1040 | " \n", 1041 | " \n", 1042 | " \n", 1043 | " \n", 1044 | " \n", 1045 | " \n", 1046 | " \n", 1047 | " \n", 1048 | " \n", 1049 | " \n", 1050 | " \n", 1051 | " \n", 1052 | " \n", 1053 | " \n", 1054 | " \n", 1055 | " \n", 1056 | " \n", 1057 | " \n", 1058 | " \n", 1059 | " \n", 1060 | " \n", 1061 | " \n", 1062 | " \n", 1063 | " \n", 1064 | " \n", 1065 | " \n", 1066 | " \n", 1067 | " \n", 1068 | " \n", 1069 | " \n", 1070 | " \n", 1071 | " \n", 1072 | " \n", 1073 | " \n", 1074 | " \n", 1075 | " \n", 1076 | " \n", 1077 | " \n", 1078 | " \n", 1079 | " \n", 1080 | " \n", 1081 | " \n", 1082 | " \n", 1083 | " \n", 1084 | " \n", 1085 | " \n", 1086 | " \n", 1087 | " \n", 1088 | " \n", 1089 | " \n", 1090 | " \n", 1091 | " \n", 1092 | " \n", 1093 | " \n", 1094 | " \n", 1095 | " \n", 1096 | " \n", 1097 | " \n", 1098 | " \n", 1099 | " \n", 1100 | " \n", 1101 | " \n", 1102 | " \n", 1103 | " \n", 1104 | " \n", 1105 | " \n", 1106 | " \n", 1107 | " \n", 1108 | " \n", 1109 | " \n", 1110 | " \n", 1111 | " \n", 1112 | " \n", 1113 | " \n", 1114 | " \n", 1115 | " \n", 1116 | " \n", 1117 | " \n", 1118 | " \n", 1119 | " \n", 1120 | " \n", 1121 | "
mean radiusmean texturemean perimetermean areamean smoothnessmean compactnessmean concavitymean concave pointsmean symmetrymean fractal dimensionradius errortexture errorperimeter errorarea errorsmoothness errorcompactness errorconcavity errorconcave points errorsymmetry errorfractal dimension errorworst radiusworst textureworst perimeterworst areaworst smoothnessworst compactnessworst concavityworst concave pointsworst symmetryworst fractal dimensiontarget
01.096100-2.0715121.2688170.9835101.5670873.2806282.6505422.5302492.2155662.2537642.487545-0.5647682.8305402.485391-0.2138141.3157040.7233900.6602391.1477470.9062861.885031-1.3580982.3015751.9994781.3065372.6143652.1076722.2940582.7482041.9353120
11.828212-0.3533221.6844731.907030-0.826235-0.486643-0.0238250.5476620.001391-0.8678890.498816-0.8754730.2630950.741749-0.604819-0.692317-0.4403930.259933-0.804742-0.0993561.804340-0.3688791.5337761.888827-0.375282-0.430066-0.1466201.086129-0.2436750.2809430
21.5784990.4557861.5651261.5575130.9413821.0520001.3622802.0354400.938859-0.3976581.227596-0.7793980.8501801.180298-0.2967440.8142570.2128891.4235750.2368270.2933011.510541-0.0239531.3462911.4550040.5269441.0819800.8542221.9532821.1512420.2012140
3-0.7682330.253509-0.592166-0.7637923.2806673.3999171.9142131.4504312.8648624.9066020.326087-0.1103120.286341-0.2881250.6890952.7418680.8187981.1140274.7285202.045711-0.2812170.133866-0.249720-0.5495383.3912913.8899751.9878392.1738736.0407264.9306720
41.748758-1.1508041.7750111.8246240.2801250.5388661.3698061.427237-0.009552-0.5619561.269426-0.7895491.2720701.1893101.481763-0.0484770.8277421.143199-0.3607750.4988891.297434-1.4654811.3373631.2196510.220362-0.3131190.6126400.728618-0.867590-0.3967510
................................................................................................
5642.1091390.7208382.0589742.3417951.0409260.2188681.9455732.318924-0.312314-0.9302092.7796340.0709632.3774912.6018971.0854290.1916370.6654162.065360-1.1374150.1678321.8995140.1175961.7510222.0135290.378033-0.2730770.6639281.627719-1.358963-0.7084670
5651.7033562.0833011.6145111.7223260.102368-0.0178170.6924341.262558-0.217473-1.0576811.2993562.2589511.1558401.290429-0.423637-0.0696970.2519800.807720-0.188995-0.4901241.5353692.0455991.4206901.493644-0.690623-0.3944730.2363650.733182-0.531387-0.9731220
5660.7016672.0437750.6720840.577445-0.839745-0.0386460.0465470.105684-0.808406-0.8948000.184730-0.2571450.2764500.180539-0.3790080.6606960.5103770.611619-0.8906320.0366940.5608681.3736450.5784920.427529-0.8088760.3504270.3264790.413705-1.103578-0.3181290
5671.8367252.3344031.9807811.7336931.5244263.2692673.2940462.6565282.1353151.0427781.1569170.6854851.4372651.008615-0.1728482.0159431.3011400.7850310.3263460.9032621.9595152.2359582.3015751.6517171.4291693.9014153.1947942.2879721.9173962.2176840
568-1.8068111.220718-1.812793-1.346604-3.109349-1.149741-1.113893-1.260710-0.819349-0.560539-0.0702170.382756-0.157311-0.4657420.049299-1.162493-1.056571-1.9117650.752168-0.382418-1.4096520.763518-1.431475-1.074867-1.857384-1.206491-1.304683-1.743529-0.048096-0.7505461
\n", 1122 | "

569 rows × 31 columns

\n", 1123 | "
" 1124 | ], 1125 | "text/plain": [ 1126 | " mean radius mean texture ... worst fractal dimension target\n", 1127 | "0 1.096100 -2.071512 ... 1.935312 0\n", 1128 | "1 1.828212 -0.353322 ... 0.280943 0\n", 1129 | "2 1.578499 0.455786 ... 0.201214 0\n", 1130 | "3 -0.768233 0.253509 ... 4.930672 0\n", 1131 | "4 1.748758 -1.150804 ... -0.396751 0\n", 1132 | ".. ... ... ... ... ...\n", 1133 | "564 2.109139 0.720838 ... -0.708467 0\n", 1134 | "565 1.703356 2.083301 ... -0.973122 0\n", 1135 | "566 0.701667 2.043775 ... -0.318129 0\n", 1136 | "567 1.836725 2.334403 ... 2.217684 0\n", 1137 | "568 -1.806811 1.220718 ... -0.750546 1\n", 1138 | "\n", 1139 | "[569 rows x 31 columns]" 1140 | ] 1141 | }, 1142 | "metadata": { 1143 | "tags": [] 1144 | }, 1145 | "execution_count": 18 1146 | } 1147 | ] 1148 | }, 1149 | { 1150 | "cell_type": "code", 1151 | "metadata": { 1152 | "id": "a2FCKsXmG1ZK", 1153 | "colab_type": "code", 1154 | "outputId": "b856c260-e09b-425b-a263-ab36ab874525", 1155 | "colab": { 1156 | "base_uri": "https://localhost:8080/", 1157 | "height": 34 1158 | } 1159 | }, 1160 | "source": [ 1161 | "X,y = data.values[:,:-1],data.values[:,-1:]\n", 1162 | "print(X.shape,y.shape)" 1163 | ], 1164 | "execution_count": 19, 1165 | "outputs": [ 1166 | { 1167 | "output_type": "stream", 1168 | "text": [ 1169 | "(569, 30) (569, 1)\n" 1170 | ], 1171 | "name": "stdout" 1172 | } 1173 | ] 1174 | }, 1175 | { 1176 | "cell_type": "markdown", 1177 | "metadata": { 1178 | "id": "Tj7_IFSgG_LP", 1179 | "colab_type": "text" 1180 | }, 1181 | "source": [ 1182 | "# Set Custom Dataset" 1183 | ] 1184 | }, 1185 | { 1186 | "cell_type": "code", 1187 | "metadata": { 1188 | "id": "J-PMlzjSG6oj", 1189 | "colab_type": "code", 1190 | "colab": {} 1191 | }, 1192 | "source": [ 1193 | "import torch\n", 1194 | "import torch.nn as nn\n", 1195 | "import torch.nn.functional as F\n", 1196 | "import torch.optim as optim\n", 1197 | "\n", 1198 | "from torch.utils.data import Dataset\n", 1199 | "from torch.utils.data import DataLoader" 1200 | ], 1201 | "execution_count": 0, 1202 | "outputs": [] 1203 | }, 1204 | { 1205 | "cell_type": "code", 1206 | "metadata": { 1207 | "id": "s-VlszMLHB9p", 1208 | "colab_type": "code", 1209 | "colab": {} 1210 | }, 1211 | "source": [ 1212 | "class MyDataset(Dataset):\n", 1213 | " def __init__(self):\n", 1214 | " self.x_data = torch.tensor(X,dtype=torch.float)\n", 1215 | " self.y_data = torch.tensor(y,dtype=torch.float)\n", 1216 | " def __len__(self):\n", 1217 | " return len(self.x_data)\n", 1218 | "\n", 1219 | " def __getitem__(self,idx):\n", 1220 | " x = self.x_data[idx]\n", 1221 | " y = self.y_data[idx]\n", 1222 | " return x,y" 1223 | ], 1224 | "execution_count": 0, 1225 | "outputs": [] 1226 | }, 1227 | { 1228 | "cell_type": "code", 1229 | "metadata": { 1230 | "id": "Ja1xKvrRHDu5", 1231 | "colab_type": "code", 1232 | "colab": {} 1233 | }, 1234 | "source": [ 1235 | "dataset = MyDataset()\n", 1236 | "dataloader = DataLoader(dataset, batch_size=len(dataset), shuffle=True)" 1237 | ], 1238 | "execution_count": 0, 1239 | "outputs": [] 1240 | }, 1241 | { 1242 | "cell_type": "code", 1243 | "metadata": { 1244 | "id": "FZoIBVcRHIyB", 1245 | "colab_type": "code", 1246 | "colab": {} 1247 | }, 1248 | "source": [ 1249 | "class BinaryClassifier(nn.Module):\n", 1250 | " def __init__(self):\n", 1251 | " super().__init__()\n", 1252 | " self.linear = nn.Linear(30, 1)\n", 1253 | " self.sigmoid = nn.Sigmoid()\n", 1254 | "\n", 1255 | " def forward(self, x):\n", 1256 | " return self.sigmoid(self.linear(x))" 1257 | ], 1258 | "execution_count": 0, 1259 | "outputs": [] 1260 | }, 1261 | { 1262 | "cell_type": "code", 1263 | "metadata": { 1264 | "id": "V8hHrM3BHfrZ", 1265 | "colab_type": "code", 1266 | "colab": {} 1267 | }, 1268 | "source": [ 1269 | "model = BinaryClassifier()\n", 1270 | "optimizer = optim.SGD(model.parameters(), lr=0.05)" 1271 | ], 1272 | "execution_count": 0, 1273 | "outputs": [] 1274 | }, 1275 | { 1276 | "cell_type": "code", 1277 | "metadata": { 1278 | "id": "lTLlS_NUHMGh", 1279 | "colab_type": "code", 1280 | "outputId": "67fd773e-1120-48a6-f9db-e7ce55ca429e", 1281 | "colab": { 1282 | "base_uri": "https://localhost:8080/", 1283 | "height": 210 1284 | } 1285 | }, 1286 | "source": [ 1287 | "nb_epochs = 1000\n", 1288 | "for epoch in range(nb_epochs + 1):\n", 1289 | " for x_train,y_train in dataloader:\n", 1290 | "\n", 1291 | " # H(x) 계산\n", 1292 | " hypothesis = model(x_train)\n", 1293 | "\n", 1294 | " # cost 계산\n", 1295 | " cost = F.binary_cross_entropy(hypothesis, y_train)\n", 1296 | "\n", 1297 | " # cost로 H(x) 개선\n", 1298 | " optimizer.zero_grad()\n", 1299 | " cost.backward()\n", 1300 | " optimizer.step()\n", 1301 | "\n", 1302 | " # 100번마다 로그 출력\n", 1303 | " if epoch % 100 == 0:\n", 1304 | " prediction = hypothesis >= torch.FloatTensor([0.5]) # 예측값이 0.5를 넘으면 True로 간주\n", 1305 | " correct_prediction = prediction.float() == y_train # 실제값과 일치하는 경우만 True로 간주\n", 1306 | " accuracy = correct_prediction.sum().item() / len(correct_prediction) # 정확도를 계산\n", 1307 | " print('Epoch {:4d}/{} Cost: {:.6f} Accuracy {:2.2f}%'.format( # 각 에포크마다 정확도를 출력\n", 1308 | " epoch, nb_epochs, cost.item(), accuracy * 100,\n", 1309 | " ))" 1310 | ], 1311 | "execution_count": 25, 1312 | "outputs": [ 1313 | { 1314 | "output_type": "stream", 1315 | "text": [ 1316 | "Epoch 0/1000 Cost: 0.559834 Accuracy 80.32%\n", 1317 | "Epoch 100/1000 Cost: 0.132256 Accuracy 97.01%\n", 1318 | "Epoch 200/1000 Cost: 0.104758 Accuracy 96.84%\n", 1319 | "Epoch 300/1000 Cost: 0.092922 Accuracy 97.54%\n", 1320 | "Epoch 400/1000 Cost: 0.085993 Accuracy 98.07%\n", 1321 | "Epoch 500/1000 Cost: 0.081315 Accuracy 98.07%\n", 1322 | "Epoch 600/1000 Cost: 0.077880 Accuracy 98.24%\n", 1323 | "Epoch 700/1000 Cost: 0.075215 Accuracy 98.42%\n", 1324 | "Epoch 800/1000 Cost: 0.073064 Accuracy 98.42%\n", 1325 | "Epoch 900/1000 Cost: 0.071277 Accuracy 98.42%\n", 1326 | "Epoch 1000/1000 Cost: 0.069759 Accuracy 98.42%\n" 1327 | ], 1328 | "name": "stdout" 1329 | } 1330 | ] 1331 | }, 1332 | { 1333 | "cell_type": "code", 1334 | "metadata": { 1335 | "id": "zQsN9V6VXNKt", 1336 | "colab_type": "code", 1337 | "colab": {} 1338 | }, 1339 | "source": [ 1340 | "" 1341 | ], 1342 | "execution_count": 0, 1343 | "outputs": [] 1344 | }, 1345 | { 1346 | "cell_type": "code", 1347 | "metadata": { 1348 | "id": "9Dw6hX9fHrnf", 1349 | "colab_type": "code", 1350 | "colab": {} 1351 | }, 1352 | "source": [ 1353 | "" 1354 | ], 1355 | "execution_count": 0, 1356 | "outputs": [] 1357 | } 1358 | ] 1359 | } -------------------------------------------------------------------------------- /4. 소프트맥스 회귀(Softmax_Regression).ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "name": "Untitled3.ipynb", 7 | "provenance": [], 8 | "toc_visible": true, 9 | "authorship_tag": "ABX9TyMf4cS4FZmSLWZlACBQ29mq", 10 | "include_colab_link": true 11 | }, 12 | "kernelspec": { 13 | "name": "python3", 14 | "display_name": "Python 3" 15 | } 16 | }, 17 | "cells": [ 18 | { 19 | "cell_type": "markdown", 20 | "metadata": { 21 | "id": "view-in-github", 22 | "colab_type": "text" 23 | }, 24 | "source": [ 25 | "\"Open" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": { 31 | "id": "HJg28KE3ttD-", 32 | "colab_type": "text" 33 | }, 34 | "source": [ 35 | "# 4. 소프트맥스 회귀(Softmax Regression)\n", 36 | "\n", 37 | "이번 챕터에서는 3개 이상의 선택지로부터 1개를 선택하는 문제인 다중 클래스 분류(Multi-Class classification)를 풀기 위한 소프트맥스 회귀에 대해서 학습합니다." 38 | ] 39 | }, 40 | { 41 | "cell_type": "markdown", 42 | "metadata": { 43 | "id": "VJg1yy2Kt2qb", 44 | "colab_type": "text" 45 | }, 46 | "source": [ 47 | "## 4.1 원-핫 인코딩(One-Hot Encoding)\n", 48 | "\n", 49 | "이번 챕터에서는 범주형 데이터를 처리할 때 레이블을 표현하는 방법인 원-핫 인코딩에 대해서 배워봅시다." 50 | ] 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "metadata": { 55 | "id": "Banjkj6tt_rk", 56 | "colab_type": "text" 57 | }, 58 | "source": [ 59 | "### 4.1.1 원-핫 인코딩(One-hot encoding)이란?\n", 60 | "\n", 61 | "원-핫 인코딩은 선택해야 하는 선택지의 개수만큼의 차원을 가지면서, 각 선택지의 인덱스에 해당하는 원소에는 1, 나머지 원소는 0의 값을 가지도록 하는 표현 방법입니다. 예를 들어 강아지, 고양이, 냉장고라는 3개의 선택지가 있다고 해보겠습니다.\n", 62 | "\n", 63 | "원-핫 인코딩을 하기 위해서는 우선 각 선택지에 순차적으로 정수 인덱스를 부여합니다. 임의로 강아지는 0번 인덱스, 고양이는 1번 인덱스, 냉장고는 2번 인덱스를 부여하였다고 해봅시다. 이때 각 선택지에 대해서 원-핫 인코딩이 된 벡터는 다음과 같습니다.\n", 64 | "\n", 65 | "강아지 = [1, 0, 0]\n", 66 | "\n", 67 | "고양이 = [0, 1, 0]\n", 68 | "\n", 69 | "냉장고 = [0, 0, 1]\n", 70 | "\n", 71 | "총 선택지는 3개였으므로 위 벡터들은 전부 3차원의 벡터가 되었습니다. 그리고 각 선택지의 벡터들을 보면 해당 선택지의 인덱스에만 1의 값을 가지고, 나머지 원소들은 0의 값을 가집니다. 예를 들어 고양이는 1번 인덱스였으므로 원-핫 인코딩으로 얻은 벡터에서 1번 인덱스만 1의 값을 가지는 것을 볼 수 있습니다.\n", 72 | "\n", 73 | "이와 같이 원-핫 인코딩으로 표현된 벡터를 원-핫 벡터(one-hot vector)라고 합니다." 74 | ] 75 | }, 76 | { 77 | "cell_type": "markdown", 78 | "metadata": { 79 | "id": "-J2KDn0yuG0v", 80 | "colab_type": "text" 81 | }, 82 | "source": [ 83 | "### 4.1.2 원-핫 벡터의 무작위성\n", 84 | "\n", 85 | "꼭 실제값을 원-핫 벡터로 표현해야만 다중 클래스 분류 문제를 풀 수 있는 것은 아니지만, 대부분의 다중 클래스 분류 문제가 각 클래스 간의 관계가 균등하다는 점에서 원-핫 벡터는 이러한 점을 표현할 수 있는 적절한 표현 방법입니다.\n", 86 | "\n", 87 | "다수의 클래스를 분류하는 문제에서는 이진 분류처럼 2개의 숫자 레이블이 아니라 클래스의 개수만큼 숫자 레이블이 필요합니다. 이때 직관적으로 생각해볼 수 있는 레이블링 방법은 분류해야 할 클래스 전체에 정수 인코딩을 하는 겁니다. 예를 들어서 분류해야 할 레이블이 {red, green, blue}와 같이 3개라면 각각 0, 1, 2로 레이블을 합니다. 또는 분류해야 할 클래스가 4개고 인덱스를 숫자 1부터 시작하고 싶다고 하면 {baby, child, adolescent, adult}라면 1, 2, 3, 4로 레이블을 해볼 수 있습니다. 그런데 일반적인 다중 클래스 분류 문제에서 레이블링 방법으로는 위와 같은 정수 인코딩이 아니라 원-핫 인코딩을 사용하는 것이 보다 클래스의 성질을 잘 표현하였다고 할 수 있습니다. 그 이유를 알아봅시다.\n", 88 | "\n", 89 | "Banana, Tomato, Apple라는 3개의 클래스가 존재하는 문제가 있다고 해봅시다. 레이블은 정수 인코딩을 사용하여 각각 1, 2, 3을 부여하였습니다. 손실 함수로 선형 회귀 챕터에서 배운 평균 제곱 오차 MSE를 사용하면 정수 인코딩이 어떤 오해를 불러일으킬 수 있는지 확인할 수 있습니다. 아래의 식은 앞서 선형 회귀에서 배웠던 MSE를 다시 그대로 가져온 것입니다. $\\hat{y}$는 예측값을 의미합니다.\n", 90 | "\n", 91 | "$Loss\\ function = \\frac{1}{n} \\sum_i^{n} \\left(y_{i} - \\hat{y_{i}}\\right)^2$\n", 92 | "\n", 93 | "직관적인 오차 크기 비교를 위해 평균을 구하는 수식은 제외하고 제곱 오차로만 판단해봅시다.\n", 94 | "\n", 95 | "실제값이 Tomato일때 예측값이 Banana이었다면 제곱 오차는 다음과 같습니다.\n", 96 | "\n", 97 | "$(2-1)^{2} = 1$\n", 98 | "\n", 99 | "실제값이 Apple일때 예측값이 Banana이었다면 제곱 오차는 다음과 같습니다.\n", 100 | "\n", 101 | "$(3-1)^{2} = 4$\n", 102 | "\n", 103 | "즉, Banana과 Tomato 사이의 오차보다 Banana과 Apple의 오차가 더 큽니다. 이는 기계에게 Banana가 Apple보다는 Tomato에 더 가깝다는 정보를 주는 것과 다름없습니다. 더 많은 클래스에 대해서 정수 인코딩을 수행했다고 해봅시다.\n", 104 | "\n", 105 | "{Banana :1, Tomato :2, Apple :3, Strawberry :4, ... Watermelon :10}\n", 106 | "\n", 107 | "이 정수 인코딩은 Banana가 Watermelon보다는 Tomato에 더 가깝다는 의미를 담고 있습니다. 이는 사용자가 부여하고자 했던 정보가 아닙니다. 이러한 정수 인코딩의 순서 정보가 도움이 되는 분류 문제도 물론 있습니다. 바로 각 클래스가 순서의 의미를 갖고 있어서 회귀를 통해서 분류 문제를 풀 수 있는 경우입니다. 예를 들어 {baby, child, adolescent, adult}나 {1층, 2층, 3층, 4층}이나 {10대, 20대, 30대, 40대}와 같은 경우가 이에 해당됩니다. 하지만 일반적인 분류 문제에서는 각 클래스는 순서의 의미를 갖고 있지 않으므로 각 클래스 간의 오차는 균등한 것이 옳습니다. 정수 인코딩과 달리 원-핫 인코딩은 분류 문제 모든 클래스 간의 관계를 균등하게 분배합니다.\n", 108 | "\n", 109 | "아래는 세 개의 카테고리에 대해서 원-핫 인코딩을 통해서 레이블을 인코딩했을 때 각 클래스 간의 제곱 오차가 균등함을 보여줍니다.\n", 110 | "\n", 111 | "$((1,0,0)-(0,1,0))^{2} = (1-0)^{2} + (0-1)^{2} + (0-0)^{2} = 2$\n", 112 | "\n", 113 | "$((1,0,0)-(0,0,1))^{2} = (1-0)^{2} + (0-0)^{2} + (0-1)^{2} = 2$\n", 114 | "\n", 115 | "다르게 표현하면 모든 클래스에 대해서 원-핫 인코딩을 통해 얻은 원-핫 벡터들은 모든 쌍에 대해서 유클리드 거리를 구해도 전부 유클리드 거리가 동일합니다. 원-핫 벡터는 이처럼 각 클래스의 표현 방법이 무작위성을 가진다는 점을 표현할 수 있습니다. 뒤에서 다시 언급되겠지만 이러한 원-핫 벡터의 관계의 무작위성은 때로는 단어의 유사성을 구할 수 없다는 단점으로 언급되기도 합니다." 116 | ] 117 | }, 118 | { 119 | "cell_type": "markdown", 120 | "metadata": { 121 | "id": "Tgfqm5FXy9HB", 122 | "colab_type": "text" 123 | }, 124 | "source": [ 125 | "##4.2 소프트맥스 회귀(Softmax Regression) 이해하기\n", 126 | "\n", 127 | "앞서 로지스틱 회귀를 통해 2개의 선택지 중에서 1개를 고르는 이진 분류(Binary Classification)를 풀어봤습니다. 이번 챕터에서는 소프트맥스 회귀를 통해 3개 이상의 선택지 중에서 1개를 고르는 다중 클래스 분류(Multi-Class Classification)를 실습해봅시다." 128 | ] 129 | }, 130 | { 131 | "cell_type": "markdown", 132 | "metadata": { 133 | "id": "UPlCvUl4ziZL", 134 | "colab_type": "text" 135 | }, 136 | "source": [ 137 | "### 4.2.1 다중 클래스 분류(Multi-class Classification)\n", 138 | "\n", 139 | "이진 분류가 두 개의 답 중 하나를 고르는 문제였다면, 세 개 이상의 답 중 하나를 고르는 문제를 다중 클래스 분류(Multi-class Classification)라고 합니다. 아래의 문제는 꽃받침 길이, 꽃받침 넓이, 꽃잎 길이, 꽃잎 넓이라는 4개의 특성(feature)로부터 setosa, versicolor, virginica라는 3개의 붓꽃 품종 중 어떤 품종인지를 예측하는 문제로 전형적인 다중 클래스 분류 문제입니다.\n", 140 | "\n", 141 | "|SepalLengthCm$(x_1)$|SepalWidthCm$(x_2)$|PetalLengthCm$(x_3)$|PetalWidthCm$(x_4)$|Species$(y)$|\n", 142 | "|---|---|---|---|---|\n", 143 | "|5.1|3.5|1.4|0.2|setosa|\n", 144 | "|4.9|3.0|1.4|0.2|setosa|\n", 145 | "|5.8|2.6|4.0|1.2|versicolor|\n", 146 | "|6.7|3.0|5.2|2.3|virginica|\n", 147 | "|5.6|2.8|4.9|2.0|virginica|\n", 148 | "\n", 149 | "위 붓꽃 품종 분류하기 문제를 어떻게 풀지 고민하기 위해 앞서 배운 로지스틱 회귀의 이진 분류를 복습해보겠습니다.\n", 150 | "\n", 151 | "이번 챕터의 설명에서 입력은 $X$, 가중치는 $W$, 편향은 $B$, 출력은 $\\hat{Y}$로 각 변수는 벡터 또는 행렬로 가정합니다.\n", 152 | "\n", 153 | "* $\\hat{Y}$은 예측값이라는 의미를 가지고 있으므로 가설식에서 $H(X)$ 대신 사용되기도 합니다.\n", 154 | "\n" 155 | ] 156 | }, 157 | { 158 | "cell_type": "markdown", 159 | "metadata": { 160 | "id": "Seh8i1R117CO", 161 | "colab_type": "text" 162 | }, 163 | "source": [ 164 | "1) 로지스틱 회귀\n", 165 | "\n", 166 | "로지스틱 회귀에서 시그모이드 함수는 예측값을 0과 1 사이의 값으로 만듭니다. 예를 들어 스팸 메일 분류기를 로지스틱 회귀로 구현하였을 때, 출력이 0.75이라면 이는 이메일이 스팸일 확률이 75%라는 의미가 됩니다. 반대로, 스팸 메일이 아닐 확률은 25%가 됩니다. 이 두 확률의 총 합은 1입니다.\n", 167 | "\n", 168 | "\n", 169 | "![대체 텍스트](https://wikidocs.net/images/page/59427/%EB%A1%9C%EC%A7%80%EC%8A%A4%ED%8B%B1%ED%9A%8C%EA%B7%80.PNG)\n", 170 | "\n", 171 | "가설 : $H(X) = sigmoid(WX + B)$" 172 | ] 173 | }, 174 | { 175 | "cell_type": "markdown", 176 | "metadata": { 177 | "id": "xT-i0zvX2QhW", 178 | "colab_type": "text" 179 | }, 180 | "source": [ 181 | "2) 소프트맥스 회귀\n", 182 | "\n", 183 | "소프트맥스 회귀는 확률의 총 합이 1이 되는 이 아이디어를 다중 클래스 분류 문제에 적용합니다. 소프트맥스 회귀는 각 클래스. 즉, 각 선택지마다 소수 확률을 할당합니다. 이때 총 확률의 합은 1이 되어야 합니다. 이렇게 되면 각 선택지가 정답일 확률로 표현됩니다.\n", 184 | "\n", 185 | "![대체 텍스트](https://wikidocs.net/images/page/59427/%EC%86%8C%ED%94%84%ED%8A%B8%EB%A7%A5%EC%8A%A4%ED%9A%8C%EA%B7%80.PNG)\n", 186 | "\n", 187 | "결국 소프트맥스 회귀는 선택지의 개수만큼의 차원을 가지는 벡터를 만들고, 해당 벡터가 벡터의 모든 원소의 합이 1이 되도록 원소들의 값을 변환시키는 어떤 함수를 지나게 만들어야 합니다. 위의 그림은 붓꽃 품종 분류하기 문제 등과 같이 선택지의 개수가 3개일때, 3차원 벡터가 어떤 함수 ?를 지나 원소의 총 합이 1이 되도록 원소들의 값이 변환되는 모습을 보여줍니다. 뒤에서 배우겠지만, 이 함수를 소프트맥스(softmax) 함수라고 합니다.\n", 188 | "\n", 189 | "가설 : $H(X) = softmax(WX + B)$" 190 | ] 191 | }, 192 | { 193 | "cell_type": "markdown", 194 | "metadata": { 195 | "id": "H7Xu4LtJ2g0q", 196 | "colab_type": "text" 197 | }, 198 | "source": [ 199 | "### 4.2.2 소프트맥스 함수(Softmax function)\n", 200 | "\n", 201 | "소프트맥스 함수는 분류해야하는 정답지(클래스)의 총 개수를 k라고 할 때, k차원의 벡터를 입력받아 각 클래스에 대한 확률을 추정합니다. 우선 수식에 대해 설명하고, 그 후에는 그림으로 이해해보겠습니다." 202 | ] 203 | }, 204 | { 205 | "cell_type": "markdown", 206 | "metadata": { 207 | "id": "2mqsbyD12mNV", 208 | "colab_type": "text" 209 | }, 210 | "source": [ 211 | "1) 소프트맥스 함수의 이해\n", 212 | "\n", 213 | "k차원의 벡터에서 i번째 원소를 $z_i$, i번째 클래스가 정답일 확률을 $p_i$로 나타낸다고 하였을 때 소프트맥스 함수는 $p_i$를 다음과 같이 정의합니다.\n", 214 | "\n", 215 | "$p_{i}=\\frac{e^{z_{i}}}{\\sum_{j=1}^{k} e^{z_{j}}}\\ \\ for\\ i=1, 2, ... k$\n", 216 | "\n", 217 | "위에서 풀어야하는 문제에 소프트맥스 함수를 차근차근 적용해봅시다. 위에서 풀어야하는 문제의 경우 k=3이므로 3차원 벡터 $z=[z_{1}\\ z_{2}\\ z_{3}]$의 입력을 받으면 소프트맥스 함수는 아래와 같은 출력을 리턴합니다.\n", 218 | "\n", 219 | "$softmax(z)=[\\frac{e^{z_{1}}}{\\sum_{j=1}^{3} e^{z_{j}}}\\ \\frac{e^{z_{2}}}{\\sum_{j=1}^{3} e^{z_{j}}}\\ \\frac{e^{z_{3}}}{\\sum_{j=1}^{3} e^{z_{j}}}] = [p_{1}, p_{2}, p_{3}] = \\hat{y} = \\text{예측값}$\n", 220 | "\n", 221 | "$p_1,p_2,p_3$ 각각은 1번 클래스가 정답일 확률, 2번 클래스가 정답일 확률, 3번 클래스가 정답일 확률을 나타내며 각각 0과 1사이의 값으로 총 합은 1이 됩니다. 여기서 분류하고자하는 3개의 클래스는 virginica, setosa, versicolor이므로 이는 결국 주어진 입력이 virginica일 확률, setosa일 확률, versicolor일 확률을 나타내는 값을 의미합니다. 여기서는 i가 1일 때는 virginica일 확률을 나타내고, 2일 때는 setosa일 확률, 3일때는 versicolor일 확률이라고 지정하였다고 합시다. 이 지정 순서는 문제를 풀고자 하는 사람의 무작위 선택입니다. 이에따라 식을 문제에 맞게 다시 쓰면 아래와 같습니다.\n", 222 | "\n", 223 | "$softmax(z)=[\\frac{e^{z_{1}}}{\\sum_{j=1}^{3} e^{z_{j}}}\\ \\frac{e^{z_{2}}}{\\sum_{j=1}^{3} e^{z_{j}}}\\ \\frac{e^{z_{3}}}{\\sum_{j=1}^{3} e^{z_{j}}}] = [p_{1}, p_{2}, p_{3}] = [p_{virginica}, p_{setosa}, p_{versicolor}]$\n", 224 | "\n", 225 | "다소 복잡해보이지만 어려운 개념이 아닙니다. 분류하고자 하는 클래스가 k개일 때, k차원의 벡터를 입력받아서 모든 벡터 원소의 값을 0과 1사이의 값으로 값을 변경하여 다시 k차원의 벡터를 리턴한다는 내용을 식으로 기재하였을 뿐입니다. 방금 배운 개념을 그림을 통해 다시 설명하면서 더 깊이 들어가보겠습니다." 226 | ] 227 | }, 228 | { 229 | "cell_type": "markdown", 230 | "metadata": { 231 | "id": "477mdIz-3JeW", 232 | "colab_type": "text" 233 | }, 234 | "source": [ 235 | "2) 그림을 통한 이해\n", 236 | "\n", 237 | "![대체 텍스트](https://wikidocs.net/images/page/35476/softmax1_final_final.PNG)\n", 238 | "\n", 239 | "위의 그림에 점차 살을 붙여봅시다. 여기서는 샘플 데이터를 1개씩 입력으로 받아 처리한다고 가정해봅시다. 즉, 배치 크기가 1입니다.\n", 240 | "\n", 241 | "위의 그림에는 두 가지 질문이 있습니다. 첫번째 질문은 소프트맥스 함수의 입력에 대한 질문입니다. 하나의 샘플 데이터는 4개의 독립 변수 $x$를 가지는데 이는 모델이 4차원 벡터를 입력으로 받음을 의미합니다. 그런데 소프트맥스의 함수의 입력으로 사용되는 벡터는 벡터의 차원이 분류하고자 하는 클래스의 개수가 되어야 하므로 어떤 가중치 연산을 통해 3차원 벡터로 변환되어야 합니다. 위의 그림에서는 소프트맥스 함수의 입력으로 사용되는 3차원 벡터를 $z$로 표현하였습니다.\n", 242 | "\n", 243 | "![대체 텍스트](https://wikidocs.net/images/page/35476/softmaxbetween1and2.PNG)\n", 244 | "\n", 245 | "샘플 데이터 벡터를 소프트맥스 함수의 입력 벡터로 차원을 축소하는 방법은 간단합니다. 소프트맥스 함수의 입력 벡터 $z$의 차원수만큼 결과값의 나오도록 가중치 곱을 진행합니다. 위의 그림에서 화살표는 총 (4 × 3 = 12) 12개이며 전부 다른 가중치를 가지고, 학습 과정에서 점차적으로 오차를 최소화하는 가중치로 값이 변경됩니다.\n", 246 | "\n", 247 | "두번째 질문은 오차 계산 방법에 대한 질문입니다. 소프트맥스 함수의 출력은 분류하고자하는 클래스의 개수만큼 차원을 가지는 벡터로 각 원소는 0과 1사이의 값을 가집니다. 이 각각은 특정 클래스가 정답일 확률을 나타냅니다. 여기서는 첫번째 원소인 $p_1$은 virginica가 정답일 확률, 두번째 원소인 $p_2$는 setosa가 정답일 확률, 세번째 원소인 $p_3$은 versicolor가 정답일 확률로 고려하고자 합니다. 그렇다면 이 예측값과 비교를 할 수 있는 실제값의 표현 방법이 있어야 합니다. 소프트맥스 회귀에서는 실제값을 원-핫 벡터로 표현합니다.\n", 248 | "\n", 249 | "![대체 텍스트](https://wikidocs.net/images/page/35476/softmax2_final.PNG)\n", 250 | "\n", 251 | "위의 그림은 소프트맥스 함수의 출력 벡터의 첫번째 원소 $p_1$가 virginica가 정답일 확률, 두번째 원소 $p_2$가 setosa가 정답일 확률, 세번째 원소 $p_3$가 versicolor가 정답일 확률을 의미한다고 하였을 때, 각 실제값의 정수 인코딩은 1, 2, 3이 되고 이에 원-핫 인코딩을 수행하여 실제값을 원-핫 벡터로 수치화한 것을 보여줍니다.\n", 252 | "\n", 253 | "![대체 텍스트](https://wikidocs.net/images/page/35476/softmax4.PNG)\n", 254 | "\n", 255 | "예를 들어 현재 풀고 있는 샘플 데이터의 실제값이 setosa라면 setosa의 원-핫 벡터는 [0 1 0]입니다. 이 경우, 예측값과 실제값의 오차가 0이 되는 경우는 소프트맥스 함수의 결과가 [0 1 0]이 되는 경우입니다. 이 두 벡터의 오차를 계산하기 위해서 소프트맥스 회귀는 비용 함수로 크로스 엔트로피 함수를 사용하는데, 이는 뒤에서 비용 함수를 설명하는 부분에서 다시 언급하겠습니다.\n", 256 | "\n", 257 | "![대체 텍스트](https://wikidocs.net/images/page/35476/softmax5.PNG)\n", 258 | "\n", 259 | "이제 앞서 배운 선형 회귀나 로지스틱 회귀와 마찬가지로 오차로부터 가중치를 업데이트 합니다.\n", 260 | "\n", 261 | "![대체 텍스트](https://wikidocs.net/images/page/35476/softmax6_final.PNG)\n", 262 | "\n", 263 | "더 정확히는 선형 회귀나 로지스틱 회귀와 마찬가지로 편향 또한 업데이트의 대상이 되는 매개 변수입니다. 소프트맥스 회귀를 벡터와 행렬 연산으로 이해해봅시다. 입력을 특성(feature)의 수만큼의 차원을 가진 입력 벡터 $x$라고 하고, 가중치 행렬을 $W$, 편향을 $b$라고 하였을 때, 소프트맥스 회귀에서 예측값을 구하는 과정을 벡터와 행렬 연산으로 표현하면 아래와 같습니다.\n", 264 | "\n", 265 | "![대체 텍스트](https://wikidocs.net/images/page/59427/%EA%B0%80%EC%84%A4.PNG)\n", 266 | "\n", 267 | "여기서 $f$는 특성의 수이며 $c$는 클래스의 개수에 해당됩니다." 268 | ] 269 | }, 270 | { 271 | "cell_type": "markdown", 272 | "metadata": { 273 | "id": "_xJMTAHS4P9r", 274 | "colab_type": "text" 275 | }, 276 | "source": [ 277 | "### 4.2.3 붓꽃 품종 분류하기 행렬 연산으로 이해하기\n", 278 | "\n", 279 | "위의 붓꽃 품종 분류 문제의 가설식을 행렬 연산으로 표현해보겠습니다. 우선 위의 예제의 데이터는 전체 샘플의 개수가 5개, 특성이 4개이므로 5 × 4 행렬 $X$로 정의합니다.\n", 280 | "\n", 281 | "$X=\n", 282 | "\\left(\n", 283 | " \\begin{array}{c}\n", 284 | " 5.1\\ 3.5\\ 1.4\\ 0.2\\ \\\\\n", 285 | " 4.9\\ 3.0\\ 1.4\\ 0.2\\ \\\\\n", 286 | " 5.8\\ 2.6\\ 4.0\\ 1.2\\ \\\\\n", 287 | " 6.7\\ 3.0\\ 5.2\\ 2.3\\ \\\\\n", 288 | " 5.6\\ 2.8\\ 4.9\\ 2.0\\ \\\\\n", 289 | " \\end{array}\n", 290 | " \\right)$\n", 291 | "\n", 292 | "편의를 위해 각 행렬의 원소 위치를 반영한 변수로 표현하겠습니다.\n", 293 | "\n", 294 | "$X=\\left(\n", 295 | " \\begin{array}{c}\n", 296 | " x_{11}\\ x_{12}\\ x_{13}\\ x_{14}\\ \\\\\n", 297 | " x_{21}\\ x_{22}\\ x_{23}\\ x_{24}\\ \\\\\n", 298 | " x_{31}\\ x_{32}\\ x_{33}\\ x_{34}\\ \\\\\n", 299 | " x_{41}\\ x_{42}\\ x_{43}\\ x_{44}\\ \\\\\n", 300 | " x_{51}\\ x_{52}\\ x_{53}\\ x_{54}\\ \\\\\n", 301 | " \\end{array}\n", 302 | " \\right)$\n", 303 | "\n", 304 | "이번 문제는 선택지가 총 3개인 문제이므로 가설의 예측값으로 얻는 행렬 $\\hat{Y}$의 열의 개수는 3개여야 합니다. 그리고 각 행은 행렬 $X$의 각 행의 예측값이므로 행의 크기는 동일해야 합니다. 결과적으로 행렬 $\\hat{Y}$의 크기는 5 × 3입니다.\n", 305 | "\n", 306 | "$\\hat{Y}=\\left(\n", 307 | " \\begin{array}{c}\n", 308 | " y_{11}\\ y_{12}\\ y_{13}\\ \\\\\n", 309 | " y_{21}\\ y_{22}\\ y_{23}\\ \\\\\n", 310 | " y_{31}\\ y_{32}\\ y_{33}\\ \\\\\n", 311 | " y_{41}\\ y_{42}\\ y_{43}\\ \\\\\n", 312 | " y_{51}\\ y_{52}\\ y_{53}\\ \\\\\n", 313 | " \\end{array}\n", 314 | " \\right)$\n", 315 | "\n", 316 | "\n", 317 | "크기 5 × 3의 행렬 $\\hat{Y}$는 크기 5 × 4 입력 행렬 $X$과 가중치 행렬 $W$의 곱으로 얻어지는 행렬이므로 가중치 행렬 $W$의 크기는 추정을 통해 4 × 3의 크기를 가진 행렬임을 알 수 있습니다.\n", 318 | "\n", 319 | "$W=\\left(\n", 320 | " \\begin{array}{c}\n", 321 | " w_{11}\\ w_{12}\\ w_{13}\\ \\\\\n", 322 | " w_{21}\\ w_{22}\\ w_{23}\\ \\\\\n", 323 | " w_{31}\\ w_{32}\\ w_{33}\\ \\\\\n", 324 | " w_{41}\\ w_{42}\\ w_{43}\\ \\\\\n", 325 | " \\end{array}\n", 326 | " \\right)$\n", 327 | "\n", 328 | " 편향 행렬 $B$는 예측값 행렬 $\\hat{Y}$와 크기가 동일해야 하므로 5 × 3의 크기를 가집니다.\n", 329 | "\n", 330 | "$B=\\left(\n", 331 | " \\begin{array}{c}\n", 332 | " b_{1}\\ b_{2}\\ b_{3}\\\\\n", 333 | " b_{1}\\ b_{2}\\ b_{3}\\\\\n", 334 | " b_{1}\\ b_{2}\\ b_{3}\\\\\n", 335 | " b_{1}\\ b_{2}\\ b_{3}\\\\\n", 336 | " b_{1}\\ b_{2}\\ b_{3}\\\\\n", 337 | " \\end{array}\n", 338 | " \\right)$\n", 339 | "\n", 340 | " 결과적으로 가설식은 다음과 같습니다.\n", 341 | "\n", 342 | "$\\hat{Y} = softmax(XW + B)$\n", 343 | "\n", 344 | "\n", 345 | "$\\left(\n", 346 | " \\begin{array}{c}\n", 347 | " y_{11}\\ y_{12}\\ y_{13}\\ \\\\\n", 348 | " y_{21}\\ y_{22}\\ y_{23}\\ \\\\\n", 349 | " y_{31}\\ y_{32}\\ y_{33}\\ \\\\\n", 350 | " y_{41}\\ y_{42}\\ y_{43}\\ \\\\\n", 351 | " y_{51}\\ y_{52}\\ y_{53}\\ \\\\\n", 352 | " \\end{array}\n", 353 | " \\right)$=$softmax\\left(\n", 354 | "\\left(\n", 355 | " \\begin{array}{c}\n", 356 | " x_{11}\\ x_{12}\\ x_{13}\\ x_{14}\\ \\\\\n", 357 | " x_{21}\\ x_{22}\\ x_{23}\\ x_{24}\\ \\\\\n", 358 | " x_{31}\\ x_{32}\\ x_{33}\\ x_{34}\\ \\\\\n", 359 | " x_{41}\\ x_{42}\\ x_{43}\\ x_{44}\\ \\\\\n", 360 | " x_{51}\\ x_{52}\\ x_{53}\\ x_{54}\\ \\\\\n", 361 | " \\end{array}\n", 362 | " \\right)\n", 363 | "\\left(\n", 364 | " \\begin{array}{c}\n", 365 | " w_{11}\\ w_{12}\\ w_{13}\\ \\\\\n", 366 | " w_{21}\\ w_{22}\\ w_{23}\\ \\\\\n", 367 | " w_{31}\\ w_{32}\\ w_{33}\\ \\\\\n", 368 | " w_{41}\\ w_{42}\\ w_{43}\\ \\\\\n", 369 | " \\end{array}\n", 370 | " \\right)\n", 371 | "+\n", 372 | "\\left(\n", 373 | " \\begin{array}{c}\n", 374 | " b_{1}\\ b_{2}\\ b_{3}\\\\\n", 375 | " b_{1}\\ b_{2}\\ b_{3}\\\\\n", 376 | " b_{1}\\ b_{2}\\ b_{3}\\\\\n", 377 | " b_{1}\\ b_{2}\\ b_{3}\\\\\n", 378 | " b_{1}\\ b_{2}\\ b_{3}\\\\\n", 379 | " \\end{array}\n", 380 | " \\right)\n", 381 | "\\right)$" 382 | ] 383 | }, 384 | { 385 | "cell_type": "markdown", 386 | "metadata": { 387 | "id": "jf-y1xHQ5JIV", 388 | "colab_type": "text" 389 | }, 390 | "source": [ 391 | "### 4.2.4 비용 함수(Cost function)\n", 392 | "\n", 393 | "소프트맥스 회귀에서는 비용 함수로 크로스 엔트로피 함수를 사용합니다. 여기서는 소프트맥스 회귀에서의 크로스 엔트로피 함수뿐만 아니라, 다양한 표기 방법에 대해서 이해해보겠습니다." 394 | ] 395 | }, 396 | { 397 | "cell_type": "markdown", 398 | "metadata": { 399 | "id": "1Fpgtj5H5SUI", 400 | "colab_type": "text" 401 | }, 402 | "source": [ 403 | "1) 크로스 엔트로피 함수\n", 404 | "\n", 405 | "아래에서 $y$는 실제값을 나타내며, $k$는 클래스의 개수로 정의합니다. $y_j$는 실제값 원-핫 벡터의 $j$번째 인덱스를 의미하며, $p_j$는 샘플 데이터가 $j$번째 클래스일 확률을 나타냅니다. 표기에 따라서 $\\hat{y}_{j}$로 표현하기도 합니다.\n", 406 | "\n", 407 | "$cost(W) = -\\sum_{j=1}^{k}y_{j}\\ log(p_{j})$\n", 408 | "\n", 409 | "이 함수가 왜 비용 함수로 적합한지 알아보겠습니다. $c$가 실제값 원-핫 벡터에서 1을 가진 원소의 인덱스라고 한다면, $p_{c}=1$은 $\\hat{y}$가 $y$를 정확하게 예측한 경우가 됩니다. 이를 식에 대입해보면 $-1 log(1) = 0$이 되기 때문에, 결과적으로 $\\hat{y}$가 $y$를 정확하게 예측한 경우의 크로스 엔트로피 함수의 값은 0이 됩니다. 즉, $-\\sum_{j=1}^{k}y_{j}\\ log(p_{j})$ 이 값을 최소화하는 방향으로 학습해야 합니다.\n", 410 | "\n", 411 | "이제 이를 $n$개의 전체 데이터에 대한 평균을 구한다고 하면 최종 비용 함수는 다음과 같습니다.\n", 412 | "\n", 413 | "$cost(W) = -\\frac{1}{n} \\sum_{i=1}^{n} \\sum_{j=1}^{k}y_{j}^{(i)}\\ log(p_{j}^{(i)})$" 414 | ] 415 | }, 416 | { 417 | "cell_type": "markdown", 418 | "metadata": { 419 | "id": "GmjNV7Mo5-pq", 420 | "colab_type": "text" 421 | }, 422 | "source": [ 423 | "2) 이진 분류에서의 크로스 엔트로피 함수\n", 424 | "\n", 425 | "로지스틱 회귀에서 배운 크로스 엔트로피 함수식과 달라보이지만, 본질적으로는 동일한 함수식입니다. 로지스틱 회귀의 크로스 엔트로피 함수식으로부터 소프트맥스 회귀의 크로스 엔트로피 함수식을 도출해봅시다.\n", 426 | "\n", 427 | "$cost(W) = -(y\\ logH(X) + (1-y)\\ log(1-H(X)))$\n", 428 | "\n", 429 | "위의 식은 앞서 로지스틱 회귀에서 배웠던 크로스 엔트로피의 함수식을 보여줍니다. 위의 식에서 $y$를 $y_1$, $y$−1을 $y_2$로 치환하고 $H(X)$를 $p_1$, 1−$H(X)$를 $p_2$로 치환해봅시다. 결과적으로 아래의 식을 얻을 수 있습니다.\n", 430 | "\n", 431 | "$-(y_{1}\\ log(p_{1})+y_{2}\\ log(p_{2}))$\n", 432 | "\n", 433 | "이 식은 아래와 같이 표현할 수 있습니다\n", 434 | "\n", 435 | "$-(\\sum_{i=1}^{2}y_{i}\\ log\\ p_{i})$\n", 436 | "\n", 437 | "소프트맥스 회귀에서는 k의 값이 고정된 값이 아니므로 2를 k로 변경합니다.\n", 438 | "\n", 439 | "$-(\\sum_{i=1}^{k}y_{i}\\ log\\ p_{i})$\n", 440 | "\n", 441 | "위의 식은 결과적으로 소프트맥스 회귀의 식과 동일합니다. 역으로 소프트맥스 회귀에서 로지스틱 회귀의 크로스 엔트로피 함수식을 얻는 것은 k를 2로 하고, $y_1$과 $y_2$를 각각 $y$와 1−$y$로 치환하고, $p_1$와 $p_2$를 각각 $H(X)$와 1−$H(X)$로 치환하면 됩니다.\n", 442 | "\n", 443 | "정리하면 소프트맥스 함수의 최종 비용 함수에서 $k$가 2라고 가정하면 결국 로지스틱 회귀의 비용 함수와 같습니다.\n", 444 | "\n", 445 | "$cost(W) = -\\frac{1}{n} \\sum_{i=1}^{n} \\sum_{j=1}^{k}y_{j}^{(i)}\\ log(p_{j}^{(i)}) = -\\frac{1}{n} \\sum_{i=1}^{n} [y^{(i)}log(p^{(i)}) + (1-y^{(i)})log(1-p^{(i)})]$" 446 | ] 447 | }, 448 | { 449 | "cell_type": "markdown", 450 | "metadata": { 451 | "id": "FRbJfYW76pxX", 452 | "colab_type": "text" 453 | }, 454 | "source": [ 455 | "## 4.3 소프트맥스 회귀의 비용 함수 구현하기\n", 456 | "\n", 457 | "이번 챕터에서는 소프트맥스 회귀를 구현해봅시다.\n", 458 | "\n", 459 | "앞으로의 모든 실습은 아래의 과정이 이미 진행되었다고 가정합니다." 460 | ] 461 | }, 462 | { 463 | "cell_type": "code", 464 | "metadata": { 465 | "id": "_ZYZYBwUt1Hw", 466 | "colab_type": "code", 467 | "outputId": "2e799856-da56-49ae-eaee-9594fa08f223", 468 | "colab": { 469 | "base_uri": "https://localhost:8080/", 470 | "height": 35 471 | } 472 | }, 473 | "source": [ 474 | "import torch\n", 475 | "import torch.nn.functional as F\n", 476 | "torch.manual_seed(1)" 477 | ], 478 | "execution_count": 0, 479 | "outputs": [ 480 | { 481 | "output_type": "execute_result", 482 | "data": { 483 | "text/plain": [ 484 | "" 485 | ] 486 | }, 487 | "metadata": { 488 | "tags": [] 489 | }, 490 | "execution_count": 1 491 | } 492 | ] 493 | }, 494 | { 495 | "cell_type": "markdown", 496 | "metadata": { 497 | "id": "HbhICiYr6wbX", 498 | "colab_type": "text" 499 | }, 500 | "source": [ 501 | "### 4.3.1 파이토치로 소프트맥스의 비용 함수 구현하기 (로우-레벨)\n", 502 | "\n", 503 | "소프트맥스 회귀를 구현함에 있어 우선 소프트맥스 함수의 비용 함수를 로우-레벨로 구현해봅시다.\n", 504 | "3개의 원소를 가진 벡터 텐서를 정의하고, 이 텐서를 통해 소프트맥스 함수를 이해해보겠습니다." 505 | ] 506 | }, 507 | { 508 | "cell_type": "code", 509 | "metadata": { 510 | "id": "5zpabP4J6v05", 511 | "colab_type": "code", 512 | "colab": {} 513 | }, 514 | "source": [ 515 | "z = torch.FloatTensor([1, 2, 3])" 516 | ], 517 | "execution_count": 0, 518 | "outputs": [] 519 | }, 520 | { 521 | "cell_type": "markdown", 522 | "metadata": { 523 | "id": "NhdtxBZ963Xt", 524 | "colab_type": "text" 525 | }, 526 | "source": [ 527 | "이 텐서를 소프트맥스 함수의 입력으로 사용하고, 그 결과를 확인해보겠습니다." 528 | ] 529 | }, 530 | { 531 | "cell_type": "code", 532 | "metadata": { 533 | "id": "v4ft4ldg62PP", 534 | "colab_type": "code", 535 | "outputId": "6915e086-ca50-4f1a-ade3-269e8d4b2050", 536 | "colab": { 537 | "base_uri": "https://localhost:8080/", 538 | "height": 35 539 | } 540 | }, 541 | "source": [ 542 | "hypothesis = F.softmax(z, dim=0)\n", 543 | "print(hypothesis)" 544 | ], 545 | "execution_count": 0, 546 | "outputs": [ 547 | { 548 | "output_type": "stream", 549 | "text": [ 550 | "tensor([0.0900, 0.2447, 0.6652])\n" 551 | ], 552 | "name": "stdout" 553 | } 554 | ] 555 | }, 556 | { 557 | "cell_type": "markdown", 558 | "metadata": { 559 | "id": "SGi5cT5567Z_", 560 | "colab_type": "text" 561 | }, 562 | "source": [ 563 | "3개의 원소의 값이 0과 1사이의 값을 가지는 벡터로 변환된 것을 볼 수 있습니다. 이 원소들의 값의 합이 1인지 확인해보겠습니다." 564 | ] 565 | }, 566 | { 567 | "cell_type": "code", 568 | "metadata": { 569 | "id": "nk3S60f365ZA", 570 | "colab_type": "code", 571 | "outputId": "e82783ab-f317-4c66-e896-ea60cdacab99", 572 | "colab": { 573 | "base_uri": "https://localhost:8080/", 574 | "height": 35 575 | } 576 | }, 577 | "source": [ 578 | "hypothesis.sum()" 579 | ], 580 | "execution_count": 0, 581 | "outputs": [ 582 | { 583 | "output_type": "execute_result", 584 | "data": { 585 | "text/plain": [ 586 | "tensor(1.)" 587 | ] 588 | }, 589 | "metadata": { 590 | "tags": [] 591 | }, 592 | "execution_count": 4 593 | } 594 | ] 595 | }, 596 | { 597 | "cell_type": "markdown", 598 | "metadata": { 599 | "id": "SsESCbCN6_hZ", 600 | "colab_type": "text" 601 | }, 602 | "source": [ 603 | "총 원소의 값의 합은 1입니다. 이번에는 비용 함수를 직접 구현해보겠습니다. 우선 임의의 3 × 5 행렬의 크기를 가진 텐서를 만듭니다." 604 | ] 605 | }, 606 | { 607 | "cell_type": "code", 608 | "metadata": { 609 | "id": "RS8U4Zcx68-e", 610 | "colab_type": "code", 611 | "colab": {} 612 | }, 613 | "source": [ 614 | "z = torch.rand(3, 5, requires_grad=True)" 615 | ], 616 | "execution_count": 0, 617 | "outputs": [] 618 | }, 619 | { 620 | "cell_type": "markdown", 621 | "metadata": { 622 | "id": "QthSkN0j7CtP", 623 | "colab_type": "text" 624 | }, 625 | "source": [ 626 | "이제 이 텐서에 대해서 소프트맥스 함수를 적용합니다. 단, 각 샘플에 대해서 소프트맥스 함수를 적용하여야 하므로 두번째 차원에 대해서 소프트맥스 함수를 적용한다는 의미에서 dim=1을 써줍니다." 627 | ] 628 | }, 629 | { 630 | "cell_type": "code", 631 | "metadata": { 632 | "id": "L3O1I8eH7BJr", 633 | "colab_type": "code", 634 | "outputId": "d7238c89-ab07-4733-9fc8-c673fe4de38e", 635 | "colab": { 636 | "base_uri": "https://localhost:8080/", 637 | "height": 71 638 | } 639 | }, 640 | "source": [ 641 | "hypothesis = F.softmax(z, dim=1)\n", 642 | "print(hypothesis)" 643 | ], 644 | "execution_count": 0, 645 | "outputs": [ 646 | { 647 | "output_type": "stream", 648 | "text": [ 649 | "tensor([[0.2645, 0.1639, 0.1855, 0.2585, 0.1277],\n", 650 | " [0.2430, 0.1624, 0.2322, 0.1930, 0.1694],\n", 651 | " [0.2226, 0.1986, 0.2326, 0.1594, 0.1868]], grad_fn=)\n" 652 | ], 653 | "name": "stdout" 654 | } 655 | ] 656 | }, 657 | { 658 | "cell_type": "markdown", 659 | "metadata": { 660 | "id": "e1fVFkbC7Fh7", 661 | "colab_type": "text" 662 | }, 663 | "source": [ 664 | "이제 각 행의 원소들의 합은 1이 되는 텐서로 변환되었습니다. 소프트맥스 함수의 출력값은 결국 예측값입니다. 즉, 위 텐서는 3개의 샘플에 대해서 5개의 클래스 중 어떤 클래스가 정답인지를 예측한 결과입니다.\n", 665 | "\n", 666 | "이제 각 샘플에 대해서 임의의 레이블을 만듭니다." 667 | ] 668 | }, 669 | { 670 | "cell_type": "code", 671 | "metadata": { 672 | "id": "b9u5-QCj7EJ0", 673 | "colab_type": "code", 674 | "outputId": "026906f9-fc9c-45cd-b521-1101b46a05c8", 675 | "colab": { 676 | "base_uri": "https://localhost:8080/", 677 | "height": 35 678 | } 679 | }, 680 | "source": [ 681 | "y = torch.randint(5, (3,)).long()\n", 682 | "print(y)" 683 | ], 684 | "execution_count": 0, 685 | "outputs": [ 686 | { 687 | "output_type": "stream", 688 | "text": [ 689 | "tensor([0, 2, 1])\n" 690 | ], 691 | "name": "stdout" 692 | } 693 | ] 694 | }, 695 | { 696 | "cell_type": "markdown", 697 | "metadata": { 698 | "id": "cOPP8aZE7IPx", 699 | "colab_type": "text" 700 | }, 701 | "source": [ 702 | "이제 각 레이블에 대해서 원-핫 인코딩을 수행합니다." 703 | ] 704 | }, 705 | { 706 | "cell_type": "code", 707 | "metadata": { 708 | "id": "blA8TpNJ7HA-", 709 | "colab_type": "code", 710 | "outputId": "6312da92-0d3e-4489-b8fb-c37594621a23", 711 | "colab": { 712 | "base_uri": "https://localhost:8080/", 713 | "height": 71 714 | } 715 | }, 716 | "source": [ 717 | "# 모든 원소가 0의 값을 가진 3 × 5 텐서 생성\n", 718 | "y_one_hot = torch.zeros_like(hypothesis) \n", 719 | "y_one_hot.scatter_(1, y.unsqueeze(1), 1)" 720 | ], 721 | "execution_count": 0, 722 | "outputs": [ 723 | { 724 | "output_type": "execute_result", 725 | "data": { 726 | "text/plain": [ 727 | "tensor([[1., 0., 0., 0., 0.],\n", 728 | " [0., 0., 1., 0., 0.],\n", 729 | " [0., 1., 0., 0., 0.]])" 730 | ] 731 | }, 732 | "metadata": { 733 | "tags": [] 734 | }, 735 | "execution_count": 8 736 | } 737 | ] 738 | }, 739 | { 740 | "cell_type": "markdown", 741 | "metadata": { 742 | "id": "VgKaZGtr7Uqt", 743 | "colab_type": "text" 744 | }, 745 | "source": [ 746 | "위의 연산에서 어떻게 원-핫 인코딩이 수행되었는지 보겠습니다. 우선, torch.zeros_like(hypothesis)를 통해 모든 원소가 0의 값을 가진 3 × 5 텐서를 만듭니다. 그리고 이 텐서는 y_one_hot에 저장이 된 상태입니다.\n", 747 | "\n", 748 | "두번째 줄을 해석해봅시다. y.unsqueeze(1)를 하면 (3,)의 크기를 가졌던 y 텐서는 (3 × 1) 텐서가 됩니다. 즉, 다시 말해서 y.unsqueeze(1)의 결과는 아래와 같습니다." 749 | ] 750 | }, 751 | { 752 | "cell_type": "code", 753 | "metadata": { 754 | "id": "-v4_k4o17KD_", 755 | "colab_type": "code", 756 | "outputId": "e4ab4764-7195-4f98-94dd-024ba095cc43", 757 | "colab": { 758 | "base_uri": "https://localhost:8080/", 759 | "height": 71 760 | } 761 | }, 762 | "source": [ 763 | "print(y.unsqueeze(1))" 764 | ], 765 | "execution_count": 0, 766 | "outputs": [ 767 | { 768 | "output_type": "stream", 769 | "text": [ 770 | "tensor([[0],\n", 771 | " [2],\n", 772 | " [1]])\n" 773 | ], 774 | "name": "stdout" 775 | } 776 | ] 777 | }, 778 | { 779 | "cell_type": "markdown", 780 | "metadata": { 781 | "id": "9DJWzpvU7ZTf", 782 | "colab_type": "text" 783 | }, 784 | "source": [ 785 | "그리고 scatter의 첫번째 인자로 dim=1에 대해서 수행하라고 알려주고, 세번째 인자에 숫자 1을 넣어주므로서 두번째 인자인 y_unsqeeze(1)이 알려주는 위치에 숫자 1을 넣도록 합니다. 앞서 텐서 조작하기 2챕터에서 연산 뒤에 _를 붙이면 In-place Operation (덮어쓰기 연산)임을 배운 바 있습니다. 이에 따라서 y_one_hot의 최종 결과는 결국 아래와 같습니다." 786 | ] 787 | }, 788 | { 789 | "cell_type": "code", 790 | "metadata": { 791 | "id": "T3cgtRqb7Vzn", 792 | "colab_type": "code", 793 | "outputId": "13d2feeb-639a-4244-ebb0-b1364bb038d7", 794 | "colab": { 795 | "base_uri": "https://localhost:8080/", 796 | "height": 71 797 | } 798 | }, 799 | "source": [ 800 | "print(y_one_hot)" 801 | ], 802 | "execution_count": 0, 803 | "outputs": [ 804 | { 805 | "output_type": "stream", 806 | "text": [ 807 | "tensor([[1., 0., 0., 0., 0.],\n", 808 | " [0., 0., 1., 0., 0.],\n", 809 | " [0., 1., 0., 0., 0.]])\n" 810 | ], 811 | "name": "stdout" 812 | } 813 | ] 814 | }, 815 | { 816 | "cell_type": "markdown", 817 | "metadata": { 818 | "id": "m1yp9HdK7dqS", 819 | "colab_type": "text" 820 | }, 821 | "source": [ 822 | "이제 비용 함수 연산을 위한 재료들을 전부 손질했습니다. 소프트맥스 회귀의 비용 함수는 다음과 같았습니다.\n", 823 | "\n", 824 | "$cost(W) = -\\frac{1}{n} \\sum_{i=1}^{n} \\sum_{j=1}^{k}y_{j}^{(i)}\\ log(p_{j}^{(i)})$\n", 825 | "\n", 826 | "마이너스 부호를 뒤로 빼면 다음 식과도 동일합니다.\n", 827 | "\n", 828 | "$cost(W) = \\frac{1}{n} \\sum_{i=1}^{n} \\sum_{j=1}^{k}y_{j}^{(i)}\\ * (-log(p_{j}^{(i)}))$\n", 829 | "\n", 830 | "이를 코드로 구현하면 아래와 같습니다.$\\sum_{j=1}^{k}$는 sum(dim=1)으로 구현하고, $\\frac{1}{n} \\sum_{i=1}^{n}$는 mean()으로 구현합니다." 831 | ] 832 | }, 833 | { 834 | "cell_type": "code", 835 | "metadata": { 836 | "id": "SDPAHwaz7b7V", 837 | "colab_type": "code", 838 | "outputId": "1ebb7f69-dafd-4249-cada-4df027d5ae50", 839 | "colab": { 840 | "base_uri": "https://localhost:8080/", 841 | "height": 35 842 | } 843 | }, 844 | "source": [ 845 | "cost = (y_one_hot * -torch.log(hypothesis)).sum(dim=1).mean()\n", 846 | "print(cost)" 847 | ], 848 | "execution_count": 0, 849 | "outputs": [ 850 | { 851 | "output_type": "stream", 852 | "text": [ 853 | "tensor(1.4689, grad_fn=)\n" 854 | ], 855 | "name": "stdout" 856 | } 857 | ] 858 | }, 859 | { 860 | "cell_type": "markdown", 861 | "metadata": { 862 | "id": "hNf9uyag7yll", 863 | "colab_type": "text" 864 | }, 865 | "source": [ 866 | "### 4.3.2 파이토치로 소프트맥스의 비용 함수 구현하기 (하이-레벨)\n", 867 | "\n", 868 | "이제 소프트맥스의 비용 함수를 좀 더 하이-레벨로 구현하는 방법에 대해서 알아봅시다." 869 | ] 870 | }, 871 | { 872 | "cell_type": "markdown", 873 | "metadata": { 874 | "id": "5kq6gBy073p1", 875 | "colab_type": "text" 876 | }, 877 | "source": [ 878 | "1) F.softmax() + torch.log() = F.log_softmax()\n", 879 | "\n", 880 | "앞서 소프트맥스 함수의 결과에 로그를 씌울 때는 다음과 같이 소프트맥스 함수의 출력값을 로그 함수의 입력으로 사용했습니다." 881 | ] 882 | }, 883 | { 884 | "cell_type": "code", 885 | "metadata": { 886 | "id": "nLm3cLEa7vxY", 887 | "colab_type": "code", 888 | "outputId": "13f7c506-8dcd-46f2-a16d-4a2d3d40329d", 889 | "colab": { 890 | "base_uri": "https://localhost:8080/", 891 | "height": 71 892 | } 893 | }, 894 | "source": [ 895 | "# Low level\n", 896 | "torch.log(F.softmax(z, dim=1))" 897 | ], 898 | "execution_count": 0, 899 | "outputs": [ 900 | { 901 | "output_type": "execute_result", 902 | "data": { 903 | "text/plain": [ 904 | "tensor([[-1.3301, -1.8084, -1.6846, -1.3530, -2.0584],\n", 905 | " [-1.4147, -1.8174, -1.4602, -1.6450, -1.7758],\n", 906 | " [-1.5025, -1.6165, -1.4586, -1.8360, -1.6776]], grad_fn=)" 907 | ] 908 | }, 909 | "metadata": { 910 | "tags": [] 911 | }, 912 | "execution_count": 13 913 | } 914 | ] 915 | }, 916 | { 917 | "cell_type": "markdown", 918 | "metadata": { 919 | "id": "o1u0e0Bs7-dN", 920 | "colab_type": "text" 921 | }, 922 | "source": [ 923 | "그런데 파이토치에서는 두 개의 함수를 결합한 F.log_softmax()라는 도구를 제공합니다." 924 | ] 925 | }, 926 | { 927 | "cell_type": "code", 928 | "metadata": { 929 | "id": "rxI4W_5v773N", 930 | "colab_type": "code", 931 | "outputId": "b7350f52-98f2-47b6-9a4a-ce223ae2aefd", 932 | "colab": { 933 | "base_uri": "https://localhost:8080/", 934 | "height": 89 935 | } 936 | }, 937 | "source": [ 938 | "# High level\n", 939 | "F.log_softmax(z, dim=1)" 940 | ], 941 | "execution_count": 0, 942 | "outputs": [ 943 | { 944 | "output_type": "execute_result", 945 | "data": { 946 | "text/plain": [ 947 | "tensor([[-1.3301, -1.8084, -1.6846, -1.3530, -2.0584],\n", 948 | " [-1.4147, -1.8174, -1.4602, -1.6450, -1.7758],\n", 949 | " [-1.5025, -1.6165, -1.4586, -1.8360, -1.6776]],\n", 950 | " grad_fn=)" 951 | ] 952 | }, 953 | "metadata": { 954 | "tags": [] 955 | }, 956 | "execution_count": 14 957 | } 958 | ] 959 | }, 960 | { 961 | "cell_type": "markdown", 962 | "metadata": { 963 | "id": "wKvCALUT8Bf2", 964 | "colab_type": "text" 965 | }, 966 | "source": [ 967 | "두 출력 결과가 동일한 것을 볼 수 있습니다. 이제 비용 함수를 보겠습니다." 968 | ] 969 | }, 970 | { 971 | "cell_type": "markdown", 972 | "metadata": { 973 | "id": "ZKDTKz8J8C97", 974 | "colab_type": "text" 975 | }, 976 | "source": [ 977 | "2) F.log_softmax() + F.nll_loss() = F.cross_entropy()\n", 978 | "\n", 979 | "앞서 로우-레벨로 구현한 비용 함수는 다음과 같았습니다." 980 | ] 981 | }, 982 | { 983 | "cell_type": "code", 984 | "metadata": { 985 | "id": "0hYfK8-D8ALN", 986 | "colab_type": "code", 987 | "outputId": "c0bf6068-6b84-4d1d-c154-d4f015d9cc88", 988 | "colab": { 989 | "base_uri": "https://localhost:8080/", 990 | "height": 35 991 | } 992 | }, 993 | "source": [ 994 | "# Low level\n", 995 | "# 첫번째 수식\n", 996 | "(y_one_hot * -torch.log(F.softmax(z, dim=1))).sum(dim=1).mean()" 997 | ], 998 | "execution_count": 0, 999 | "outputs": [ 1000 | { 1001 | "output_type": "execute_result", 1002 | "data": { 1003 | "text/plain": [ 1004 | "tensor(1.4689, grad_fn=)" 1005 | ] 1006 | }, 1007 | "metadata": { 1008 | "tags": [] 1009 | }, 1010 | "execution_count": 15 1011 | } 1012 | ] 1013 | }, 1014 | { 1015 | "cell_type": "markdown", 1016 | "metadata": { 1017 | "id": "ZHTi8KM-8IZq", 1018 | "colab_type": "text" 1019 | }, 1020 | "source": [ 1021 | "그런데 위의 수식에서 torch.log(F.softmax(z, dim=1))를 방금 배운 F.log_softmax()로 대체할 수 있습니다." 1022 | ] 1023 | }, 1024 | { 1025 | "cell_type": "code", 1026 | "metadata": { 1027 | "id": "SGT8hEQ_8G28", 1028 | "colab_type": "code", 1029 | "outputId": "dc526328-fc0b-4baf-d435-2d23b2c7007a", 1030 | "colab": { 1031 | "base_uri": "https://localhost:8080/", 1032 | "height": 35 1033 | } 1034 | }, 1035 | "source": [ 1036 | "# 두번째 수식\n", 1037 | "(y_one_hot * - F.log_softmax(z, dim=1)).sum(dim=1).mean()" 1038 | ], 1039 | "execution_count": 0, 1040 | "outputs": [ 1041 | { 1042 | "output_type": "execute_result", 1043 | "data": { 1044 | "text/plain": [ 1045 | "tensor(1.4689, grad_fn=)" 1046 | ] 1047 | }, 1048 | "metadata": { 1049 | "tags": [] 1050 | }, 1051 | "execution_count": 16 1052 | } 1053 | ] 1054 | }, 1055 | { 1056 | "cell_type": "markdown", 1057 | "metadata": { 1058 | "id": "BGWBk8m08Ld0", 1059 | "colab_type": "text" 1060 | }, 1061 | "source": [ 1062 | "이를 더 간단하게 하면 다음과 같습니다. F.nll_loss()를 사용할 때는 원-핫 벡터를 넣을 필요없이 바로 실제값을 인자로 사용합니다." 1063 | ] 1064 | }, 1065 | { 1066 | "cell_type": "code", 1067 | "metadata": { 1068 | "id": "VdnEquzq8KGv", 1069 | "colab_type": "code", 1070 | "outputId": "3c9319a3-5fb4-49c8-8fe9-351163903ca4", 1071 | "colab": { 1072 | "base_uri": "https://localhost:8080/", 1073 | "height": 35 1074 | } 1075 | }, 1076 | "source": [ 1077 | "# High level\n", 1078 | "# 세번째 수식\n", 1079 | "F.nll_loss(F.log_softmax(z, dim=1), y)" 1080 | ], 1081 | "execution_count": 0, 1082 | "outputs": [ 1083 | { 1084 | "output_type": "execute_result", 1085 | "data": { 1086 | "text/plain": [ 1087 | "tensor(1.4689, grad_fn=)" 1088 | ] 1089 | }, 1090 | "metadata": { 1091 | "tags": [] 1092 | }, 1093 | "execution_count": 17 1094 | } 1095 | ] 1096 | }, 1097 | { 1098 | "cell_type": "markdown", 1099 | "metadata": { 1100 | "id": "lSLAR8We8PUh", 1101 | "colab_type": "text" 1102 | }, 1103 | "source": [ 1104 | "여기서 nll이란 Negative Log Likelihood의 약자입니다. 위에서 nll_loss는 F.log_softmax()를 수행한 후에 남은 수식들을 수행합니다. 이를 더 간단하게 하면 다음과 같이 사용할 수 있습니다. F.cross_entropy()는 F.log_softmax()와 F.nll_loss()를 포함하고 있습니다." 1105 | ] 1106 | }, 1107 | { 1108 | "cell_type": "code", 1109 | "metadata": { 1110 | "id": "Y6bvWSsM8N51", 1111 | "colab_type": "code", 1112 | "outputId": "6c5b24c1-0c2a-42e2-8e1f-3ad7bcc0dcfd", 1113 | "colab": { 1114 | "base_uri": "https://localhost:8080/", 1115 | "height": 35 1116 | } 1117 | }, 1118 | "source": [ 1119 | "# 네번째 수식\n", 1120 | "F.cross_entropy(z, y)" 1121 | ], 1122 | "execution_count": 0, 1123 | "outputs": [ 1124 | { 1125 | "output_type": "execute_result", 1126 | "data": { 1127 | "text/plain": [ 1128 | "tensor(1.4689, grad_fn=)" 1129 | ] 1130 | }, 1131 | "metadata": { 1132 | "tags": [] 1133 | }, 1134 | "execution_count": 18 1135 | } 1136 | ] 1137 | }, 1138 | { 1139 | "cell_type": "markdown", 1140 | "metadata": { 1141 | "id": "HzdEkUGg8UZw", 1142 | "colab_type": "text" 1143 | }, 1144 | "source": [ 1145 | "F.cross_entropy는 비용 함수에 소프트맥스 함수까지 포함하고 있음을 기억하고 있어야 구현 시 혼동하지 않습니다." 1146 | ] 1147 | }, 1148 | { 1149 | "cell_type": "markdown", 1150 | "metadata": { 1151 | "id": "3FiHIs1z8sFd", 1152 | "colab_type": "text" 1153 | }, 1154 | "source": [ 1155 | "## 4.4 소프트맥스 회귀 구현하기\n", 1156 | "\n", 1157 | "이번 챕터에서는 소프트맥스 회귀를 로우-레벨과 F.cross_entropy를 사용해서 구현해보겠습니다.\n", 1158 | "앞으로의 모든 실습은 아래의 과정이 이미 진행되었다고 가정합니다.\n", 1159 | "\n", 1160 | "필요한 도구들을 임포트합니다." 1161 | ] 1162 | }, 1163 | { 1164 | "cell_type": "code", 1165 | "metadata": { 1166 | "id": "8bpBjEag8SIx", 1167 | "colab_type": "code", 1168 | "colab": {} 1169 | }, 1170 | "source": [ 1171 | "import torch\n", 1172 | "import torch.nn as nn\n", 1173 | "import torch.nn.functional as F\n", 1174 | "import torch.optim as optim" 1175 | ], 1176 | "execution_count": 0, 1177 | "outputs": [] 1178 | }, 1179 | { 1180 | "cell_type": "markdown", 1181 | "metadata": { 1182 | "id": "d1TqcMSe855U", 1183 | "colab_type": "text" 1184 | }, 1185 | "source": [ 1186 | "훈련 데이터와 레이블을 텐서로 선언합니다." 1187 | ] 1188 | }, 1189 | { 1190 | "cell_type": "code", 1191 | "metadata": { 1192 | "id": "1Xotge9884fC", 1193 | "colab_type": "code", 1194 | "colab": {} 1195 | }, 1196 | "source": [ 1197 | "x_train = [[1, 2, 1, 1],\n", 1198 | " [2, 1, 3, 2],\n", 1199 | " [3, 1, 3, 4],\n", 1200 | " [4, 1, 5, 5],\n", 1201 | " [1, 7, 5, 5],\n", 1202 | " [1, 2, 5, 6],\n", 1203 | " [1, 6, 6, 6],\n", 1204 | " [1, 7, 7, 7]]\n", 1205 | "y_train = [2, 2, 2, 1, 1, 1, 0, 0]\n", 1206 | "x_train = torch.FloatTensor(x_train)\n", 1207 | "y_train = torch.LongTensor(y_train)" 1208 | ], 1209 | "execution_count": 0, 1210 | "outputs": [] 1211 | }, 1212 | { 1213 | "cell_type": "markdown", 1214 | "metadata": { 1215 | "id": "our3kN7A88ie", 1216 | "colab_type": "text" 1217 | }, 1218 | "source": [ 1219 | "x_train의 각 샘플은 4개의 특성을 가지고 있으며, 총 8개의 샘플이 존재합니다. y_train은 각 샘플에 대한 레이블인데, 여기서는 0, 1, 2의 값을 가지는 것으로 보아 총 3개의 클래스가 존재합니다." 1220 | ] 1221 | }, 1222 | { 1223 | "cell_type": "markdown", 1224 | "metadata": { 1225 | "id": "PvCVzJi98_st", 1226 | "colab_type": "text" 1227 | }, 1228 | "source": [ 1229 | "### 4.4.1 소프트맥스 회귀 구현하기(로우-레벨)\n", 1230 | "\n", 1231 | "이제 x_train의 크기와 y_train의 크기를 확인합니다." 1232 | ] 1233 | }, 1234 | { 1235 | "cell_type": "code", 1236 | "metadata": { 1237 | "id": "FzfXwOZi87Oy", 1238 | "colab_type": "code", 1239 | "outputId": "2fa78f7e-1330-422a-a576-e4a2ce1c262f", 1240 | "colab": { 1241 | "base_uri": "https://localhost:8080/", 1242 | "height": 53 1243 | } 1244 | }, 1245 | "source": [ 1246 | "print(x_train.shape)\n", 1247 | "print(y_train.shape)" 1248 | ], 1249 | "execution_count": 0, 1250 | "outputs": [ 1251 | { 1252 | "output_type": "stream", 1253 | "text": [ 1254 | "torch.Size([8, 4])\n", 1255 | "torch.Size([8])\n" 1256 | ], 1257 | "name": "stdout" 1258 | } 1259 | ] 1260 | }, 1261 | { 1262 | "cell_type": "markdown", 1263 | "metadata": { 1264 | "id": "CTFm7-ki9Eid", 1265 | "colab_type": "text" 1266 | }, 1267 | "source": [ 1268 | "x_train의 크기는 8 × 4이며, y_train의 크기는 8 × 1입니다. 그런데 최종 사용할 레이블은 y_train에서 원-핫 인코딩을 한 결과이어야 합니다. 클래스의 개수는 3개이므로 y_train에 원-핫 인코딩한 결과는 8 × 3의 개수를 가져야 합니다." 1269 | ] 1270 | }, 1271 | { 1272 | "cell_type": "code", 1273 | "metadata": { 1274 | "id": "vLZQkOym9DiI", 1275 | "colab_type": "code", 1276 | "outputId": "f5473e4d-3c36-4afa-f0ff-3bf3795dad89", 1277 | "colab": { 1278 | "base_uri": "https://localhost:8080/", 1279 | "height": 35 1280 | } 1281 | }, 1282 | "source": [ 1283 | "y_one_hot = torch.zeros(8, 3)\n", 1284 | "y_one_hot.scatter_(1, y_train.unsqueeze(1), 1)\n", 1285 | "print(y_one_hot.shape)" 1286 | ], 1287 | "execution_count": 0, 1288 | "outputs": [ 1289 | { 1290 | "output_type": "stream", 1291 | "text": [ 1292 | "torch.Size([8, 3])\n" 1293 | ], 1294 | "name": "stdout" 1295 | } 1296 | ] 1297 | }, 1298 | { 1299 | "cell_type": "markdown", 1300 | "metadata": { 1301 | "id": "rinOwCv89HUs", 1302 | "colab_type": "text" 1303 | }, 1304 | "source": [ 1305 | "y_train에서 원-핫 인코딩을 한 결과인 y_one_hot의 크기는 8 × 3입니다. 즉, W 행렬의 크기는 4 × 3이어야 합니다.\n", 1306 | "W와 b를 선언하고, 옵티마이저로는 경사 하강법을 사용합니다. 그리고 학습률은 0.1로 설정합니다." 1307 | ] 1308 | }, 1309 | { 1310 | "cell_type": "code", 1311 | "metadata": { 1312 | "id": "g4HdbIML9F4C", 1313 | "colab_type": "code", 1314 | "colab": {} 1315 | }, 1316 | "source": [ 1317 | "# 모델 초기화\n", 1318 | "W = torch.zeros((4, 3), requires_grad=True)\n", 1319 | "b = torch.zeros(1, requires_grad=True)\n", 1320 | "# optimizer 설정\n", 1321 | "optimizer = optim.SGD([W, b], lr=0.1)" 1322 | ], 1323 | "execution_count": 0, 1324 | "outputs": [] 1325 | }, 1326 | { 1327 | "cell_type": "markdown", 1328 | "metadata": { 1329 | "id": "d-SCOa9S9J5Y", 1330 | "colab_type": "text" 1331 | }, 1332 | "source": [ 1333 | "F.softmax()와 torch.log()를 사용하여 가설과 비용 함수를 정의하고, 총 1,000번의 에포크를 수행합니다." 1334 | ] 1335 | }, 1336 | { 1337 | "cell_type": "code", 1338 | "metadata": { 1339 | "id": "B5gq0Nto9Iwc", 1340 | "colab_type": "code", 1341 | "outputId": "e1ebb4f8-5f66-4586-e244-dd409f837f39", 1342 | "colab": { 1343 | "base_uri": "https://localhost:8080/", 1344 | "height": 215 1345 | } 1346 | }, 1347 | "source": [ 1348 | "nb_epochs = 1000\n", 1349 | "for epoch in range(nb_epochs + 1):\n", 1350 | "\n", 1351 | " # 가설\n", 1352 | " hypothesis = F.softmax(x_train.matmul(W) + b, dim=1) \n", 1353 | "\n", 1354 | " # 비용 함수\n", 1355 | " cost = (y_one_hot * -torch.log(hypothesis)).sum(dim=1).mean()\n", 1356 | "\n", 1357 | " # cost로 H(x) 개선\n", 1358 | " optimizer.zero_grad()\n", 1359 | " cost.backward()\n", 1360 | " optimizer.step()\n", 1361 | "\n", 1362 | " # 100번마다 로그 출력\n", 1363 | " if epoch % 100 == 0:\n", 1364 | " print('Epoch {:4d}/{} Cost: {:.6f}'.format(\n", 1365 | " epoch, nb_epochs, cost.item()\n", 1366 | " ))" 1367 | ], 1368 | "execution_count": 0, 1369 | "outputs": [ 1370 | { 1371 | "output_type": "stream", 1372 | "text": [ 1373 | "Epoch 0/1000 Cost: 1.098612\n", 1374 | "Epoch 100/1000 Cost: 0.761050\n", 1375 | "Epoch 200/1000 Cost: 0.689991\n", 1376 | "Epoch 300/1000 Cost: 0.643229\n", 1377 | "Epoch 400/1000 Cost: 0.604117\n", 1378 | "Epoch 500/1000 Cost: 0.568255\n", 1379 | "Epoch 600/1000 Cost: 0.533922\n", 1380 | "Epoch 700/1000 Cost: 0.500291\n", 1381 | "Epoch 800/1000 Cost: 0.466908\n", 1382 | "Epoch 900/1000 Cost: 0.433507\n", 1383 | "Epoch 1000/1000 Cost: 0.399962\n" 1384 | ], 1385 | "name": "stdout" 1386 | } 1387 | ] 1388 | }, 1389 | { 1390 | "cell_type": "markdown", 1391 | "metadata": { 1392 | "id": "pqjpU4yF9NbP", 1393 | "colab_type": "text" 1394 | }, 1395 | "source": [ 1396 | "### 4.4.2 소프트맥스 회귀 구현하기(하이-레벨)\n", 1397 | "\n", 1398 | "이제는 F.cross_entropy()를 사용하여 비용 함수를 구현해보겠습니다. 주의할 점은 F.cross_entropy()는 그 자체로 소프트맥스 함수를 포함하고 있으므로 가설에서는 소프트맥스 함수를 사용할 필요가 없습니다.\n", 1399 | "\n", 1400 | "위와 동일한 x_train과 y_train을 사용합니다." 1401 | ] 1402 | }, 1403 | { 1404 | "cell_type": "code", 1405 | "metadata": { 1406 | "id": "ecdZG8fo9LjU", 1407 | "colab_type": "code", 1408 | "outputId": "94512ca9-2b68-4e52-c215-270a6b32fc13", 1409 | "colab": { 1410 | "base_uri": "https://localhost:8080/", 1411 | "height": 215 1412 | } 1413 | }, 1414 | "source": [ 1415 | "# 모델 초기화\n", 1416 | "W = torch.zeros((4, 3), requires_grad=True)\n", 1417 | "b = torch.zeros(1, requires_grad=True)\n", 1418 | "# optimizer 설정\n", 1419 | "optimizer = optim.SGD([W, b], lr=0.1)\n", 1420 | "\n", 1421 | "nb_epochs = 1000\n", 1422 | "for epoch in range(nb_epochs + 1):\n", 1423 | "\n", 1424 | " # Cost 계산\n", 1425 | " z = x_train.matmul(W) + b\n", 1426 | " cost = F.cross_entropy(z, y_train)\n", 1427 | "\n", 1428 | " # cost로 H(x) 개선\n", 1429 | " optimizer.zero_grad()\n", 1430 | " cost.backward()\n", 1431 | " optimizer.step()\n", 1432 | "\n", 1433 | " # 100번마다 로그 출력\n", 1434 | " if epoch % 100 == 0:\n", 1435 | " print('Epoch {:4d}/{} Cost: {:.6f}'.format(\n", 1436 | " epoch, nb_epochs, cost.item()\n", 1437 | " ))" 1438 | ], 1439 | "execution_count": 0, 1440 | "outputs": [ 1441 | { 1442 | "output_type": "stream", 1443 | "text": [ 1444 | "Epoch 0/1000 Cost: 1.098612\n", 1445 | "Epoch 100/1000 Cost: 0.761050\n", 1446 | "Epoch 200/1000 Cost: 0.689991\n", 1447 | "Epoch 300/1000 Cost: 0.643229\n", 1448 | "Epoch 400/1000 Cost: 0.604117\n", 1449 | "Epoch 500/1000 Cost: 0.568255\n", 1450 | "Epoch 600/1000 Cost: 0.533922\n", 1451 | "Epoch 700/1000 Cost: 0.500291\n", 1452 | "Epoch 800/1000 Cost: 0.466908\n", 1453 | "Epoch 900/1000 Cost: 0.433506\n", 1454 | "Epoch 1000/1000 Cost: 0.399962\n" 1455 | ], 1456 | "name": "stdout" 1457 | } 1458 | ] 1459 | }, 1460 | { 1461 | "cell_type": "markdown", 1462 | "metadata": { 1463 | "id": "T_PRU8_c9X2-", 1464 | "colab_type": "text" 1465 | }, 1466 | "source": [ 1467 | "### 4.4.3 소프트맥스 회귀 nn.Module로 구현하기\n", 1468 | "\n", 1469 | "이번에는 nn.Module로 소프트맥스 회귀를 구현해봅시다. 선형 회귀에서 구현에 사용했던 nn.Linear()를 사용합니다. output_dim이 1이었던 선형 회귀때와 달리 output_dim은 이제 클래스의 개수여야 합니다." 1470 | ] 1471 | }, 1472 | { 1473 | "cell_type": "code", 1474 | "metadata": { 1475 | "id": "Yr6huZQu9TgU", 1476 | "colab_type": "code", 1477 | "colab": {} 1478 | }, 1479 | "source": [ 1480 | "# 모델을 선언 및 초기화. 4개의 특성을 가지고 3개의 클래스로 분류. input_dim=4, output_dim=3.\n", 1481 | "model = nn.Linear(4, 3)" 1482 | ], 1483 | "execution_count": 0, 1484 | "outputs": [] 1485 | }, 1486 | { 1487 | "cell_type": "markdown", 1488 | "metadata": { 1489 | "id": "LWg9vbca9d6Z", 1490 | "colab_type": "text" 1491 | }, 1492 | "source": [ 1493 | "아래에서 F.cross_entropy()를 사용할 것이므로 따로 소프트맥스 함수를 가설에 정의하지 않습니다." 1494 | ] 1495 | }, 1496 | { 1497 | "cell_type": "code", 1498 | "metadata": { 1499 | "id": "-xFLtVqA9c0V", 1500 | "colab_type": "code", 1501 | "outputId": "86bdc35d-438f-430e-eaff-ea39a114c604", 1502 | "colab": { 1503 | "base_uri": "https://localhost:8080/", 1504 | "height": 215 1505 | } 1506 | }, 1507 | "source": [ 1508 | "# optimizer 설정\n", 1509 | "optimizer = optim.SGD(model.parameters(), lr=0.1)\n", 1510 | "\n", 1511 | "nb_epochs = 1000\n", 1512 | "for epoch in range(nb_epochs + 1):\n", 1513 | "\n", 1514 | " # H(x) 계산\n", 1515 | " prediction = model(x_train)\n", 1516 | "\n", 1517 | " # cost 계산\n", 1518 | " cost = F.cross_entropy(prediction, y_train)\n", 1519 | "\n", 1520 | " # cost로 H(x) 개선\n", 1521 | " optimizer.zero_grad()\n", 1522 | " cost.backward()\n", 1523 | " optimizer.step()\n", 1524 | "\n", 1525 | " # 20번마다 로그 출력\n", 1526 | " if epoch % 100 == 0:\n", 1527 | " print('Epoch {:4d}/{} Cost: {:.6f}'.format(\n", 1528 | " epoch, nb_epochs, cost.item()\n", 1529 | " ))" 1530 | ], 1531 | "execution_count": 0, 1532 | "outputs": [ 1533 | { 1534 | "output_type": "stream", 1535 | "text": [ 1536 | "Epoch 0/1000 Cost: 1.849513\n", 1537 | "Epoch 100/1000 Cost: 0.689894\n", 1538 | "Epoch 200/1000 Cost: 0.609258\n", 1539 | "Epoch 300/1000 Cost: 0.551218\n", 1540 | "Epoch 400/1000 Cost: 0.500141\n", 1541 | "Epoch 500/1000 Cost: 0.451947\n", 1542 | "Epoch 600/1000 Cost: 0.405051\n", 1543 | "Epoch 700/1000 Cost: 0.358733\n", 1544 | "Epoch 800/1000 Cost: 0.312912\n", 1545 | "Epoch 900/1000 Cost: 0.269522\n", 1546 | "Epoch 1000/1000 Cost: 0.241922\n" 1547 | ], 1548 | "name": "stdout" 1549 | } 1550 | ] 1551 | }, 1552 | { 1553 | "cell_type": "markdown", 1554 | "metadata": { 1555 | "id": "e5Kybhfc9g_G", 1556 | "colab_type": "text" 1557 | }, 1558 | "source": [ 1559 | "### 4.4.4 소프트맥스 회귀 클래스로 구현하기\n", 1560 | "\n", 1561 | "이제 소프트맥스 회귀를 nn.Module을 상속받은 클래스로 구현해봅시다." 1562 | ] 1563 | }, 1564 | { 1565 | "cell_type": "code", 1566 | "metadata": { 1567 | "id": "KduI8Bsy9frZ", 1568 | "colab_type": "code", 1569 | "colab": {} 1570 | }, 1571 | "source": [ 1572 | "class SoftmaxClassifierModel(nn.Module):\n", 1573 | " def __init__(self):\n", 1574 | " super().__init__()\n", 1575 | " self.linear = nn.Linear(4, 3) # Output이 3!\n", 1576 | "\n", 1577 | " def forward(self, x):\n", 1578 | " return self.linear(x)" 1579 | ], 1580 | "execution_count": 0, 1581 | "outputs": [] 1582 | }, 1583 | { 1584 | "cell_type": "code", 1585 | "metadata": { 1586 | "id": "pYk6c38F9lEM", 1587 | "colab_type": "code", 1588 | "colab": {} 1589 | }, 1590 | "source": [ 1591 | "model = SoftmaxClassifierModel()" 1592 | ], 1593 | "execution_count": 0, 1594 | "outputs": [] 1595 | }, 1596 | { 1597 | "cell_type": "code", 1598 | "metadata": { 1599 | "id": "vuISUBPo9mUm", 1600 | "colab_type": "code", 1601 | "outputId": "3a00517d-6ff4-49ed-9406-5903c61fae56", 1602 | "colab": { 1603 | "base_uri": "https://localhost:8080/", 1604 | "height": 215 1605 | } 1606 | }, 1607 | "source": [ 1608 | "# optimizer 설정\n", 1609 | "optimizer = optim.SGD(model.parameters(), lr=0.1)\n", 1610 | "\n", 1611 | "nb_epochs = 1000\n", 1612 | "for epoch in range(nb_epochs + 1):\n", 1613 | "\n", 1614 | " # H(x) 계산\n", 1615 | " prediction = model(x_train)\n", 1616 | "\n", 1617 | " # cost 계산\n", 1618 | " cost = F.cross_entropy(prediction, y_train)\n", 1619 | "\n", 1620 | " # cost로 H(x) 개선\n", 1621 | " optimizer.zero_grad()\n", 1622 | " cost.backward()\n", 1623 | " optimizer.step()\n", 1624 | "\n", 1625 | " # 20번마다 로그 출력\n", 1626 | " if epoch % 100 == 0:\n", 1627 | " print('Epoch {:4d}/{} Cost: {:.6f}'.format(\n", 1628 | " epoch, nb_epochs, cost.item()\n", 1629 | " ))" 1630 | ], 1631 | "execution_count": 0, 1632 | "outputs": [ 1633 | { 1634 | "output_type": "stream", 1635 | "text": [ 1636 | "Epoch 0/1000 Cost: 1.845720\n", 1637 | "Epoch 100/1000 Cost: 0.647150\n", 1638 | "Epoch 200/1000 Cost: 0.568868\n", 1639 | "Epoch 300/1000 Cost: 0.515699\n", 1640 | "Epoch 400/1000 Cost: 0.471727\n", 1641 | "Epoch 500/1000 Cost: 0.432486\n", 1642 | "Epoch 600/1000 Cost: 0.395879\n", 1643 | "Epoch 700/1000 Cost: 0.360506\n", 1644 | "Epoch 800/1000 Cost: 0.325227\n", 1645 | "Epoch 900/1000 Cost: 0.289217\n", 1646 | "Epoch 1000/1000 Cost: 0.254086\n" 1647 | ], 1648 | "name": "stdout" 1649 | } 1650 | ] 1651 | }, 1652 | { 1653 | "cell_type": "markdown", 1654 | "metadata": { 1655 | "id": "XLtaEXxE9qKW", 1656 | "colab_type": "text" 1657 | }, 1658 | "source": [ 1659 | "소프트맥스 회귀의 레이블은 왜 원-핫 인코딩으로 사용하지 않는가? https://stackoverflow.com/questions/55549843/pytorch-doesnt-support-one-hot-vector" 1660 | ] 1661 | }, 1662 | { 1663 | "cell_type": "markdown", 1664 | "metadata": { 1665 | "id": "9DXUq1Eh9w6u", 1666 | "colab_type": "text" 1667 | }, 1668 | "source": [ 1669 | "## 4.5 소프트맥스 회귀로 MNIST 데이터 분류하기\n", 1670 | "\n", 1671 | "이번 챕터에서는 MNIST 데이터에 대해서 이해하고, 파이토치(PyTorch)로 소프트맥스 회귀를 구현하여 MNIST 데이터를 분류하는 실습을 진행해봅시다.\n", 1672 | "\n", 1673 | "MNIST 데이터는 아래의 링크에 공개되어져 있습니다.\n", 1674 | "링크 : http://yann.lecun.com/exdb/mnist" 1675 | ] 1676 | }, 1677 | { 1678 | "cell_type": "markdown", 1679 | "metadata": { 1680 | "id": "Z1F8zdif91WW", 1681 | "colab_type": "text" 1682 | }, 1683 | "source": [ 1684 | "### 4.5.1 MNIST 데이터 이해하기\n", 1685 | "\n", 1686 | "![대체 텍스트](https://wikidocs.net/images/page/60324/mnist.png)\n", 1687 | "\n", 1688 | "MNIST는 숫자 0부터 9까지의 이미지로 구성된 손글씨 데이터셋입니다. 이 데이터는 과거에 우체국에서 편지의 우편 번호를 인식하기 위해서 만들어진 훈련 데이터입니다. 총 60,000개의 훈련 데이터와 레이블, 총 10,000개의 테스트 데이터와 레이블로 구성되어져 있습니다. 레이블은 0부터 9까지 총 10개입니다. 이 예제는 머신 러닝을 처음 배울 때 접하게 되는 가장 기본적인 예제이기도 합니다.\n", 1689 | "\n", 1690 | "MNIST 문제는 손글씨로 적힌 숫자 이미지가 들어오면, 그 이미지가 무슨 숫자인지 맞추는 문제입니다. 예를 들어 숫자 5의 이미지가 입력으로 들어오면 이게 숫자 5다! 라는 것을 맞춰야 합니다. 이 문제는 사람에게는 굉장히 간단하지만 기계에게는 그렇지가 않습니다.\n", 1691 | "\n", 1692 | "우선 MNIST 문제를 더 자세히 보겠습니다. 각각의 이미지는 아래와 같이 28 픽셀 × 28 픽셀의 이미지입니다.\n", 1693 | "\n", 1694 | "![대체 텍스트](https://wikidocs.net/images/page/60324/mnist_SVbcYYG.png)\n", 1695 | "\n", 1696 | "이 문제를 풀기 위해 여기서는 28 픽셀 × 28 픽셀 = 784 픽셀이므로, 각 이미지를 총 784의 원소를 가진 벡터로 만들어줄겁니다. 이렇게 되면 총 784개의 특성을 가진 샘플이 되는데, 이는 앞서 우리가 풀었던 그 어떤 문제들보다 특성이 굉장히 많은 샘플입니다.\n", 1697 | "\n", 1698 | "![대체 텍스트](https://wikidocs.net/images/page/60324/%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C.png)\n", 1699 | "\n", 1700 | "784차원의 벡터로 만드는 코드를 미리보기로 보면 아래와 같습니다." 1701 | ] 1702 | }, 1703 | { 1704 | "cell_type": "code", 1705 | "metadata": { 1706 | "id": "VcP86WrN-cTA", 1707 | "colab_type": "code", 1708 | "colab": {} 1709 | }, 1710 | "source": [ 1711 | "import torch\n", 1712 | "import torchvision.datasets as dsets\n", 1713 | "import torchvision.transforms as transforms\n", 1714 | "from torch.utils.data import DataLoader\n", 1715 | "import torch.nn as nn\n", 1716 | "import matplotlib.pyplot as plt\n", 1717 | "import random" 1718 | ], 1719 | "execution_count": 0, 1720 | "outputs": [] 1721 | }, 1722 | { 1723 | "cell_type": "code", 1724 | "metadata": { 1725 | "id": "cZPq91qL-gO-", 1726 | "colab_type": "code", 1727 | "colab": {} 1728 | }, 1729 | "source": [ 1730 | "# MNIST dataset\n", 1731 | "# 자세한건 후술\n", 1732 | "mnist_train = dsets.MNIST(root='MNIST_data/',\n", 1733 | " train=True,\n", 1734 | " transform=transforms.ToTensor(),\n", 1735 | " download=True)\n", 1736 | "\n", 1737 | "mnist_test = dsets.MNIST(root='MNIST_data/',\n", 1738 | " train=False,\n", 1739 | " transform=transforms.ToTensor(),\n", 1740 | " download=True)" 1741 | ], 1742 | "execution_count": 0, 1743 | "outputs": [] 1744 | }, 1745 | { 1746 | "cell_type": "code", 1747 | "metadata": { 1748 | "id": "tVGv7MaF-Fnx", 1749 | "colab_type": "code", 1750 | "outputId": "51968730-0ccd-4819-902d-561f58a0cd18", 1751 | "colab": { 1752 | "base_uri": "https://localhost:8080/", 1753 | "height": 53 1754 | } 1755 | }, 1756 | "source": [ 1757 | "X,y = mnist_train[0]\n", 1758 | "print('X size = ',X.size())\n", 1759 | "print('입력 이미지를 [batch_size × 784]의 크기로 reshape = ',X.view(-1, 28*28).size())" 1760 | ], 1761 | "execution_count": 0, 1762 | "outputs": [ 1763 | { 1764 | "output_type": "stream", 1765 | "text": [ 1766 | "X size = torch.Size([1, 28, 28])\n", 1767 | "입력 이미지를 [batch_size × 784]의 크기로 reshape = torch.Size([1, 784])\n" 1768 | ], 1769 | "name": "stdout" 1770 | } 1771 | ] 1772 | }, 1773 | { 1774 | "cell_type": "markdown", 1775 | "metadata": { 1776 | "id": "ryq0StXy_RYH", 1777 | "colab_type": "text" 1778 | }, 1779 | "source": [ 1780 | "### 4.5.2 토치비전(torchvision) 소개하기\n", 1781 | "\n", 1782 | "본격적인 실습에 들어가기에 앞서 토치비전(torchvision)이라는 도구를 설명하겠습니다. torchvision은 유명한 데이터셋들, 이미 구현되어져 있는 유명한 모델들, 일반적인 이미지 전처리 도구들을 포함하고 있는 패키지입니다. 아래의 링크는 torchvision에 어떤 데이터셋들(datasets)과 모델들(models) 그리고 어떤 전처리 방법들(transforms)을 제공하고 있는지 보여줍니다.\n", 1783 | "\n", 1784 | "링크 : https://pytorch.org/docs/stable/torchvision/index.html\n", 1785 | "\n", 1786 | "자연어 처리를 위해서는 토치텍스트(torchtext)라는 패키지가 있습니다." 1787 | ] 1788 | }, 1789 | { 1790 | "cell_type": "markdown", 1791 | "metadata": { 1792 | "id": "3yGecyVi_XTF", 1793 | "colab_type": "text" 1794 | }, 1795 | "source": [ 1796 | "### 4.5.3 분류기 구현을 위한 사전 설정\n", 1797 | "\n", 1798 | "현재 환경에서 GPU 연산이 가능하다면 GPU 연산을 하고, 그렇지 않다면 CPU 연산을 하도록 합니다" 1799 | ] 1800 | }, 1801 | { 1802 | "cell_type": "code", 1803 | "metadata": { 1804 | "id": "nCBzu7iL_CNm", 1805 | "colab_type": "code", 1806 | "outputId": "da97faee-52c3-4f9b-b084-f35a1a287f2f", 1807 | "colab": { 1808 | "base_uri": "https://localhost:8080/", 1809 | "height": 35 1810 | } 1811 | }, 1812 | "source": [ 1813 | "USE_CUDA = torch.cuda.is_available() # GPU를 사용가능하면 True, 아니라면 False를 리턴\n", 1814 | "device = torch.device(\"cuda\" if USE_CUDA else \"cpu\") # GPU 사용 가능하면 사용하고 아니면 CPU 사용\n", 1815 | "print(\"다음 기기로 학습합니다:\", device)" 1816 | ], 1817 | "execution_count": 0, 1818 | "outputs": [ 1819 | { 1820 | "output_type": "stream", 1821 | "text": [ 1822 | "다음 기기로 학습합니다: cpu\n" 1823 | ], 1824 | "name": "stdout" 1825 | } 1826 | ] 1827 | }, 1828 | { 1829 | "cell_type": "markdown", 1830 | "metadata": { 1831 | "id": "GCUBKg1d_huv", 1832 | "colab_type": "text" 1833 | }, 1834 | "source": [ 1835 | "구글의 Colab에서 '런타임 > 런타임 유형 변경 > 하드웨어 가속기 > GPU'를 선택하면 USE_CUDA의 값이 True가 되면서 '다음 기기로 학습합니다: cuda'라는 출력이 나옵니다. 즉, GPU로 연산하겠다는 의미입니다. 반면에 '하드웨어 가속기 > None'을 선택하면 USE_CUDA의 값이 False가 되면서 '다음 기기로 학습합니다: cpu'라는 출력이 나옵니다. 즉, CPU로 연산하겠다는 의미입니다.\n", 1836 | "\n", 1837 | "위의 방법은 앞으로 자주 쓰이게되므로 기억해둡시다.\n", 1838 | "\n", 1839 | "랜덤 시드를 고정합니다." 1840 | ] 1841 | }, 1842 | { 1843 | "cell_type": "code", 1844 | "metadata": { 1845 | "id": "b2hM4ToG_e7x", 1846 | "colab_type": "code", 1847 | "colab": {} 1848 | }, 1849 | "source": [ 1850 | "# for reproducibility\n", 1851 | "random.seed(777)\n", 1852 | "torch.manual_seed(777)\n", 1853 | "if device == 'cuda':\n", 1854 | " torch.cuda.manual_seed_all(777)" 1855 | ], 1856 | "execution_count": 0, 1857 | "outputs": [] 1858 | }, 1859 | { 1860 | "cell_type": "markdown", 1861 | "metadata": { 1862 | "id": "UoXhNdpG_lvS", 1863 | "colab_type": "text" 1864 | }, 1865 | "source": [ 1866 | "하이퍼파라미터를 변수로 둡니다." 1867 | ] 1868 | }, 1869 | { 1870 | "cell_type": "code", 1871 | "metadata": { 1872 | "id": "jRofyaVg_kds", 1873 | "colab_type": "code", 1874 | "colab": {} 1875 | }, 1876 | "source": [ 1877 | "# hyperparameters\n", 1878 | "training_epochs = 15\n", 1879 | "batch_size = 100" 1880 | ], 1881 | "execution_count": 0, 1882 | "outputs": [] 1883 | }, 1884 | { 1885 | "cell_type": "markdown", 1886 | "metadata": { 1887 | "id": "9HA1CB3O_o8Y", 1888 | "colab_type": "text" 1889 | }, 1890 | "source": [ 1891 | "### 4.5.4 MNIST 분류기 구현하기\n", 1892 | "\n", 1893 | "torchvision.datasets.dsets.MNIST를 사용하여 MNIST 데이터셋을 불러올 수 있습니다." 1894 | ] 1895 | }, 1896 | { 1897 | "cell_type": "code", 1898 | "metadata": { 1899 | "id": "rWlmWt8Y_ny7", 1900 | "colab_type": "code", 1901 | "colab": {} 1902 | }, 1903 | "source": [ 1904 | "# MNIST dataset\n", 1905 | "mnist_train = dsets.MNIST(root='MNIST_data/',\n", 1906 | " train=True,\n", 1907 | " transform=transforms.ToTensor(),\n", 1908 | " download=True)\n", 1909 | "\n", 1910 | "mnist_test = dsets.MNIST(root='MNIST_data/',\n", 1911 | " train=False,\n", 1912 | " transform=transforms.ToTensor(),\n", 1913 | " download=True)" 1914 | ], 1915 | "execution_count": 0, 1916 | "outputs": [] 1917 | }, 1918 | { 1919 | "cell_type": "markdown", 1920 | "metadata": { 1921 | "id": "ksYN8l7I_y9U", 1922 | "colab_type": "text" 1923 | }, 1924 | "source": [ 1925 | "첫번째 인자 root는 MNIST 데이터를 다운로드 받을 경로입니다. 두번째 인자 train은 인자로 True를 주면, MNIST의 훈련 데이터를 리턴받으며 False를 주면 테스트 데이터를 리턴받습니다. 세번째 인자 transform은 현재 데이터를 파이토치 텐서로 변환해줍니다. 네번째 인자 download는 해당 경로에 MNIST 데이터가 없다면 다운로드 받겠다는 의미입니다.\n", 1926 | "\n", 1927 | "이렇게 데이터를 다운로드했다면 앞서 미니 배치와 데이터로드 챕터에서 학습했던 데이터로더(DataLoader)를 사용합니다." 1928 | ] 1929 | }, 1930 | { 1931 | "cell_type": "code", 1932 | "metadata": { 1933 | "id": "wreSv3ZJ_xwf", 1934 | "colab_type": "code", 1935 | "colab": {} 1936 | }, 1937 | "source": [ 1938 | "# dataset loader\n", 1939 | "data_loader = DataLoader(dataset=mnist_train,\n", 1940 | " batch_size=batch_size, # 배치 크기는 100\n", 1941 | " shuffle=True,\n", 1942 | " drop_last=True)" 1943 | ], 1944 | "execution_count": 0, 1945 | "outputs": [] 1946 | }, 1947 | { 1948 | "cell_type": "markdown", 1949 | "metadata": { 1950 | "id": "pSs6GUW9_1_I", 1951 | "colab_type": "text" 1952 | }, 1953 | "source": [ 1954 | "이때 DataLoader에는 4개의 인자가 있습니다. 첫번째 인자인 DataLoader는 로드할 대상을 의미하며, 두번째 인자인 batch_size는 배치 크기, shuffle은 매 에포크마다 미니 배치를 셔플할 것인지의 여부, drop_last는 마지막 배치를 버릴 것인지를 의미합니다.\n", 1955 | "\n", 1956 | "drop_last를 하는 이유를 이해하기 위해서 1,000개의 데이터가 있다고 했을 때, 배치 크기가 128이라고 해봅시다. 1,000을 128로 나누면 총 7개가 나오고 나머지로 104개가 남습니다. 이때 104개를 마지막 배치로 한다고 하였을 때 128개를 충족하지 못하였으므로 104개를 그냥 버릴 수도 있습니다. 이때 마지막 배치를 버리려면 drop_last=True를 해주면 됩니다. 이는 다른 미니 배치보다 개수가 적은 마지막 배치를 경사 하강법에 사용하여 마지막 배치가 상대적으로 과대 평가되는 현상을 막아줍니다.\n", 1957 | "\n", 1958 | "이제 모델을 설계합니다. input_dim은 784이고, output_dim은 10입니다." 1959 | ] 1960 | }, 1961 | { 1962 | "cell_type": "code", 1963 | "metadata": { 1964 | "id": "OQINkA2A_0XB", 1965 | "colab_type": "code", 1966 | "colab": {} 1967 | }, 1968 | "source": [ 1969 | "# MNIST data image of shape 28 * 28 = 784\n", 1970 | "linear = nn.Linear(784, 10, bias=True).to(device)" 1971 | ], 1972 | "execution_count": 0, 1973 | "outputs": [] 1974 | }, 1975 | { 1976 | "cell_type": "markdown", 1977 | "metadata": { 1978 | "id": "I6T5wJHI_7JW", 1979 | "colab_type": "text" 1980 | }, 1981 | "source": [ 1982 | "to() 함수는 연산을 어디서 수행할지를 정합니다. to() 함수는 모델의 매개변수를 지정한 장치의 메모리로 보냅니다. CPU를 사용할 경우에는 필요가 없지만, GPU를 사용하려면 to('cuda')를 해 줄 필요가 있습니다. 아무것도 지정하지 않은 경우에는 CPU 연산이라고 보면 됩니다.\n", 1983 | "\n", 1984 | "bias는 편향 b를 사용할 것인지를 나타냅니다. 기본값은 True이므로 굳이 할 필요는 없지만 명시적으로 True를 해주었습니다.\n", 1985 | "이제 비용 함수와 옵티마이저를 정의합니다." 1986 | ] 1987 | }, 1988 | { 1989 | "cell_type": "code", 1990 | "metadata": { 1991 | "id": "QR8LARFX_51D", 1992 | "colab_type": "code", 1993 | "colab": {} 1994 | }, 1995 | "source": [ 1996 | "# 비용 함수와 옵티마이저 정의\n", 1997 | "criterion = nn.CrossEntropyLoss().to(device) # 내부적으로 소프트맥스 함수를 포함하고 있음.\n", 1998 | "optimizer = torch.optim.SGD(linear.parameters(), lr=0.1)" 1999 | ], 2000 | "execution_count": 0, 2001 | "outputs": [] 2002 | }, 2003 | { 2004 | "cell_type": "markdown", 2005 | "metadata": { 2006 | "id": "QPApN_GZ_-3l", 2007 | "colab_type": "text" 2008 | }, 2009 | "source": [ 2010 | "앞서 소프트맥스 회귀를 배울 때는 torch.nn.functional.cross_entropy()를 사용하였으나 여기서는 torch.nn.CrossEntropyLoss()을 사용하고 있습니다. 둘 다 파이토치에서 제공하는 크로스 엔트로피 함수로 둘 다 소프트맥스 함수를 포함하고 있습니다." 2011 | ] 2012 | }, 2013 | { 2014 | "cell_type": "code", 2015 | "metadata": { 2016 | "id": "kIUfsatZ_9xs", 2017 | "colab_type": "code", 2018 | "outputId": "358eceb9-1df7-4265-9c15-f458b0dc7638", 2019 | "colab": { 2020 | "base_uri": "https://localhost:8080/", 2021 | "height": 305 2022 | } 2023 | }, 2024 | "source": [ 2025 | "for epoch in range(training_epochs): # 앞서 training_epochs의 값은 15로 지정함.\n", 2026 | " avg_cost = 0\n", 2027 | " total_batch = len(data_loader)\n", 2028 | "\n", 2029 | " for X, Y in data_loader:\n", 2030 | " # 배치 크기가 100이므로 아래의 연산에서 X는 (100, 784)의 텐서가 된다.\n", 2031 | " X = X.view(-1, 28 * 28).to(device)\n", 2032 | " # 레이블은 원-핫 인코딩이 된 상태가 아니라 0 ~ 9의 정수.\n", 2033 | " Y = Y.to(device)\n", 2034 | "\n", 2035 | " optimizer.zero_grad()\n", 2036 | " hypothesis = linear(X)\n", 2037 | " cost = criterion(hypothesis, Y)\n", 2038 | " cost.backward()\n", 2039 | " optimizer.step()\n", 2040 | "\n", 2041 | " avg_cost += cost / total_batch\n", 2042 | "\n", 2043 | " print('Epoch:', '%04d' % (epoch + 1), 'cost =', '{:.9f}'.format(avg_cost))\n", 2044 | "\n", 2045 | "print('Learning finished')" 2046 | ], 2047 | "execution_count": 0, 2048 | "outputs": [ 2049 | { 2050 | "output_type": "stream", 2051 | "text": [ 2052 | "Epoch: 0001 cost = 0.535468519\n", 2053 | "Epoch: 0002 cost = 0.359274149\n", 2054 | "Epoch: 0003 cost = 0.331187546\n", 2055 | "Epoch: 0004 cost = 0.316578060\n", 2056 | "Epoch: 0005 cost = 0.307158172\n", 2057 | "Epoch: 0006 cost = 0.300180733\n", 2058 | "Epoch: 0007 cost = 0.295130223\n", 2059 | "Epoch: 0008 cost = 0.290851504\n", 2060 | "Epoch: 0009 cost = 0.287417054\n", 2061 | "Epoch: 0010 cost = 0.284379601\n", 2062 | "Epoch: 0011 cost = 0.281825215\n", 2063 | "Epoch: 0012 cost = 0.279800683\n", 2064 | "Epoch: 0013 cost = 0.277808994\n", 2065 | "Epoch: 0014 cost = 0.276154310\n", 2066 | "Epoch: 0015 cost = 0.274440825\n", 2067 | "Learning finished\n" 2068 | ], 2069 | "name": "stdout" 2070 | } 2071 | ] 2072 | }, 2073 | { 2074 | "cell_type": "code", 2075 | "metadata": { 2076 | "id": "iNZfeIZ9AAP8", 2077 | "colab_type": "code", 2078 | "outputId": "140bbedf-82c8-4495-e428-6152916c6307", 2079 | "colab": { 2080 | "base_uri": "https://localhost:8080/", 2081 | "height": 391 2082 | } 2083 | }, 2084 | "source": [ 2085 | "# 테스트 데이터를 사용하여 모델을 테스트한다.\n", 2086 | "with torch.no_grad(): # torch.no_grad()를 하면 gradient 계산을 수행하지 않는다.\n", 2087 | " X_test = mnist_test.test_data.view(-1, 28 * 28).float().to(device)\n", 2088 | " Y_test = mnist_test.test_labels.to(device)\n", 2089 | "\n", 2090 | " prediction = linear(X_test)\n", 2091 | " correct_prediction = torch.argmax(prediction, 1) == Y_test\n", 2092 | " accuracy = correct_prediction.float().mean()\n", 2093 | " print('Accuracy:', accuracy.item())\n", 2094 | "\n", 2095 | " # MNIST 테스트 데이터에서 무작위로 하나를 뽑아서 예측을 해본다\n", 2096 | " r = random.randint(0, len(mnist_test) - 1)\n", 2097 | " X_single_data = mnist_test.test_data[r:r + 1].view(-1, 28 * 28).float().to(device)\n", 2098 | " Y_single_data = mnist_test.test_labels[r:r + 1].to(device)\n", 2099 | "\n", 2100 | " print('Label: ', Y_single_data.item())\n", 2101 | " single_prediction = linear(X_single_data)\n", 2102 | " print('Prediction: ', torch.argmax(single_prediction, 1).item())\n", 2103 | "\n", 2104 | " plt.imshow(mnist_test.test_data[r:r + 1].view(28, 28), cmap='Greys', interpolation='nearest')\n", 2105 | " plt.show()" 2106 | ], 2107 | "execution_count": 0, 2108 | "outputs": [ 2109 | { 2110 | "output_type": "stream", 2111 | "text": [ 2112 | "Accuracy: 0.8863000273704529\n", 2113 | "Label: 8\n", 2114 | "Prediction: 3\n" 2115 | ], 2116 | "name": "stdout" 2117 | }, 2118 | { 2119 | "output_type": "stream", 2120 | "text": [ 2121 | "/usr/local/lib/python3.6/dist-packages/torchvision/datasets/mnist.py:60: UserWarning: test_data has been renamed data\n", 2122 | " warnings.warn(\"test_data has been renamed data\")\n", 2123 | "/usr/local/lib/python3.6/dist-packages/torchvision/datasets/mnist.py:50: UserWarning: test_labels has been renamed targets\n", 2124 | " warnings.warn(\"test_labels has been renamed targets\")\n" 2125 | ], 2126 | "name": "stderr" 2127 | }, 2128 | { 2129 | "output_type": "display_data", 2130 | "data": { 2131 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPsAAAD4CAYAAAAq5pAIAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjEsIGh0\ndHA6Ly9tYXRwbG90bGliLm9yZy+j8jraAAAN6ElEQVR4nO3df4xU9bnH8c9ztcSErsrCisSaCxb+\n0DRc2qwbCKbhpt5GNAYao5ZoQ5NNqD9I2qQYtcYU/9AYcrHR7E0NVcLeK5fahBL4gyiWNDGYSFgJ\nV1Bzr79QQGAHjbL1F93l6R976F1xz3fWOWfmTH3er2QyM+eZs+fJhA9n5nznnK+5uwB8/f1T1Q0A\naA3CDgRB2IEgCDsQBGEHgji3lRubNm2az5w5s5WbBEI5ePCgTpw4YePVCoXdzK6R9KikcyQ94e4P\np14/c+ZMDQwMFNkkgITu7u7cWsMf483sHEn/IWmxpCskLTOzKxr9ewCaq8h39h5Jb7j7W+5+StLv\nJS0ppy0AZSsS9kskHRrz/HC27AvMbIWZDZjZQK1WK7A5AEU0/Wi8u69z92537+7q6mr25gDkKBL2\nI5IuHfP8W9kyAG2oSNj3SJpjZrPMbJKkH0vaVk5bAMrW8NCbuw+b2UpJz2p06G29u79SWmcASlVo\nnN3dt0vaXlIvAJqIn8sCQRB2IAjCDgRB2IEgCDsQBGEHgiDsQBCEHQiCsANBEHYgCMIOBEHYgSAI\nOxAEYQeCIOxAEIQdCIKwA0EQdiAIwg4EQdiBIAg7EARhB4Ig7EAQhB0IgrADQRB2IAjCDgRB2IEg\nCDsQRKFZXIF2durUqdzapEmTWthJeygUdjM7KGlI0oikYXfvLqMpAOUrY8/+r+5+ooS/A6CJ+M4O\nBFE07C5ph5m9ZGYrxnuBma0wswEzG6jVagU3B6BRRcN+lbt/T9JiSXea2ffPfoG7r3P3bnfv7urq\nKrg5AI0qFHZ3P5LdD0raIqmnjKYAlK/hsJvZZDPrOPNY0g8lHSirMQDlKnI0frqkLWZ25u/8t7s/\nU0pXgKShoaFkva+vL1nfvHlzbu2yyy5LrtvR0ZGsP/bYY8n65MmTk/UqNBx2d39L0r+U2AuAJmLo\nDQiCsANBEHYgCMIOBEHYgSA4xRVNtX///tza4sWLk+seO3YsWR8ZGUnWs2Hhce3duze5rrsn6/39\n/cn68PBwsl4F9uxAEIQdCIKwA0EQdiAIwg4EQdiBIAg7EATj7EiqN9787rvvJusLFizIraXGwSXp\n9ttvT9brnaY6d+7c3NrHH3+cXPeGG25I1h9//PFkvR2xZweCIOxAEIQdCIKwA0EQdiAIwg4EQdiB\nIBhnR9KePXuS9fnz5yfrF154YW5t9+7dyXXnzJmTrNdz+vTp3NqsWbOS686ePTtZ7+3tbainKrFn\nB4Ig7EAQhB0IgrADQRB2IAjCDgRB2IEgGGcP7r333kvWU+ejS1JnZ2eyvnr16txa0XH0kydPJuv3\n3Xdfbu3QoUPJdS+44IJk/f3330/Wp06dmqxXoe6e3czWm9mgmR0Ys6zTzJ4zs9ez+ynNbRNAURP5\nGL9B0jVnLbtH0k53nyNpZ/YcQBurG3Z3f17SB2ctXiLpzPw3/ZKWltwXgJI1eoBuursfzR4fkzQ9\n74VmtsLMBsxsoFarNbg5AEUVPhrvo1ckzL0qobuvc/dud+/u6uoqujkADWo07MfNbIYkZfeD5bUE\noBkaDfs2Scuzx8slbS2nHQDNUnec3cw2SVokaZqZHZb0a0kPS/qDmfVKekfSTc1sEs2TOudbqn/d\n+FWrViXrd9xxR26t3rXbU+tK0rPPPpusDw7mf+Ds6elJrrtmzZpkvaOjI1lvR3XD7u7Lcko/KLkX\nAE3Ez2WBIAg7EARhB4Ig7EAQhB0IglNcUUhfX1+ynroU9ZYtWwpt+8orr0zWn3rqqdza1VdfXWjb\n/4jYswNBEHYgCMIOBEHYgSAIOxAEYQeCIOxAEIyzB1fvksr11LsU9TPPPJNbW7RoUXLd1Di5JF10\n0UXJ+rnn8s97LPbsQBCEHQiCsANBEHYgCMIOBEHYgSAIOxAEA5FfA5988klu7dFHH02ue//995fd\nzhekpmy+6667mrptfBF7diAIwg4EQdiBIAg7EARhB4Ig7EAQhB0IgnH2NvD2228n61u3bk3WH3jg\ngdzaRx99lFz3lltuSdZvvPHGZH3lypXJ+kMPPZRb6+3tTa7b2dmZrOOrqbtnN7P1ZjZoZgfGLFtt\nZkfMbF92u7a5bQIoaiIf4zdIumac5b9x93nZbXu5bQEoW92wu/vzkj5oQS8AmqjIAbqVZvZy9jF/\nSt6LzGyFmQ2Y2UCtViuwOQBFNBr230r6tqR5ko5KWpv3Qndf5+7d7t7d1dXV4OYAFNVQ2N39uLuP\nuPtpSb+T1FNuWwDK1lDYzWzGmKc/knQg77UA2kPdcXYz2yRpkaRpZnZY0q8lLTKzeZJc0kFJP2ti\nj21vaGgoWV+1alWyvmHDhmT94osvTtbXrFmTW7v11luT65533nnJupkl6/W+mi1cuDC3Vu99Y5y9\nXHXD7u7Lxln8ZBN6AdBE/FwWCIKwA0EQdiAIwg4EQdiBIDjFNfP5558n67fddltuLTUtsSR99tln\nyfr69euT9aVLlybrkydPTtaLGB4eTta3b+ccqH8U7NmBIAg7EARhB4Ig7EAQhB0IgrADQRB2IIgw\n4+yffvppsl5vrLu/vz+3tmzZeCcG/r/UpZ4lafbs2cl6M9X7fcGmTZuS9QcffDBZP//883Nrzfx9\nAL6MPTsQBGEHgiDsQBCEHQiCsANBEHYgCMIOBBFmnP3uu+9O1jdu3Jis79q1K7e2YMGC5Lr1Lsdc\nz4kTJ5L1N998M7f2wgsvJNd95JFHkvVjx44l6/WmdH7iiSdyax0dHcl1US727EAQhB0IgrADQRB2\nIAjCDgRB2IEgCDsQRJhx9r6+vmR96tSpyfqHH36YW7v++uuT646MjCTr9ezYsSNZd/fc2uWXX55c\nd/ny5cn6zTffnKzPnTs3WUf7qLtnN7NLzezPZvaqmb1iZj/Plnea2XNm9np2P6X57QJo1EQ+xg9L\n+qW7XyFpvqQ7zewKSfdI2unucyTtzJ4DaFN1w+7uR919b/Z4SNJrki6RtETSmWs19UtKz1EEoFJf\n6QCdmc2U9F1JuyVNd/ejWemYpOk566wwswEzG6jVagVaBVDEhMNuZt+UtFnSL9z95Niajx4hGvco\nkbuvc/dud+/u6uoq1CyAxk0o7Gb2DY0GfaO7/zFbfNzMZmT1GZIGm9MigDLUHXqz0fMzn5T0mruP\nPR9ym6Tlkh7O7rc2pcOSvPjii8n62rVrk/XUpaSLXhL5uuuuS9bvvffeZH3SpEm5tfnz5zfUE75+\nJjLOvlDSTyTtN7N92bJfaTTkfzCzXknvSLqpOS0CKEPdsLv7Lkl5V1/4QbntAGgWfi4LBEHYgSAI\nOxAEYQeCIOxAEGFOce3p6UnWn3766RZ1AlSDPTsQBGEHgiDsQBCEHQiCsANBEHYgCMIOBEHYgSAI\nOxAEYQeCIOxAEIQdCIKwA0EQdiAIwg4EQdiBIAg7EARhB4Ig7EAQhB0IgrADQRB2IAjCDgRRN+xm\ndqmZ/dnMXjWzV8zs59ny1WZ2xMz2Zbdrm98ugEZNZJKIYUm/dPe9ZtYh6SUzey6r/cbd/7157QEo\ny0TmZz8q6Wj2eMjMXpN0SbMbA1Cur/Sd3cxmSvqupN3ZopVm9rKZrTezKTnrrDCzATMbqNVqhZoF\n0LgJh93Mvilps6RfuPtJSb+V9G1J8zS651873nruvs7du929u6urq4SWATRiQmE3s29oNOgb3f2P\nkuTux919xN1PS/qdpPTMiQAqNZGj8SbpSUmvufsjY5bPGPOyH0k6UH57AMoykaPxCyX9RNJ+M9uX\nLfuVpGVmNk+SSzoo6WdN6RBAKSZyNH6XJBuntL38dgA0C7+gA4Ig7EAQhB0IgrADQRB2IAjCDgRB\n2IEgCDsQBGEHgiDsQBCEHQiCsANBEHYgCMIOBGHu3rqNmdUkvTNm0TRJJ1rWwFfTrr21a18SvTWq\nzN7+2d3Hvf5bS8P+pY2bDbh7d2UNJLRrb+3al0RvjWpVb3yMB4Ig7EAQVYd9XcXbT2nX3tq1L4ne\nGtWS3ir9zg6gdareswNoEcIOBFFJ2M3sGjP7XzN7w8zuqaKHPGZ20Mz2Z9NQD1Tcy3ozGzSzA2OW\ndZrZc2b2enY/7hx7FfXWFtN4J6YZr/S9q3r685Z/ZzezcyT9n6R/k3RY0h5Jy9z91ZY2ksPMDkrq\ndvfKf4BhZt+X9BdJ/+nu38mWrZH0gbs/nP1HOcXd726T3lZL+kvV03hnsxXNGDvNuKSlkn6qCt+7\nRF83qQXvWxV79h5Jb7j7W+5+StLvJS2poI+25+7PS/rgrMVLJPVnj/s1+o+l5XJ6awvuftTd92aP\nhySdmWa80vcu0VdLVBH2SyQdGvP8sNprvneXtMPMXjKzFVU3M47p7n40e3xM0vQqmxlH3Wm8W+ms\nacbb5r1rZPrzojhA92VXufv3JC2WdGf2cbUt+eh3sHYaO53QNN6tMs40439X5XvX6PTnRVUR9iOS\nLh3z/FvZsrbg7key+0FJW9R+U1EfPzODbnY/WHE/f9dO03iPN8242uC9q3L68yrCvkfSHDObZWaT\nJP1Y0rYK+vgSM5ucHTiRmU2W9EO131TU2yQtzx4vl7S1wl6+oF2m8c6bZlwVv3eVT3/u7i2/SbpW\no0fk35R0XxU95PR1maT/yW6vVN2bpE0a/Vj3V40e2+iVNFXSTkmvS/qTpM426u2/JO2X9LJGgzWj\not6u0uhH9Jcl7ctu11b93iX6asn7xs9lgSA4QAcEQdiBIAg7EARhB4Ig7EAQhB0IgrADQfwNSYoy\nhpT0G8IAAAAASUVORK5CYII=\n", 2132 | "text/plain": [ 2133 | "
" 2134 | ] 2135 | }, 2136 | "metadata": { 2137 | "tags": [] 2138 | } 2139 | } 2140 | ] 2141 | }, 2142 | { 2143 | "cell_type": "code", 2144 | "metadata": { 2145 | "id": "lVd3jq2pAEv1", 2146 | "colab_type": "code", 2147 | "colab": {} 2148 | }, 2149 | "source": [ 2150 | "" 2151 | ], 2152 | "execution_count": 0, 2153 | "outputs": [] 2154 | } 2155 | ] 2156 | } --------------------------------------------------------------------------------