├── 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 | ""
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 | "
"
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 | "\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 | "\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 | "\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 | "\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 | "
"
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 | "\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 | "\n",
87 | "\n",
88 | "RNN을 표현할 때는 일반적으로 위의 그림에서 좌측과 같이 화살표로 사이클을 그려서 재귀 형태로 표현하기도 하지만, 우측과 같이 사이클을 그리는 화살표 대신 여러 시점으로 펼쳐서 표현하기도 합니다. 두 그림은 동일한 그림으로 단지 사이클을 그리는 화살표를 사용하여 표현하였느냐, 시점의 흐름에 따라서 표현하였느냐의 차이일 뿐 둘 다 동일한 RNN을 표현하고 있습니다.\n",
89 | "\n",
90 | "피드 포워드 신경망에서는 뉴런이라는 단위를 사용했지만, RNN에서는 뉴런이라는 단위보다는 입력층과 출력층에서는 각각 입력 벡터와 출력 벡터, 은닉층에서는 은닉 상태라는 표현을 주로 사용합니다. 그래서 사실 위의 그림에서 회색과 초록색으로 표현한 각 네모들은 기본적으로 벡터 단위를 가정하고 있습니다. 피드 포워드 신경망과의 차이를 비교하기 위해서 RNN을 뉴런 단위로 시각화해보겠습니다.\n",
91 | "\n",
92 | "\n",
93 | "\n",
94 | "위의 그림은 입력 벡터의 차원이 4, 은닉 상태의 크기가 2, 출력층의 출력 벡터의 차원이 2인 RNN이 시점이 2일 때의 모습을 보여줍니다. 다시 말해 뉴런 단위로 해석하면 입력층의 뉴런 수는 4, 은닉층의 뉴런 수는 2, 출력층의 뉴런 수는 2입니다.\n",
95 | "\n",
96 | "\n",
97 | "\n",
98 | "RNN은 입력과 출력의 길이를 다르게 설계 할 수 있으므로 다양한 용도로 사용할 수 있습니다. 위 그림은 입력과 출력의 길이에 따라서 달라지는 RNN의 다양한 형태를 보여줍니다. 위 구조가 자연어 처리에서 어떻게 사용될 수 있는지 예를 들어봅시다. RNN 셀의 각 시점 별 입, 출력의 단위는 사용자가 정의하기 나름이지만 가장 보편적인 단위는 '단어 벡터'입니다.\n",
99 | "\n",
100 | "예를 들어 하나의 입력에 대해서 여러개의 출력(one-to-many)의 모델은 하나의 이미지 입력에 대해서 사진의 제목을 출력하는 이미지 캡셔닝(Image Captioning) 작업에 사용할 수 있습니다. 사진의 제목은 단어들의 나열이므로 시퀀스 출력입니다.\n",
101 | "\n",
102 | "\n",
103 | "\n",
104 | "또한 단어 시퀀스에 대해서 하나의 출력(many-to-one)을 하는 모델은 입력 문서가 긍정적인지 부정적인지를 판별하는 감성 분류(sentiment classification), 또는 메일이 정상 메일인지 스팸 메일인지 판별하는 스팸 메일 분류(spam detection)에 사용할 수 있습니다. 위 그림은 RNN으로 스팸 메일을 분류할 때의 아키텍처를 보여줍니다.\n",
105 | "\n",
106 | "\n",
107 | "\n",
108 | "다 대 다(many-to-many)의 모델의 경우에는 입력 문장으로 부터 대답 문장을 출력하는 챗봇과 입력 문장으로부터 번역된 문장을 출력하는 번역기, 개체명 인식이나 품사 태깅과 같은 작업이 속합니다. 위 그림은 개체명 인식을 수행할 때의 RNN 아키텍처를 보여줍니다.\n",
109 | "\n",
110 | "이제 RNN에 대한 수식을 정의해보겠습니다.\n",
111 | "\n",
112 | "\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 | "\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 | "\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 | "\n",
749 | "\n",
750 | "양방향 RNN은 하나의 출력값을 예측하기 위해 기본적으로 두 개의 메모리 셀을 사용합니다. 첫번째 메모리 셀은 앞에서 배운 것처럼 앞 시점의 은닉 상태(Forward States)를 전달받아 현재의 은닉 상태를 계산합니다. 위의 그림에서는 주황색 메모리 셀에 해당됩니다. 두번째 메모리 셀은 앞에서 배운 것과는 다릅니다. 앞 시점의 은닉 상태가 아니라 뒤 시점의 은닉 상태(Backward States)를 전달 받아 현재의 은닉 상태를 계산합니다. 위의 그림에서는 초록색 메모리 셀에 해당됩니다. 그리고 이 두 개의 값 모두가 출력층에서 출력값을 예측하기 위해 사용됩니다.\n",
751 | "\n",
752 | "물론, 양방향 RNN도 다수의 은닉층을 가질 수 있습니다. 아래의 그림은 양방향 순환 신경망에서 은닉층이 1개 더 추가되어 은닉층이 2개인 깊은(deep) 양방향 순환 신경망의 모습을 보여줍니다.\n",
753 | "\n",
754 | "\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 | "\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 | "\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 | "\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 | "\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 | "\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 | "\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 | "\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 | "\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 | "
"
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", 244 | " | mean radius | \n", 245 | "mean texture | \n", 246 | "mean perimeter | \n", 247 | "mean area | \n", 248 | "mean smoothness | \n", 249 | "mean compactness | \n", 250 | "mean concavity | \n", 251 | "mean concave points | \n", 252 | "mean symmetry | \n", 253 | "mean fractal dimension | \n", 254 | "radius error | \n", 255 | "texture error | \n", 256 | "perimeter error | \n", 257 | "area error | \n", 258 | "smoothness error | \n", 259 | "compactness error | \n", 260 | "concavity error | \n", 261 | "concave points error | \n", 262 | "symmetry error | \n", 263 | "fractal dimension error | \n", 264 | "worst radius | \n", 265 | "worst texture | \n", 266 | "worst perimeter | \n", 267 | "worst area | \n", 268 | "worst smoothness | \n", 269 | "worst compactness | \n", 270 | "worst concavity | \n", 271 | "worst concave points | \n", 272 | "worst symmetry | \n", 273 | "worst fractal dimension | \n", 274 | "
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | \n", 279 | "17.99 | \n", 280 | "10.38 | \n", 281 | "122.80 | \n", 282 | "1001.0 | \n", 283 | "0.11840 | \n", 284 | "0.27760 | \n", 285 | "0.30010 | \n", 286 | "0.14710 | \n", 287 | "0.2419 | \n", 288 | "0.07871 | \n", 289 | "1.0950 | \n", 290 | "0.9053 | \n", 291 | "8.589 | \n", 292 | "153.40 | \n", 293 | "0.006399 | \n", 294 | "0.04904 | \n", 295 | "0.05373 | \n", 296 | "0.01587 | \n", 297 | "0.03003 | \n", 298 | "0.006193 | \n", 299 | "25.380 | \n", 300 | "17.33 | \n", 301 | "184.60 | \n", 302 | "2019.0 | \n", 303 | "0.16220 | \n", 304 | "0.66560 | \n", 305 | "0.7119 | \n", 306 | "0.2654 | \n", 307 | "0.4601 | \n", 308 | "0.11890 | \n", 309 | "
| 1 | \n", 312 | "20.57 | \n", 313 | "17.77 | \n", 314 | "132.90 | \n", 315 | "1326.0 | \n", 316 | "0.08474 | \n", 317 | "0.07864 | \n", 318 | "0.08690 | \n", 319 | "0.07017 | \n", 320 | "0.1812 | \n", 321 | "0.05667 | \n", 322 | "0.5435 | \n", 323 | "0.7339 | \n", 324 | "3.398 | \n", 325 | "74.08 | \n", 326 | "0.005225 | \n", 327 | "0.01308 | \n", 328 | "0.01860 | \n", 329 | "0.01340 | \n", 330 | "0.01389 | \n", 331 | "0.003532 | \n", 332 | "24.990 | \n", 333 | "23.41 | \n", 334 | "158.80 | \n", 335 | "1956.0 | \n", 336 | "0.12380 | \n", 337 | "0.18660 | \n", 338 | "0.2416 | \n", 339 | "0.1860 | \n", 340 | "0.2750 | \n", 341 | "0.08902 | \n", 342 | "
| 2 | \n", 345 | "19.69 | \n", 346 | "21.25 | \n", 347 | "130.00 | \n", 348 | "1203.0 | \n", 349 | "0.10960 | \n", 350 | "0.15990 | \n", 351 | "0.19740 | \n", 352 | "0.12790 | \n", 353 | "0.2069 | \n", 354 | "0.05999 | \n", 355 | "0.7456 | \n", 356 | "0.7869 | \n", 357 | "4.585 | \n", 358 | "94.03 | \n", 359 | "0.006150 | \n", 360 | "0.04006 | \n", 361 | "0.03832 | \n", 362 | "0.02058 | \n", 363 | "0.02250 | \n", 364 | "0.004571 | \n", 365 | "23.570 | \n", 366 | "25.53 | \n", 367 | "152.50 | \n", 368 | "1709.0 | \n", 369 | "0.14440 | \n", 370 | "0.42450 | \n", 371 | "0.4504 | \n", 372 | "0.2430 | \n", 373 | "0.3613 | \n", 374 | "0.08758 | \n", 375 | "
| 3 | \n", 378 | "11.42 | \n", 379 | "20.38 | \n", 380 | "77.58 | \n", 381 | "386.1 | \n", 382 | "0.14250 | \n", 383 | "0.28390 | \n", 384 | "0.24140 | \n", 385 | "0.10520 | \n", 386 | "0.2597 | \n", 387 | "0.09744 | \n", 388 | "0.4956 | \n", 389 | "1.1560 | \n", 390 | "3.445 | \n", 391 | "27.23 | \n", 392 | "0.009110 | \n", 393 | "0.07458 | \n", 394 | "0.05661 | \n", 395 | "0.01867 | \n", 396 | "0.05963 | \n", 397 | "0.009208 | \n", 398 | "14.910 | \n", 399 | "26.50 | \n", 400 | "98.87 | \n", 401 | "567.7 | \n", 402 | "0.20980 | \n", 403 | "0.86630 | \n", 404 | "0.6869 | \n", 405 | "0.2575 | \n", 406 | "0.6638 | \n", 407 | "0.17300 | \n", 408 | "
| 4 | \n", 411 | "20.29 | \n", 412 | "14.34 | \n", 413 | "135.10 | \n", 414 | "1297.0 | \n", 415 | "0.10030 | \n", 416 | "0.13280 | \n", 417 | "0.19800 | \n", 418 | "0.10430 | \n", 419 | "0.1809 | \n", 420 | "0.05883 | \n", 421 | "0.7572 | \n", 422 | "0.7813 | \n", 423 | "5.438 | \n", 424 | "94.44 | \n", 425 | "0.011490 | \n", 426 | "0.02461 | \n", 427 | "0.05688 | \n", 428 | "0.01885 | \n", 429 | "0.01756 | \n", 430 | "0.005115 | \n", 431 | "22.540 | \n", 432 | "16.67 | \n", 433 | "152.20 | \n", 434 | "1575.0 | \n", 435 | "0.13740 | \n", 436 | "0.20500 | \n", 437 | "0.4000 | \n", 438 | "0.1625 | \n", 439 | "0.2364 | \n", 440 | "0.07678 | \n", 441 | "
| ... | \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 | "
| 564 | \n", 477 | "21.56 | \n", 478 | "22.39 | \n", 479 | "142.00 | \n", 480 | "1479.0 | \n", 481 | "0.11100 | \n", 482 | "0.11590 | \n", 483 | "0.24390 | \n", 484 | "0.13890 | \n", 485 | "0.1726 | \n", 486 | "0.05623 | \n", 487 | "1.1760 | \n", 488 | "1.2560 | \n", 489 | "7.673 | \n", 490 | "158.70 | \n", 491 | "0.010300 | \n", 492 | "0.02891 | \n", 493 | "0.05198 | \n", 494 | "0.02454 | \n", 495 | "0.01114 | \n", 496 | "0.004239 | \n", 497 | "25.450 | \n", 498 | "26.40 | \n", 499 | "166.10 | \n", 500 | "2027.0 | \n", 501 | "0.14100 | \n", 502 | "0.21130 | \n", 503 | "0.4107 | \n", 504 | "0.2216 | \n", 505 | "0.2060 | \n", 506 | "0.07115 | \n", 507 | "
| 565 | \n", 510 | "20.13 | \n", 511 | "28.25 | \n", 512 | "131.20 | \n", 513 | "1261.0 | \n", 514 | "0.09780 | \n", 515 | "0.10340 | \n", 516 | "0.14400 | \n", 517 | "0.09791 | \n", 518 | "0.1752 | \n", 519 | "0.05533 | \n", 520 | "0.7655 | \n", 521 | "2.4630 | \n", 522 | "5.203 | \n", 523 | "99.04 | \n", 524 | "0.005769 | \n", 525 | "0.02423 | \n", 526 | "0.03950 | \n", 527 | "0.01678 | \n", 528 | "0.01898 | \n", 529 | "0.002498 | \n", 530 | "23.690 | \n", 531 | "38.25 | \n", 532 | "155.00 | \n", 533 | "1731.0 | \n", 534 | "0.11660 | \n", 535 | "0.19220 | \n", 536 | "0.3215 | \n", 537 | "0.1628 | \n", 538 | "0.2572 | \n", 539 | "0.06637 | \n", 540 | "
| 566 | \n", 543 | "16.60 | \n", 544 | "28.08 | \n", 545 | "108.30 | \n", 546 | "858.1 | \n", 547 | "0.08455 | \n", 548 | "0.10230 | \n", 549 | "0.09251 | \n", 550 | "0.05302 | \n", 551 | "0.1590 | \n", 552 | "0.05648 | \n", 553 | "0.4564 | \n", 554 | "1.0750 | \n", 555 | "3.425 | \n", 556 | "48.55 | \n", 557 | "0.005903 | \n", 558 | "0.03731 | \n", 559 | "0.04730 | \n", 560 | "0.01557 | \n", 561 | "0.01318 | \n", 562 | "0.003892 | \n", 563 | "18.980 | \n", 564 | "34.12 | \n", 565 | "126.70 | \n", 566 | "1124.0 | \n", 567 | "0.11390 | \n", 568 | "0.30940 | \n", 569 | "0.3403 | \n", 570 | "0.1418 | \n", 571 | "0.2218 | \n", 572 | "0.07820 | \n", 573 | "
| 567 | \n", 576 | "20.60 | \n", 577 | "29.33 | \n", 578 | "140.10 | \n", 579 | "1265.0 | \n", 580 | "0.11780 | \n", 581 | "0.27700 | \n", 582 | "0.35140 | \n", 583 | "0.15200 | \n", 584 | "0.2397 | \n", 585 | "0.07016 | \n", 586 | "0.7260 | \n", 587 | "1.5950 | \n", 588 | "5.772 | \n", 589 | "86.22 | \n", 590 | "0.006522 | \n", 591 | "0.06158 | \n", 592 | "0.07117 | \n", 593 | "0.01664 | \n", 594 | "0.02324 | \n", 595 | "0.006185 | \n", 596 | "25.740 | \n", 597 | "39.42 | \n", 598 | "184.60 | \n", 599 | "1821.0 | \n", 600 | "0.16500 | \n", 601 | "0.86810 | \n", 602 | "0.9387 | \n", 603 | "0.2650 | \n", 604 | "0.4087 | \n", 605 | "0.12400 | \n", 606 | "
| 568 | \n", 609 | "7.76 | \n", 610 | "24.54 | \n", 611 | "47.92 | \n", 612 | "181.0 | \n", 613 | "0.05263 | \n", 614 | "0.04362 | \n", 615 | "0.00000 | \n", 616 | "0.00000 | \n", 617 | "0.1587 | \n", 618 | "0.05884 | \n", 619 | "0.3857 | \n", 620 | "1.4280 | \n", 621 | "2.548 | \n", 622 | "19.15 | \n", 623 | "0.007189 | \n", 624 | "0.00466 | \n", 625 | "0.00000 | \n", 626 | "0.00000 | \n", 627 | "0.02676 | \n", 628 | "0.002783 | \n", 629 | "9.456 | \n", 630 | "30.37 | \n", 631 | "59.16 | \n", 632 | "268.6 | \n", 633 | "0.08996 | \n", 634 | "0.06444 | \n", 635 | "0.0000 | \n", 636 | "0.0000 | \n", 637 | "0.2871 | \n", 638 | "0.07039 | \n", 639 | "
569 rows × 30 columns
\n", 643 | "| \n", 712 | " | mean radius | \n", 713 | "mean texture | \n", 714 | "mean perimeter | \n", 715 | "mean area | \n", 716 | "mean smoothness | \n", 717 | "mean compactness | \n", 718 | "mean concavity | \n", 719 | "mean concave points | \n", 720 | "mean symmetry | \n", 721 | "mean fractal dimension | \n", 722 | "radius error | \n", 723 | "texture error | \n", 724 | "perimeter error | \n", 725 | "area error | \n", 726 | "smoothness error | \n", 727 | "compactness error | \n", 728 | "concavity error | \n", 729 | "concave points error | \n", 730 | "symmetry error | \n", 731 | "fractal dimension error | \n", 732 | "worst radius | \n", 733 | "worst texture | \n", 734 | "worst perimeter | \n", 735 | "worst area | \n", 736 | "worst smoothness | \n", 737 | "worst compactness | \n", 738 | "worst concavity | \n", 739 | "worst concave points | \n", 740 | "worst symmetry | \n", 741 | "worst fractal dimension | \n", 742 | "target | \n", 743 | "
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | \n", 748 | "1.096100 | \n", 749 | "-2.071512 | \n", 750 | "1.268817 | \n", 751 | "0.983510 | \n", 752 | "1.567087 | \n", 753 | "3.280628 | \n", 754 | "2.650542 | \n", 755 | "2.530249 | \n", 756 | "2.215566 | \n", 757 | "2.253764 | \n", 758 | "2.487545 | \n", 759 | "-0.564768 | \n", 760 | "2.830540 | \n", 761 | "2.485391 | \n", 762 | "-0.213814 | \n", 763 | "1.315704 | \n", 764 | "0.723390 | \n", 765 | "0.660239 | \n", 766 | "1.147747 | \n", 767 | "0.906286 | \n", 768 | "1.885031 | \n", 769 | "-1.358098 | \n", 770 | "2.301575 | \n", 771 | "1.999478 | \n", 772 | "1.306537 | \n", 773 | "2.614365 | \n", 774 | "2.107672 | \n", 775 | "2.294058 | \n", 776 | "2.748204 | \n", 777 | "1.935312 | \n", 778 | "0 | \n", 779 | "
| 1 | \n", 782 | "1.828212 | \n", 783 | "-0.353322 | \n", 784 | "1.684473 | \n", 785 | "1.907030 | \n", 786 | "-0.826235 | \n", 787 | "-0.486643 | \n", 788 | "-0.023825 | \n", 789 | "0.547662 | \n", 790 | "0.001391 | \n", 791 | "-0.867889 | \n", 792 | "0.498816 | \n", 793 | "-0.875473 | \n", 794 | "0.263095 | \n", 795 | "0.741749 | \n", 796 | "-0.604819 | \n", 797 | "-0.692317 | \n", 798 | "-0.440393 | \n", 799 | "0.259933 | \n", 800 | "-0.804742 | \n", 801 | "-0.099356 | \n", 802 | "1.804340 | \n", 803 | "-0.368879 | \n", 804 | "1.533776 | \n", 805 | "1.888827 | \n", 806 | "-0.375282 | \n", 807 | "-0.430066 | \n", 808 | "-0.146620 | \n", 809 | "1.086129 | \n", 810 | "-0.243675 | \n", 811 | "0.280943 | \n", 812 | "0 | \n", 813 | "
| 2 | \n", 816 | "1.578499 | \n", 817 | "0.455786 | \n", 818 | "1.565126 | \n", 819 | "1.557513 | \n", 820 | "0.941382 | \n", 821 | "1.052000 | \n", 822 | "1.362280 | \n", 823 | "2.035440 | \n", 824 | "0.938859 | \n", 825 | "-0.397658 | \n", 826 | "1.227596 | \n", 827 | "-0.779398 | \n", 828 | "0.850180 | \n", 829 | "1.180298 | \n", 830 | "-0.296744 | \n", 831 | "0.814257 | \n", 832 | "0.212889 | \n", 833 | "1.423575 | \n", 834 | "0.236827 | \n", 835 | "0.293301 | \n", 836 | "1.510541 | \n", 837 | "-0.023953 | \n", 838 | "1.346291 | \n", 839 | "1.455004 | \n", 840 | "0.526944 | \n", 841 | "1.081980 | \n", 842 | "0.854222 | \n", 843 | "1.953282 | \n", 844 | "1.151242 | \n", 845 | "0.201214 | \n", 846 | "0 | \n", 847 | "
| 3 | \n", 850 | "-0.768233 | \n", 851 | "0.253509 | \n", 852 | "-0.592166 | \n", 853 | "-0.763792 | \n", 854 | "3.280667 | \n", 855 | "3.399917 | \n", 856 | "1.914213 | \n", 857 | "1.450431 | \n", 858 | "2.864862 | \n", 859 | "4.906602 | \n", 860 | "0.326087 | \n", 861 | "-0.110312 | \n", 862 | "0.286341 | \n", 863 | "-0.288125 | \n", 864 | "0.689095 | \n", 865 | "2.741868 | \n", 866 | "0.818798 | \n", 867 | "1.114027 | \n", 868 | "4.728520 | \n", 869 | "2.045711 | \n", 870 | "-0.281217 | \n", 871 | "0.133866 | \n", 872 | "-0.249720 | \n", 873 | "-0.549538 | \n", 874 | "3.391291 | \n", 875 | "3.889975 | \n", 876 | "1.987839 | \n", 877 | "2.173873 | \n", 878 | "6.040726 | \n", 879 | "4.930672 | \n", 880 | "0 | \n", 881 | "
| 4 | \n", 884 | "1.748758 | \n", 885 | "-1.150804 | \n", 886 | "1.775011 | \n", 887 | "1.824624 | \n", 888 | "0.280125 | \n", 889 | "0.538866 | \n", 890 | "1.369806 | \n", 891 | "1.427237 | \n", 892 | "-0.009552 | \n", 893 | "-0.561956 | \n", 894 | "1.269426 | \n", 895 | "-0.789549 | \n", 896 | "1.272070 | \n", 897 | "1.189310 | \n", 898 | "1.481763 | \n", 899 | "-0.048477 | \n", 900 | "0.827742 | \n", 901 | "1.143199 | \n", 902 | "-0.360775 | \n", 903 | "0.498889 | \n", 904 | "1.297434 | \n", 905 | "-1.465481 | \n", 906 | "1.337363 | \n", 907 | "1.219651 | \n", 908 | "0.220362 | \n", 909 | "-0.313119 | \n", 910 | "0.612640 | \n", 911 | "0.728618 | \n", 912 | "-0.867590 | \n", 913 | "-0.396751 | \n", 914 | "0 | \n", 915 | "
| ... | \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 | "
| 564 | \n", 952 | "2.109139 | \n", 953 | "0.720838 | \n", 954 | "2.058974 | \n", 955 | "2.341795 | \n", 956 | "1.040926 | \n", 957 | "0.218868 | \n", 958 | "1.945573 | \n", 959 | "2.318924 | \n", 960 | "-0.312314 | \n", 961 | "-0.930209 | \n", 962 | "2.779634 | \n", 963 | "0.070963 | \n", 964 | "2.377491 | \n", 965 | "2.601897 | \n", 966 | "1.085429 | \n", 967 | "0.191637 | \n", 968 | "0.665416 | \n", 969 | "2.065360 | \n", 970 | "-1.137415 | \n", 971 | "0.167832 | \n", 972 | "1.899514 | \n", 973 | "0.117596 | \n", 974 | "1.751022 | \n", 975 | "2.013529 | \n", 976 | "0.378033 | \n", 977 | "-0.273077 | \n", 978 | "0.663928 | \n", 979 | "1.627719 | \n", 980 | "-1.358963 | \n", 981 | "-0.708467 | \n", 982 | "0 | \n", 983 | "
| 565 | \n", 986 | "1.703356 | \n", 987 | "2.083301 | \n", 988 | "1.614511 | \n", 989 | "1.722326 | \n", 990 | "0.102368 | \n", 991 | "-0.017817 | \n", 992 | "0.692434 | \n", 993 | "1.262558 | \n", 994 | "-0.217473 | \n", 995 | "-1.057681 | \n", 996 | "1.299356 | \n", 997 | "2.258951 | \n", 998 | "1.155840 | \n", 999 | "1.290429 | \n", 1000 | "-0.423637 | \n", 1001 | "-0.069697 | \n", 1002 | "0.251980 | \n", 1003 | "0.807720 | \n", 1004 | "-0.188995 | \n", 1005 | "-0.490124 | \n", 1006 | "1.535369 | \n", 1007 | "2.045599 | \n", 1008 | "1.420690 | \n", 1009 | "1.493644 | \n", 1010 | "-0.690623 | \n", 1011 | "-0.394473 | \n", 1012 | "0.236365 | \n", 1013 | "0.733182 | \n", 1014 | "-0.531387 | \n", 1015 | "-0.973122 | \n", 1016 | "0 | \n", 1017 | "
| 566 | \n", 1020 | "0.701667 | \n", 1021 | "2.043775 | \n", 1022 | "0.672084 | \n", 1023 | "0.577445 | \n", 1024 | "-0.839745 | \n", 1025 | "-0.038646 | \n", 1026 | "0.046547 | \n", 1027 | "0.105684 | \n", 1028 | "-0.808406 | \n", 1029 | "-0.894800 | \n", 1030 | "0.184730 | \n", 1031 | "-0.257145 | \n", 1032 | "0.276450 | \n", 1033 | "0.180539 | \n", 1034 | "-0.379008 | \n", 1035 | "0.660696 | \n", 1036 | "0.510377 | \n", 1037 | "0.611619 | \n", 1038 | "-0.890632 | \n", 1039 | "0.036694 | \n", 1040 | "0.560868 | \n", 1041 | "1.373645 | \n", 1042 | "0.578492 | \n", 1043 | "0.427529 | \n", 1044 | "-0.808876 | \n", 1045 | "0.350427 | \n", 1046 | "0.326479 | \n", 1047 | "0.413705 | \n", 1048 | "-1.103578 | \n", 1049 | "-0.318129 | \n", 1050 | "0 | \n", 1051 | "
| 567 | \n", 1054 | "1.836725 | \n", 1055 | "2.334403 | \n", 1056 | "1.980781 | \n", 1057 | "1.733693 | \n", 1058 | "1.524426 | \n", 1059 | "3.269267 | \n", 1060 | "3.294046 | \n", 1061 | "2.656528 | \n", 1062 | "2.135315 | \n", 1063 | "1.042778 | \n", 1064 | "1.156917 | \n", 1065 | "0.685485 | \n", 1066 | "1.437265 | \n", 1067 | "1.008615 | \n", 1068 | "-0.172848 | \n", 1069 | "2.015943 | \n", 1070 | "1.301140 | \n", 1071 | "0.785031 | \n", 1072 | "0.326346 | \n", 1073 | "0.903262 | \n", 1074 | "1.959515 | \n", 1075 | "2.235958 | \n", 1076 | "2.301575 | \n", 1077 | "1.651717 | \n", 1078 | "1.429169 | \n", 1079 | "3.901415 | \n", 1080 | "3.194794 | \n", 1081 | "2.287972 | \n", 1082 | "1.917396 | \n", 1083 | "2.217684 | \n", 1084 | "0 | \n", 1085 | "
| 568 | \n", 1088 | "-1.806811 | \n", 1089 | "1.220718 | \n", 1090 | "-1.812793 | \n", 1091 | "-1.346604 | \n", 1092 | "-3.109349 | \n", 1093 | "-1.149741 | \n", 1094 | "-1.113893 | \n", 1095 | "-1.260710 | \n", 1096 | "-0.819349 | \n", 1097 | "-0.560539 | \n", 1098 | "-0.070217 | \n", 1099 | "0.382756 | \n", 1100 | "-0.157311 | \n", 1101 | "-0.465742 | \n", 1102 | "0.049299 | \n", 1103 | "-1.162493 | \n", 1104 | "-1.056571 | \n", 1105 | "-1.911765 | \n", 1106 | "0.752168 | \n", 1107 | "-0.382418 | \n", 1108 | "-1.409652 | \n", 1109 | "0.763518 | \n", 1110 | "-1.431475 | \n", 1111 | "-1.074867 | \n", 1112 | "-1.857384 | \n", 1113 | "-1.206491 | \n", 1114 | "-1.304683 | \n", 1115 | "-1.743529 | \n", 1116 | "-0.048096 | \n", 1117 | "-0.750546 | \n", 1118 | "1 | \n", 1119 | "
569 rows × 31 columns
\n", 1123 | "