diff --git "a/Week1_\353\263\265\354\212\265\352\263\274\354\240\234_\354\247\204\354\230\210\354\233\220.ipynb" "b/Week1_\353\263\265\354\212\265\352\263\274\354\240\234_\354\247\204\354\230\210\354\233\220.ipynb" new file mode 100644 index 0000000..38e06b1 --- /dev/null +++ "b/Week1_\353\263\265\354\212\265\352\263\274\354\240\234_\354\247\204\354\230\210\354\233\220.ipynb" @@ -0,0 +1,982 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + } + }, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# **Week1 복습과제**\n", + "\n", + "1. [Pytorch 기본]\n", + "1. [Linear Regression]\n", + "1. [Logistic Regression]" + ], + "metadata": { + "id": "9mabISNcCPiV" + } + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "id": "T-Govjsw7kBQ" + }, + "outputs": [], + "source": [ + "# import libraries\n", + "import numpy as np\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "\n", + "#input data: 첨부된 .csv 파일 다운받아 사용해주세요" + ] + }, + { + "cell_type": "markdown", + "source": [ + "
\n", + "## 1. Pytorch 기본" + ], + "metadata": { + "id": "0qcO6qTxCUTU" + } + }, + { + "cell_type": "code", + "source": [ + "# numpy array\n", + "array = [[1,2,3],[4,5,6]]\n", + "first_array = np.array(array) # 2x3 array\n", + "print(\"Array Type: {}\".format(type(first_array))) # type\n", + "print(\"Array Shape: {}\".format(first_array.shape)) # shape\n", + "print(first_array)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "_KdP4S5g7udz", + "outputId": "e98e3657-845e-4719-cfe4-ff1f9ca21a0c" + }, + "execution_count": 21, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Array Type: \n", + "Array Shape: (2, 3)\n", + "[[1 2 3]\n", + " [4 5 6]]\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "- 우리는 NumPy 배열을 살펴보았습니다.\n", + "- 이제 텐서(PyTorch 배열)를 구현하는 방법을 살펴보겠습니다.\n", + "- import torch를 사용하여 PyTorch 라이브러리를 가져옵니다.\n", + "- torch.Tensor() 메서드를 사용하여 텐서를 생성합니다.\n", + "- type: 배열의 타입을 나타냅니다. 이 예제에서는 텐서입니다.\n", + "- shape: 배열의 형태를 나타냅니다. (행 × 열)" + ], + "metadata": { + "id": "HfLiM-u1Cj5l" + } + }, + { + "cell_type": "code", + "source": [ + "# import pytorch library\n", + "import torch\n", + "\n", + "# pytorch array\n", + "tensor = torch.Tensor(array)\n", + "print(\"Array Type: {}\".format(type(tensor))) # type\n", + "print(\"Array Shape: {}\".format(tensor.shape)) # shape\n", + "print(tensor)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "d-uAA50o7ua4", + "outputId": "4082ae56-0cb9-4c5d-b999-11409289eee7" + }, + "execution_count": 22, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Array Type: \n", + "Array Shape: torch.Size([2, 3])\n", + "tensor([[1., 2., 3.],\n", + " [4., 5., 6.]])\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "- 할당(Allocation)은 코딩에서 가장 많이 사용되는 기법 중 하나입니다.\n", + "- 따라서 PyTorch를 사용하여 이를 구현하는 방법을 배워봅시다.\n", + "- 학습을 위해 NumPy와 Tensor를 비교해 봅시다.\n", + " - np.ones() = torch.ones()\n", + " - np.random.rand() = torch.rand()" + ], + "metadata": { + "id": "pEvocwKPC6J1" + } + }, + { + "cell_type": "code", + "source": [ + "# numpy ones\n", + "print(\"Numpy {}\\n\".format(np.ones((2, 3)))) # 2x3 in numpy\n", + "\n", + "# pytorch ones\n", + "print(torch.ones(2, 3)) # 2x3 in tensor" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "QlHQXzBR7uYo", + "outputId": "6290d1c6-5d6c-40e1-f60d-103b464b7bc3" + }, + "execution_count": 23, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Numpy [[1. 1. 1.]\n", + " [1. 1. 1.]]\n", + "\n", + "tensor([[1., 1., 1.],\n", + " [1., 1., 1.]])\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "# numpy random\n", + "print(\"Numpy {}\\n\".format(np.random.rand(2, 3))) # 2x3 random numpy array\n", + "\n", + "# pytorch random\n", + "print(torch.rand(2,3)) # 2x3 random tensor" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "xEV88BpO7uWc", + "outputId": "3c38724c-1a9a-42c9-801e-ee01f30a2f4e" + }, + "execution_count": 24, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Numpy [[0.58414461 0.90142161 0.71102625]\n", + " [0.7456714 0.11130255 0.25981121]]\n", + "\n", + "tensor([[0.5474, 0.6665, 0.9605],\n", + " [0.3300, 0.9341, 0.6581]])\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "- 텐서와 NumPy 배열 간의 변환을 살펴봅시다.\n", + " - torch.from_numpy(): NumPy → Tensor\n", + " - .numpy(): Tensor → NumPy" + ], + "metadata": { + "id": "2TAOGUteDLOv" + } + }, + { + "cell_type": "code", + "source": [ + "# random numpy array\n", + "array = np.random.rand(2,2)\n", + "print(\"{} {}\\n\".format(type(array), array))\n", + "\n", + "# numpy -> tensor\n", + "from_numpy_to_tensor = torch.from_numpy(array)\n", + "print(\"{}\\n\".format(from_numpy_to_tensor))\n", + "\n", + "# tensor -> numpy\n", + "tensor = from_numpy_to_tensor\n", + "from_tensor_to_numpy = tensor.numpy()\n", + "print(\"{} {}\\n\".format(type(from_tensor_to_numpy),from_tensor_to_numpy))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "yWShZwKC7uUM", + "outputId": "1b505559-6e34-418a-a08f-836c22da6cf1" + }, + "execution_count": 25, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + " [[0.74835298 0.61922162]\n", + " [0.21009369 0.82054833]]\n", + "\n", + "tensor([[0.7484, 0.6192],\n", + " [0.2101, 0.8205]], dtype=torch.float64)\n", + "\n", + " [[0.74835298 0.61922162]\n", + " [0.21009369 0.82054833]]\n", + "\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "### PyTorch 기본 수학 연산\n", + "- 크기 변경(Resize): view()\n", + "- a와 b는 텐서입니다.\n", + "- 덧셈(Addition): torch.add(a, b) = a + b\n", + "- 뺄셈(Subtraction): a.sub(b) = a - b\n", + "- 원소별 곱(Element-wise Multiplication): torch.mul(a, b) = a * b\n", + "- 원소별 나눗셈(Element-wise Division): torch.div(a, b) = a / b\n", + "- 평균(Mean): a.mean()\n", + "- 표준 편차(Standard Deviation, std): a.std()\n" + ], + "metadata": { + "id": "bsJkMU_lDZgy" + } + }, + { + "cell_type": "code", + "source": [ + "# 텐서 생성\n", + "tensor = torch.rand(3,3)\n", + "print(\"\\n\",tensor)\n", + "\n", + "# 크기 변경\n", + "print(\"{}{}\\n\".format(tensor.view(9).shape,tensor.view(9)))\n", + "\n", + "# 덧셈\n", + "print(\"Addition: {}\\n\".format(torch.add(tensor,tensor)))\n", + "\n", + "# 뺄셈\n", + "print(\"Subtraction: {}\\n\".format(tensor.sub(tensor)))\n", + "\n", + "# 원소별 곱\n", + "print(\"Element wise multiplication: {}\\n\".format(torch.mul(tensor,tensor)))\n", + "\n", + "# 원소별 나눗셈\n", + "print(\"Element wise division: {}\\n\".format(torch.div(tensor,tensor)))\n", + "\n", + "# 평균\n", + "tensor = torch.Tensor([1,2,3,4,5])\n", + "print(\"Mean: {}\".format(tensor.mean()))\n", + "\n", + "# 표준편차\n", + "print(\"std: {}\".format(tensor.std()))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "V6iLiAPS7uQs", + "outputId": "17a4877e-059e-4a0d-ce8e-e9d6e9d6c517" + }, + "execution_count": 26, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + " tensor([[0.8580, 0.7117, 0.7819],\n", + " [0.0454, 0.9763, 0.2539],\n", + " [0.7846, 0.8037, 0.2608]])\n", + "torch.Size([9])tensor([0.8580, 0.7117, 0.7819, 0.0454, 0.9763, 0.2539, 0.7846, 0.8037, 0.2608])\n", + "\n", + "Addition: tensor([[1.7160, 1.4234, 1.5638],\n", + " [0.0908, 1.9525, 0.5077],\n", + " [1.5693, 1.6074, 0.5215]])\n", + "\n", + "Subtraction: tensor([[0., 0., 0.],\n", + " [0., 0., 0.],\n", + " [0., 0., 0.]])\n", + "\n", + "Element wise multiplication: tensor([[0.7362, 0.5065, 0.6113],\n", + " [0.0021, 0.9531, 0.0644],\n", + " [0.6156, 0.6460, 0.0680]])\n", + "\n", + "Element wise division: tensor([[1., 1., 1.],\n", + " [1., 1., 1.],\n", + " [1., 1., 1.]])\n", + "\n", + "Mean: 3.0\n", + "std: 1.5811388492584229\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Variables \n", + "- 변수는 그래디언트(Gradients)를 누적합니다.\n", + "- 우리는 PyTorch를 신경망에 사용할 것입니다. 신경망에서는 역전파(Backpropagation) 과정에서 그래디언트를 계산하게 됩니다. 따라서 그래디언트를 다룰 필요가 있습니다.\n", + "- 변수(Variable)와 텐서(Tensor)의 차이점은 변수가 그래디언트를 누적한다는 것입니다. \n", + "- 변수를 사용하여 수학 연산을 수행할 수도 있습니다. \n", + "- 역전파를 수행하려면 변수가 필요합니다." + ], + "metadata": { + "id": "HxVIlKSQEIXx" + } + }, + { + "cell_type": "code", + "source": [ + "from torch.autograd import Variable\n", + "\n", + "# variable 정의\n", + "var = Variable(torch.ones(3), requires_grad = True)\n", + "var" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "UfpDKji97uOv", + "outputId": "26771b79-4881-4d08-d94a-344ff022127d" + }, + "execution_count": 27, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "tensor([1., 1., 1.], requires_grad=True)" + ] + }, + "metadata": {}, + "execution_count": 27 + } + ] + }, + { + "cell_type": "code", + "source": [ + "# 기본적인 역전파(backward propagation) 수행\n", + "# 주어진 함수 y = x^2\n", + "array = [2,4]\n", + "tensor = torch.Tensor(array)\n", + "x = Variable(tensor, requires_grad = True)\n", + "y = x**2\n", + "print(\" y = \",y)\n", + "\n", + "# 방정식 요약: o = 1/2*sum(y)\n", + "o = (1/2)*sum(y)\n", + "print(\" o = \",o)\n", + "\n", + "# 역전파 실행(그래디언트 계산)\n", + "o.backward()\n", + "\n", + "# 변수는 그래디언트를 누적. 여기서는 x 하나만 존재.\n", + "# 따라선 변수 x는 그래디언트를 가져야 함\n", + "# x의 그래디언트 출\n", + "print(\"gradients: \",x.grad)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "t4p2w1z_7uMv", + "outputId": "7cdd2080-9b60-4204-e372-87d063ab37a1" + }, + "execution_count": 28, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + " y = tensor([ 4., 16.], grad_fn=)\n", + " o = tensor(10., grad_fn=)\n", + "gradients: tensor([2., 4.])\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "
\n", + "## 2. 선형 회귀\n", + "\n", + "- y = Ax + B\n", + " - A = 기울기\n", + " - B = 절편 (y축과 교차하는 점)\n", + "\n", + "- 자동차 가격이 낮으면 더 많이 팔리고, 자동차 가격이 높으면 덜 팔린다는 사실을 우리는 알고 있으며, 이에 대한 데이터셋을 가지고 있습니다.\n", + "\n", + "- 목표는 자동차 가격이 100일 때 팔린 자동차의 수를 예측하는 것입니다." + ], + "metadata": { + "id": "Jq1tID2f9DOP" + } + }, + { + "cell_type": "code", + "source": [ + "# 자동차 회사에서 과거 판매 데이터를 수집했다고 가정\n", + "# 자동차 가격 데이터 정의\n", + "car_prices_array = [3,4,5,6,7,8,9]\n", + "car_price_np = np.array(car_prices_array,dtype=np.float32) # numpy array로 변환\n", + "car_price_np = car_price_np.reshape(-1,1) #reshape\n", + "car_price_tensor = Variable(torch.from_numpy(car_price_np)) # define variable\n", + "\n", + "# 자동차 판매량 데이터 정의\n", + "number_of_car_sell_array = [ 7.5, 7, 6.5, 6.0, 5.5, 5.0, 4.5]\n", + "number_of_car_sell_np = np.array(number_of_car_sell_array,dtype=np.float32) # numpy array 로 변환\n", + "number_of_car_sell_np = number_of_car_sell_np.reshape(-1,1) #reshape\n", + "number_of_car_sell_tensor = Variable(torch.from_numpy(number_of_car_sell_np)) # define variable\n", + "\n", + "# 데이터 시각화\n", + "import matplotlib.pyplot as plt\n", + "plt.scatter(car_prices_array,number_of_car_sell_array)\n", + "plt.xlabel(\"Car Price $\")\n", + "plt.ylabel(\"Number of Car Sell\")\n", + "plt.title(\"Car Price$ VS Number of Car Sell\")\n", + "plt.show()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 420 + }, + "id": "5r4kXCwf7uKP", + "outputId": "8d317c25-0ef5-4310-83af-080402ac5d7c" + }, + "execution_count": 29, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "
" + ], + "image/png": "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/Oc/TalSpcypU6fsfX19fU3Dhg3Nn3/+6fQ5LhcWFmbuvvtus3fv3hxXb3K7ctO7d28TFhaW4zhxcXHm7/9rk2Sio6NNaGioGTdunBk3bpwJCAgwN998s5k0aZKJjIw0b7zxhnnxxReNl5eXueOOOxz2b9mypalUqZIJCgoygwYNMm+99ZZp3ry5kWRmzJjh8J4587O5VFOdOnVMYGCgGTVqlJk8ebL9Z5ybS6+rTZs25u233zaDBg0ynp6e5rbbbjPnz583xhjz+eef23//4uPjzQcffGC2bt2a6/HOnDljSpYsae688848z/l3VapUMU888YSZNGmSmTBhgmncuLGRZL766qt8v7YjR44YSebuu+82Z86cueL5z5w5Y6Kjo02FChXM888/b6ZMmWJ69eplbDabGTJkSI4auHJz4yLc4LqSlpZmJOX4oLiS3IaA2rdvnyMghYWFGUlm8eLFTh33Urj5/fffze+//26SkpLMK6+8Ymw2m4mOjnbquH8PN//5z3+MJDN//vwcfbOzs40xxnzwwQfGw8PDrFq1ymH7lClTjCTz008/GWMufhB4eHiYF154wRjjergxxpjhw4cbSWbXrl32trS0NOPj42O6d+/u0PdS7aVLlzYdO3Y0L7/8stm4caPT57oUbowx5pFHHjE+Pj72YceCCDfe3t4Or3/q1KlGkgkODnYYkomNjc3xIdiyZUsjybzxxhv2tszMTHPrrbeaoKAge7hw9mdzqSYPDw+zffv2q743x48fN15eXqZdu3YmKyvL3j5p0iQjybz33ns5Xv/VhqW2bt1qJOUIBVfy939L58+fN1FRUTkCkiuvzRhjevXq5TBk+vrrr5udO3fm6DdmzBhTunRps3v3bof2ESNGGE9PT5OcnOxQA+HmxsWwFK4r6enpkpTrJfS8+Pr62v87LS1Nf/zxh1q2bKl9+/YpLS3NoW94eLjat2/v9LHPnDmjwMBABQYGKiIiQs8//7xiYmL0+eef5+u4n332mW655RZ17do1x7ZLQy3z5s1TnTp1VLt2bf3xxx/2x5133ilJ+u677yRJpUqV0v33369XX31V/fr10+HDh3X48GGnX5t0cWhKujh5+PIaMzIy7ENSl4waNUpz5sxR/fr1tWTJEr3wwgtq2LChGjRooJ07d7p03hdffFF//fWXxo0b59J+V9K6dWuHYazbb79dknTfffc5/D5dat+3b5/D/iVKlNBjjz1mf+7l5aXHHntMx48f18aNGyU5/7O5pGXLloqMjLxq7cuWLdP58+c1dOhQhwno/fv3l7+/vxYtWuTMW+DgWv8tpaamKi0tTS1atNCmTZty9HX2tUkXh44nTZqk8PBwff7553rmmWdUp04dtW7d2uF3dt68eWrRooXKlSvn8P62adNGWVlZ+uGHH5x+LbA2wg2uK/7+/pKkU6dOOb3PTz/9pDZt2qh06dIqW7asAgMD7Xd/5BZuXOHj46OlS5dq6dKl+uGHH3To0CH99NNPqlatWr6Ou3fv3quup7Jnzx5t377dHqouPWrWrClJOn78uL3vzJkzNXr0aP38889KSkpS8+bNFR4eroSEBKfqiY6OVlRUlD766CN725w5c3TTTTflGta6d++uVatWKTU1Vd9++60eeughbd68WZ06dXL6Ti1Jqlatmh5++GFNmzZNKSkpTu93JTfffLPD84CAAElSaGhoru2pqakO7ZUqVVLp0qUd2i6955fm6Ljys5Gc/724NEeqVq1aDu1eXl6qVq2aU3Oo/i4//5a++uorNWnSRD4+PipfvrwCAwMVHx+f49+R5Nq/JQ8PDw0cOFAbN27UH3/8oS+//FIdO3bUihUr7HfjSRff38WLF+d4f9u0aSMp5/uLGxe3guO64u/vr0qVKikxMdGp/nv37lXr1q1Vu3ZtTZgwQaGhofLy8tLXX3+t//73vzkmIV7+l6kzPD097f9jvRJXj3sl2dnZqlevniZMmJDr9ss/rL29vTVixAiNGDFCrVq10kMPPaRPPvlEffv2la+vr8MHR1569uypESNGaMOGDapSpYq+++47PfbYYypRIu//ffj7+6tt27Zq27atSpYsqVmzZunnn39Wy5YtnX6dL7zwgj744AONHz9eXbp0ybH975OGL8lrXRZPT0+X2o0xzhV6GVd+NlLB/l64KiIiQiVKlLBPqr6aVatW6d5779U//vEPvfPOOwoJCVHJkiWVkJDgcGXvkvy+tgoVKujee+/Vvffeq1atWun777/XwYMHFRYWpuzsbLVt21bPPvtsrvteCpEA4QbXnXvuuUfTpk3TmjVrFBMTc8W+CxcuVGZmphYsWODwl/vfhweKi+rVq181uFWvXl1bt25V69at8/yAz0u7du3Up08fVapUSZ9++qlT4aZ79+6KjY3VnDlzFBYWpqysrBxDUlfSqFEjzZo1y+UrMNWrV1fPnj01depU+1DR5cqVK+dwp9Al+bmK4YwjR47ozJkzDldvdu/eLUn24a5r+dlcSVhYmKSL6yJdflXw/Pnz2r9/v1MB++9KlSqlO++8UytWrNChQ4dyBK+/++yzz+Tj46MlS5bI29vb3u7sVcD8aNSokb7//nulpKQoLCxM1atX1+nTp/P1enFjYVgK151nn31WpUuX1qOPPqpjx47l2L537169+eabkv73V/nlf4WnpaUV6v+Qr8V9992nrVu35pizI/3vNdx///06fPiwpk+fnqPPuXPndObMGUkXb0nObaXm7OxsZWdnO/2X9c0336wWLVpo7ty5mj17tsLDw9W0aVOHPmfPntWaNWty3f+bb76RlHNIxRkvvviiLly4oFdffTXHturVqystLU2//PKLvS0lJSXX964g/PXXXw5LA5w/f15Tp05VYGCgGjZsKMn5n42r2rRpIy8vL7311lsOv8szZsxQWlqa7r777nwdNy4uTsYYPfzwwzp9+nSO7Rs3btSsWbMkXfy3ZLPZHK6MHThwQF988UW+zn3J0aNHtWPHjhzt58+f1/Lly+Xh4WFfjPD+++/XmjVrtGTJkhz9T548qb/++uuaaoF1cOUG153q1atrzpw5euCBB1SnTh2HFYpXr16tefPm2dfmaNeunby8vNSpUyc99thjOn36tKZPn66goKACm8tRkIYPH65PP/1U3bp1U9++fdWwYUOdOHFCCxYs0JQpU3TLLbfo4Ycf1ieffKLHH39c3333nZo1a6asrCz9+uuv+uSTT+zr6aSkpKh58+bq27evmjZtqvT0dC1YsEDz589XamqqHnroIafr6tmzp/7v//5PR44c0QsvvJBj+9mzZ9W0aVM1adJEHTp0UGhoqE6ePKkvvvhCq1atUpcuXVS/fn2X349LV28ufcBe7sEHH9Rzzz2nrl27avDgwTp79qzi4+NVs2bNXCe4XqtKlSpp/PjxOnDggGrWrKm5c+dqy5YtmjZtmkqWLClJTv9sXBUYGKjY2FiNGjVKHTp00L333qtdu3bpnXfe0W233Waf+O2qpk2bavLkyXriiSdUu3ZthxWKV65cqQULFuill16SdHHV6gkTJqhDhw566KGHdPz4cU2ePFkREREOAdNVv/32mxo3bqw777xTrVu3VnBwsI4fP66PPvpIW7du1dChQ3XTTTdJuvjvY8GCBbrnnnvUp08fNWzYUGfOnNG2bdv06aef6sCBA/a+uMG59V4t4Brs3r3b9O/f31StWtV4eXkZPz8/06xZM/P222+bjIwMe78FCxaY6Oho4+PjY6pWrWrGjx9v3nvvvRy3hV5+K7Iz8lrn5u+udNy/3wpujDF//vmnGTRokKlcubLx8vIyVapUMb179zZ//PGHvc/58+fN+PHjTd26dY23t7cpV66cadiwoRk1apRJS0szxhiTkZFhJkyYYJo1a2ZuuukmI8mUKVPGNGzY0HzyySdOv05jjDlx4oTx9vY2ksyOHTtybL9w4YKZPn266dKliwkLCzPe3t6mVKlSpn79+ua1114zmZmZVz1HXu/Tnj17jKenZ64rFH/77bcmKirKeHl5mVq1apnZs2fneSv4wIEDHdr2799vJJnXXnvNoT23284vrVC8YcMGExMTY3x8fExYWFiOtZKMce5nk1dNVzNp0iRTu3ZtU7JkSVOxYkUzYMAAk5qa6tDH2VvBL7dx40bz0EMPmUqVKpmSJUuacuXKmdatW5tZs2Y53Ho+Y8YMU6NGDePt7W1q165tEhISnH6/85Kenm7efPNN0759e1OlShVTsmRJ4+fnZ2JiYsz06dPtSyBccurUKRMbG2siIiKMl5eXuemmm0zTpk3N66+/br8l/1IN3Ap+47IZk49ZcwCuO61atdLMmTNzXdUXAKyEOTcAAMBSCDfADaJPnz4qW7asu8sAgELHsBQAALAUrtwAAABLIdwAAABLIdwAAABLueEW8cvOztaRI0fk5+dXoMujAwCAwmOM0alTp1SpUiV5eFz52swNF26OHDly1e9QAQAAxdOhQ4dUpUqVK/a54cKNn5+fpItvjr+/v5urAQAAzkhPT1doaKj9c/xKbrhwc2koyt/fn3ADAMB1xpkpJUwoBgAAlkK4AQAAlkK4AQAAlkK4AQAAlkK4AQAAlkK4AQAAlkK4AQAAlkK4AQAAlkK4AQAAlnLDrVBcWLKyjdbtP6HjpzIU5OejxuHl5enBF3MCAFDU3HrlpmrVqrLZbDkeAwcOzLX/zJkzc/T18fEp4qpzWpyYoubjV6j79LUa8vEWdZ++Vs3Hr9DixBR3lwYAwA3HrVdu1q9fr6ysLPvzxMREtW3bVt26dctzH39/f+3atcv+3JnvmChMixNTNGD2Jpm/tR9Ny9CA2ZsU37OBOkSFuKU2AABuRG4NN4GBgQ7Px40bp+rVq6tly5Z57mOz2RQcHFzYpTklK9to1MIdOYKNJBlJNkmjFu5Q28hghqgAACgixWZC8fnz5zV79mz17dv3ildjTp8+rbCwMIWGhqpz587avn37FY+bmZmp9PR0h0dBWbf/hFLSMvLcbiSlpGVo3f4TBXZOAABwZcUm3HzxxRc6efKk+vTpk2efWrVq6b333tOXX36p2bNnKzs7W02bNtVvv/2W5z5jx45VQECA/REaGlpgNR8/lXewyU8/AABw7WzGmNxGVYpc+/bt5eXlpYULFzq9z4ULF1SnTh11795dY8aMybVPZmamMjMz7c/T09MVGhqqtLQ0+fv7X1PNa/b+qe7T116130f9myimeoVrOhcAADey9PR0BQQEOPX5XSxuBT948KCWLVum+fPnu7RfyZIlVb9+fSUlJeXZx9vbW97e3tdaYq4ah5dXSICPjqZl5DrvxiYpOODibeEAAKBoFIthqYSEBAUFBenuu+92ab+srCxt27ZNISHuuRvJ08OmuE6Rki4Gmctdeh7XKZLJxAAAFCG3h5vs7GwlJCSod+/eKlHC8UJSr169FBsba38+evRoffvtt9q3b582bdqknj176uDBg3r00UeLumy7DlEhiu/ZQMEBjuvtBAf4cBs4AABu4PZhqWXLlik5OVl9+/bNsS05OVkeHv/LX6mpqerfv7+OHj2qcuXKqWHDhlq9erUiIyOLsuQcOkSFqG1kMCsUAwBQDBSbCcVFxZUJSQAAoHhw5fPb7cNSAAAABYlwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALIVwAwAALKWEuwvA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77//vlq1aqVHHnlELVq0UEREhMLCwvThhx+qR48ehVEnAACAU1y+cnPixAlVq1ZN0sX5NZdu/W7evLl++OGHgq0OAADARS6Hm2rVqmn//v2SpNq1a+uTTz6RdPGKTtmyZQu0OAAAAFe5HG4eeeQRbd26VZI0YsQITZ48WT4+Pho2bJiGDx9e4AUCAAC4wuUViv/u4MGD2rhxoyIiIhQdHV1QdRUaVigGAOD6U6grFP9dWFiYwsLCrvUwAAAABcLpYakVK1YoMjJS6enpObalpaWpbt26WrVqVYEWBwAA4Cqnw83EiRPVv3//XC8FBQQE6LHHHtOECRMKtDgAAABXOR1utm7dqg4dOuS5vV27dtq4cWOBFAUAAJBfToebY8eOqWTJknluL1GihH7//fcCKQoAACC/nA43lStXVmJiYp7bf/nlF4WEhBRIUQAAAPnldLi566679O9//1sZGRk5tp07d05xcXG65557CrQ4AAAAVzm9zs2xY8fUoEEDeXp6atCgQapVq5Yk6ddff9XkyZOVlZWlTZs2qWLFioVa8LVinRsAAK4/hbLOTcWKFbV69WoNGDBAsbGxupSJbDab2rdvr8mTJxf7YAMAAKzPpUX8wsLC9PXXXys1NVVJSUkyxqhGjRoqV65cYdUHAADgknytUFyuXDnddtttBV0LAADANXP5izMBAACKM8INAACwFMINAACwFKfCTYMGDZSamipJGj16tM6ePVuoRQEAAOSXU+Fm586dOnPmjCRp1KhROn36dKEWBQAAkF9O3S1166236pFHHlHz5s1ljNHrr7+uMmXK5Nr3P//5T4EWCADOyMo2Wrf/hI6fylCQn48ah5eXp4fN3WUBcAOnVijetWuX4uLitHfvXm3atEmRkZEqUSJnLrLZbNq0aZNLBRw+fFjPPfecvvnmG509e1YRERFKSEhQo0aN8txn5cqVeuqpp7R9+3aFhobqxRdfVJ8+fZw6HysUA9azODFFoxbuUEra/74eJiTAR3GdItUhiu+8A6zAlc9vp79+4RIPDw8dPXpUQUFB11SkJKWmpqp+/fq64447NGDAAAUGBmrPnj2qXr26qlevnus++/fvV1RUlB5//HE9+uijWr58uYYOHapFixapffv2Vz0n4QawlsWJKRowe5P+/j+yS9ds4ns2IOAAFlCo4aYgjRgxQj/99JNWrVrl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UYeoEMX3bKBRC3c4TC4ODvBRXKdIbgOHZeRrnZtLQWTWrFlq3769tmzZoqioKJeP89tvv6l79+76888/FRgYqObNm2vt2rUKDAyUJCUnJ8vD43/rDKampqp///46evSoypUrp4YNG2r16tWKjIzMz8sAgBtOh6gQtY0MZoViWJpLE4rT0tL0yiuv6O2339att96q8ePHq0WLFoVZX4FzZUISAAAoHgplQvGrr76q8ePHKzg4WB999FGuw1QAAADu5vSVGw8PD/n6+qpNmzby9PTMs9/8+fMLrLjCwJUbAACuP4Vy5aZXr158jxMAACj2nA43M2fOLMQyAAAACobH1bsAAABcPwg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUgg3AADAUkq4uwAAAIqzrGyjdftP6PipDAX5+ahxeHl5etjcXRauwK1XbkaOHCmbzebwqF279hX3mTdvnmrXri0fHx/Vq1dPX3/9dRFVCwC40SxOTFHz8SvUffpaDfl4i7pPX6vm41docWKKu0vDFbh9WKpu3bpKSUmxP3788cc8+65evVrdu3dXv379tHnzZnXp0kVdunRRYmJiEVYMALgRLE5M0YDZm5SSluHQfjQtQwNmbyLgFGNuDzclSpRQcHCw/XHTTTfl2ffNN99Uhw4dNHz4cNWpU0djxoxRgwYNNGnSpCKsGABgdVnZRqMW7pDJZdultlELdygrO7cecDe3h5s9e/aoUqVKqlatmnr06KHk5OQ8+65Zs0Zt2rRxaGvfvr3WrFmT5z6ZmZlKT093eAAAcCXr9p/IccXmckZSSlqG1u0/UXRFwWluDTe33367Zs6cqcWLFys+Pl779+9XixYtdOrUqVz7Hz16VBUrVnRoq1ixoo4ePZrnOcaOHauAgAD7IzQ0tEBfAwDAeo6fyjvY5KcfipZb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\n" + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "- 이제 이 그래프는 우리가 수집한 데이터입니다.\n", + "- 우리는 자동차 가격이 100달러일 때 판매된 자동차의 수가 무엇일지를 묻는 질문을 가지고 있습니다.\n", + "- 이 질문을 해결하기 위해 우리는 선형 회귀를 사용해야 합니다.\n", + "- 우리는 이 데이터에 맞는 선을 찾아야 합니다. 목표는 최소한의 오류로 선을 맞추는 것입니다.\n", + "\n", + "---\n", + "\n", + "- **선형 회귀의 단계**\n", + " 1. LinearRegression 클래스를 생성합니다.\n", + " 2. 이 LinearRegression 클래스로 모델을 정의합니다.\n", + " 3. MSE: 평균 제곱 오차(Mean Squared Error)\n", + " 4. 최적화 (SGD: 확률적 경사 하강법)\n", + " 5. 역전파 (Backpropagation)\n", + " 6. 예측 (Prediction)" + ], + "metadata": { + "id": "MOlzmdG89JCT" + } + }, + { + "cell_type": "code", + "source": [ + "# PyTorch를 이용한 선형 회귀 모델 구현\n", + "\n", + "import torch\n", + "from torch.autograd import Variable\n", + "import torch.nn as nn\n", + "import warnings\n", + "warnings.filterwarnings(\"ignore\")\n", + "\n", + "# 선형 회귀 클래스 정의\n", + "class LinearRegression(nn.Module):\n", + " def __init__(self, input_size, output_size):\n", + " super(LinearRegression, self).__init__()\n", + " self.linear = nn.Linear(input_dim,output_dim) # apply linear function\n", + "\n", + " def forward(self, x):\n", + " return self.linear(x)\n", + "\n", + "# 모델 정의\n", + "input_dim = 1\n", + "output_dim = 1\n", + "model = LinearRegression(input_dim,output_dim)\n", + "\n", + "# 손실 함수 (MSE)\n", + "mse = nn.MSELoss()\n", + "\n", + "# 옵티마이저 (SGD 사용)\n", + "learning_rate = 0.02\n", + "optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)\n", + "\n", + "# 모델 학습\n", + "loss_list = []\n", + "iteration_number = 1001\n", + "for iteration in range(iteration_number):\n", + " optimizer.zero_grad() # 기울기 초기화\n", + " results = model(car_price_tensor) # 예측값 계산\n", + " loss = mse(results, number_of_car_sell_tensor) # 손실 계산\n", + " # 역전파 실행\n", + " loss.backward()\n", + " # 가중치 업데이트\n", + " optimizer.step()\n", + " # loss 저장\n", + " loss_list.append(loss.data)\n", + " # loss 출력\n", + " if iteration % 50 == 0:\n", + " print(f'epoch {iteration}, loss {loss.data}')\n", + "\n", + "# 손실 그래프 시각화\n", + "plt.plot(range(iteration_number), loss_list)\n", + "plt.xlabel(\"Number of Iterations\")\n", + "plt.ylabel(\"Loss\")\n", + "plt.show()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 783 + }, + "id": "6MvDUc9b7uHf", + "outputId": "1fc864fd-f7b0-4c7d-a07d-a5a2585271d6" + }, + "execution_count": 30, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "epoch 0, loss 70.01976776123047\n", + "epoch 50, loss 4.674535274505615\n", + "epoch 100, loss 3.158778667449951\n", + "epoch 150, loss 2.1345200538635254\n", + "epoch 200, loss 1.4423836469650269\n", + "epoch 250, loss 0.9746794700622559\n", + "epoch 300, loss 0.6586312651634216\n", + "epoch 350, loss 0.4450644552707672\n", + "epoch 400, loss 0.3007489740848541\n", + "epoch 450, loss 0.2032289206981659\n", + "epoch 500, loss 0.1373298615217209\n", + "epoch 550, loss 0.09279962629079819\n", + "epoch 600, loss 0.06270873546600342\n", + "epoch 650, loss 0.042374614626169205\n", + "epoch 700, loss 0.028634270653128624\n", + "epoch 750, loss 0.01934942603111267\n", + "epoch 800, loss 0.013075322844088078\n", + "epoch 850, loss 0.008835666812956333\n", + "epoch 900, loss 0.005970746278762817\n", + "epoch 950, loss 0.004034693352878094\n", + "epoch 1000, loss 0.0027263991069048643\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "
" + ], + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjIAAAGwCAYAAACzXI8XAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAOzpJREFUeJzt3Xt4VNW9//HPTC6TQDITEiAhkADWS0AuRVAIoFJNTam1KGiVQy1efvVYoxXxUjnWG60C9qiojVI9HtRWSkuP4OWpWIyKtYZbFCsoERFMNCTIJZlwyXXW748kOwx3Z5KsDLxfz7OfZNbes+c7i9p8nrXW3ttljDECAACIQG7bBQAAAISKIAMAACIWQQYAAEQsggwAAIhYBBkAABCxCDIAACBiEWQAAEDEirZdQHsLBAIqKytTYmKiXC6X7XIAAMAxMMaourpa6enpcrsPP+5y3AeZsrIyZWRk2C4DAACEoLS0VH369Dns/uM+yCQmJkpq6giv12u5GgAAcCz8fr8yMjKcv+OHc9wHmZbpJK/XS5ABACDCHG1ZCIt9AQBAxCLIAACAiEWQAQAAEYsgAwAAIhZBBgAARCyCDAAAiFgEGQAAELEIMgAAIGIRZAAAQMQiyAAAgIhlNcj069dPLpfroC0vL0+SVFNTo7y8PKWkpCghIUGTJk1SRUWFzZIBAEAnYjXIrF69Wlu3bnW2ZcuWSZIuu+wySdItt9yiV199VYsWLdLy5ctVVlamiRMn2iwZAAB0Ii5jjLFdRItp06bptdde08aNG+X3+9WjRw8tWLBAl156qSRpw4YNGjBggAoLCzVq1KhjOqff75fP51NVVVWbPjRy15467alrUGJcjHzxMW12XgAAcOx/vzvNGpm6ujr96U9/0jXXXCOXy6WioiLV19crJyfHOSYrK0uZmZkqLCw87Hlqa2vl9/uDtvbw0BvFGjvnbT3//pZ2OT8AADi6ThNklixZosrKSl111VWSpPLycsXGxiopKSnouNTUVJWXlx/2PLNmzZLP53O2jIyMdqxa6jzjWQAAnHg6TZB59tlnNX78eKWnp4d1nhkzZqiqqsrZSktL26jCYC5Xu5wWAAB8C9G2C5CkL7/8Um+++aZeeuklpy0tLU11dXWqrKwMGpWpqKhQWlraYc/l8Xjk8Xjas1xJUkuOMWJIBgAAWzrFiMz8+fPVs2dPXXjhhU7b8OHDFRMTo4KCAqetuLhYJSUlys7OtlHmITG1BACAPdZHZAKBgObPn6+pU6cqOrq1HJ/Pp2uvvVbTp09XcnKyvF6vbrrpJmVnZx/zFUvtiaklAADssx5k3nzzTZWUlOiaa645aN+jjz4qt9utSZMmqba2Vrm5uXryySctVHl4DMgAAGCP9SBzwQUX6HC3somLi1N+fr7y8/M7uKqjc7WskmFuCQAAazrFGplIxNQSAAD2EWTCxHgMAAD2EGRC5Fx+TZIBAMAagkyIXMwtAQBgHUEmTNwQDwAAewgyYWJqCQAAewgyIWJmCQAA+wgyYWJABgAAewgyIXJuiAcAAKwhyITIxY19AQCwjiATJq5aAgDAHoJMiJhYAgDAPoJMuBiQAQDAGoJMiJw1MnbLAADghEaQCRGPKAAAwD6CTJgMly0BAGANQSZEPP0aAAD7CDKhYmYJAADrCDJhYkAGAAB7CDIh4hEFAADYR5AJEY8oAADAPoJMmHhEAQAA9hBkQsTEEgAA9hFkQsTUEgAA9hFkAABAxCLIhIirlgAAsI8gEyYeUQAAgD0EmRDx9GsAAOwjyISIiSUAAOwjyISJmSUAAOwhyITKxZgMAAC2EWRC1BJjuLMvAAD2EGTCxNQSAAD2EGRCxMwSAAD2EWRC1HJDPAZkAACwhyATJqaWAACwhyATIqaWAACwz3qQ+frrr/XTn/5UKSkpio+P1+DBg7VmzRpnvzFG99xzj3r16qX4+Hjl5ORo48aNFitu0ppjGJIBAMAWq0Fm165dGjNmjGJiYvT666/rk08+0cMPP6xu3bo5xzz00EN6/PHHNW/ePK1cuVJdu3ZVbm6uampqLFbeiqklAADsibb54XPmzFFGRobmz5/vtPXv39/53RijuXPn6te//rUmTJggSXrhhReUmpqqJUuW6IorrujwmlswtQQAgH1WR2ReeeUVjRgxQpdddpl69uypYcOG6ZlnnnH2b968WeXl5crJyXHafD6fRo4cqcLCwkOes7a2Vn6/P2hrD67mJMOIDAAA9lgNMl988YWeeuopnXLKKXrjjTf0i1/8Qr/85S/1/PPPS5LKy8slSampqUHvS01NdfYdaNasWfL5fM6WkZHRrt+BO/sCAGCP1SATCAR0xhln6MEHH9SwYcN03XXX6ec//7nmzZsX8jlnzJihqqoqZystLW3DigEAQGdiNcj06tVLAwcODGobMGCASkpKJElpaWmSpIqKiqBjKioqnH0H8ng88nq9QVt7YmoJAAB7rAaZMWPGqLi4OKjts88+U9++fSU1LfxNS0tTQUGBs9/v92vlypXKzs7u0FoPxGJfAADss3rV0i233KLRo0frwQcf1E9+8hOtWrVKTz/9tJ5++mlJTQtqp02bpt/+9rc65ZRT1L9/f919991KT0/XxRdfbLN0HlEAAEAnYDXInHnmmVq8eLFmzJihmTNnqn///po7d66mTJniHHPHHXdoz549uu6661RZWamxY8dq6dKliouLs1h5K6aWAACwx2XM8f2n2O/3y+fzqaqqqk3Xy8xbvkmzX9+gSWf00cM/Gdpm5wUAAMf+99v6IwoiVcsSGS6/BgDAHoJMuMgxAABYQ5AJEVctAQBgH0EmRFy1BACAfQSZMB3na6UBAOjUCDIhYmoJAAD7CDJhYjwGAAB7CDIAACBiEWRC5GqeW2KJDAAA9hBkwkSOAQDAHoJMiFjrCwCAfQSZELVctcTl1wAA2EOQCRMxBgAAewgyIWJqCQAA+wgyIXI5c0t26wAA4ERGkAmTIckAAGANQSZEPKIAAAD7CDIhaskxXLQEAIA9BBkAABCxCDKh4hEFAABYR5AJkTO1xGJfAACsIcgAAICIRZAJUesjCuzWAQDAiYwgEyZyDAAA9hBkQuTiIQUAAFhHkAkRU0sAANhHkAkbSQYAAFsIMiFiYgkAAPsIMiFiagkAAPsIMmEixwAAYA9BJkRctQQAgH0EmVA5U0uMyQAAYAtBBgAARCyCTIhaHxoJAABsIciEyNV82RIzSwAA2EOQAQAAEYsgEyKmlgAAsM9qkLnvvvvkcrmCtqysLGd/TU2N8vLylJKSooSEBE2aNEkVFRUWKz4YVy0BAGCP9RGZ008/XVu3bnW29957z9l3yy236NVXX9WiRYu0fPlylZWVaeLEiRarbeXiNjIAAFgXbb2A6GilpaUd1F5VVaVnn31WCxYs0HnnnSdJmj9/vgYMGKAVK1Zo1KhRHV1qEIIMAAD2WR+R2bhxo9LT03XSSSdpypQpKikpkSQVFRWpvr5eOTk5zrFZWVnKzMxUYWHhYc9XW1srv98ftLUnZpYAALDHapAZOXKknnvuOS1dulRPPfWUNm/erLPPPlvV1dUqLy9XbGyskpKSgt6Tmpqq8vLyw55z1qxZ8vl8zpaRkdEutfOIAgAA7LM6tTR+/Hjn9yFDhmjkyJHq27ev/vrXvyo+Pj6kc86YMUPTp093Xvv9/nYJM87Tr7luCQAAa6xPLe0vKSlJp556qj7//HOlpaWprq5OlZWVQcdUVFQcck1NC4/HI6/XG7QBAIDjU6cKMrt379amTZvUq1cvDR8+XDExMSooKHD2FxcXq6SkRNnZ2RarDMYaGQAA7LE6tXTbbbfpoosuUt++fVVWVqZ7771XUVFRmjx5snw+n6699lpNnz5dycnJ8nq9uummm5SdnW39iiWJRxQAANAZWA0yX331lSZPnqwdO3aoR48eGjt2rFasWKEePXpIkh599FG53W5NmjRJtbW1ys3N1ZNPPmmzZAAA0IlYDTILFy484v64uDjl5+crPz+/gyo6dq2PKGBIBgAAWzrVGplI4ly1RI4BAMAaggwAAIhYBJkQtdwQjwEZAADsIciEyNW6SAYAAFhCkAEAABGLIBMirloCAMA+ggwAAIhYBJkQcfk1AAD2EWRCxlVLAADYRpABAAARiyATotapJcZkAACwhSATIm4jAwCAfQQZAAAQsQgyIXI1zy0xswQAgD0EmRAxtQQAgH0EGQAAELEIMiFqfWgkYzIAANhCkAmRc/m13TIAADihEWQAAEDEIsiEyCWuWgIAwDaCDAAAiFgEmVA5a2QYkgEAwBaCTIi4aAkAAPsIMgAAIGIRZELEIwoAALCPIBMiHlEAAIB9BBkAABCxCDIhcu7sy9wSAADWEGRC5HImlwAAgC0EGQAAELEIMiFqnVqyWwcAACcygkyImFgCAMA+gkyYeEQBAAD2EGRCxdQSAADWEWQAAEDEIsiEqOXyawZkAACwhyATIm6IBwCAfZ0myMyePVsul0vTpk1z2mpqapSXl6eUlBQlJCRo0qRJqqiosFckAADoVDpFkFm9erX+8Ic/aMiQIUHtt9xyi1599VUtWrRIy5cvV1lZmSZOnGipymA8NBIAAPusB5ndu3drypQpeuaZZ9StWzenvaqqSs8++6weeeQRnXfeeRo+fLjmz5+v999/XytWrLBYcROXM7dktw4AAE5k1oNMXl6eLrzwQuXk5AS1FxUVqb6+Pqg9KytLmZmZKiwsPOz5amtr5ff7gzYAAHB8irb54QsXLtQHH3yg1atXH7SvvLxcsbGxSkpKCmpPTU1VeXn5Yc85a9Ys3X///W1d6kEYkAEAwD5rIzKlpaW6+eab9eKLLyouLq7NzjtjxgxVVVU5W2lpaZude388ogAAAPusBZmioiJt27ZNZ5xxhqKjoxUdHa3ly5fr8ccfV3R0tFJTU1VXV6fKysqg91VUVCgtLe2w5/V4PPJ6vUFbe+LyawAA7LE2tXT++efr448/Dmq7+uqrlZWVpV/96lfKyMhQTEyMCgoKNGnSJElScXGxSkpKlJ2dbaPkIEwtAQBgn7Ugk5iYqEGDBgW1de3aVSkpKU77tddeq+nTpys5OVler1c33XSTsrOzNWrUKBslH4DJJQAAbLO62PdoHn30Ubndbk2aNEm1tbXKzc3Vk08+abusIMwsAQBgT6cKMu+8807Q67i4OOXn5ys/P99OQUfQOrVEkgEAwBbr95GJVEwsAQBgH0EmTEwtAQBgD0EmRC2PKCDIAABgD0EGAABELIJMiFgjAwCAfQSZEDlXLTG3BACANSEFmdLSUn311VfO61WrVmnatGl6+umn26wwAACAowkpyPzHf/yH3n77bUlNT6n+/ve/r1WrVumuu+7SzJkz27TAzsrVPLnEeAwAAPaEFGTWrVuns846S5L017/+VYMGDdL777+vF198Uc8991xb1tdpuVgkAwCAdSEFmfr6enk8HknSm2++qR//+MeSpKysLG3durXtqosALJEBAMCekILM6aefrnnz5umf//ynli1bph/84AeSpLKyMqWkpLRpgZ0djygAAMCekILMnDlz9Ic//EHjxo3T5MmTNXToUEnSK6+84kw5He+YWgIAwL6QHho5btw4bd++XX6/X926dXPar7vuOnXp0qXNiosETC0BAGBPSCMy+/btU21trRNivvzyS82dO1fFxcXq2bNnmxbYWXHVEgAA9oUUZCZMmKAXXnhBklRZWamRI0fq4Ycf1sUXX6ynnnqqTQvsrJhaAgDAvpCCzAcffKCzzz5bkvS3v/1Nqamp+vLLL/XCCy/o8ccfb9MCOzumlgAAsCekILN3714lJiZKkv7xj39o4sSJcrvdGjVqlL788ss2LbCzah2RIckAAGBLSEHm5JNP1pIlS1RaWqo33nhDF1xwgSRp27Zt8nq9bVogAADA4YQUZO655x7ddttt6tevn8466yxlZ2dLahqdGTZsWJsW2Fk5i30ZkAEA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0qVLTY8ePbg0+Aj2v2rJGPq4raxatcpER0ebBx54wGzcuNG8+OKLpkuXLuZPf/qTc8zs2bNNUlKSefnll82///1vM2HChENewjps2DCzcuVK895775lTTjnlhL4s+EBTp041vXv3di6/fumll0z37t3NHXfc4RxDP3971dXV5sMPPzQffvihkWQeeeQR8+GHH5ovv/zSGNM2fVpZWWlSU1PNlVdeadatW2cWLlxounTpwuXXNj3xxBMmMzPTxMbGmrPOOsusWLHCdkkRRdIht/nz5zvH7Nu3z9xwww2mW7dupkuXLuaSSy4xW7duDTrPli1bzPjx4018fLzp3r27ufXWW019fX0Hf5vIcWCQoY/bzquvvmoGDRpkPB6PycrKMk8//XTQ/kAgYO6++26TmppqPB6POf/8801xcXHQMTt27DCTJ082CQkJxuv1mquvvtpUV1d35Nfo1Px+v7n55ptNZmamiYuLMyeddJK56667gi7ppZ+/vbfffvuQ/388depUY0zb9elHH31kxo4dazwej+ndu7eZPXt2m9TvMma/WyICAABEENbIAACAiEWQAQAAEYsgAwAAIhZBBgAARCyCDAAAiFgEGQAAELEIMgAAIGIRZAAAQMQiyAA4qi1btsjlcmnt2rW2S3Fs2LBBo0aNUlxcnL773e/aLudb6devn+bOnWu7DOC4QJABIsBVV10ll8ul2bNnB7UvWbJELpfLUlV23XvvveratauKi4uDHmi3v6uuukoXX3yx83rcuHGaNm1axxQo6bnnnlNSUtJB7atXr9Z1113XYXUAxzOCDBAh4uLiNGfOHO3atct2KW2mrq4u5Pdu2rRJY8eOVd++fZWSktKGVR1dOHVLUo8ePdSlS5c2qgY4sRFkgAiRk5OjtLQ0zZo167DH3HfffQdNs8ydO1f9+vVzXreMUjz44INKTU1VUlKSZs6cqYaGBt1+++1KTk5Wnz59NH/+/IPOv2HDBo0ePVpxcXEaNGiQli9fHrR/3bp1Gj9+vBISEpSamqorr7xS27dvd/aPGzdON954o6ZNm6bu3bsrNzf3kN8jEAho5syZ6tOnjzwej7773e9q6dKlzn6Xy6WioiLNnDlTLpdL99133xF6rvV7L1++XI899phcLpdcLpe2bNkSVt2PPPKIBg8erK5duyojI0M33HCDdu/eLUl65513dPXVV6uqqsr5vJY6D5xaKikp0YQJE5SQkCCv16uf/OQnqqiocPa3/Lv+8Y9/VL9+/eTz+XTFFVeourraOeZvf/ubBg8erPj4eKWkpCgnJ0d79uw5ar8AkY4gA0SIqKgoPfjgg3riiSf01VdfhXWut956S2VlZXr33Xf1yCOP6N5779WPfvQjdevWTStXrtT111+v//zP/zzoc26//Xbdeuut+vDDD5Wdna2LLrpIO3bskCRVVlbqvPPO07Bhw7RmzRotXbpUFRUV+slPfhJ0jueff16xsbH617/+pXnz5h2yvscee0wPP/yw/vu//1v//ve/lZubqx//+MfauHGjJGnr1q06/fTTdeutt2rr1q267bbbjvqdH3vsMWVnZ+vnP/+5tm7dqq1btyojIyOsut1utx5//HGtX79ezz//vN566y3dcccdkqTRo0dr7ty58nq9zucdqs5AIKAJEyZo586dWr58uZYtW6YvvvhCl19+edBxmzZt0pIlS/Taa6/ptdde0/Lly52pxq1bt2ry5Mm65ppr9Omnn+qdd97RxIkTxTOBcUJok2doA2hXU6dONRMmTDDGGDNq1ChzzTXXGGOMWbx4sdn/P+N7773XDB06NOi9jz76qOnbt2/Qufr27WsaGxudttNOO82cffbZzuuGhgbTtWtX8+c//9kYY8zmzZuNJDN79mznmPr6etOnTx8zZ84cY4wxv/nNb8wFF1wQ9NmlpaVGkikuLjbGGHPuueeaYcOGHfX7pqenmwceeCCo7cwzzzQ33HCD83ro0KHm3nvvPeJ59u+3ls+/+eabg45py7oXLVpkUlJSnNfz5883Pp/voOP69u1rHn30UWOMMf/4xz9MVFSUKSkpcfavX7/eSDKrVq0yxjT9u3bp0sX4/X7nmNtvv92MHDnSGGNMUVGRkWS2bNly1BqB4w0jMkCEmTNnjp5//nl9+umnIZ/j9NNPl9vd+p9/amqqBg8e7LyOiopSSkqKtm3bFvS+7Oxs5/fo6GiNGDHCqeOjjz7S22+/rYSEBGfLysqS1DSa0GL48OFHrM3v96usrExjxowJah8zZkxY3/lwwqn7zTff1Pnnn6/evXsrMTFRV155pXbs2KG9e/ce8+d/+umnysjIUEZGhtM2cOBAJSUlBX3ffv36KTEx0Xndq1cv599n6NChOv/88zV48GBddtlleuaZZ46rtVTAkRBkgAhzzjnnKDc3VzNmzDhon9vtPmg6ob6+/qDjYmJigl67XK5DtgUCgWOua/fu3brooou0du3aoG3jxo0655xznOO6du16zOfsCKHWvWXLFv3oRz/SkCFD9H//938qKipSfn6+pPAXAx/Kkf59oqKitGzZMr3++usaOHCgnnjiCZ122mnavHlzm9cBdDYEGSACzZ49W6+++qoKCwuD2nv06KHy8vKgMNOW935ZsWKF83tDQ4OKioo0YMAASdIZZ5yh9evXq1+/fjr55JODtm8TXrxer9LT0/Wvf/0rqP1f//qXBg4cGFb9sbGxamxsDGoLte6ioiIFAgE9/PDDGjVqlE499VSVlZUd9fMONGDAAJWWlqq0tNRp++STT1RZWfmtvq/L5dKYMWN0//3368MPP1RsbKwWL158zO8HIhVBBohAgwcP1pQpU/T4448HtY8bN07ffPONHnroIW3atEn5+fl6/fXX2+xz8/PztXjxYm3YsEF5eXnatWuXrrnmGklSXl6edu7cqcmTJ2v16tXatGmT3njjDV199dVH/WN+oNtvv11z5szRX/7yFxUXF+vOO+/U2rVrdfPNN4dVf79+/bRy5Upt2bJF27dvVyAQCLnuk08+WfX19XriiSf0xRdf6I9//ONBi5f79eun3bt3q6CgQNu3bz/klFNOTo7z7/nBBx9o1apV+tnPfqZzzz1XI0aMOKbvtXLlSj344INas2aNSkpK9NJLL+mbb75xQiZwPCPIABFq5syZB039DBgwQE8++aTy8/M1dOhQrVq16piu6DlWs2fP1uzZszV06FC99957euWVV9S9e3dJckZRGhsbdcEFF2jw4MGaNm2akpKSgtbjHItf/vKXmj59um699VYNHjxYS5cu1SuvvKJTTjklrPpvu+02RUVFaeDAgerRo4dKSkpCrnvo0KF65JFHNGfOHA0aNEgvvvjiQZfGjx49Wtdff70uv/xy9ejRQw899NBB53G5XHr55ZfVrVs3nXPOOcrJydFJJ52kv/zlL8f8vbxer95991398Ic/1Kmnnqpf//rXevjhhzV+/Phj7xwgQrnMgRPqAAAAEYIRGQAAELEIMgAAIGIRZAAAQMQiyAAAgIhFkAEAABGLIAMAACIWQQYAAEQsggwAAIhYBBkAABCxCDIAACBiEWQAAEDE+v8k0RvJKmGLowAAAABJRU5ErkJggg==\n" + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "- 반복 횟수는 1000입니다.\n", + "- 손실 값은 거의 0에 가까우며, 이는 그래프나 1000번째 epoch에서의 손실 값에서 확인할 수 있습니다.\n", + "- 이제 우리는 훈련된 모델을 가지고 있습니다.\n", + "- 훈련된 모델을 사용할 때, 자동차 가격을 예측해 봅시다." + ], + "metadata": { + "id": "9odY_Wb59N8H" + } + }, + { + "cell_type": "code", + "source": [ + "# car price 예측\n", + "predicted = model(car_price_tensor).data.numpy()\n", + "plt.scatter(car_prices_array,number_of_car_sell_array,label = \"original data\",color =\"red\") # original data\n", + "plt.scatter(car_prices_array,predicted,label = \"predicted data\",color =\"blue\") # predicted data\n", + "\n", + "# car price가 10$ 일 때, car sell은?\n", + "predicted_10 = model(Variable(torch.Tensor([[10]]))).data.numpy()\n", + "plt.scatter(10,predicted_10,label = \"car price 10$\",color =\"green\")\n", + "plt.legend()\n", + "plt.xlabel(\"Car Price $\")\n", + "plt.ylabel(\"Number of Car Sell\")\n", + "plt.title(\"Original vs Predicted values\")\n", + "plt.show()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 420 + }, + "id": "FtsHa2uk7uDy", + "outputId": "efbc1afc-8993-418d-ee8f-b95fe633a933" + }, + "execution_count": 31, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "
" + ], + "image/png": "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cpOeng4XF5di5XK5HM+ePdNIUERERETlpXZy4+HhgYSEhGLl27Ztg7e3t0aCIiIiIiovtZ8KPn36dAwYMADp6emQy+WIjY3FpUuXsG7dOuzevbsyYiQiIiJSmdo9N926dcOuXbtw4MABmJiYYPr06UhOTsauXbvw9ttvV0aMRERERCrjs6WIiIjolafxZ0uV5unTp9i8eTMeP36MDh06wNXVtSKHIyIiIqowlZObcePG4dmzZ1i6dCkAID8/H61bt8bFixdRs2ZNTJw4Efv374efn1+lBUtERET0MirPudm3b5/SnJr169cjNTUVV65cQWZmJnr16oUvv/yyUoIkIiIiUpXKyU1qaio8PDwUr/ft24eePXvCwcEBEokEYWFhOHv2bKUESURERKQqlZMbAwMDPD/3+Pjx42jdurXida1atZCZmanZ6IiIiIjUpHJy4+7ujl27dgEALly4gNTUVAQFBSnqb968CWtra81HSERERKQGlScUT5o0CX369MFPP/2ECxcuoFOnTnByclLU79mzBy1btqyUIImIiIhUpXLPzfvvv489e/agWbNmGDt2LDZv3qxUX7NmTYwYMULjARIRERGpg4v4ERER0StPne9vtR+/QERERPQqY3JDREREeoXJDREREekVtZIbIQRSU1Px9OnTyoqHiIiIqELUTm5cXFyQlpZWWfEQERERVYhayY2BgQFcXV1x//79yoqHiIiIqELUnnMzb948TJw4EefPn6+MeIiIiIgqRO11bmrXro3Hjx+joKAANWrUgLGxsVL9gwcPNBqgpnGdGyIiotePOt/fKj9+oUhkZGR54yIiIiKqdGonNwMGDKiMOIiIiIg0Qu3k5nlPnz5Ffn6+UhmHeoiIiEiX1J5Q/OjRI4waNQpWVlYwMTFB7dq1lTYiIiIiXVI7uZk0aRJ+/fVXrFixAoaGhlizZg1mzZqFevXqYd26dZURIxEREZHK1B6W2rVrF9atW4fAwEAMGjQI/v7+cHFxgYODA9avX4++fftWRpxE9KqTyYCEBCAjA7C1Bfz9AalU11ERURWkds/NgwcP4OzsDKBwfk3Rrd/t2rXDb7/9pnYA6enp+Oijj1C3bl0YGxvD09MTp06dKnOf+Ph4+Pj4wNDQEC4uLoiJiVH7vESkQbGxgKMjEBQEfPhh4Z+OjoXlRERapnZy4+zsjJSUFABA48aNsWXLFgCFPTq1atVS61iZmZlo27Ytqlevjp9//hkXL17EokWLypy7k5KSgs6dOyMoKAhJSUkIDw/H0KFDERcXp+6lEJEmxMYCPXtCdus24hGAjeiDeARAdisD6NmTCQ4RaZ3ai/h99dVXkEqlGDNmDA4cOICuXbtCCIFnz55h8eLFCAsLU/lYn332GY4cOYKEhASV95k8eTJ++uknpRWS+/Tpg6ysLOzdu/el+3MRPyINkskAR0fE3noTYYjCLdgrquyQhiiEI8Q+EUhJ4RAVEVWIOt/faic3L7p58yZOnz4NFxcXNGvWTK19PTw8EBwcjFu3buHQoUOoX78+RowYgWHDhpW6z1tvvQUfHx+lxQSjo6MRHh6O7OzsYu3z8vKQl5eneJ2TkwN7e3smN0SaEB+P2KAl6IltKPxB8m9nsARyAMA29ETIwTFAYKAuIiQiPaFOcqP2sNSLHBwcEBISonZiAwDXr1/HihUr4Orqiri4OHz66acYM2YM1q5dW+o+d+7cgbW1tVKZtbU1cnJy8OTJk2LtIyIiYGFhodjs7e2LtSGi8pGl30EYooolNgAg/nkdjkjI0u9oPTYiqrpUTm5+/fVXeHh4ICcnp1hddnY2mjRpotbwEgDI5XL4+Phg7ty58Pb2xscff4xhw4Zh5cqVah2nLFOmTEF2drZiS0tL09ixiaq6hL8a/zMUVfKPEgEDpKEBEv5qrN3AiKhKUzm5iYyMxLBhw0rsCrKwsMDw4cOxePFitU5ua2sLDw8PpTJ3d3ekpqaWuo+NjQ3u3r2rVHb37l2Ym5sXe4gnABgaGsLc3FxpIyLNyLBUrcdW1XZERJqgcnLz+++/49133y21/p133sHp06fVOnnbtm1x6dIlpbLLly/DwcGh1H38/Pzwyy+/KJXt378ffn5+ap2biCrOtr5qP0JUbUdEpAkq/8S5e/cuqlevXmp9tWrV8Ndff6l18rFjx+L48eOYO3curl69ig0bNmD16tUYOXKkos2UKVPQv39/xetPPvkE169fx6RJk/Dnn3/i66+/xpYtWzB27Fi1zk1EFefvD9jZARKUfF+CBAL29oXtiIi0ReXkpn79+kq3X7/ojz/+gK2trVonf/PNN7Fjxw5s3LgRTZs2xezZsxEZGam0ynFGRobSMJWTkxN++ukn7N+/H15eXli0aBHWrFmD4OBgtc5NRBUnlQJRUQAkEkgkygmORCIAiQSRkbwLnIi0S+VbwUePHo34+HgkJibCyMhIqe7Jkydo2bIlgoKCsGTJkkoJVFO4zg2R5sXGAmFhwK1b/5bZ2wORkUBIiM7CIiI9Uinr3Ny9exc+Pj6QSqUYNWoUGjVqBAD4888/sXz5cshkMpw5c6bYbdqvGiY3RJWDj5YiospUaYv43bx5E59++ini4uJQtJtEIkFwcDCWL18OJyenikWuBUxuiIiIXj/qfH+r9VRwBwcH7NmzB5mZmbh69SqEEHB1dS3zWVBERERE2qRWclOkdu3aePPNNzUdCxEREVGFcfEJIiIi0itMboiIiEivMLkhIiIivaJScuPj44PMzEwAwBdffIHHjx9XalBERERE5aVScpOcnIxHjx4BAGbNmoXc3NxKDYqIiIiovFS6W6p58+YYNGgQ2rVrByEE/vvf/8LU1LTEttOnT9dogERERETqUGkRv0uXLmHGjBm4du0azpw5Aw8PD1SrVjwvkkgkOHPmTKUEqilcxI+IiOj1U2krFAOAgYEB7ty5AysrqwoFqStMboiIiF4/lbZCMQDI5fJyB0ZERERU2cq1QvG1a9cQGRmJ5ORkAICHhwfCwsLQsGFDjQZHREREpC6117mJi4uDh4cHTp48iWbNmqFZs2Y4ceIEmjRpgv3791dGjEREREQqU3vOjbe3N4KDgzFv3jyl8s8++wz79u3jhGIiIiLSOHW+v9XuuUlOTsaQIUOKlQ8ePBgXL15U93BEREREGqV2cmNpaYmkpKRi5UlJSa/tHVRERESkP9SeUDxs2DB8/PHHuH79Otq0aQMAOHLkCObPn49x48ZpPEAiIiIidag950YIgcjISCxatAi3b98GANSrVw8TJ07EmDFjIJFIKiVQTeGcGyIiotdPpS7i97yHDx8CAMzMzMp7CK1jckNERPT6qdRF/J73OiU1REREVDWoPaGYiIiI6FXG5IaIiIj0CpMbIiIi0itqJTfPnj1D+/btceXKlcqKh4iIiKhC1Epuqlevjj/++KOyYiEiIiKqMLWHpT766CN8++23lRELERERUYWpfSt4QUEBvvvuOxw4cAC+vr4wMTFRql+8eLHGgiMiIiJSl9rJzfnz5+Hj4wMAuHz5slLdq746MREREek/tZObgwcPVkYcRERERBpR7lvBr169iri4ODx58gRA4TOniIiIiHRN7eTm/v37aN++Pdzc3NCpUydkZGQAAIYMGYLx48drPEAiIiIidaid3IwdOxbVq1dHamoqatasqSjv3bs39u7dq9HgiIiIiNSl9pybffv2IS4uDnZ2dkrlrq6uuHnzpsYCIyIiIioPtXtuHj16pNRjU+TBgwcwNDTUSFBERERE5aV2cuPv749169YpXkskEsjlcixYsABBQUEaDY6IiIhIXWoPSy1YsADt27fHqVOnkJ+fj0mTJuHChQt48OABjhw5UhkxEhG98mQyICEByMgAbG0Bf39AKtV1VERVk9o9N02bNsXly5fRrl07dOvWDY8ePUJISAjOnj2Lhg0bqnWsmTNnQiKRKG2NGzcutX1MTEyx9kZGRupeAhGRRsXGAo6OAkFBwIcfAkFBha9jY3UdGVHVpHbPDQBYWFhg6tSpGgmgSZMmOHDgwL8BVSs7JHNzc1y6dEnxmqsiE5EuxcYCPXsICAgA//48Sr8l0LMHsG27BCEhuouPqCoqV3KTmZmJb7/9FsnJyQAADw8PDBo0CHXq1FE/gGrVYGNjo3J7iUSiVnsiosoikwFhHz+GgBFe7AgXMIAEcoR//ATdutXkEBWRFqk9LPXbb7/B0dERS5YsQWZmJjIzM7FkyRI4OTnht99+UzuAK1euoF69enB2dkbfvn2RmppaZvvc3Fw4ODjA3t4e3bp1w4ULF9Q+JxGRJiTEy3Drfk2U9qNUwABp92siIV6m3cCIqji1k5uRI0eid+/eSElJQWxsLGJjY3H9+nX06dMHI0eOVOtYrVq1QkxMDPbu3YsVK1YgJSUF/v7+ePjwYYntGzVqhO+++w7/+9//8MMPP0Aul6NNmza4detWqefIy8tDTk6O0kZEpAkZ8Zde3kiNdkSkGRKh5kOhjI2NkZSUhEaNGimVX7p0Cc2bN1c8a6o8srKy4ODggMWLF2PIkCEvbf/s2TO4u7sjNDQUs2fPLrHNzJkzMWvWrGLl2dnZMDc3L3esRETx035B0JftX9ru4Oe/IHD2y9sRUelycnJgYWGh0ve32j03Pj4+irk2z0tOToaXl5e6h1NSq1YtuLm54erVqyq1r169Ory9vctsP2XKFGRnZyu2tLS0CsVIRFTEP1AKO6RBAnmJ9RLIYY9U+Adywg2RNqk0ofiPP/5Q/H3MmDEICwvD1atX0bp1awDA8ePHsXz5csybN69CweTm5uLatWvo16+fSu1lMhnOnTuHTp06ldrG0NCQKycTUaWQBvojqu4n6Hl/FSSQQzz3+2JRwhNZ90tIA1foKkSiKkmlYSkDAwNIJBK8rKlEIoFMpvrEuQkTJqBr165wcHDA7du3MWPGDCQlJeHixYuwtLRE//79Ub9+fURERAAAvvjiC7Ru3RouLi7IysrCwoULsXPnTpw+fRoeHh4qnVOdbi0iopeKjUVsj/UIQyRuwV5RbI9URGIsQrb3Be8FJ6o4db6/Veq5SUlJ0UhgL7p16xZCQ0Nx//59WFpaol27djh+/DgsLS0BAKmpqTAw+Pc3oczMTAwbNgx37txB7dq14evri6NHj6qc2BARaVxICEK2A93GtENCuhMyYAtbZMDf7gakUYuZ2BDpgNoTil937LkhokrB5y8QVSqN99y86Pbt2zh8+DDu3bsHuVx5It2YMWPKc0giotebVAoEBuo6CiJCOZKbmJgYDB8+HDVq1EDdunWVHn8gkUiY3BAREZFOqT0sZW9vj08++QRTpkxRmg/zuuCwFBER0eunUte5efz4Mfr06fNaJjZERESk/9TOUIYMGYKtW7dWRixEREREFab2sJRMJkOXLl3w5MkTeHp6onr16kr1ixcv1miAmsZhKSIiotdPpd4tFRERgbi4OMWzpV6cUExERESkS2onN4sWLcJ3332HgQMHVkI4RERERBWj9pwbQ0NDtG3btjJiISIiIqowtZObsLAwLF26tDJiISIiIqowtYelTp48iV9//RW7d+9GkyZNik0ojo2N1VhwREREROpSO7mpVasWQvggOCIiInpFqZ3cREdHV0YcRERERBrBZYaJiIhIr6jdc+Pk5FTmejbXr1+vUEBEREREFaF2chMeHq70+tmzZzh79iz27t2LiRMnaiouIiIionJRO7kJCwsrsXz58uU4depUhQMiIiIiqgiNzbnp2LEjtm/frqnDEREREZWLxpKbbdu2oU6dOpo6HBEREVG5qD0s5e3trTShWAiBO3fu4K+//sLXX3+t0eCIiIiI1KV2ctO9e3el1wYGBrC0tERgYCAaN26sqbiIiIiIykUihBC6DkKbcnJyYGFhgezsbJibm+s6HCIiIlKBOt/fXMSPiIiI9IrKw1IGBgZlLt4HABKJBAUFBRUOioiIiKi8VE5uduzYUWrdsWPH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\n" + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "
\n", + "## 3. 로지스틱 회귀\n", + "\n", + "- 선형 회귀는 분류 문제에서 적합하지 않습니다.\n", + "- 우리는 분류 문제를 해결하기 위해 로지스틱 회귀를 사용합니다.\n", + "- 선형 회귀 + 로지스틱 함수(소프트맥스) = 로지스틱 회귀\n", + "\n", + " \n", + "- **로지스틱 회귀의 단계**\n", + " 1. 라이브러리 가져오기\n", + " 2. 데이터셋 준비\n", + " - 우리는 MNIST 데이터셋을 사용합니다.\n", + " - 28x28 이미지와 0부터 9까지의 10개의 레이블이 있습니다.\n", + " - 데이터는 정규화되지 않았기 때문에 각 이미지를 255로 나눠 기본적인 정규화를 진행합니다.\n", + " - 데이터를 분할하기 위해 sklearn 라이브러리의 `train_test_split` 메서드를 사용합니다.\n", + " - 학습 데이터 크기는 80%, 테스트 데이터 크기는 20%입니다.\n", + " - 특성(feature)과 목표(target) 텐서를 생성합니다. 이후 텐서에서 변수(variable)를 생성합니다. 이 변수는 기울기 누적을 위해 정의됩니다.\n", + " - `batch_size` = 배치 크기는 예를 들어, 1000개의 샘플이 있을 때, 이 샘플을 한 번에 모두 훈련시킬 수도 있고, 100개의 샘플씩 10개의 그룹으로 나누어 순차적으로 훈련시킬 수도 있습니다. 예를 들어, `batch_size = 100`이라면, 모든 데이터를 한 번 훈련시키는 데 336개의 그룹을 사용합니다. 각 그룹은 100개의 샘플을 가지고 있으며, 총 33600개의 샘플을 훈련합니다.\n", + " - `epoch`: 1 epoch는 모든 샘플을 한 번 훈련시키는 것입니다.\n", + " - 예를 들어, 33600개의 샘플이 있고, 배치 크기(batch_size)는 100, epoch는 29로 설정한 경우, 29번 훈련을 진행합니다. 그럼 총 몇 번의 반복(iteration)이 필요한지 계산해 봅시다:\n", + " - 훈련 데이터 1번 = 33600개의 샘플 훈련\n", + " - 하지만 데이터를 336개의 그룹으로 나누었으므로, 1 epoch는 336번의 반복이 필요합니다.\n", + " - 29 epoch 동안 훈련하므로, 총 반복 횟수는 9744번입니다(대략 10000번).\n", + " - `TensorDataset()`: 텐서를 래핑하는 데이터셋. 각 샘플은 텐서를 첫 번째 차원으로 인덱싱하여 검색됩니다.\n", + " - `DataLoader()`: 데이터셋과 샘플을 결합하고, 데이터셋에 대한 다중 프로세스 반복기를 제공합니다.\n", + " - 데이터셋의 이미지를 하나 시각화해봅니다.\n", + " 3. 로지스틱 회귀 모델 생성\n", + " - 선형 회귀와 비슷합니다.\n", + " - 하지만 예측을 위해 모델에 로지스틱 함수(소프트맥스)가 포함되어야 합니다.\n", + " - PyTorch에서는 로지스틱 함수가 손실 함수에 포함되어 있으며, 이후 단계에서 이를 사용합니다.\n", + " 4. 모델 인스턴스화\n", + " - `input_dim = 28*28` # 이미지 크기 px*px\n", + " - `output_dim = 10` # 레이블 0,1,2,3,4,5,6,7,8,9\n", + " - 모델을 생성합니다.\n", + " 5. 손실 함수 인스턴스화\n", + " - 교차 엔트로피 손실\n", + " - 손실을 계산하는 함수로, 소프트맥스(로지스틱 함수)도 포함되어 있습니다.\n", + " 6. 옵티마이저 인스턴스화\n", + " - SGD 옵티마이저\n", + " 7. 모델 훈련\n", + " 8. 예측\n", + "- 결과적으로, 그래프에서 볼 수 있듯이 손실 값은 감소하고, 정확도는 약 85%까지 증가하며 모델이 훈련되고 있음을 확인할 수 있습니다." + ], + "metadata": { + "id": "7uXrwVTU9VFH" + } + }, + { + "cell_type": "code", + "source": [ + "import torch\n", + "import torch.nn as nn\n", + "from torch.autograd import Variable\n", + "from torch.utils.data import DataLoader\n", + "import pandas as pd\n", + "from sklearn.model_selection import train_test_split" + ], + "metadata": { + "id": "FvnI_38-8CfS" + }, + "execution_count": 32, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# 데이터셋 준비\n", + "# 데이터 로드\n", + "train = pd.read_csv(r\"./train.csv\", dtype=np.float32)\n", + "\n", + "# 데이터를 특징(픽셀 값)과 라벨(0~9 숫자)로 분리\n", + "targets_numpy = train.label.values\n", + "features_numpy = train.loc[:, train.columns != \"label\"].values / 255 # 정규화\n", + "\n", + "# 학습 데이터 80%, 테스트 데이터 20%로 분할\n", + "features_train, features_test, targets_train, targets_test = train_test_split(\n", + " features_numpy, targets_numpy, test_size=0.2, random_state=42\n", + ")\n", + "\n", + "# 학습 데이터셋을 텐서로 변환 (경사 계산을 위해 Variable 생성 필요)\n", + "featuresTrain = torch.from_numpy(features_train)\n", + "targetsTrain = torch.from_numpy(targets_train).type(torch.LongTensor) # 데이터 타입은 long\n", + "\n", + "# 테스트 데이터셋을 텐서로 변환\n", + "featuresTest = torch.from_numpy(features_test)\n", + "targetsTest = torch.from_numpy(targets_test).type(torch.LongTensor)\n", + "## 힌트: PyTorch Tesnor를 생성해주세요!\n", + "## 어떤 데이터를 PyTorch Tensor를 변환하고, 어떤 데이터타입을 선택할지 직접 작성해주세요\n", + "\n", + "# 배치 크기, 반복 횟수 및 에포크 설정\n", + "batch_size = 100\n", + "n_iters = 10000\n", + "num_epochs = int(n_iters / (len(features_train) / batch_size))\n", + "\n", + "# PyTorch 학습 및 테스트 데이터셋 생성\n", + "train = torch.utils.data.TensorDataset(featuresTrain, targetsTrain)\n", + "test = torch.utils.data.TensorDataset(featuresTest, targetsTest)\n", + "## 힌트:입력 데이터와 레이블을 텐서로 변환하여, 이들을 TensorDataset으로 묶어 train과 test 데이터셋을 구성합니다\n", + "## TensorDataset에는 두 개의 텐서를 전달해야 하며, 각각 특징과 레이블에 해당합니다.\n", + "## 첫 번째 텐서는 입력 데이터, 두 번째 텐서는 정답 데이터이며 입력 데이터와 정답 데이터의 샘플 수가 동일해야 합니다.\n", + "\n", + "# 데이터 로더 생성\n", + "train_loader = DataLoader(train, batch_size=batch_size, shuffle=False)\n", + "test_loader = DataLoader(test, batch_size=batch_size, shuffle=False)\n", + "\n", + "# 데이터셋 중 하나의 이미지를 시각화\n", + "plt.imshow(features_numpy[10].reshape(28, 28))\n", + "plt.axis(\"off\")\n", + "plt.title(str(targets_numpy[10]))\n", + "plt.savefig('graph.png')\n", + "plt.show()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 428 + }, + "id": "cPmM5DM48Cb6", + "outputId": "dae9582e-794b-4c72-b8aa-06d1498134a6" + }, + "execution_count": 33, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "
" + ], + "image/png": "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\n" + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "source": [ + "# 로지스틱 회귀 모델 생성\n", + "class LogisticRegressionModel(nn.Module):\n", + " def __init__(self, input_dim, output_dim):\n", + " super(LogisticRegressionModel, self).__init__()\n", + " # 선형 계층 설정\n", + " self.linear = nn.Linear(input_dim, output_dim)\n", + " # 로지스틱 함수는 손실 함수에 포함되어 있으므로 별도로 정의할 필요 없음\n", + "\n", + " def forward(self, x):\n", + " out = self.linear(x)\n", + " return out\n", + "\n", + "# 모델 인스턴스화\n", + "input_dim = 28 * 28 # 이미지 크기 (픽셀 * 픽셀)\n", + "output_dim = 10 # 출력 라벨 (0~9)\n", + "\n", + "# 로지스틱 회귀 모델 생성\n", + "model = LogisticRegressionModel(input_dim, output_dim)\n", + "\n", + "# 크로스 엔트로피 손실 함수\n", + "error = nn.CrossEntropyLoss()\n", + "\n", + "# SGD 옵티마이저 설정\n", + "learning_rate = 0.001\n", + "optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)\n", + "## 힌트: 모델 파라미터를 model.parameters()로 넘겨줍니다\n", + "## 학습률(learning rate)은 훈련 속도를 결정합니다\n", + "## 최적화 함수가 SGD이므로, torch.optim.SGD를 사용합니다" + ], + "metadata": { + "id": "S73aSJuz8CZq" + }, + "execution_count": 35, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# 모델 학습\n", + "count = 0\n", + "loss_list = []\n", + "iteration_list = []\n", + "for epoch in range(num_epochs):\n", + " for i, (images, labels) in enumerate(train_loader):\n", + "\n", + " # 변수 정의\n", + " train = Variable(images.view(-1, 28 * 28))\n", + " labels = Variable(labels)\n", + "\n", + " # 경사 초기화\n", + " optimizer.zero_grad()\n", + "\n", + " # 순전파\n", + " outputs = model(train)\n", + "\n", + " # 소프트맥스 및 크로스 엔트로피 손실 계산\n", + " loss = error(outputs, labels)\n", + "\n", + " # 역전파를 통한 경사 계산\n", + " loss.backward()\n", + "\n", + " # 가중치 업데이트\n", + " optimizer.step()\n", + "\n", + " count += 1\n", + "\n", + " # 정확도 측정\n", + " if count % 50 == 0:\n", + " correct = 0\n", + " total = 0\n", + " # 테스트 데이터셋 예측 수행\n", + " for images, labels in test_loader:\n", + " test = Variable(images.view(-1, 28 * 28))\n", + "\n", + " # 순전파\n", + " outputs = model(test)\n", + "\n", + " # 최댓값을 기준으로 예측값 결정\n", + " predicted = torch.max(outputs.data, 1)[1]\n", + "\n", + " # 전체 라벨 개수\n", + " total += len(labels)\n", + "\n", + " # 맞춘 개수 계산\n", + " correct += (predicted == labels).sum()\n", + "\n", + " accuracy = 100 * correct / float(total)\n", + "\n", + " # 손실 및 반복 횟수 저장\n", + " loss_list.append(loss.data)\n", + " iteration_list.append(count)\n", + "\n", + " # 500번마다 손실 출력\n", + " if count % 500 == 0:\n", + " print('Iteration: {} Loss: {} Accuracy: {}%'.format(count, loss.data, accuracy))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Oc4X8eZt8CXq", + "outputId": "85e1e957-0de9-4aaf-b409-f18f53599e5f" + }, + "execution_count": 36, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Iteration: 500 Loss: 1.838348388671875 Accuracy: 65.80824279785156%\n", + "Iteration: 1000 Loss: 1.5546265840530396 Accuracy: 73.49445343017578%\n", + "Iteration: 1500 Loss: 1.3525042533874512 Accuracy: 76.9809799194336%\n", + "Iteration: 2000 Loss: 1.171697974205017 Accuracy: 79.0015869140625%\n", + "Iteration: 2500 Loss: 1.1237819194793701 Accuracy: 80.46751403808594%\n", + "Iteration: 3000 Loss: 0.9832191467285156 Accuracy: 81.49761962890625%\n", + "Iteration: 3500 Loss: 0.9783283472061157 Accuracy: 81.85420227050781%\n", + "Iteration: 4000 Loss: 0.9412599802017212 Accuracy: 82.2900161743164%\n", + "Iteration: 4500 Loss: 0.7790096402168274 Accuracy: 82.9239273071289%\n", + "Iteration: 5000 Loss: 0.8179266452789307 Accuracy: 83.4389877319336%\n", + "Iteration: 5500 Loss: 0.7581114172935486 Accuracy: 83.47860717773438%\n", + "Iteration: 6000 Loss: 0.6648285388946533 Accuracy: 83.6370849609375%\n", + "Iteration: 6500 Loss: 0.7140300273895264 Accuracy: 83.95404052734375%\n", + "Iteration: 7000 Loss: 0.6943249702453613 Accuracy: 84.15213775634766%\n", + "Iteration: 7500 Loss: 0.6818585991859436 Accuracy: 84.31061553955078%\n", + "Iteration: 8000 Loss: 0.7642678618431091 Accuracy: 84.42947387695312%\n", + "Iteration: 8500 Loss: 0.5290299654006958 Accuracy: 84.42947387695312%\n", + "Iteration: 9000 Loss: 0.6268017292022705 Accuracy: 84.62757873535156%\n", + "Iteration: 9500 Loss: 0.4723178446292877 Accuracy: 84.78605651855469%\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "# 시각화\n", + "plt.plot(iteration_list,loss_list)\n", + "plt.xlabel(\"Number of iteration\")\n", + "plt.ylabel(\"Loss\")\n", + "plt.title(\"Logistic Regression: Loss vs Number of iteration\")\n", + "plt.show()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 413 + }, + "id": "FctKP-1E8CVh", + "outputId": "956ba417-8dd4-4b52-b036-fa879b4b2d4b" + }, + "execution_count": 38, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "
" + ], + "image/png": "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1aqqqvD222/D09MTo0aNalDf2iNmgtqBTz75xGpNwiOPPILnn39eXjPlwQcfhIuLC95//32Ul5fjlVdekds+8cQT+OKLL3DdddfhoYcekqfIR0VFITc3t86/WsaNG4fQ0FAMGzYMISEhOHHiBN555x1MmDABXl5eAIABAwYAAJ566ilMmzYNrq6umDhxovyLwNzjjz+Ob7/9FlOmTMFdd92FAQMGIDc3Fz/99BNWrFiBfv36NfoejR8/Hr1798Ybb7yBuXPnYtSoUbjvvvuwdOlSJCYmYty4cXB1dcWZM2ewdu1avPnmm7jtttsQEhKCRx55BK+//jpuuukmXH/99Th8+DB+++03BAYGNuivOW9vbyxfvhz/+Mc/cNVVV2HatGkICgpCUlISfvnlFwwbNgzvvPMOTp8+jTFjxuD2229Hz5494eLigh9++AEZGRny9OxPP/0U7733Hm6++WZ06tQJhYWF+PDDD+Ht7S1/iFjT0O+DxmjsFPmsrCw5U2MuNjYWd9xxBxYuXIhnnnkG119/PW666SacOnUK7733Hq6++mrceeedAIwrBc+bNw9TpkxB165dUVVVhc8//xwqlQq33norAODZZ5/F9u3bMWHCBERHRyMzMxPvvfceIiIiMHz48Hr7efvtt+Oxxx7DY489Bn9//1pZgXvuuQe5ubm49tprERERgUuXLuHtt99GXFyczSnOdRk1ahRGjRpVq7gVAO666y688cYbSEhIwN13343MzEysWLECvXr1qlXMbg9du3bF3Xffjb/++gshISH45JNPkJGRgZUrV8ptHn/8cfz000+48cYbMWvWLAwYMADFxcX4+++/8e233+LixYuNXsYCAPr164eZM2figw8+QH5+PkaNGoV9+/bh008/xeTJk62u4dQUbm5u6NmzJ77++mt07doV/v7+6N27N3r37o13330Xw4cPR58+fTBnzhx07NgRGRkZ2L17N1JSUnD48OF6z3/jjTfi888/h4+PD3r27Indu3fj999/R0BAgEW7uLg4qFQqvPzyyygoKIBGo8G1116L4ODgWue899578f7772PWrFk4cOAAYmJi8O2332Lnzp1YtmyZ/HuWrHDMpDRqCdI0TVuP5ORkIYQQBw8eFAkJCcLT01O4u7uL0aNHi127dtU636FDh8SIESOERqMRERERYunSpeKtt94SAER6errcrub0zffff1+MHDlSBAQECI1GIzp16iQef/xxUVBQYHH+5557TnTo0EEolUqL6fI1p8gLYZyeP2/ePNGhQwehVqtFRESEmDlzpsjOzq7znkRHR4sJEyZYfW7VqlW1pnR/8MEHYsCAAcLNzU14eXmJPn36iCeeeEKkpqbKbaqqqsTTTz8tQkNDhZubm7j22mvFiRMnREBAgLj//vtr/XvYmr6+ZcsWkZCQIHx8fIRWqxWdOnUSs2bNEvv37xdCCJGdnS3mzp0runfvLjw8PISPj48YPHiw+Oabb+RzHDx4UEyfPl1ERUUJjUYjgoODxY033iifQwIrU4Ab8n1g6xq2bNlSazpvY6fI2/o+HTNmjNzunXfeEd27dxeurq4iJCREPPDAAyIvL09+/vz58+Kuu+4SnTp1ElqtVvj7+4vRo0eL33//XW6zefNmMWnSJBEeHi7UarUIDw8X06dPrzWluy7Dhg2zOl1bCCG+/fZbMW7cOBEcHCzUarWIiooS9913n0hLS6v3vKixVINEur/W7v0XX3whOnbsKNRqtYiLixMbNmywOUW+5vIK0nlrLilg7d9Z+tnZsGGD6Nu3r9BoNKJ79+5WlyMoLCwUCxcuFJ07dxZqtVoEBgaKoUOHitdee01UVFTU2ae6VFZWimeeeUbExsYKV1dXERkZKRYuXGgxZV2IK5siL4QQu3btEgMGDBBqtbrWz8q5c+fEjBkzRGhoqHB1dRUdOnQQN954o/j222/lNnX9rOfl5YnZs2eLwMBA4enpKRISEsTJkyet/p778MMPRceOHYVKpbL4+bK2NEJGRoZ8XrVaLfr06VPrZ6+ue27td0J7oBCiHVZCkd3Mnz8f77//PoqKiuosKG1v8vPz4efnh+effx5PPfWUo7tDRERWsCaIGqzmNN2cnBx8/vnnGD58eLsOgKxNX5bqFhy5ASMREdWNNUHUYEOGDEF8fDx69OiBjIwMfPzxx9DpdHj66acd3TWH+vrrr7Fq1SrccMMN8PT0xI4dO/DVV19h3LhxGDZsmKO7R0RENjAIoga74YYb8O233+KDDz6AQqHAVVddhY8//hgjR450dNccqm/fvnBxccErr7wCnU4nF0tbK/IlIqLWgzVBRERE1C6xJoiIiIjaJQZBRERE1C6xJsgKg8GA1NRUeHl5OWyfGiIiImocIQQKCwsRHh5ea0NZaxgEWZGamurwvZKIiIioaZKTkxEREVFvOwZBVkhLjCcnJzdpzyQiIiJqeTqdDpGRkQ3eKoRBkBXSEJi3tzeDICIiIifT0FIWFkYTERFRu+TQIGjp0qW4+uqr4eXlheDgYEyePBmnTp2q8zUffvghRowYAT8/P/j5+WHs2LHYt2+fRZtZs2ZBoVBYPK6//vrmvBQiIiJyMg4NgrZt24a5c+diz5492LRpEyorKzFu3DgUFxfbfM3WrVsxffp0bNmyBbt370ZkZCTGjRuHy5cvW7S7/vrrkZaWJj+++uqr5r4cIiIiciKtasXorKwsBAcHY9u2bQ3eikGv18PPzw/vvPMOZsyYAcCYCcrPz8e6deua1A+dTgcfHx8UFBSwJoiIiMhJNPbzu1XVBBUUFAAA/P39G/yakpISVFZW1nrN1q1bERwcjG7duuGBBx5ATk6OXftKREREzq3VZIIMBgNuuukm5OfnY8eOHQ1+3YMPPogNGzbg2LFj0Gq1AIA1a9bA3d0dsbGxOHfuHP7973/D09MTu3fvhkqlqnWO8vJylJeXy19LU+yYCSIiInIejc0EtZop8nPnzsXRo0cbFQC99NJLWLNmDbZu3SoHQAAwbdo0+f/79OmDvn37olOnTti6dSvGjBlT6zxLly7FM888c2UXQERERE6lVQyHzZs3Dz///DO2bNnSoBUeAeC1117DSy+9hI0bN6Jv3751tu3YsSMCAwNx9uxZq88vXLgQBQUF8iM5ObnR10BERETOxaGZICEEHnroIfzwww/YunUrYmNjG/S6V155BS+88AI2bNiAgQMH1ts+JSUFOTk5CAsLs/q8RqOBRqNpVN+JiIjIuTk0EzR37lx88cUXWL16Nby8vJCeno709HSUlpbKbWbMmIGFCxfKX7/88st4+umn8cknnyAmJkZ+TVFREQCgqKgIjz/+OPbs2YOLFy9i8+bNmDRpEjp37oyEhIQWv0YiIiJqnRwaBC1fvhwFBQWIj49HWFiY/Pj666/lNklJSUhLS7N4TUVFBW677TaL17z22msAAJVKhSNHjuCmm25C165dcffdd2PAgAH4888/me0hIiIiWauZHdaacJ0gIiIi5+PU6wS1dQaDwB8nM8C4k4iIyPEYBLWgr/cn465V+zHjk31IyilxdHeIiIjaNQZBLaisUg+1ixJ/nsnGuGXb8N2BFEd3iYiIqN1iENSCZg+LxYb5IzG0UwDKKg14+sejKKvUO7pbRERE7RKDoBYWG+iBL+8ZjHAfLUoq9Nh5NtvRXSIiImqXGAQ5gEKhwLheoQCAjccyHNwbIiKi9olBkIOM6xkCAPj9RAb0Bs4WIyIiamkMghzk6lh/+Li5Iqe4Agcu5Tm6O0RERO0OgyAHcVUpMaZ7MABg47F0B/eGiIio/WEQ5EDjehmHxDYe5wKKRERELY1BkAON7BoEjYsSSbklOJdV7OjuEBERtSsMghzIXe2CHmHGvU1Opusc3BsiIqL2hUGQg3UN8QQAnM4ocnBPiIiI2hcGQQ7WNcQLAHA6vdDBPSEiImpfGAQ5WLdQUxCUySCIiIioJTEIcjApE3Qxu5j7iBEREbUgBkEOFuylgY+bKwwCOM8ZYkRERC2GQZCDKRQKdJPqgjI4JEZERNRSGAS1Al1MM8ROMQgiIiJqMQyCWgGpOPoMgyAiIqIWwyCoFZCKo5kJIiIiajkMgloBKQhKzi1FSUWVg3tDRETUPjAIagX8PdQI9NQAAM5w5WgiIqIWwSColegWyuJoIiKilsQgqJWQ64K4fQYREVGLYBDUSvQI5W7yRERELYlBUCvRPcyYCTqRVgghhIN7Q0RE1PYxCGoluoZ4QakAcosrkFVY7ujuEBERtXkMgloJrasKsYEeAIATrAsiIiJqdgyCWpHuYaa6oDTWBRERETU3BkGtSA/T9hknmQkiIiJqdgyCWpHuphliJ5gJIiIianYuju4AVesRbgyCzmYWoaLKgNT8Uri6KNHB183BPSMiImp7mAlqRcJ9tPDSuqDKILD2QDLG/Xc7Jr+7E3oDp8wTERHZG4OgVkShUMiLJv5n3VFU6A3IKizH5bxSB/eMiIio7WEQ1MpIiyaar5d4LoubqhIREdkbg6BWppepLshL44J+kb4AGAQRERE1BxZGtzKT4jrgQnYJEnqFYMvJTBxOzmcQRERE1AwcmglaunQprr76anh5eSE4OBiTJ0/GqVOn6n3d2rVr0b17d2i1WvTp0we//vqrxfNCCCxatAhhYWFwc3PD2LFjcebMmea6DLvSuqrw5Pju6B/lh07BngCAc5nFDu4VERFR2+PQIGjbtm2YO3cu9uzZg02bNqGyshLjxo1DcbHtD/1du3Zh+vTpuPvuu3Ho0CFMnjwZkydPxtGjR+U2r7zyCt566y2sWLECe/fuhYeHBxISElBWVtYSl2U3nYJMQRAzQURERHanEK1oy/KsrCwEBwdj27ZtGDlypNU2U6dORXFxMX7++Wf52DXXXIO4uDisWLECQgiEh4fj0UcfxWOPPQYAKCgoQEhICFatWoVp06bV2w+dTgcfHx8UFBTA29vbPhfXBMXlVei1eAMA4NDT18HPQ+2wvhAREbV2jf38blWF0QUFBQAAf39/m212796NsWPHWhxLSEjA7t27AQAXLlxAenq6RRsfHx8MHjxYblNTeXk5dDqdxaM18NC4INxHCwA4n81sEBERkT21miDIYDBg/vz5GDZsGHr37m2zXXp6OkJCQiyOhYSEID09XX5eOmarTU1Lly6Fj4+P/IiMjLySS7Er1gURERE1j1YTBM2dOxdHjx7FmjVrWvy9Fy5ciIKCAvmRnJzc4n2whXVBREREzaNVTJGfN28efv75Z2zfvh0RERF1tg0NDUVGRobFsYyMDISGhsrPS8fCwsIs2sTFxVk9p0ajgUajuYIraD6dgjwAMAgiIiKyN4dmgoQQmDdvHn744Qf88ccfiI2Nrfc1Q4YMwebNmy2Obdq0CUOGDAEAxMbGIjQ01KKNTqfD3r175TbOpDoTxOEwIiIie3JoJmju3LlYvXo1fvzxR3h5eck1Oz4+PnBzM+6cPmPGDHTo0AFLly4FADzyyCMYNWoUXn/9dUyYMAFr1qzB/v378cEHHwAw7r81f/58PP/88+jSpQtiY2Px9NNPIzw8HJMnT3bIdV4JqSYoKbcE5VV6aFxUDu4RERFR2+DQIGj58uUAgPj4eIvjK1euxKxZswAASUlJUCqrE1ZDhw7F6tWr8Z///Af//ve/0aVLF6xbt86imPqJJ55AcXEx7r33XuTn52P48OFYv349tFpts1+TvQV7aeCpcUFReRWSckrQJcTL0V0iIiJqE1rVOkGtRWtZJ0hy0zs7cCSlACvuHIDre4c6ujtEREStklOvE0TWRfm7AwBS8koc3BMiIqK2g0GQE5CCoKRcBkFERET2wiDICUQyCCIiIrI7BkFOQMoEJTMIIiIishsGQU5ADoLySmEwsI6diIjIHhgEOYEwHy1USgUqqgzILCx3dHeIiIjaBAZBTsBFpUS4r3GNo2TOECMiIrILBkFOQp4hlmMMgi7lFENXVunILhERETk1BkFOwnya/Ik0Hca+sQ0Pf3XIwb0iIiJyXgyCnESk2QyxX46koVIvsO9CLrjgNxERUdM4dO8warhIP2mGWAlOphcCAEoq9MgsLEeIt/PtiUZERORoDIKchDQcdjxVh+IKvXz8QnYxgyAiIqIm4HCYk5CCIPMACDAGQURERNR4DIKchK+7K7w01Yk7tcr4T3eRQRAREVGTMAhyEgqFAhGmbBAA3BQXDgA4zyCIiIioSRgEOZEofzcAQJCXBjf2DQPATBAREVFTMQhyIp2CPAEA13YLlv//Uk4J9NxPjIiIqNE4O8yJ3DOiI7SuKtx5TTR83FyhVilRoTcgNb9UXkeIiIiIGoaZICfi76HGw2O6wN9DDZVSgagAY+BzMYdDYkRERI3FIMiJxQR4AOA0eSIioqZgEOTEOgYxCCIiImoqBkFOjJkgIiKipmMQ5MRiA41BEKfJExERNR6DICcmBUHJeaWo1Bsc3BsiIiLnwiDIiYV4a+ClcYHeIHA8Vefo7hARETkVBkFOTKFQYGjnAADA1lNZDu4NERGRc2EQ5OTiuwUDALadznRwT4iIiJwLgyAnF98tCACQmJyP/JIKB/eGiIjIeTAIcnJhPm7oFuIFgwC2n8l2dHeIiIicBoOgNmCUKRu0jXVBREREDcYgqA2I72oKgk5nwcAd5YmIiBqEQVAbMDDGHx5qFbKLynE8jVPliYiIGoJBUBugdlFieJdAAMA3+5Md3BsiIiLnwCCojZg5JAaAMQjKKSp3bGeIiIicAIOgNmJIpwD0i/BBWaUBn+66iNT8UsxauQ9LfjoGIVgnREREVBODoDZCoVDg/lGdAACf7r6EW5fvwtZTWVi16yK2neasMSIiopoYBLUh43qFIjbQAwWllUgrKIPaxfjP+9JvJzlrjIiIqAaHBkHbt2/HxIkTER4eDoVCgXXr1tXZftasWVAoFLUevXr1ktssWbKk1vPdu3dv5itpHVRKBeaN7gwAiIv0xYb5I+GldcHJ9EL8ePiyg3tHRETUujg0CCouLka/fv3w7rvvNqj9m2++ibS0NPmRnJwMf39/TJkyxaJdr169LNrt2LGjObrfKt06IAIb5o/E2vuHIDbQAw/EG4fIXttwGuVVegf3joiIqPVwceSbjx8/HuPHj29wex8fH/j4+Mhfr1u3Dnl5eZg9e7ZFOxcXF4SGhtqtn86mW6iX/P+zh8biw+3ncTm/FEcvF2BAtL8De0ZERNR6OHVN0Mcff4yxY8ciOjra4viZM2cQHh6Ojh074o477kBSUlKd5ykvL4dOp7N4tBVuahViAj0AAFmF3GCViIhI4rRBUGpqKn777Tfcc889FscHDx6MVatWYf369Vi+fDkuXLiAESNGoLCw0Oa5li5dKmeZfHx8EBkZ2dzdb1EBHmoAQG4xgyAiIiKJ0wZBn376KXx9fTF58mSL4+PHj8eUKVPQt29fJCQk4Ndff0V+fj6++eYbm+dauHAhCgoK5EdycttaddlfDoK4iCIREZHEoTVBTSWEwCeffIJ//OMfUKvVdbb19fVF165dcfbsWZttNBoNNBqNvbvZavh7GK8th5kgIiIimVNmgrZt24azZ8/i7rvvrrdtUVERzp07h7CwsBboWevE4TAiIqLaHBoEFRUVITExEYmJiQCACxcuIDExUS5kXrhwIWbMmFHrdR9//DEGDx6M3r1713rusccew7Zt23Dx4kXs2rULN998M1QqFaZPn96s19Ka+TMIIiIiqsWhw2H79+/H6NGj5a8XLFgAAJg5cyZWrVqFtLS0WjO7CgoK8N133+HNN9+0es6UlBRMnz4dOTk5CAoKwvDhw7Fnzx4EBQU134W0cv6exiAop4hBEBERkcShQVB8fHydm3uuWrWq1jEfHx+UlJTYfM2aNWvs0bU2hcNhREREtTllTRA1jvlwGHeUJyIiMmIQ1A4EmGaHVegNKCyvcnBviIiIWgcGQe2Am1oFN1cVACCXdUFEREQAGAS1GwFScTTrgoiIiAAwCGo3WBxNRERkiUFQO8GtM4iIiCwxCGonuHUGERGRJQZB7YRUE8TCaCIiIiMGQe0Et84gIiKyxCConZCCIA6HERERGTEIaic4O4yIiMgSg6B2gsNhRERElhgEtRMB8uwwTpEnIiICGAS1G/6m2WFllQaUVHD/MCIiIgZB7YSHWgW1i/GfO4fT5ImIiBgEtRcKhaJWcbTBIPCvb4/g3z/8DSGEI7tHRETU4hgEtSM1i6O/PZCCr/cnY/XeJKTryhzZNSIiohbHIKgdkYKg7KJyFJZV4pUNp+TnLmQXO6pbREREDuHi6A5Qy5GGw34+kob9F/OQXVQ9U+xidgmGdnJUz4iIiFoeM0HtyMAYfwDAttNZ+Hp/MgCgS7AnAOBiDjNBRETUvjAIakfuvCYaa+8fgtHdggAA43qG4M5rogFwOIyIiNofDoe1M1fH+GPl7EHIKSqHt5srdp3LAQBcZBBERETtDIOgdirA07iCdGyABwDgUm4JDAYBpVLhyG4RERG1GA6HtXPhvlq4qhSoqDIgtaDU0d0hIiJqMQyC2jkXlRKR/u4AjDPEiIiI2gsGQSQPiXGGGBERtScMgggxgaYgyEZx9MGkPPz2d1pLdomIiKjZMQgixASYhsOsZIJS8kpwx4d78cCXB7H5REZLd42IiKjZMAgiORNkba2gJT8dR2mlHgDw/C8nUFFlaNG+ERERNRcGQYQYU01Qcm4p9Ibq3eQ3Hc/A7ycy4KJUwM/dFReyi/HZ7osO6iUREZF9MQgihPu6Qa1SokJvQGq+cZp8WaUeS346BgC4Z0RHPDm+OwDgzc1nLPYcIyIiclYMgggqpQJRprqg86YhsT3nc3A5vxRBXho8PKYzbhsQiV7h3igsq8J3B1Ic2V0iIiK7YBBEAIBuoV4AgCPJ+QCAg0nG/47oHAh3tQtUSgUm9gsHACSa2hARETkzBkEEABhk2mF+38VcAMChpDwAQP9oP7lNvwhfAMBhBkFERNQGMAgiAMaNVQHg4KU8VFQZkGjKBF0V5Su36RPhA4UCSC0oQ6auzAG9JCIish8GQQTAOBzmpXVBcYUePx1ORWF5FdzVKnQL8ZLbeGpc0CXYEwBwOKXAUV0lIiKyCwZBBMBYHD3QNPT1wfZzAIzDXy4qy2+RuEhfABwSIyIi58cgiGSDYgMAAKczigAA/c2GwiT9pCAoJR8Gg8DL60/imf8dQ1ION18lIiLn4tAgaPv27Zg4cSLCw8OhUCiwbt26Ottv3boVCoWi1iM9Pd2i3bvvvouYmBhotVoMHjwY+/bta8araDsGxfpZfH1VlF+tNlJxdGJyPr47mILlW89h5c6LGP36Vjy+9jDKq/Qt0VUiIqIr5tAgqLi4GP369cO7777bqNedOnUKaWlp8iM4OFh+7uuvv8aCBQuwePFiHDx4EP369UNCQgIyMzPt3f02p08HX2hcqr8lrGWCuoV6QeOiRGFZFZ7533EAQMdAD+gNAmsPpOCvC3kt1V0iIqIr4tAgaPz48Xj++edx8803N+p1wcHBCA0NlR9KZfVlvPHGG5gzZw5mz56Nnj17YsWKFXB3d8cnn3xi7+63OWoXpVzzExPgjgBPTa02riolenfwAQAUlVchyt8dv80fgatjjFmj/NKKFusvERHRlXDKmqC4uDiEhYXhuuuuw86dO+XjFRUVOHDgAMaOHSsfUyqVGDt2LHbv3m3zfOXl5dDpdBaP9mpop0AAwKBYf5ttpEAJAP59Q3doXFTw1roCAArLqpq1f0RERPbiVEFQWFgYVqxYge+++w7fffcdIiMjER8fj4MHDwIAsrOzodfrERISYvG6kJCQWnVD5pYuXQofHx/5ERkZ2azX0ZrdO7IjFt3YE09c391mmxFdjIHS8M6BSOgVCgDw0roAAArLKpu/k0RERHbg4ugONEa3bt3QrVs3+euhQ4fi3Llz+O9//4vPP/+8yedduHAhFixYIH+t0+nabSDkplbhruGxdbaJ7xaMdXOHoVuIFxQKBQDAy5QJKmImiIiInIRTBUHWDBo0CDt27AAABAYGQqVSISMjw6JNRkYGQkNDbZ5Do9FAo6ld/0K2mQ+JAYCnKROkYxBEREROwqmGw6xJTExEWFgYAECtVmPAgAHYvHmz/LzBYMDmzZsxZMgQR3WxXageDmMQREREzsGhmaCioiKcPXtW/vrChQtITEyEv78/oqKisHDhQly+fBmfffYZAGDZsmWIjY1Fr169UFZWho8++gh//PEHNm7cKJ9jwYIFmDlzJgYOHIhBgwZh2bJlKC4uxuzZs1v8+toTL7kwmjVBRETkHBwaBO3fvx+jR4+Wv5bqcmbOnIlVq1YhLS0NSUlJ8vMVFRV49NFHcfnyZbi7u6Nv3774/fffLc4xdepUZGVlYdGiRUhPT0dcXBzWr19fq1ia7MvblAkqKmcmiIiInINCCCEc3YnWRqfTwcfHBwUFBfD29nZ0d5zC5hMZuPvT/ejTwQf/e2i4o7tDRETtUGM/v52+JohaBw6HERGRs2EQRHbBwmgiInI2DILILuQgiDVBRETkJBgEkV14aYzDYRVVBu4kT0REToFBENmFtFgiwCExIiJyDgyCyC5USgU81CoADIKIiMg5MAgiu+H+YURE5EwYBJHdeHIneSIiciIMgshuvLiJKhEROREGQWQ38nAYp8kTEZETYBBEduPF4TAiInIiDILIbrw0XDWaiIicR5OCoOTkZKSkpMhf79u3D/Pnz8cHH3xgt46R82EmiIiInEmTgqD/+7//w5YtWwAA6enpuO6667Bv3z489dRTePbZZ+3aQXIerAkiIiJn0qQg6OjRoxg0aBAA4JtvvkHv3r2xa9cufPnll1i1apU9+0dOhLPDiIjImTQpCKqsrIRGowEA/P7777jpppsAAN27d0daWpr9ekdOxZM1QURE5ESaFAT16tULK1aswJ9//olNmzbh+uuvBwCkpqYiICDArh0k5yENh7EmiIiInEGTgqCXX34Z77//PuLj4zF9+nT069cPAPDTTz/Jw2TU/nibhsNsbZvx1b4kTHp3J85kFLZkt4iIiKxyqb9JbfHx8cjOzoZOp4Ofn598/N5774W7u7vdOkfOpToTVDsIOpiUh/+sOwq9QeChrw7hx3nDoHFRtXQXiYiIZE3KBJWWlqK8vFwOgC5duoRly5bh1KlTCA4OtmsHyXnY2jtMV1aJh786BL1BAABOphfi9Y2nW7x/RERE5poUBE2aNAmfffYZACA/Px+DBw/G66+/jsmTJ2P58uV27SA5D2l2WHGFXg54AGDxj8eQkleKSH83/Heqcej0wz/PY9e5bIf0k4iICGhiEHTw4EGMGDECAPDtt98iJCQEly5dwmeffYa33nrLrh0k5yEFQUD1WkFllXqsS7wMAFg2tT9u7h+B6YMiIQTwBrNBRETkQE0KgkpKSuDl5QUA2LhxI2655RYolUpcc801uHTpkl07SM5D46KC2sX4LSUNiV3ILoYQgI+bK66K8gUAzB/bFSqlAvsv5eFsZpHVc1XpDfhg+zkcT9W1SN+JiKj9aVIQ1LlzZ6xbtw7JycnYsGEDxo0bBwDIzMyEt7e3XTtIzqXm/mEXsosBAB2DPKBQKAAAId5ajO5mrB37Zn+y1fP8fiITL/56Es/871hzd5mIiNqpJgVBixYtwmOPPYaYmBgMGjQIQ4YMAWDMCvXv39+uHSTnUr1/mDEIOp9lzPR0DPS0aDft6kgAwHcHUlBRZYDBIFBSUT2r7FS6cRp9cm5Js/eZiIjapyZNkb/tttswfPhwpKWlyWsEAcCYMWNw8803261z5Hyq9w8zDoedz6rOBJmL7xaEYC8NMgvL8eKvJ7D5ZAbyiivxx6OjEOytxVlT8JRZWA69QUClVLTgVRARUXvQpEwQAISGhqJ///5ITU2Vd5QfNGgQunfvbrfOkfOpuXXGeWk4LNAyCHJRKXHbgAgAwKpdF5GcW4qi8irsvZALADhnqhWqMgjkFJW3SN+JiKh9aVIQZDAY8Oyzz8LHxwfR0dGIjo6Gr68vnnvuORgMBnv3kZyI+SaqQojq4bAgz1ptp10dBTdXFdQqJWJNQdLxNB0MBoHz2dUF0+m6shboORERtTdNGg576qmn8PHHH+Oll17CsGHDAAA7duzAkiVLUFZWhhdeeMGunSTnYb5/WE5xBXRlVVAogOiA2iuJRwW4Y+M/R0LjqsTGYxn4z7qjOJ6qQ2pBKcoqq4PptIIy9I1osUsgIqJ2oklB0KeffoqPPvpI3j0eAPr27YsOHTrgwQcfZBDUjkmZoILSSrkeqIOvG7Su1rfIiPQ3Bke9wo2zCo+l6mpNm89gJoiIiJpBk4bDcnNzrdb+dO/eHbm5uVfcKXJeXUOM60f9eTobF7JtD4XV1D3UG0oFkF1Ujj3nLb+H0goYBBERkf01KQjq168f3nnnnVrH33nnHfTt2/eKO0XOa3zvULgoFTiepsOGYxkAahdFW+OmVsnB0v8OpxqPmbJHGQyCiIioGTRpOOyVV17BhAkT8Pvvv8trBO3evRvJycn49ddf7dpBci5+HmrEdwvC7ycy8cfJTAC1p8fb0jPMG2czi3A5vxQAMCjWH9tOZzETREREzaJJmaBRo0bh9OnTuPnmm5Gfn4/8/HzccsstOHbsGD7//HN795GczKS4DhZf11wo0Zae4ZarjQ/rHACANUFERNQ8mpQJAoDw8PBaBdCHDx/Gxx9/jA8++OCKO0bOa2yPEHioVSiu0ANoeCaoV40gaGinQADGKfJCCHnbDSIiInto8mKJRLa4qVVI6B0KANC6KhHqrW3Q63qGVQdBod5adA42ZpBKKvTQlVXZehkREVGTMAiiZjFlgHFvsH4RvlA2cMuLAE+NHDB1DvaE1lUFHzfjukMcEiMiIntzaBC0fft2TJw4EeHh4VAoFFi3bl2d7b///ntcd911CAoKgre3N4YMGYINGzZYtFmyZAkUCoXFg1t5tLwhnQLw3QND8Nb0xm2oK9UFdTINoYX5GIMiFkcTEZG9Naom6JZbbqnz+fz8/Ea9eXFxMfr164e77rqr3nMDxqDpuuuuw4svvghfX1+sXLkSEydOxN69ey12r+/Vqxd+//13+WsXlyaXPtEVGBDt3+jX3D4wAn9fLsBNceEAgBBvLU6mF3KaPBER2V2jogMfH596n58xY0aDzzd+/HiMHz++we2XLVtm8fWLL76IH3/8Ef/73/8sgiAXFxeEhoY2+LzUelzfOwzX9w6Tv2YmiIiImkujgqCVK1c2Vz+axGAwoLCwEP7+lhmHM2fOIDw8HFqtFkOGDMHSpUsRFRVl8zzl5eUoL6/eqVyn0zVbn6lxQkw1QuabqP55JguPrz2Cf43vhpv7c1MxIiJqGqcujH7ttddQVFSE22+/XT42ePBgrFq1CuvXr8fy5ctx4cIFjBgxAoWFhTbPs3TpUvj4+MiPyMjIlug+NYCUCUovMC6gWFqhx5Pf/Y10XRkWrTuGrMLyul5ORERkk9MGQatXr8YzzzyDb775BsHBwfLx8ePHY8qUKejbty8SEhLw66+/Ij8/H998843Ncy1cuBAFBQXyIzk5uSUugRogRAqCdMZg590tZ+UVpQvLq/DK+pMO6xsRETk3pwyC1qxZg3vuuQfffPMNxo4dW2dbX19fdO3aFWfPnrXZRqPRwNvb2+JBrYOUCbqcV4KNx9LxwfbzAIAH4jsBANYeSMGBS3kO6x8RETkvpwuCvvrqK8yePRtfffUVJkyYUG/7oqIinDt3DmFhYfW2pdYnzNsNAKArq8K9nx9Ahd6AUV2D8ERCN0wZYKwHmvvlQfxyJA1CCEd2lYiInIxDg6CioiIkJiYiMTERAHDhwgUkJiYiKSkJgHGYyny22erVqzFjxgy8/vrrGDx4MNLT05Geno6CggK5zWOPPYZt27bh4sWL2LVrF26++WaoVCpMnz69Ra+N7MPH3RWPXtcVV0X5IibAHd1CvPDcpN5QKBT41/juiA5wR7quDHNXH8Sczw5Ab2AgREREDaMQDvzzeevWrRg9enSt4zNnzsSqVaswa9YsXLx4EVu3bgUAxMfHY9u2bTbbA8C0adOwfft25OTkICgoCMOHD8cLL7yATp06NbhfOp0OPj4+KCgo4NBYK1daoceKbeewfOs5VOgN+Oa+IRgU2/j1iYiIyPk19vPboUFQa8UgyPnc9/l+bDiWgSfHd8f9oxoe8BIRUdvR2M9vp6sJIrLmqig/AMBBFkkTEVEDMQiiNuGqaFMQlJTPAmkiImoQBkHUJvTp4ANXlQLZReVIySt1dHeIiMgJMAiiNkHrqkLPcOPedgeTOCRGRET1YxBEbcZVUb4AWBdEREQNwyCI2gypOPoAM0FERNQADIKozZCKo0+kFaKkosrBvSEiotaOQRC1GeE+WoR4a6A3CBxJKaj/BURE1K4xCKI2Q6FQyENih5PzHdsZIiJq9RgEUZvSI8y4QuipjEIH94SIiFo7BkHUpnQL9QIAnEpvWBC09VQmknNLmrNLRETUSjEIojaluykIOpNZhCq9oc6264+mYdbKv/DQV4daomt46beTmPPZflRU1d0vIiJqGQyCqE2J9HOHu1qFiioDLubYzvAIIfDOlrMAgCMp+Sgub/7ZZKt2XcCm4xnYfT6n2d+LiIjqxyCI2hSlUoEuIfUPif15JhtHL+sAAAaBZp9NVqk3oKzSmAHafjqrWd+LiIgahkEQtTnd5SBIZ7PN8q3nAAAKhfHrxGaeTWaeadrGIIiIqFVgEERtjlQcfdJGJuhQUh52n8+Bi1KBmUNiAACJyc27ynRhWXUQdDazCJfzuckrEZGjMQiiNkcKgk6bpskfSy3A+awi+fnVe5MAAJPiOuCGPmEAgENJ+RBC1DpXUXkVrn19Kwa98Dv+s+7vJm/OWlSj5ohDYkREjscgiNocKQi6lFuC/RdzcdM7O3Hze7tQVF6FSr0Bm05kAABuGxCBPh18oFIqkFlYjrSCslrn2n0uB+ezipFZWI4v9iThlvd2IamOgmtbzDNBAIMgIqLWgEEQtTmBnhoEeqohBDB39UHoDQIFpZX45Ugq9p7PRX5JJQI81BgU6w83tUqeVm+tLuiAaUf6YZ0DEOajBQCczWr8QoxF5ZUAAI2L8Udux9nseqfwExFR82IQRG2SlA3K0JXLx9b8lYzfjqYBAMb1CoFKaayKjov0BWArCMoFYBw66xVuXI3aWsaoPlImqF+kL3zdXVFYVoWZK/dh3uqDzV6UTURE1jEIojapW4i3/P/3jeoIF6UCh5Lyse7QZQBAQq9Q+XkpCDpUo96nosqAw6ap8wOi/RBqygSlNyEIkmqCfNxcMaZ7CABg59kc/HwkDW9tPtPo8xER0ZVjEERtUr9IHwBA1xBPPDauG8b2MAYexRV6eGldMLRToNy2v2nT1b8vF1is5nw01fi1n7srOgZ6INT7CoIgUybIS+OCxTf1xJvT4nDP8Ngmn4+IiK4cgyBqk27sG47XpvTDF3cPhqtKiamDIuXnrusRArVL9bd+x0APBHtpUFZpwPcHU+TjB031QAOi/aBQKBDq4wYASNc1PRPkpXWBt9YVk+I6YHL/DgCA7KLyul5KRETNhEEQtUkqpQK3DYhAsCl7M7JLEDr4GoMYaVq8RKlU4N6RHQEA72w5K2eD9l+UgiB/AJALo6+kJshT6yIfC/LSAAByiiugN9Senk9ERM2LQRC1CyqlAh/PGojXp/TDmB7BtZ6/Y3A0Aj01SMkrxXcHUyCEwAFTjdDAGONwmT1qgjw1rvIxfw81FApAbxDIK6lo9DmJiOjKMAiidqN7qDduHRABhbRXhhk3tQoPxHcCALzzx1lsOJaBrMJyuKoU6NPBWF8k1QQVlVehsKyyUe9dZCUT5KpSwt9dDYBDYkREjsAgiMjkjsFRCPLS4HJ+Ke7/4gAAoHcHH2hdVQAAD40LvExBTEYj64LkmiCNi8XxQE/jkFhWIYMgIqKWxiCIyETrqsKrt/XF4Fh/RPi5wUvjgulXR1m0sVYXJITAcz8fx6sbTto8d6E8HGYZBEl1QQyCiIhankv9TYjaj/huwYjvVrtmSBLq44bTGUUWQdCe87n4eMcFAMC9IzvBx8211uuk4TPz4TCgOgjicBgRUctjJoioEcJMdUEZZkHQl3svyf9vK6Mj1wQxE0RE1GowCCJqhBBpOMxUE5RVWI4Nx9Ll520GQWbrBJkL9FTX+ToCyqv0ju4CEbVRDIKIGiGsxjT5tQeSUamvXuMny8qwlt4gUFJh/CC3mQnicJhVG4+lo/fiDfjuQEr9jYmIGolBEFEjhJoVRhsMAqv3JgEA3NXGGWTZVjI6UhYIsFIT5Kk1vc5ynaDsonJ8uP08covb9/pBBy7loVIv8NfFXEd3hYjaIAZBRI0gZYIydGXYejoTKXml8Na6YFJcOADrGR0pCFK7KKFxUVk8F+iltvq6j/68gBd+PYH3t5+z+zU4EymDVmgWSBIR2QtnhxE1grRgYm5xBd7afBYAMPXqSPi6267tMd88taYg0zpBucUVqNQb4Koy/l1yIbsIQPX+Ze2VHASVMQgiIvtjJoioEXzcXKF1Nf7YJCbnw1WlwN3DO9Y5y6uo3Pr0eADwc1dDpTSuYG0+9JWab6w5+vtyAar0hlqvA4DL+aV48MsDONCGA6XSSmPw09gVuomIGoJBEFEjKBQKhJl2kweAm/t3QKiPts71fgptTI8HjJu3Wpshdjm/FABQVmnAmcwiq315feMp/Pp3Oj7ddbFpF+MEpExQETNBRNQMHBoEbd++HRMnTkR4eDgUCgXWrVtX72u2bt2Kq666ChqNBp07d8aqVatqtXn33XcRExMDrVaLwYMHY9++ffbvPLVb0pCYQmFcHBGoHtayngmyHQQBtbfOKKmossgKHU7Or/Wa/JIK/HwkDQDa9OarHA4joubk0CCouLgY/fr1w7vvvtug9hcuXMCECRMwevRoJCYmYv78+bjnnnuwYcMGuc3XX3+NBQsWYPHixTh48CD69euHhIQEZGZmNtdlUDsT7mvMBF3XIwSdgz0BVE91zymugN4gLNrLNUFWhsPMXysVR0tDYZLDKfm1XvPtgRRUVBmHyQpK2+5QUamUCWJhNBE1A4cWRo8fPx7jx49vcPsVK1YgNjYWr7/+OgCgR48e2LFjB/773/8iISEBAPDGG29gzpw5mD17tvyaX375BZ988gmefPJJ+18EtTt3D4+FEAL/vK6rfMzfQw2FwrgmUF5JhZzdAeoeDgNqZ5GkoTBJYnKBxddCCKzelyR/3ZaDoJIK470rKq+C3iDk+ikiIntwqpqg3bt3Y+zYsRbHEhISsHv3bgBARUUFDhw4YNFGqVRi7NixchuiK9Uz3BtvTI1DpL+7fMxVpYSfaYZYzbogefNUG5mgwBpF1ammIKhHmDcA4HRGoZwRAYx7lZ3PKpa/bstBkPl1MxtERPbmVEFQeno6QkJCLI6FhIRAp9OhtLQU2dnZ0Ov1Vtukp6fDlvLycuh0OosHUWPZqguq3jes9saq5q+TgqfLecYgaEC0L4K9NNAbBI6lVmeDvtmfDAC4rqfx+1xXWglDjSG4tqKkkkEQETUfpwqCmsvSpUvh4+MjPyIjIx3dJXJCtqbJS1Pk660JqpEJ6uDrjn6RvgCM0/Ele8/nAADuGBwFADAIoKiibQYIJWaZIE6TJyJ7c6ogKDQ0FBkZGRbHMjIy4O3tDTc3NwQGBkKlUlltExoaavO8CxcuREFBgfxITk5ulv5T22ZrM1Rbm6dKahZGp5iCoHBfLfpF+AAADqcYM0GZujKkFpRBqQCujvGHxsX4I1xQ0vYChCq9QS7+BjhNnojsz6mCoCFDhmDz5s0WxzZt2oQhQ4YAANRqNQYMGGDRxmAwYPPmzXIbazQaDby9vS0eRI1la62g+gqj5SnyunIIIeRMUISfG+Ii/QAAf13IhRACh0wZoa4hXvDQuMDX3TjEVlddUFml3imHy8yHwgBOkyci+3NoEFRUVITExEQkJiYCME6BT0xMRFKScebLwoULMWPGDLn9/fffj/Pnz+OJJ57AyZMn8d577+Gbb77BP//5T7nNggUL8OGHH+LTTz/FiRMn8MADD6C4uFieLUbUXGwPh9UdBEX4ucHNVYXC8iocS9XJO9SH+7phYIwfNC5KpOvKcDqjSB4WizMNk/m4GYMgnY0g6GxmIXov3oCnfzx6RdfmCOZF0QCg43AYEdmZQ4Og/fv3o3///ujfvz8AYwDTv39/LFq0CACQlpYmB0QAEBsbi19++QWbNm1Cv3798Prrr+Ojjz6Sp8cDwNSpU/Haa69h0aJFiIuLQ2JiItavX1+rWJrI3moOa+WXVEAIUV0YbWM4TOuqwrDOgQCAr/Ylocog4KJUINhLC62rCtd0DAAAbD+dhcSkfAC1g6B8G0HQgUt5qDIIfLUvCSl5JVd+kS2opEYQxMJoIrI3h64TFB8fDyFsp+mtrQYdHx+PQ4cO1XneefPmYd68eVfaPaJGkYa1sgsr8N2BFDy69jCeuqFHdU2QjdlhADCmRzB+P5GBHw5dBgCE+mjlNXFGdQ3CttNZ2HIqE0dMCyfGRfkCqA6CbA2H5ZlqhQwC+Hz3JSy8oceVXWQLKqlR7M3hMCKyN6eqCSJqzaRMULquDP/9/TQA4P3t5+UAxVYmCABGdwsGUJ396OBbvT/ZyK5BAIBd53JQXKGHu1qFLsFeAADv+oIgs+03vtqXVCuwaM1qDoexMJqI7I1BEJGdSOv9FJRWIsW01k92Ubkc2NiqCQKMmZ/eHaoL8s2DoE5BHhZf9+ngI2eJ6s8EVQdBurIqfHfwcqOuyZFqDodxijwR2RuDICI78XNXW2zrEOKtsXje1hR5ybXdq+vWOvhVBz0KhQKjugXJX0tDYUD9QVBusfF4txBj5mjVzgt1DkG/vvEU5q4+WGv/M0eoHQQxE0RE9sUgiMhOlEoFAjyMawW5q1X4eObVUJhiIhelQl7Tx5Yx3YPl/w83y/wAxrogSX9TUTRQOwhasy8JT353RA5i8k2ZoHtGxEKhAM5lFSOn2Pqu8waDwIpt5/DLkTScySys73KbXWlljZogFkYTkZ0xCCKyI6kuaNrVUejdwQfxpuDFS+sChaLuzT/7dPCRXx9tti8ZAAztFACNixIqpQL9o/zk4zWnyL+8/iTW/JUs7zyfawqCIv3dEeqtBQAk5VqfJZZXUoFKvTF4Sisos9rG3M6z2Rj92lbsv5hbb9um4HAYETU3BkFEdjRnREeM6R6MB+I7AQDuvCYaABDspa33tUqlAm9OjcOj13WVp8VLvLSu+PSuQfh45kCEeFefyzwTVFhWKc8Gy9QZg5h809d+7mpEmQKrZBtBUKbZ+kZp+fUHQZ/uuogL2cXyXmb2JhVGSwtCcoo8EdmbQ6fIE7U1k/t3wOT+HeSvr+0ejGVT49ApyLNBrx/aORBDTWsG1VQzMALM1gkqqS7GBowBjcEg5OEwPw9XRPm7Y++FXFzKsR4EZeiqA5+0glKrbSRCCBw0rVl0Kr15hs6Ky41BUKi3FvkllawJIiK7YxBE1IwUCoVFUGRv5pkg8yAoq7AcurJKSPXNvm7VmSBbw2EWmaB6hsOSc0vl7UFOZxTBYBBQKuse7musElNNUJCXBifTCxkEEZHdcTiMyIn5mIaKdGWVFsFNpq4cuaYCaC+NC9QuSkQF1B0EZVkEQXVngg4m5cn/X1qpR3KN1ahPZxTi/W3nUF6lr/nSBpOGw6ThP64TRET2xiCIyIlJmSAhgJNpOvl4ZmGZXB/k62FsI2eCbAyHZVoMh9WdCTpwKc/i65M1hsRe+u0klv52EptPZDbkMqySCqODTcXiFXoDyiqbHlQREdXEIIjIiWlcVNC6Gn+Mj6aaB0Hl8mrR/u7GaftSEJSuK7MaTNQsjK5rPSEpEyQFYTXrgqT6otT86ozSJzsuYOuphgdFUiYo0FMjLzXA4mgisicGQUROTgpEzmRUByKZheXyatG+piDI30Mtr1ptXj9k/hpJaaUeulLLgKNKb4DBIFBcXiVnfm65yljvVDMIktYtkobkzmYW4tmfj+OJb480+LqkLT48NS7wVBv7zbogIrInBkFETk4KgqrMVnnOKSpHdpEpE2RawFGhUCBSLo4urnWezELLIbBUs7qgA5dyMezlP3DDW3/i9xMZ0BsEwn208iKOpzJqBEEllkFQqmnKfWZhOSqqDA26Lmk4zE2tklfbbgtrBeWXVKCYGS2iVoFBEJGTk4IgiUJh3DX+XFYRgOp1dgAgyt+4EnXNuiAhBDJ1xkyQtyngSDfVBf1yJA3TP9yLDF05TqYXYsE3hwEA/aP90D3UuN/ZhexiuQi6Sm+QV3eWAjFpJhkAZJn9f11KTUN27mqVvPmssxdH55dUYMzr2zD9wz2O7goRgUEQkdMzD4ICPNQING3ketqUnZFqggAgOsADAJCUazkcpiurQrkpQ9M3wheAMRO093wO5q4+iIoqA0Z3C0IHXzd5S44BUX4I8dbAx80VeoPA2cwi+VyS3GJjwGMeBJkXYNfFMhMkzYJz7iBo/8U85BRX4EhKgbyGExE5DoMgIifnbRYERfi5ybvZS0GQr0d1EBRpY62gLNNQmLfWBTGBpgLqgjL8fCQNAJDQKwQfzbwaa+69Bh183aBUACO6BEKhUKBbqHFzVqkuyHwzV2k4LKeo+gPfvPaoLlJhtLvaRa5lcvbCaGk7E8C4jxsRORaDICInZ54JivB3R7Bp9/qySmNmxzwTFGWjJkgaCgv21iLMxzhklppfhp3nsgEAt1wVAZXSWFO04Z8jsfGfo9DFtDO9tEO9FASZZzikzVrNh8AaGgRJhdEebagmKDE5X/5/abiSiByHK0YTOTmfGpmgvBq7xPuZ1QRFm2WChBDypq5SYBLspUGYj3FxwsTkPJzPKoZSYbllh6fGBZ2Dq7cBkTJBJ61kggrLqlBRZZBrgwAgq0nDYc4/O0wIgcMMgohaFWaCiJycZRDkXmuzVj+z4bBw01BWWaXBIjsjretjDIKMmSBpuKZPB59axdfmOgYZ64ykVaPNgyDAuDt9TiMzQXqDkGuU3NUuck2QMw+HXcwpsahpOpfJ4TAiR2MQROTkzAOUSD83eThM4m8WBKldlHKQYz5DTM4EeWvlTJDE1oauEmlF52zTOaSd6yU5RRWWs8MaEARJQ2GAcXaYl8b5h8OkLJDaxfhr9zwzQUQOxyCIyMnVzARJhdES8ynyQHXm5lSNxRUBY0ATWiMIGtap7iAoyNPYXldWhbJKfa1MUE5xeaMLo6WiaIUC0Lgo5SnyzjwcJtUDXdcjBABwKbekwWsmEVHzYBBE5OTMg5yIGpkgD7UKGheVRfu+ET4AgL9TCuRj0rT1IC8NtK4qOXukdlFiYIxfne/v7eYCtcr4qyS7qLxWJuhCdrHFQo41F2W0RqoHcndVQaFQyMNhzhwESTPDxvUKgafGBXqDsLpopT2tO3QZD3xxwCKzRkTVGAQRObkIP3coFUDHQA9oXVUWNUG+ZjPDJH06+AIAjpgFQVlyJsj4WmlIbECUH7SulkFUTQqFAoGexvfJLqqolQmSpupL+39lF1XIaw3ZUl0UbcwAeTr5cFhFlQHHTHu7xUX6opMpG3e2meuC3vrjDH47mo59F3Kb9X2o/alrb0FnwiCIyMmFeGvx7QND8eldgwAYszkS83ogiZQJOp1RKG+kWl0TZHxtpJ9xFtnwLnUPhUmk98wqLEdBqXHoS6p9OZ1urH2J8neHQmEses4trnuhwNJKY+bCXW0MwKRVrGtmmZzFyXQdKqoM8HV3RZS/OzoFGWfXNecMMYNBIMW0KKYUVBLZw86z2ej/3Cb8YlpHzJkxCCJqA66K8pMXQtS6quSgoWY9EGDM8gR6qlFlEDiepkNJRZU860oqcp5/XRfcN7IjZg2NadD7WwZBxkAlNsCy9ijES4sAU1BW35CYPBxmCoK6hHhBoQDOZxcjJa+krpe2SodNWbe+Eb5QKBToFNw8QVB6QZlcZ5RRWIYKvfH/SxkEkR3tPJuN/JJKbD+d5eiuXDEGQURtkBSUWMsEKRQK9OlQXRckLZTo5qqSh526h3pj4Q094KFp2FJi0lYd5jVBUgG2FBQFeqkRZBpuq6842nyNIOl6Bsf6AwB++zu9QX1qTY6nGoOgPh2Me61Jw2H2XDV67/kcDH1pM57/5TgAINlsaxRpHzYie5B+PovbQK0ZgyCiNkiq7fGzUhMEVO8PdiSlAAcu5QEAwny18uKJjWU1ExToYdEm0FMjZ5qydHUHQaU1MkEAMKFPGADg57+dLwUv1QP1CjcGn9Jw2PnMIrvVVqxLvAyDAP48Y1zl23xrlDIGQWRH0s9nsROv2yVhEETUBoWYantsB0HGD+PDKfl4f/s5AMCtV0U0+f3Mg6D8UikT5GnRJsCjOghq+HBYdSYqoXcolArjejvJuU0fEjMYBAz1FGbbU6XeIK+m3SvcmAmKCnCHSqlAYXlVg9ZNqo8QAltPGYcmLuUUo6xSb3GPOBxG9iRlFovbwPcVgyCiNujOa6IxulsQbooLt/q8NBx2NrMIpzOK4KlxwZ3XRDf5/aThsJT86rVvpOEwuY2XWi68rn84zLIwGjBmtwbHGrfv+LWJ2SAhBG5ZvgujXtuCI2abmdb0zV/JWPLTMbsES+eyilBRZYCXxkUuONe4qNDB17ho5aUrCOgkZzKLkFZgDCwNwrgsgUUQxEwQ2VEJM0FE1JoNjPHHytmDag1JSYK9tQj1rp5Kf+c10XVujVEfKRMkbQWhUirkD3yJcTjMVBPUhOEwAJjQ1zgk9sOhy9h1LrvRRdL5JZVITM5Hcm4ppqzYjR8TL1tt9+JvJ7Bq10Uk1hEoNdRx01BYjzBvKJXVw43Sdib2mPG29VSmxddnMovkbUwABkFkX9LwKoMgInJa0pCY2kWJu4bHXNG5pFWqpQ9bHzdX+HuoYV5iFOipthgOO5Gmw3/W/S0v1GhOSrO7uVoWZl9vGhI7mV6I//twL4a/vAVvbDrd4LqaDLNhuPIqAx5Zk4g/z1jOcCmt0MuBydnMK5+9JdUD9TQNhUmkoLPmukpNIQ2FaV2Nv9LPZhRaFka3gWELaj2kTC2Hw4jIaUlrAP3foKham642VqBXja063FyhUirga5ZdCvTUyMNhKXmluPfz/fhiTxIW/Xis1vlKrQyHSedYPLEXBsf6y8Ntb20+0+Chq3TTkFG3EC+5Buq9Lecs2pjXK9ljCvsx08ywXnYOgvJLKnAhuxhF5VX466JxMUTpmo6m6pBuFlw2JBNUWFaJZb+ftkvgR21baaVxyLstZIIaNv+ViNqcOwZHo2eYN+Iifa/4XB5qFdxcVfKHrbfpA97fQ408U1Yl0FMDpSk1ZF4TtP5YOg5cysOA6OrtOWpOkTc3c2gMZprWL/ps90Us/ukYPt19CXkllXj99n5wVdn+204ahgvz1eLRcV3xY+Jl7D6fg6OXC9DbVCeVYTZUd+4KAwIhhDwcJs0Mk/i4GX/9NjUI+r8P9+J4mg6dgjxQqReI8nfHdT1D8OXeJOw+l2PRtiGZoB8OXcay38/gdEYh3rtjQJP6RO2D9EdKSYUeBoOwGOZ1NswEEbVTKqUCA2P84VJH0NBQCoXCYqVqaZHGAA/jMTdXFTw0LhZtAKCfKQB76bcTFkNaJZXWa4JqmjEkBsumxsFFqcBPh1Mx57P9dX7gZ5iyIyFeWoT7usk1Rh/9eb5WG+DK1/FJySuFrqwKrioFOgdbzpaTMkG6JgRBVXoDTqbrLPoY3y0IXUO8ANTO/DQkEyQFfGcymAmiupmvQF7i5PVmDIKIyC6k/cMAyMNg0mKNAabnzFeznjowEivuvAoaFyX+upiH309UF/faKoy2ZlJcB3w4cyC0rkpsPZWFez/fb7NGSKoJkpYQmDOiIwDg5yNpSCsw1tCYB0GXcopRXtX0X/JSPVDXEC95GxHJlQyHZRdVwCCMgeycEbG4pqM/7hoWizAfLTzM7pnK9Bd6Q9YJkmapXcopqXdvN2rfzINqZx8SYxBERHZhnuWRPuD9TcGPNIUeAO4b1Qlje4Tg3xN6IMzHDXcNjwUAfLrrotxGKrx0UzdsxH50t2B8cfdgaFyU+PNMNv66mGe1XXqBtEeasQaqdwcfXNPRH1UGgS/3JAGwDIIMwhgUAEBecQXe3XIWI175A1NW7GrQENNxG/VAwJUFQVLAFuKlwVMTemLNvUMQE+gBhUKBzqZsEADEBBhn6DUkE5Rkus4KvQGp+aX1tKb2zPx7n0EQERFqBEGmRRqlvcLMg6C5ozvjo5kD5SDg+l6hACAP7wBAQanxF6unpv5MkGRgjD9uuaoDAOCTHRestsmUM0HVheCT44yvOXLZGLBk1Ji+fzazCGczCzHy1S14dcMpJOeW4q+LeXj25+P19ul4mnGRxB5h9g2C5GE9n9oF7V3Mht26hZqGx+oJ2PQGYTGl/nx28+5u35pcyilGYnK+o7vhNCr1BlSZZQqLyzkcRkRkEehIH/D9TNtz9I/ytfk6qVYmu6gCucUVqNIb5FlZnWqsOl2f2cOMWaWNx9OtriotBQ/mayRJayldyC6yaCMNK53LLMLnuy+hsKwKHQM98MiYLlAogK/2JdW7i7Z0zpr1QEB18XhTgiBplltYPUFQl2BjEFRmms1jS1pBKSr11R9sF5pxd/vWZvbKvzD53Z21lkog60pqBNRFzARduXfffRcxMTHQarUYPHgw9u3bZ7NtfHw8FApFrceECRPkNrNmzar1/PXXX98Sl0LUblkURps+4Mf2DMH+/4zFg/GdbL7OQ+Mir558JqMQF3OKUVFlgLtaVWvBxfp0DfHCiC6BMAjL4TXAmO2QtqiQaoIAINY01f5yXikqqgxyEDTItGHrqYxC/HrUuGnr0zf2xD+v6ypfzz+/TsSgF37HiFf+wLtbzsqrZUvvJ63VExNQe9HKKxoO09XOaEm6hFjJBNUzHCYNhUkutJNMkBBCroV68ru/UVxeheTcEiz+8SgOJlkfUm3vamYVS5x8E1WHB0Fff/01FixYgMWLF+PgwYPo168fEhISkJmZabX9999/j7S0NPlx9OhRqFQqTJkyxaLd9ddfb9Huq6++aonLIWq3gqxkggBjhqi+jVm7mj64z2QWyftsdQ3xatLU27tM2aCv/0q2+Cs1p6gcBgEoFUCAWV+DPDXwUKtgEEBSbrE8HDa0k3EdpU3HM5BVWA4fN1cM62w8Nn9sV1wd44cKvQGZheVIzi3FqxtOYcJbf+KoaVgtNb8UFXoD1Colwk1BnrkrGg6rMxNkDHwUiuqsUH0fVDW37mgvw2FllQa5CPxyfinmrj6IG9/egU93X8Jbm884uHetU82AmpmgK/TGG29gzpw5mD17Nnr27IkVK1bA3d0dn3zyidX2/v7+CA0NlR+bNm2Cu7t7rSBIo9FYtPPz87N6PiKyD2tT5Buqi6mY90xGIU7KdTRedb3EplFdgxDp74bC8irsu1C9Xo60eGCQl0aeNQUYp/fHmIbEjqQUyL/kh3Y27lNWbsruJPQKkWd4uaqU+OKewfj14RH45eHhePW2vgjwUONMZhEeW3sYAHAxxxhISJul1iQFQRVVhkbv8i7tE2YtExTp746Hr+2MheO7y1tzlFUa6lxMUir+lmqX2ksmqLDcMgDdeipLDkrr29qlvaoZUNccHnM2Dg2CKioqcODAAYwdO1Y+plQqMXbsWOzevbtB5/j4448xbdo0eHhYppu3bt2K4OBgdOvWDQ888ABycnJsnAEoLy+HTqezeBBR45jXBDU6CDJlLE5nVGeCuofWLiZuCKVSgf6Rxj96pHMB1QXPoVYCB6kuaO9548rLPm6u6BriBRez4OXGvpab0WpcVOgZ7o1e4T6YMjAS/3touPyehWWVuGgKJKwNhQGAp8ZFDo4amw2ShuzCfGpnmABgwbhuuHdkJ4slBsqrbNcFJeUa+zq6WxAAY1akrNK4EF5DN5Etq9Tjs90XkV3kPMFDUZnxA91L44JZpgU4R3U13oOc4ua7jtIKPZ7933HsPW/7c6m1qhmwc3bYFcjOzoZer0dISIjF8ZCQEKSnp9f7+n379uHo0aO45557LI5ff/31+Oyzz7B582a8/PLL2LZtG8aPHw+93nrEunTpUvj4+MiPyMjIpl8UUTsV5KWR9wrzdVfX3bgGOROUWSTPEpPqWZqiuymLJGWVgOrAIbiOIGiPKXMU4q2Bq0opZ4j8PdQY2imgzvcM93WTh6dOpBXiQnaJ6dzW65oUCoW8ZlJjNlEVQsiZIGsBnTmtS3UQVFddkJQJuirKD14aFwgBJOWW4MEvD+LqF35HbnFFvf36YPt5LPrxGP676XRDLqNVkIZyvLQuWDyxJw4vHoeXbu0DAMgpqmjwnnSNte10Fj7ZeQEvrT/ZLOdvTiyMbkU+/vhj9OnTB4MGDbI4Pm3aNNx0003o06cPJk+ejJ9//hl//fUXtm7davU8CxcuREFBgfxITk5ugd4TtS1aVxUeG9cN94/qZJEVaojqGWLlSMkzFhN3v4IgqIcpi2Q+7T5TLiau3TcpWyMFA9IwU2fT7LTre4c2aGVtaT2gY6kF8nCYFEhZ05S6oILSSjmrE2zlWswplQpoTEN4toIgIYRcGB0d4C4Xiq/dn4z1x9KRU1yBwyn59fZr59lsAMZ9y5yFlAny1LpAoVDIG/8CQJVB1Pp3qagyYM/5HIsC+KbILzEGlZdyas9gbO1qBkEcDrsCgYGBUKlUyMjIsDiekZGB0NDQOl9bXFyMNWvW4O677673fTp27IjAwECcPXvW6vMajQbe3t4WDyJqvLmjO+PJ8d0b/TpPsxligDHD0dhskjkpE3Quq3rF53SzLTNqqhmoSBvKPhDfCTf2DcO80Z0b9L7S/mBHL+vk4bBYG8NhQNOCICkL5O+hhta1/nWUpP3XbK0VlFdSiULTX/OR/u5yVuyTnRflNlIhti1llXocMq21czajsNkyKPamk4IgTfWinBoXFbxMGbrsIssM2MqdFzDtgz342MY6VA0lZU9yiytQWNa0veMcpeZwGDNBV0CtVmPAgAHYvHmzfMxgMGDz5s0YMmRIna9du3YtysvLceedd9b7PikpKcjJyUFYWNgV95mImof51O7uTSyKloR6a+Hj5gq9Qci7oks1QdYWGOxYIwiSskX9In3xzv9dZXV2lzXSJqxHUvKRZJpxVVcmqClrBaVbWeuoLm6mQMlW8fUlU8Yq1FsLratKDoLMt84w35HemiMpBXJ2pLhCj9R6gqbWQvoA99Ra1rBJMx1zatQ3nTft03bgUm6d561v2xHzwCHJynpWrVmtTBCDoCuzYMECfPjhh/j0009x4sQJPPDAAyguLsbs2bMBADNmzMDChQtrve7jjz/G5MmTERBgOU5fVFSExx9/HHv27MHFixexefNmTJo0CZ07d0ZCQkKLXBMRNZ75In9NLYqWKBQKeThNqgvKqGNtHT8PtcW0/lArgVJDSMNhZzKLUGUQ0Lgo6wxWmpIJkhZKbGgfpSDI1nCY9CEcZdpiI9YsaJPqwjPqCYJqFviezii00bJ1KTJlYbw0ltuzSHvd5dSohco1DWOdSLN9fTvOZKP34g34fPfFOt63OnCwtqhna1Yzo1jEFaOvzNSpU/Haa69h0aJFiIuLQ2JiItavXy8XSyclJSEtzXJV1lOnTmHHjh1Wh8JUKhWOHDmCm266CV27dsXdd9+NAQMG4M8//4RG07g6BSJqOV3M9ry6knqgmueQ6oIyrSyUaM48YxNsZcisIcJ8tHJNCWCsNaprraOWCIKkITNbw2FSXUq0vzEI6hhYHYxOHxRl8Z627LtozIxIs93OOslO9OaF0eYCPKxngvJMQdHl/FKrw1hCCLy8/iRKK/XYdtr2CtTmmSBnqwuSgmkpuHb22WEN252wmc2bNw/z5s2z+py1YuZu3brZHHN2c3PDhg0b7Nk9ImoBFpmgKxwOM55DKo4uRHmVXp7hZK0mCDAOiR021bXYCpTqo1Ao0CvcG3+eMRYJx9iYGSaRgiBdaSUq9Qa888dZpOSVoqxKj7gIX8wZ2bHWa9IbODNMItUEWStgLa/SY69pRly0KRPUM9wbN/fvgA6+bhgQ7Ycv9yYhvY41cyr1Bhy4ZFxdeVzPEPx2NN1pMkFSLZSnjUxQVpH1TBBgzHYNiPa3eH7n2Rz8bVoss64Zdc48HCYF00FeGiTlljj9itGtIggiIuoa4gU/d1e4qpQW2Yimqs4EFcoL36ldlDbXMDJfz8fakFlD9Qr3MQuCbNcDAZaZoM0nMvGm2SrFvxxJw7DOgehZYwd6uSaokcNhNWuCknNLMHf1QRxJMX5oDzWthq1SKvDfqXEAgOOmmV51DYf9fbkAJRV6+Lq74oY+YfjtaDrOZDpJJshsdpi5QBs1QXlmgc2JtNpB0PJt1ZNv6lr2wJmDICmYDvRUIym3hIXRRET24KFxwU/zhuOHucPklZmvRNcQLygUQFZhOfZeMA7XhHjb3sJDytooFJarXzdW7w7VQUtdM8MAyyDoRJox4BgQ7YcB0cbFHr89kFLrNY3NBGlt1ATd8+l+HEkpgK+7Kz6eORBXRdVeVV8KtHKLK1BepUdybgn6P7sRL/1Wvb7NPtO9vTrGX17b6WxmkVPMECu0MjsMMH7AA8a1giR6g0C+2bDlqXTLbNeRlHzsPFtdG2WeNarJvCbI0UHQ+qNpcga0IUorjX2XAkVOkScispNIf3eLqfJXwkPjIte5PP6tcSuLPqbZW9Z0NdUkhXlr4dqANYFskabJA43LBElDSON7h2LuaOMGresSL9dakyZdXi26YUGQu5Up8hVVBpwyvd93DwzFmB4hVl/r5+4qB6SZunJsPZWJvJJK/JR4WW4jBUGDY/0RE+ABF6UCReVVSCsoQ6XecMVr6tRHCCEvg9BYNmuCpEyQ2arR+SUVMI/ragZBq0wb9o7oYsyoFZRWokpv/drNsyeX80pttmtuybkluP+Lg3jwy4MNfo30fRRo+kOBmSAiolZKmmUmBDC8cyCen9zHZtseYd544ebeeP32uCt6z2h/d4R4a6B2UcqBlS3mQZAUlHQL9cLILkEI8tIgt7gCW05VbyZdWqGXi6itTfW3xtrsMOkcCoXtbT2Mzyvk+qh0XRlOmwqeUwvKUGAa7pFqYPpH+UHtopRnl/19uQC3Ld+FIUs3N2jFaUlybgniX92COz7aU29tka6sEret2I1BLzTuPSTycJjGcog0wFTcbr5OUF6NzM6JdJ1FtuugqS7qruHGDXyFsF3wbh44VBmqVwBvadLCpGkFpfVO65dUD4cZvy+Ky6ucIutnC4MgImqzbugbBg+1CnNHd8Kndw2ymLllzR2DozGknu0x6qNUKvDt/UOx7sFh9b6ftE5QVmG5vLhitxAvuKiUuKV/BwCWQ2IpecahEw+1qta0blukwugyK0GQl9n+ZbZIw27pBWUWQcnJdB0yC8uQVVgOhaJ6w1tpvafFPx7D4ZQC5BRXYNPx+rdBkry5+Qwu5pRg59kc3PDmnzZ3cy+pqMJdK//CgUt5KCitxFFTMNYYhfVkgsz3QcstNt6zDr5ucFEqUFhWJQcvReVVuGQa1urbwUfeDiXPRl2QFARJe9PZc0hMV1aJT3ddbFCGRsp0GeoI2GqSgmlpyNgg6t6XrrVjEEREbdZN/cLx95IEPJ7Qvd4Pe3uK9HevVdBsjXkmyCCMG89KHy63DYgAAGw5mYks0/T+1fuSABgXcbRV21STtSny0geeTwM2upWKxDN0ZRYFz6cyCnHMVDjdMdAD7mrjB3+XYGMwZL7A4qbjlrsC2HIxuxg/HDIOtQ3pGIAqg8Abm07jWKoxwKnUG/DtgRQs/e0Ebn9/N/absi9A9RIIEiEEnvrhbyz68ajN9ysy7SJfszBaWiyxsKxKHmqTZxd6a9DRtLWINCR2Kl0HIYzPBXhq5OC3ZvZI6pc0rVzaLsaeQdDbm89g8U/H8PYf1oNHc+Y1T7kN3DBW+j4KMAvwnXlIjEEQEbVpda3T42g1gxBjMbexv11CvBAX6Ysqg8DSX08gs7AMq/cag6AH4js1+D2sDYfppCDIrf4gSMoEHU/VWQw5nUgrlGeP9TSrgzJf+fuajsbZU3+eyba5TpG5d7achd4gEN8tCF/dew3G9TTWKv18xLhW3KqdF/HY2sN4f9t5HL2sg7tahb4RxveuOYPtXFYRvtybhM92X7I5VGa+i7w5bzcXOUsjvVYKaPw91OhmGmY9YVqD6rhp8cSepmUZ/EwBgrX3La8yoFJvHD6SAmV7rhV0MCkfALD3fN2rWgOWs99yiho2nCgNh7mrVXK9mTOvFcQgiIjIQTzVLjCP0WouEvn0jT2gVADfH7qMOZ/uR3mVAf2jfDHcNJ29IdzUpg1UrWWCGhIEmWqP/jRtkCo5la6TMzS9zLJeV0X5Qa1SIibAHR/NvBoRfm4orzLgzzO2Fw8ELLNAj4zpAgC4KS4cAPDzkVToDQKf7bkIALihTygW3dgTvz48Qr4XWTUyQeYztWo+BxgzMoU2psgrFAp5raDsQmNwIAU0fu5q+d9JygRJwWAPKQgy7XuXZyUIMs+aSBv92mvVaL1ByH05llpgc6sUSXax7ZonW6Rzuqtd4GEKHoudeNVoBkFERA6iVCrkuiAAtQqpB0T7Y961xoDgsGk9n4fHdGnwUBhQd2F0Y4IgKZCIMS2qeCq9EEcvGz9wzYOgcF83bH50FH56aDg8NS4Ya5p59vuJuofE1h5Iht4gMKprEPqbputf2z0Ybq4qJOeW4p0/ziI5txTeWhe8PiUOdw2PRUygB4JNw4c1M0G7zlUHbdlFtYOg8ioDqkzFwDWnyAPVq0Znm4aJpIDG36M6CDqUlA8hBI6nSRmxGkGQlZogKfvkoVbJswcv5RbXeW+W/X4a81YfrLd4+VxWkfzvXKkX9dZJWWSCGlhYLmWC3FxV8JAyQU68YCKDICIiB/I1C0S6Wdku5OFrOyMu0hcA0DfCB/Fdgxp1fmvrBEkL+TVmOEwyrlco1Coliiv0ci2L+bIAgLEmytu0Kel1piGtzScyLT7E/ziZYRGoSDPPru0eLB9zV7tgTA/j18s2nwYATBkYKRd7A0CwqX/mNUF6g8Aes+Ega5kgKQukUAAe6tpBkDQFXBomktb98fNQ45qOAdC6KpGUW4LE5HycSrfMBPl7GK/dWnaletNWF0SZlnBIqmM4TAiB97acw89H0uQtYABYnVb/d4pl0HMwKa9WG3MWNUENHA6Tt81Qq8wyQQyCiIioCcwDka7BtYMgF5USy++8CndeE4VXbuvbqCwQUD07zNpwmHcDgqCaq2f3CPOSC3qB2vul1TQo1h9eWhfkFFcgMdn4oZypK8Oczw7g7lX75cLjC6bZcbE11la6sW8YAMhr9NwxOKpG/2pngk6k6SxmO1kLguRgRO1itW4s0ENaMLFGJshdDQ+zDNdbm8+grNIAN1eVvNxAXTVB0vt6aIxBkEIB6MqqrPYRMAasFaaAJzXfeI1HLxeg95INWPj9ERjMAsujpuFJKft34JJlELT/Yi4W/XhU7oN5/xqaCSo1qwnicBgREV0RKRAJ9dbanK0V5uOG5yf3kdc9agxr22Y0ZjisZhDUJdjLonapVz2z4FxVSowyZa+kOp2jqQXQGwRKK/U4n1UMvUHgUo4xCJJmXkniuwXLwy4jugSiY5DllirSZreZheXyejXmGSbA+nCYrS0zJHJNkOm1uabsmRTg3NTPWK+05ZSx1qlbqJc8A7HOmiCzYmw3tUpeVVxaMbwm8wxXar5xXZ8/z2SjrNKAr/YlY+lvJ+TnpeGvyablFQ6ahuskb/1xFp/tvoRfTYXm5velITVBVXqDHJBZDIcxE0RERE0hBSLWhsLsQc4ENTEIUrso5enQCoVxWrf5Brc9w22vwi2RanykD2mpeBcwbkR6Oa8UlXoBtYsS4T6WK4ZrXVWYenUUVEoF7h9Ve1actKRARZUBulLjh/Guc8ZgS8oSWR0Ok6bH21hvSV412jRMVF0TZLxno7oFyesBAbBYEqG6Jqh2YCHVz0jBVw/T646l2gqCqjNcUhCUnFc9fPbhnxfw4fbz0BuEfI7/GxQFV5UCWYXl8oKIQPV9SMotMd4vs+07GrLYpPn3kMVwGGuCiIioKaSVd2vODLOXK50iD1Rng6L83aF1VclTxIHqaeF16W36oJeCoBNp1Ysuns4oxPlsYz1QbICH1aGppyb0wIH/jMUwK7PitK4q+ToyCstQUWWQt/KY2NeYrcmykgmyNTNMIv27SDOo8sxmhwGAxkWF8b3D5Pbm96F6naDqITlp2KpQLow2vq+USTtuIxOUoavue4oUBJlqsa6K8gUALP3tBH5MvIySCj3cXFXoGe4tB6fmdUHSNSTnldQKehoyRV4aClMoAI2LUr4GZoKIiKhJZgyJxowh0Zg5NKZZzl9dE1RdSCtlgnzd6l7RWiLNEJMWQuzRiOEwAOjVwQcKhXG7jZyicouhn1PpRTbrgSQqpQK+7rb7Ks0Qy9SV40hKPkoq9PD3UGN4F+vT5wHzLTPqHg7LKSpHRZVBXl3avP5pkmkKP1BdFG1sYwzKpEDjn18nYuhLf6CgpNKiMBqoDp6Op1qfyWU1E2QKgp64vjsm9guHQQBPfv+38Xzh3lApFRhgyr5JdUFCCDkzlZxbUmuIsDGZIHdXFRQKhVkmiDVBRETUBB2DPPHspN4It9PGsTVdaU0QAET6GfvW0zQMFuytxcPXdsbc0Z0Q4Vd/vz01LnKAs+9CLi7kVE8JP5NZiPNZpiAoyHoQVB/zVa2lpQQGRPvJ9UJWa4JMwYg0i62mQI/qrTPyTcGDUmHZfnDHAPTu4I0IPzeLYFDKFhWUVqKsUo+fj6QiXVeGvy8XyFkTaYFGaRjtfHYxSqwMK2XqLGuC9AaBy6ZgKNLfHUsm9oSfu6u8Ua20SfBV0b4AqpdWKK3Uy9tbpOSVyoXQ0pBebnFFvXuAydPjTRkgT43z1wQ1bPMZIiJySlIQVFJh3OhSoVA0OgiaM7IjfNzVmDkkWj62YFy3RvWjd7gPzmcV47uDKRDCuE6ONM1eGgqylQmqj5wJKizHmUzjUFuvcG+5Xii3uAJ6g7DYOkXOyNjIBEmvzS6qQKppjzA/d7XFcJ1KqcAPDw6DUqGwOLf5fT14KU9eITqtoLR6OMz0vsFeWgR6apBdVI6T6YW4ypTBkZhnsTILy5GcW4JKvYCrSoFQby1USgWW3NQLj6xJBAD0NgVB0vT7DFPfzTM9mYXluGyqFeoS4oUDl/JQoTeguEJv834A5tPjjfkTd1NbbptBREStktY0HGYQQIXegIoqg/xh1tAgKMLPHQuu6yoXCzeFlKGQZlMNiPFHgIcaQlTXrXRsahBklgmSiq57hfvA30MNpcJ47Tk19saqryYoxFuDMB8t9AaB9UeNG8D6WVkKwFWlrLUvnYtKKd/bnWYz1TJ0ZbWGw4x9lYbEatcFmQ+HCQHsu2isd+rg6ya/7039wnHrVREI8dZgZFfjEKBU05RTbJw1l1dsuXDjkZR8AECEn5scKNe3VpA8Pd7V2HcpkCvhFHkiImqNpA84ACirMMhZIIWi9u7pzUnKUEgLJvYI85L3GZNGYa40E5SSVyJv8trLVBvj72F9hlhhWd2zwxQKhTy1/8dE43Yeda2HVJPUVpqpBgBpBWW1hsOA6iExa8XRNTeG3XPeeL5IU6ZH6utrU/piz8Ix8hCgVNNUqRcoKK2sNVMtMTnf2M6jesPXmoFiTdJwmBRYc8VoIiJq1VxVSnkz0NJKvRwEeWmsLxLYXHp1sCyg7hnmjW5m24R4a10aFWSYCzZNhd9zPhd6g4CfuyvCTMXc5sNa5qSMTF2B4EhTEJRmGlLyr6M4uyY/05pPR8xWcU4vKLNYLFFSXRxtGQQJIeSaIGl4S9oY1TwIAoyBkPlCmhoXlVzvk11UXisIOp1hHDYM8FTL972+4mjzwmjza3DmmiAGQUREbZz5NPmCUuMHna2FGZuLt9ZV3ncMMH7wdzWbZdYxyLPRq2FLpMJoKcDoFe4jnyvQlBGpmQmSFy2sIwga1jnQYqjL2nCYLVJxtPlWIWkFZdXDcJraw2En03UW7YvKq+TAQ9o6RS6K9rMMgqyRtv7IKqyoFeBIbxPYmCDIlPGRdo+XriE1v0zul2Tb6Sxcv2w7Xt1w0uoWH60FgyAiojZOa7Z1RmOLou1JGhJTuygRG+hhkQlqaj0QUD0cJjGfqRXkZWM4TC6Mtn0ffNxc5eADqJ763hDWAqZ0XfVwmHlNUHSAB9zVKpRVGnDBtGYSUL1GkJfGBZ1qrJQd6V//rDx5raOicnnNopr1SwEeGnkxzPqDIMvhsEg/d6iUCqTryjD61a34z7q/sfNsNr7Ycwl3rfoLJ9ML8e6Wc7jjo70WtU2tCYMgIqI2zjIT5PggqFuIF1xUSnQxC4KaWg8EVG+dIelpJQiqOU2+vm0zJKPMNqz1a8RwmPnQnnmQIU1N9zILvlRKhbzO0KGkfPm4FDgEeWsQ7mt5jVH+9WeCzANAaaHEriGWi3I2ZjispMZwWFSAO75/YCiGdAxAhd6AL/Yk4Y6P9uI/645CbxCI7xYET40L9l7IxbT396CyFWaEGAQREbVx0vBFWaUeBY3YQd7ebunfAYNj/XHvyI5yH6Rd6pu6RhBgXBDSy2K2VfVWHkGe1jNB9U2Rl4w0C4IaU7NkHjAN7ugPravx41YKNDw0Kov2g2L9ARjrmiRSn4O9NOhQYz2mhgyHBZllgnJNNUF9O1hucxLoqZGzVvVtoloqrxNU3fd+kb5YPWcwvrxnMKYMiECgpxoKBTB/bBesnHU1fpw3DAEeapzPLsavf6fV2+eWxnWCiIjaOK28VpAeBab9tRwRBAV7a/H1fUMsjt09PBa/Hk3DiM5BNl7VwHN7aVBYVgU3V5VFVsnmcJhpdlh9M+T6dPCBn7sr8koqGzk7rPr+9gj1xom0QnllbKB2BmpIxwAs33oOu89ly+s5SUXRwV5adDBbTNNT4wLfBtR0BZptAitlgvpE+ODr/clm/VTLmaq84gqUVFRh/dF0HEvV4VJOMe64JhqjuwUDsB4EAcai7GGdAzGscyAMBuNsNCmw6hTkiZlDY/DGptP4YPt53NQvvMm1X82BQRARURtnfTisaTOx7G3OyI6YY8oMXYkQby3OZRWjR5iXRd2LeTZEIoRo0OwwwDhU9cyk3vjzdBaGdqq9d5kt5tt89AjzRui5HIsgyKtGLdLAGD+4qhRILShDUm4JogM85OGwYC+NvHUJYJwZ1pBAoromqEKuCYryd4evuyvySyqhdVXCXa0ymyJfgSU/HcM3+1Pkc+QWV8hBUPVwmO17plQqatVD3XlNNN7behbHUnXYfT6nUfexuXE4jIiojZP+ci9zcGF0c5KKo3vV2NVeniFlFgSVVxnkVZzrGw4DjIsRvjqlH9QuDf/INM8a9Qj3lqfsA8btN6ThMYm72kUuwt5tWltIWiMoxFsLjYtKvsbIBmxVAtQojDbbAFYaSgvw0EChUMhrCl3ILsb3B41rIo3rGQKgenkAwPj9A1SvGN1Q/h5q3DYgAgDw4fbzjXptc2MQRETUxrWWwujmNLl/B3QP9cKtpg9biZQJyi+plPfXMt/mQdoJ3d6kveACPTUI99EixCwI8tS4WM3kDOkYAKB6gUV5OMy0DpJ0zpprBNkiBYDZhdU1QX4ervJ+b9JwmfleZ1UGgcGx/nh2Um9jHwrL5Wn7NfcOa4y7h3eEQmFcMfyiWUbM0TgcRkTUxmnNgiBdGw2C4rsFI940bGPOx80VrioFKvUCX+9PxqbjGdCYMjqezbhgZAdfN6y4cwDCfLRQKBQWmSAvG5u2XtMpAG/9cRa7z+cYF0qUZoeZgpnOwZ5ITM5H1xBPq6+vSQpy0nVl8rpA/h5qOYiStkEJ8LBcYuD++E4I9DRuOaI3COQUlyPYS1u9d5irZU1QQ8QGeqB/pC8OJuXjcEo+Yq5gNqA9MQgiImrjpOELR68T5AhKpQIBHhqk68rw9LqjFs95N/O2Idf3DpX/X5oFB9SeGSa5KsoPahclsgrLcS6r2KIwGgCeSOiGQbH+uKlfeIPeXxoOkwIgjYsSbq4q9DcNu/UIM06X93ZzgYtSgSqDQLcQL8R3DYJCoUCgpwaZheXIKDAFQdLeYerGB0EA0CXYCweT8nE+i5kgIiJqIdJf7gWlle0uCAKMmZR0XRkUCmDW0Bh4aV1xPLUA1/cOa7E+hPlYzu6yRuuqwoAoP+w+n4Mv9lySF3SUhsOCvbW4fWBkg99T66qCl8ZFPo+/hxoKhQLX9w7Flsfi5bWGFAoFgr00SC0ow32jOspDdaE+WmQWliNdV4Y+8JHrqhoyM82ajqZlEM5lFdXTsuUwCCIiauP6mf7y//lIqrxtQ3sKgmYPi8Ha/Sn453Vd5fV4Wpr57C5PG8NhADC2Zwh2n8/Bql0XARgLqL0aULxtS6CXRg6CpBlrCoWi1uKUz03ujRNpOossk3E7kgKk68qgNwik5JUAMK5w3RTSqtfMBBERUYtJ6BWKcB8tUs1m+rSnIOiWqyJwy1UR9TdsRgEeark2qa6gZtbQGFTpDfh4xwVkFpYjJsDjitbVCfRUy1Pz69r2Y0yPEIzpEWJxTBrCyygoQ1pBKSr1Aq4qhcXQXmNImaDz2UUwGESLbuBrC4MgIqI2zlWlxF3DY/H8LycAAApF/evjkH0plQoEe2lxOb/UZk0QYFyX6L5RnTBrWAy2nMxE5+CGFUHbItUFAY3b9gOozl6l68qQlGPMAkn7hTVFpL87XFUKlFUakKYrs1gA0lE4RZ6IqB2YenWknIHwasZZUWSbNEOsrk1bJRoXFa7vHYbOwV71tq1LkNnmso1Z8RqQhsOADJ1xAUeg4dPzrXFVKeU6pHOZraMuiEEQEVE74KV1xbRBxqJanyYWttKVCZWDoKbNrmoK80yQb2MzQaYgKL2gDJdypXqgpgdBgHldEIMgIiJqQXNGdETvDt641cH1Me3VhD5h6ODrhlHdrmyftMYwD4L8Gxn8hvoYX2s+HNaQ3evr0tEUBJ1rJcXRrSIIevfddxETEwOtVovBgwdj3759NtuuWrUKCoXC4qHVWhZpCSGwaNEihIWFwc3NDWPHjsWZM2ea+zKIiFq1YG8tfn5oBOaP7erorrRL4/uEYeeT12JAdMvNUJMWTARQa0+v+kjDYYVlVTiZrgNw5UFQJ7Pi6NbA4UHQ119/jQULFmDx4sU4ePAg+vXrh4SEBGRmZtp8jbe3N9LS0uTHpUuXLJ5/5ZVX8NZbb2HFihXYu3cvPDw8kJCQgLKyMhtnJCIiansCr6AmyEvrCg/TwohS5qap0+MlciYok5kgAMAbb7yBOXPmYPbs2ejZsydWrFgBd3d3fPLJJzZfo1AoEBoaKj9CQqqn9QkhsGzZMvznP//BpEmT0LdvX3z22WdITU3FunXrWuCKiIiIWoegK5gdBlRngySR/lc2o0vKBKXryiz2cHMUhwZBFRUVOHDgAMaOHSsfUyqVGDt2LHbv3m3zdUVFRYiOjkZkZCQmTZqEY8eOyc9duHAB6enpFuf08fHB4MGD6zwnERFRW2NZGN34gnjzICjISwP3K9xw1tddjQBTRupCK6gLcmgQlJ2dDb1eb5HJAYCQkBCkp6dbfU23bt3wySef4Mcff8QXX3wBg8GAoUOHIiUlBQDk1zXmnOXl5dDpdBYPIiIiZ+emVuG2AREY2yMY4T6Nz+KYr3R9pfVAEnmGWCuoC3K61bKGDBmCIUOGyF8PHToUPXr0wPvvv4/nnnuuSedcunQpnnnmGXt1kYiIqNV4bUq/Jr/WPBMUbacgqGOQB/ZdzG0VawU5NBMUGBgIlUqFjIwMi+MZGRkIDQ218SpLrq6u6N+/P86ePQsA8usac86FCxeioKBAfiQnJzf2UoiIiNqcUO/q4bQrWSjRXL9IX1zT0R/h7X3FaLVajQEDBmDz5s3yMYPBgM2bN1tke+qi1+vx999/IyzMuBtwbGwsQkNDLc6p0+mwd+9em+fUaDTw9va2eBAREbV35sNhV7pQomT6oCisuXcIpg2Kssv5roTDh8MWLFiAmTNnYuDAgRg0aBCWLVuG4uJizJ49GwAwY8YMdOjQAUuXLgUAPPvss7jmmmvQuXNn5Ofn49VXX8WlS5dwzz33ADDOHJs/fz6ef/55dOnSBbGxsXj66acRHh6OyZMnO+oyiYiInI75cJi9aoJaE4cHQVOnTkVWVhYWLVqE9PR0xMXFYf369XJhc1JSEpTK6oRVXl4e5syZg/T0dPj5+WHAgAHYtWsXevbsKbd54oknUFxcjHvvvRf5+fkYPnw41q9fX2tRRSIiIrLNojDaTpmg1kQhhBCO7kRro9Pp4OPjg4KCAg6NERFRu2UwCMxe9RdcVUp8OGMAFIrWvfFuYz+/HZ4JIiIiotZJqVTg07sGObobzcbhK0YTEREROQKDICIiImqXGAQRERFRu8QgiIiIiNolBkFERETULjEIIiIionaJQRARERG1SwyCiIiIqF1iEERERETtEoMgIiIiapcYBBEREVG7xCCIiIiI2iUGQURERNQuMQgiIiKidsnF0R1ojYQQAACdTufgnhAREVFDSZ/b0ud4fRgEWVFYWAgAiIyMdHBPiIiIqLEKCwvh4+NTbzuFaGi41I4YDAakpqbCy8sLCoXiis+n0+kQGRmJ5ORkeHt726GHZAvvdcvhvW5ZvN8th/e65dj7XgshUFhYiPDwcCiV9Vf8MBNkhVKpREREhN3P6+3tzR+oFsJ73XJ4r1sW73fL4b1uOfa81w3JAElYGE1ERETtEoMgIiIiapcYBLUAjUaDxYsXQ6PROLorbR7vdcvhvW5ZvN8th/e65Tj6XrMwmoiIiNolZoKIiIioXWIQRERERO0SgyAiIiJqlxgEERERUbvEIKgFvPvuu4iJiYFWq8XgwYOxb98+R3epVVu6dCmuvvpqeHl5ITg4GJMnT8apU6cs2pSVlWHu3LkICAiAp6cnbr31VmRkZFi0SUpKwoQJE+Du7o7g4GA8/vjjqKqqsmizdetWXHXVVdBoNOjcuTNWrVrV3JfXqr300ktQKBSYP3++fIz32n4uX76MO++8EwEBAXBzc0OfPn2wf/9++XkhBBYtWoSwsDC4ublh7NixOHPmjMU5cnNzcccdd8Db2xu+vr64++67UVRUZNHmyJEjGDFiBLRaLSIjI/HKK6+0yPW1Fnq9Hk8//TRiY2Ph5uaGTp064bnnnrPYT4r3umm2b9+OiRMnIjw8HAqFAuvWrbN4viXv69q1a9G9e3dotVr06dMHv/76a+MvSFCzWrNmjVCr1eKTTz4Rx44dE3PmzBG+vr4iIyPD0V1rtRISEsTKlSvF0aNHRWJiorjhhhtEVFSUKCoqktvcf//9IjIyUmzevFns379fXHPNNWLo0KHy81VVVaJ3795i7Nix4tChQ+LXX38VgYGBYuHChXKb8+fPC3d3d7FgwQJx/Phx8fbbbwuVSiXWr1/fotfbWuzbt0/ExMSIvn37ikceeUQ+znttH7m5uSI6OlrMmjVL7N27V5w/f15s2LBBnD17Vm7z0ksvCR8fH7Fu3Tpx+PBhcdNNN4nY2FhRWloqt7n++utFv379xJ49e8Sff/4pOnfuLKZPny4/X1BQIEJCQsQdd9whjh49Kr766ivh5uYm3n///Ra9Xkd64YUXREBAgPj555/FhQsXxNq1a4Wnp6d488035Ta8103z66+/iqeeekp8//33AoD44YcfLJ5vqfu6c+dOoVKpxCuvvCKOHz8u/vOf/whXV1fx999/N+p6GAQ1s0GDBom5c+fKX+v1ehEeHi6WLl3qwF45l8zMTAFAbNu2TQghRH5+vnB1dRVr166V25w4cUIAELt37xZCGH9QlUqlSE9Pl9ssX75ceHt7i/LyciGEEE888YTo1auXxXtNnTpVJCQkNPcltTqFhYWiS5cuYtOmTWLUqFFyEMR7bT//+te/xPDhw20+bzAYRGhoqHj11VflY/n5+UKj0YivvvpKCCHE8ePHBQDx119/yW1+++03oVAoxOXLl4UQQrz33nvCz89PvvfSe3fr1s3el9RqTZgwQdx1110Wx2655RZxxx13CCF4r+2lZhDUkvf19ttvFxMmTLDoz+DBg8V9993XqGvgcFgzqqiowIEDBzB27Fj5mFKpxNixY7F7924H9sy5FBQUAAD8/f0BAAcOHEBlZaXFfe3evTuioqLk+7p792706dMHISEhcpuEhATodDocO3ZMbmN+DqlNe/y3mTt3LiZMmFDrfvBe289PP/2EgQMHYsqUKQgODkb//v3x4Ycfys9fuHAB6enpFvfJx8cHgwcPtrjXvr6+GDhwoNxm7NixUCqV2Lt3r9xm5MiRUKvVcpuEhAScOnUKeXl5zX2ZrcLQoUOxefNmnD59GgBw+PBh7NixA+PHjwfAe91cWvK+2ut3CoOgZpSdnQ29Xm/x4QAAISEhSE9Pd1CvnIvBYMD8+fMxbNgw9O7dGwCQnp4OtVoNX19fi7bm9zU9Pd3qfZeeq6uNTqdDaWlpc1xOq7RmzRocPHgQS5curfUc77X9nD9/HsuXL0eXLl2wYcMGPPDAA3j44Yfx6aefAqi+V3X9vkhPT0dwcLDF8y4uLvD392/Uv0db9+STT2LatGno3r07XF1d0b9/f8yfPx933HEHAN7r5tKS99VWm8bed+4iT63a3LlzcfToUezYscPRXWmTkpOT8cgjj2DTpk3QarWO7k6bZjAYMHDgQLz44osAgP79++Po0aNYsWIFZs6c6eDetS3ffPMNvvzyS6xevRq9evVCYmIi5s+fj/DwcN5rssBMUDMKDAyESqWqNZMmIyMDoaGhDuqV85g3bx5+/vlnbNmyBREREfLx0NBQVFRUID8/36K9+X0NDQ21et+l5+pq4+3tDTc3N3tfTqt04MABZGZm4qqrroKLiwtcXFywbds2vPXWW3BxcUFISAjvtZ2EhYWhZ8+eFsd69OiBpKQkANX3qq7fF6GhocjMzLR4vqqqCrm5uY3692jrHn/8cTkb1KdPH/zjH//AP//5TznbyXvdPFryvtpq09j7ziCoGanVagwYMACbN2+WjxkMBmzevBlDhgxxYM9aNyEE5s2bhx9++AF//PEHYmNjLZ4fMGAAXF1dLe7rqVOnkJSUJN/XIUOG4O+//7b4Ydu0aRO8vb3lD6IhQ4ZYnENq057+bcaMGYO///4biYmJ8mPgwIG444475P/nvbaPYcOG1Vrq4fTp04iOjgYAxMbGIjQ01OI+6XQ67N271+Je5+fn48CBA3KbP/74AwaDAYMHD5bbbN++HZWVlXKbTZs2oVu3bvDz82u262tNSkpKoFRafrypVCoYDAYAvNfNpSXvq91+pzSqjJoabc2aNUKj0YhVq1aJ48ePi3vvvVf4+vpazKQhSw888IDw8fERW7duFWlpafKjpKREbnP//feLqKgo8ccff4j9+/eLIUOGiCFDhsjPS9O2x40bJxITE8X69etFUFCQ1Wnbjz/+uDhx4oR499132920bWvMZ4cJwXttL/v27RMuLi7ihRdeEGfOnBFffvmlcHd3F1988YXc5qWXXhK+vr7ixx9/FEeOHBGTJk2yOr24f//+Yu/evWLHjh2iS5cuFtOL8/PzRUhIiPjHP/4hjh49KtasWSPc3d3b9LTtmmbOnCk6dOggT5H//vvvRWBgoHjiiSfkNrzXTVNYWCgOHTokDh06JACIN954Qxw6dEhcunRJCNFy93Xnzp3CxcVFvPbaa+LEiRNi8eLFnCLfWr399tsiKipKqNVqMWjQILFnzx5Hd6lVA2D1sXLlSrlNaWmpePDBB4Wfn59wd3cXN998s0hLS7M4z8WLF8X48eOFm5ubCAwMFI8++qiorKy0aLNlyxYRFxcn1Gq16Nixo8V7tFc1gyDea/v53//+J3r37i00Go3o3r27+OCDDyyeNxgM4umnnxYhISFCo9GIMWPGiFOnTlm0ycnJEdOnTxeenp7C29tbzJ49WxQWFlq0OXz4sBg+fLjQaDSiQ4cO4qWXXmr2a2tNdDqdeOSRR0RUVJTQarWiY8eO4qmnnrKYcs173TRbtmyx+vt55syZQoiWva/ffPON6Nq1q1Cr1aJXr17il19+afT1KIQwW0KTiIiIqJ1gTRARERG1SwyCiIiIqF1iEERERETtEoMgIiIiapcYBBEREVG7xCCIiIiI2iUGQURERNQuMQgiohZx8eJFKBQKJCYmOrorspMnT+Kaa66BVqtFXFyc1Tbx8fGYP39+i/arIRQKBdatW+fobhA5NQZBRO3ErFmzoFAo8NJLL1kcX7duHRQKhYN65ViLFy+Gh4cHTp06VWsfIsn333+P5557Tv46JiYGy5Yta6EeAkuWLLEaoKWlpWH8+PEt1g+itohBEFE7otVq8fLLLyMvL8/RXbGbioqKJr/23LlzGD58OKKjoxEQEGC1jb+/P7y8vJr8HrZcSb8B4y7aGo3GTr0hap8YBBG1I2PHjkVoaCiWLl1qs421zMOyZcsQExMjfz1r1ixMnjwZL774IkJCQuDr64tnn30WVVVVePzxx+Hv74+IiAisXLmy1vlPnjyJoUOHQqvVonfv3ti2bZvF80ePHsX48ePh6emJkJAQ/OMf/0B2drb8fHx8PObNm4f58+cjMDAQCQkJVq/DYDDg2WefRUREBDQaDeLi4rB+/Xr5eYVCgQMHDuDZZ5+FQqHAkiVLrJ7HfDgsPj4ely5dwj//+U8oFAqLDNqOHTswYsQIuLm5ITIyEg8//DCKi4vl52NiYvDcc89hxowZ8Pb2xr333gsA+Ne//oWuXbvC3d0dHTt2xNNPPy3vnr1q1So888wzOHz4sPx+q1atkvtvPhz2999/49prr4WbmxsCAgJw7733oqioqNa/2WuvvYawsDAEBARg7ty5Fjt1E7U3DIKI2hGVSoUXX3wRb7/9NlJSUq7oXH/88QdSU1Oxfft2vPHGG1i8eDFuvPFG+Pn5Ye/evbj//vtx33331Xqfxx9/HI8++igOHTqEIUOGYOLEicjJyQEA5Ofn49prr0X//v2xf/9+rF+/HhkZGbj9/9u735Cm2jcO4N+mSWRZUkMc0oQyGWsd/0YxdP0B55sssBoxwjIoyKXVioooyQi2UoajRdSb+SJ0RI2EKCEYlVuRRNsKzeSw/lCTCoOYRLTtel7E7/Qc1y/t5/PjeZ52fV6d+zrn3Pd1OLBd3OfezpYtsj66urqQlZUFv9+PCxcu/DC/zs5OdHR0oL29HeFwGEajEXV1dRgZGQHw7XGSVquF1WpFNBrFwYMHJ73ma9euoaCgAG1tbYhGo4hGowC+zSjV1taivr4e4XAYHo8H/f39sFgssvPb29shCAIeP36M48ePAwDmzp0Lt9uNwcFBdHZ24tKlS3A4HAAAk8kEq9UKrVYrjWcymVLyGh8fh9FoRG5uLgYGBnDlyhXcvn07ZXyfzwdRFOHz+dDV1QW32y0VVYylpV9+5Spj7F+poaGBNmzYQEREK1eupMbGRiIi8nq99OePgtbWVhIEQXauw+EgtVot60utVlMikZBixcXFVFVVJbXj8ThlZ2dTd3c3ERFFIhECIHsb9NevX6mgoIDsdjsREZ06dYpqampkY79+/ZoASG+iNhgMVFpaOun1qlQqOn36tCxWWVlJe/bskdqCIFBra+tP+zEYDNTS0iK11Wo1ORwO2TE7d+6kXbt2yWL37t0jhUJBnz9/ls7buHHjpHmfPXuWysvLpfaP7gcREQDyer1ERHTx4kXKzc2lWCwm7b9x4wYpFAoaHR0lou/3LB6PS8ds3ryZTCbTpDkx9rvK/HtLMMbY38Fut2Pt2rVTmv34b7RaLRSK75PJeXl5WLZsmdTOyMjAggUL8O7dO9l5q1atkrYzMzNRUVGBoaEhAEAoFILP58OcOXNSxhNFEUuXLgUAlJeX/zS3T58+4e3bt9Dr9bK4Xq9HKBSa4hVOXSgUQjgcxuXLl6UYESGZTCISiUCj0QAAKioqUs71eDxwOp0QRRGxWAzxeBw5OTm/NP7Q0BAEQUB2drYU0+v1SCaTGB4eRl5eHoBv9ywjI0M6Jj8/H0+ePPmlsRj7nXARxFgaqq6uhtFoxNGjR7F9+3bZPoVCASKSxX60bmTmzJmy9owZM34YSyaTU84rFoth/fr1sNvtKfvy8/Ol7T9/2f8TxGIx7N69G83NzSn7Fi1aJG1PzPv+/fswm804efIkjEYj5s2bh56eHnR0dPxf8pzu/WHsd8NFEGNpymazoaSkBMXFxbK4UqnE6OgoiEha+PtX/rfPgwcPUF1dDQCIx+N49OiRtHalrKwMV69eRWFhITIz//ePp5ycHKhUKvj9fhgMBinu9/uxYsWKaeWflZWFRCIhi5WVlWFwcBBLliz5pb4CgQDUajWOHTsmxV6+fDnpeBNpNBq43W6Mj49LhZbf74dCoUi5v4yx73hhNGNpSqfTwWw2w+l0yuKrV6/G+/fvcebMGYiiCJfLhZs3b/5l47pcLni9Xjx79gxNTU34+PEjGhsbAQBNTU0YGxvD1q1bMTAwAFEU0dfXhx07dkxaCEx06NAh2O12eDweDA8P48iRIwgGg2hpaZlW/oWFhbh79y7evHkj/Wrt8OHDCAQCsFgsCAaDGBkZwfXr11MWJk9UVFSEV69eoaenB6Iowul0wuv1powXiUQQDAbx4cMHfPnyJaUfs9mMWbNmoaGhAU+fPoXP58PevXuxbds26VEYYywVF0GMpbG2traUxyEajQbnz5+Hy+WCIAh4+PDhtNYOTWSz2WCz2SAIAvr7+9Hb24uFCxcCgDR7k0gkUFNTA51Oh3379mH+/Pmy9UdT0dzcjAMHDsBqtUKn0+HWrVvo7e1FUVHRtPJva2vDixcvsHjxYiiVSgDA8uXLcefOHTx//hxVVVUoLS3FiRMnoFKpftpXXV0d9u/fD4vFgpKSEgQCAelXY/9RX1+P2tparFmzBkqlEt3d3Sn9zJ49G319fRgbG0NlZSU2bdqEdevW4dy5c9O6VsZ+dzNo4sN/xhhjjLE0wDNBjDHGGEtLXAQxxhhjLC1xEcQYY4yxtMRFEGOMMcbSEhdBjDHGGEtLXAQxxhhjLC1xEcQYY4yxtMRFEGOMMcbSEhdBjDHGGEtLXAQxxhhjLC1xEcQYY4yxtMRFEGOMMcbS0h+vF7caMCVSzAAAAABJRU5ErkJggg==\n" + }, + "metadata": {} + } + ] + } + ] +} \ No newline at end of file diff --git "a/Week1_\354\230\210\354\212\265\352\263\274\354\240\234_\354\247\204\354\230\210\354\233\220.ipynb" "b/Week1_\354\230\210\354\212\265\352\263\274\354\240\234_\354\247\204\354\230\210\354\233\220.ipynb" new file mode 100644 index 0000000..a1a2d77 --- /dev/null +++ "b/Week1_\354\230\210\354\212\265\352\263\274\354\240\234_\354\247\204\354\230\210\354\233\220.ipynb" @@ -0,0 +1,2377 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "gpuType": "T4" + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# 2.2.1 텐서 다루기" + ], + "metadata": { + "id": "wKHnAGMnsXt7" + } + }, + { + "cell_type": "markdown", + "source": [ + "### 텐서 생성 및 변환" + ], + "metadata": { + "id": "xSuDqtMzs5gz" + } + }, + { + "cell_type": "code", + "source": [ + "import torch\n", + "print(torch.tensor([[1,2],[3,4]])) #2차원 형태의 텐서 생성\n", + "# print(torch.tensor([[1,2],[3,4]], device=\"cuda:0\")) #GPU에 텐서 생성 (CUDA를 지원하지 않아 오류 발생)\n", + "print(torch.tensor([[1,2],[3,4]], dtype=torch.float64)) #dtype을 이용하여 텐서 생성" + ], + "metadata": { + "id": "9usOwIATtB39", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "fe753312-6d45-497d-d44c-341d6a49b9d9" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "tensor([[1, 2],\n", + " [3, 4]])\n", + "tensor([[1., 2.],\n", + " [3., 4.]], dtype=torch.float64)\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "#텐서를 ndarray로 변환\n", + "temp = torch.tensor([[1,2],[3,4]])\n", + "print(temp.numpy()) #텐서를 ndarray로 변환\n", + "temp = torch.tensor([[1,2],[3,4]], device=\"cuda:0\")\n", + "print(temp.to(\"cpu\").numpy()) #GPU상의 텐서를 CPU의 텐서로 변환한 후 ndarray로 변환" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "zuY0oFRIZmQr", + "outputId": "8535c3ce-3b9f-4f97-bc30-5a678c3cd6a8" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[[1 2]\n", + " [3 4]]\n", + "[[1 2]\n", + " [3 4]]\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "### 텐서의 인덱스 조작\n", + "\n", + "\n", + "* torch.FloatTensor: 32비트의 부동 소수점\n", + "* torch.DoubleTensor: 64비트의 부동 소수점\n", + "* torch.LongTensor: 64비트의 부호가 있는 정수\n", + "\n", + "\n", + "\n" + ], + "metadata": { + "id": "4PjZDmX891VX" + } + }, + { + "cell_type": "code", + "source": [ + "temp = torch.FloatTensor([1, 2, 3, 4, 5, 6, 7]) #파이토치로 1차원 벡터 생성\n", + "print(temp[0], temp[1], temp[-1]) #인덱스로 접근 / -1 인덱스 : 맨 뒤에서부터 시작\n", + "print('-----------------')\n", + "print(temp[2:5], temp[4:-1]) #슬라이드로 접근" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "lih3yZWMm-kZ", + "outputId": "05404160-edce-4c8c-f3dd-9c5163ecafcb" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "tensor(1.) tensor(2.) tensor(7.)\n", + "-----------------\n", + "tensor([3., 4., 5.]) tensor([5., 6.])\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "### 텐서 연산 및 차원 조작\n", + "* 텐서 간의 타입이 같아야 연산 가능\n" + ], + "metadata": { + "id": "zNIQbJgO-QLs" + } + }, + { + "cell_type": "code", + "source": [ + "#벡터 간 사칙 연산\n", + "v = torch.tensor([1, 2, 3]) #길이가 3인 벡터 생성\n", + "w = torch.tensor([3, 4, 6])\n", + "print(w - v)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "7qQwAI7uoVrD", + "outputId": "e1837ee2-d3a9-40e3-d205-feca35dcda87" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "tensor([2, 2, 3])\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "#텐서의 차원 조작\n", + "temp = torch.tensor([[1, 2], [3, 4]]) #2x2 행렬 생성\n", + "print(temp.shape)\n", + "print('----------------')\n", + "print(temp.view(4, 1)) #2x2 행렬을 4x1로 변형\n", + "print('----------------')\n", + "print(temp.view(-1)) #2x2 행렬을 1차원 벡터로 변형\n", + "print('----------------')\n", + "print(temp.view(1, -1))\n", + "#-1 : (1,?). 다른 차원으로부터 해당 값을 유추하겠다는 뜻. temp의 원소 개수(2x2=4) 유지한 채 (1,?)형태 만족 -> (1,4)가 됨\n", + "print('----------------')\n", + "print(temp.view(-1, 1)) #(4,1)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "tVBb7KP-pagr", + "outputId": "e750b32a-473d-4588-f3de-017c1090e5db" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "torch.Size([2, 2])\n", + "----------------\n", + "tensor([[1],\n", + " [2],\n", + " [3],\n", + " [4]])\n", + "----------------\n", + "tensor([1, 2, 3, 4])\n", + "----------------\n", + "tensor([[1, 2, 3, 4]])\n", + "----------------\n", + "tensor([[1],\n", + " [2],\n", + " [3],\n", + " [4]])\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# 2.2.2 데이터 준비\n", + "\n", + "\n", + "* 데이터 호출: 파이썬 라이브러리(판다스) or 파이토치에서 제공하는 데이터 이용\n", + "* 이미지 데이터 - 분산된 데이터 읽음 -> 전처리 -> 배치 단위로 분할하여 처리\n", + "* 텍스트 데이터 - 임베딩 -> 서로 다른 길이의 시퀀스를 배치 단위로 분할하여 처리" + ], + "metadata": { + "id": "krdnB9tj4kYy" + } + }, + { + "cell_type": "markdown", + "source": [ + "### 단순하게 파일을 불러와서 사용\n", + "- 판다스 라이브러리 이용하여 JSON, PDF, CSV 파일 불러오는 방법" + ], + "metadata": { + "id": "w_oUWxK65NAT" + } + }, + { + "cell_type": "code", + "source": [ + "pip install pandas" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "SgEkXLtJ5fXj", + "outputId": "738e03a2-ec70-479c-be9a-44ee17c91929" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Requirement already satisfied: pandas in /usr/local/lib/python3.13/dist-packages (2.2.3)\n", + "Requirement already satisfied: numpy>=1.26.0 in /usr/local/lib/python3.13/dist-packages (from pandas) (2.1.3)\n", + "Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.13/dist-packages (from pandas) (2.9.0.post0)\n", + "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.13/dist-packages (from pandas) (2025.2)\n", + "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.13/dist-packages (from pandas) (2026.3)\n", + "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.13/dist-packages (from python-dateutil>=2.8.2->pandas) (1.17.0)\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "```python\n", + "import pandas as pd #pandas 라이브러리 호출\n", + "import torch #torch 라이브러리 호출\n", + "data = pd.read_csv('../class2.csv') #csv 파일을 불러옴\n", + "x = torch.from_numpy(data['x'].values).unsqueeze(dim=1).float()\n", + "#CSV 파일의 x 칼럼의 값을 넘파이 배열로 받아 Tensor(dtype)으로 바꾸어줌\n", + "y = torch.from_numpy(data['y'].values).unsqueeze(dim=1).float()\n", + "#CSV 파일의 y 칼럼의 값을 넘파이 배열로 받아 Tensor(dtype)으로 바꾸어줌\n", + "```" + ], + "metadata": { + "id": "WXYjr5CQrgQ3" + } + }, + { + "cell_type": "markdown", + "source": [ + "``` python\n", + "import pandas as pd #pandas 라이브러리 호출\n", + "import torch #torch 라이브러리 호출\n", + "data = pd.read_csv('../class2.csv') #csv 파일을 불러옴\n", + "x = torch.from_numpy(data['x'].values).unsqueeze(dim=1).float()\n", + "#CSV 파일의 x 칼럼의 값을 넘파이 배열로 받아 Tensor(dtype)으로 바꾸어줌\n", + "y = torch.from_numpy(data['y'].values).unsqueeze(dim=1).float()\n", + "#CSV 파일의 y 칼럼의 값을 넘파이 배열로 받아 Tensor(dtype)으로 바꾸어줌\n", + "```" + ], + "metadata": { + "id": "cw3DtQY3sB0X" + } + }, + { + "cell_type": "markdown", + "source": [ + "### 커스텀 데이터셋을 만들어 사용\n", + "* 데이터를 조금씩 나누어 불러서 사용하는 방식(한 번에 X)" + ], + "metadata": { + "id": "gB7waZ9f-xGN" + } + }, + { + "cell_type": "markdown", + "source": [ + "```python\n", + "class CustomDataset(torch.utils.data.Dataset):\n", + " def __init__(self): #필요한 변수 선언, 데이터셋 전처리하는 함수\n", + " def __len__(self): #데이터셋 길이 = 샘플의 수 가져오는 함수\n", + " def __getitem__(self, index): #데이터셋에서 특정 데이터 가져오는 함수(index번째 데이터 반환, 반환값은 텐서 형태여야함)\n", + "```" + ], + "metadata": { + "id": "F6Yv06HxsIl6" + } + }, + { + "cell_type": "markdown", + "source": [ + "예제" + ], + "metadata": { + "id": "CWzzy3H0_asx" + } + }, + { + "cell_type": "markdown", + "source": [ + "```python\n", + "import pandas as pd\n", + "import torch\n", + "from torch.utils.data import Dataset\n", + "from torch.utils.data import DataLoader\n", + "\n", + "class CustomDataset(Dataset):\n", + " def __init__(self, csv_file): #csv_file 파라미터 통해 데이터셋 불러옴\n", + " self.label = pd.read_csv(csv_file)\n", + " def __len__(self): #전체 데이터셋 크기(size) 반환\n", + " return len(self.label)\n", + " def __getitem(self, idx): #전체 x와 y 데이터 중 해당 idx 번째 데이터 가져옴\n", + " sample = torch.tensor(self.label.iloc[idx,0:3]).int()\n", + " label = torch.tensor(self.label.ilox[idx,3]).int()\n", + " return sample, label\n", + "tensor_dataset = CustomDataset('../covtype.csv') #데이터셋으로 covtype.csv 사용\n", + "dataset = DataLoader(tensor_dataset, batch_size=4, shuffle=True) #데이터셋을 torch.tuils.data.DataLoader에 파라미터로 전달\n", + "```" + ], + "metadata": { + "id": "oRjy-OldsNtL" + } + }, + { + "cell_type": "markdown", + "source": [ + "데이터로더는 다음과 같이 for문을 이용하여 구문 반복 실행하는 것과 같음" + ], + "metadata": { + "id": "OMzY1VZiBn6o" + } + }, + { + "cell_type": "markdown", + "source": [ + "```python\n", + "for i, data in enumerate(dataset,0):\n", + " print(i, end='')\n", + " batch=data[0]\n", + " print(batch.size())\n", + "```" + ], + "metadata": { + "id": "0P8KgopFsTHz" + } + }, + { + "cell_type": "markdown", + "source": [ + "출력" + ], + "metadata": { + "id": "R4A-qKsEB5GW" + } + }, + { + "cell_type": "markdown", + "source": [ + "```python\n", + "0torch.Size([4, 3])\n", + "1torch.Size([4, 3])\n", + "2torch.Size([4, 3])\n", + "3torch.Size([4, 3])\n", + "4torch.Size([4, 3])\n", + "```" + ], + "metadata": { + "id": "Qt9JEGggsWw6" + } + }, + { + "cell_type": "markdown", + "source": [ + "### 파이토치에서 제공하는 데이터셋 사용\n", + "* requests 라이브러리 설치해야함 \n", + "request는 HTTP 요청에 대한 처리 위해 사용 \n", + "기본 내장 모듈 X 별도 설치 필요\n" + ], + "metadata": { + "id": "QgmPq3R0CkAy" + } + }, + { + "cell_type": "code", + "source": [ + "pip install requests" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "xyln06cJDMIN", + "outputId": "6b64d1ac-bd24-49ee-8d80-c3db9811eb14" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Requirement already satisfied: requests in /usr/local/lib/python3.13/dist-packages (2.32.4)\n", + "Requirement already satisfied: charset_normalizer<4,>=2 in /usr/local/lib/python3.13/dist-packages (from requests) (3.4.9)\n", + "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.13/dist-packages (from requests) (3.19)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.13/dist-packages (from requests) (2.5.0)\n", + "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.13/dist-packages (from requests) (2026.7.22)\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "예제\n" + ], + "metadata": { + "id": "6NTJjsSzDLGY" + } + }, + { + "cell_type": "code", + "source": [ + "import torchvision.transforms as transforms\n", + "\n", + "mnist_transform = transforms.Compose([\n", + " transforms.ToTensor(),\n", + " transforms.Normalize((0.5,), (1.0,))\n", + "]) #평균 0.5, 표준편차 1.0이 되도록 데이터 분포(normalize) 조정\n", + "from torchvision.datasets import MNIST\n", + "import requests\n", + "download_root = '../chap02/data/MNIST_DATASET' #내려받을 경로 지정\n", + "\n", + "train_dataset = MNIST(download_root, transform=mnist_transform, train=True,\n", + " download=True) #훈련 데이터셋\n", + "valid_dataset = MNIST(download_root, transform=mnist_transform, train=False,\n", + " download=True) #검증 데이터셋\n", + "test_dataset = MNIST(download_root, transform=mnist_transform, train=False,\n", + " download=True) #테스트 데이터셋" + ], + "metadata": { + "id": "82R2YAqSDOkd" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# 2.2.2 모델 정의\n", + "\n", + "\n", + "* 파이토치에서 모델 정의하기 위해서는 모듈을 상속한 클래스를 사용\n", + "\n" + ], + "metadata": { + "id": "D4Rn3eksEY_j" + } + }, + { + "cell_type": "markdown", + "source": [ + "### 단순 신경망을 정의하는 방법\n", + "\n", + "* nn.Module을 상속받지 않는 단순한 모델 만들 때 사용.\n", + "* 구현 쉽고 단순\n", + "\n" + ], + "metadata": { + "id": "kJzVOtf_EsWW" + } + }, + { + "cell_type": "markdown", + "source": [ + "```python\n", + "model = nn.Linear(in_features=1, out_features=1, bias=True)\n", + "```" + ], + "metadata": { + "id": "s6o3odyssbbQ" + } + }, + { + "cell_type": "markdown", + "source": [ + "### nn.Module()을 상속하여 정의하는 방법\n", + "\n", + "\n", + "* nn.Module()을 상속받는 모델은 __init__()과 forward() 함수 포함함\n", + "\n", + "* __init()에서는 모델에서 사용될 모듈, 활성화 함수 등을 정의함 \n", + "forward()함수에서는 모델에서 실행되어야 하는 연산 정의함\n", + "\n", + "\n" + ], + "metadata": { + "id": "xPIRdqEcFhmV" + } + }, + { + "cell_type": "markdown", + "source": [ + "```python\n", + "class MLP(Module):\n", + " def __init__(self, inputs):\n", + " super(MLP, self).__init__()\n", + " self.layer = Linear(inputs, 1) #계층 정의\n", + " self.activation = Sigmoid() #활성화 함수 정의\n", + " def forward(self, X):\n", + " X = self.layer(X)\n", + " X = self.activation(X)\n", + " return X\n", + " ```" + ], + "metadata": { + "id": "WsiBM_bHsiNs" + } + }, + { + "cell_type": "markdown", + "source": [ + "### Sequential 신경망을 정의하는 방법\n", + "\n", + "\n", + "* nn.Sequential 사용시 __init__()에서 사용할 네트워크 모델 정의 + forward() 모델에서 실행되어야 할 계산을 가독성 좋게 작성 가능\n", + "* Sequential 객체는 그 안의 각 모듈을 순차적으로 실행해줌\n", + "* nn.Sequential은 모델 계층 복잡할수록 효과 뛰어남\n", + "\n" + ], + "metadata": { + "id": "y3e_JjXKGUzB" + } + }, + { + "cell_type": "code", + "source": [ + "import torch.nn as nn\n", + "class MLP(nn.Module):\n", + " def __init__(self):\n", + " super(MLP, self).__init__()\n", + " self.layer1 = nn.Sequential(\n", + " nn.Conv2d(in_channels=3, out_channels=64, kernel_size=5),\n", + " nn.ReLU(inplace=True),\n", + " nn.MaxPool2d(2))\n", + " self.layer2 = nn.Sequential(\n", + " nn.Conv2d(in_channels=64, out_channels=30, kernel_size=5),\n", + " nn.ReLU(inplace=True),\n", + " nn.MaxPool2d(2))\n", + " self.layer3 = nn.Sequential(\n", + " nn.Linear(in_features=30*5*5, out_features=10, bias=True),\n", + " nn.ReLU(inplace=True))\n", + " def forward(self, x):\n", + " x = self.layer1(x)\n", + " x = self.layer2(x)\n", + " x = x.view(x.shape[0], -1)\n", + " x = self.layer3(x)\n", + " return x\n", + "model = MLP()\n", + "print(\"Printing children\\n---------------------\")\n", + "print(list(model.children()))\n", + "print(\"\\n\\nPrinting Modules\\n---------------------\")\n", + "print(list(model.modules()))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "51xXdiJxGzLp", + "outputId": "e92d35b1-d070-4446-cff7-d3c638cf0cd9" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Printing children\n", + "---------------------\n", + "[Sequential(\n", + " (0): Conv2d(3, 64, kernel_size=(5, 5), stride=(1, 1))\n", + " (1): ReLU(inplace=True)\n", + " (2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n", + "), Sequential(\n", + " (0): Conv2d(64, 30, kernel_size=(5, 5), stride=(1, 1))\n", + " (1): ReLU(inplace=True)\n", + " (2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n", + "), Sequential(\n", + " (0): Linear(in_features=750, out_features=10, bias=True)\n", + " (1): ReLU(inplace=True)\n", + ")]\n", + "\n", + "\n", + "Printing Modules\n", + "---------------------\n", + "[MLP(\n", + " (layer1): Sequential(\n", + " (0): Conv2d(3, 64, kernel_size=(5, 5), stride=(1, 1))\n", + " (1): ReLU(inplace=True)\n", + " (2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n", + " )\n", + " (layer2): Sequential(\n", + " (0): Conv2d(64, 30, kernel_size=(5, 5), stride=(1, 1))\n", + " (1): ReLU(inplace=True)\n", + " (2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n", + " )\n", + " (layer3): Sequential(\n", + " (0): Linear(in_features=750, out_features=10, bias=True)\n", + " (1): ReLU(inplace=True)\n", + " )\n", + "), Sequential(\n", + " (0): Conv2d(3, 64, kernel_size=(5, 5), stride=(1, 1))\n", + " (1): ReLU(inplace=True)\n", + " (2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n", + "), Conv2d(3, 64, kernel_size=(5, 5), stride=(1, 1)), ReLU(inplace=True), MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False), Sequential(\n", + " (0): Conv2d(64, 30, kernel_size=(5, 5), stride=(1, 1))\n", + " (1): ReLU(inplace=True)\n", + " (2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n", + "), Conv2d(64, 30, kernel_size=(5, 5), stride=(1, 1)), ReLU(inplace=True), MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False), Sequential(\n", + " (0): Linear(in_features=750, out_features=10, bias=True)\n", + " (1): ReLU(inplace=True)\n", + "), Linear(in_features=750, out_features=10, bias=True), ReLU(inplace=True)]\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "### 함수로 신경망을 정의하는 방법\n", + "* 함수로 선언 시 변수에 저장해 놓은 계층들 재사용 가능 But 모델 복잡해짐\n", + "* 복잡한 모델의 경우, 함수 이용보단 nn.Module() 상속받아 사용하는 것이 더 편리" + ], + "metadata": { + "id": "KcjrLs2FUYId" + } + }, + { + "cell_type": "markdown", + "source": [ + "```python\n", + "def MML(in_features=1, hidden_features=20, out_features=1):\n", + " hidden = nn.Linear(in_features=in_features, out_features=hidden_features,\n", + " bias=True)\n", + " activation = nn.ReLU()\n", + " output = nn.Linear(in_features=hidden_features, out_features=out_features,\n", + " bias=True)\n", + " net = nn.Sequential(hidden, activation, output)\n", + " return net\n", + "```" + ], + "metadata": { + "id": "opQycijQsk-T" + } + }, + { + "cell_type": "markdown", + "source": [ + "# 모델의 파라미터 정의" + ], + "metadata": { + "id": "dshHYquCVLOl" + } + }, + { + "cell_type": "markdown", + "source": [ + "모델의 파라미터 정의하는 예시 코드" + ], + "metadata": { + "id": "xANj9a5wVggU" + } + }, + { + "cell_type": "markdown", + "source": [ + "```python\n", + "from torch.optim import optimizer\n", + "criterion = torch.nn.MSELoss()\n", + "optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)\n", + "scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer=optimizer,\n", + " lr_lambda=lambda epoch: 0.95 ** epoch)\n", + "for epoch in range(1, 100+1):\n", + " for x, y in dataloader:\n", + " optimizer.zero_grad()\n", + " loss_fn(model(x), y).backward()\n", + " optimizer.step()\n", + "scheduler.step()\n", + "```" + ], + "metadata": { + "id": "1sNcklDJst5h" + } + }, + { + "cell_type": "markdown", + "source": [ + "# 2.2.5 모델 훈련\n", + "\n", + "\n", + "1. optimizer.zero_grad() - 기울기 초기화\n", + "2. loss.backward() - 기울기 자동 계산(누적됨)\n", + "\n" + ], + "metadata": { + "id": "25G75dXzWpRJ" + } + }, + { + "cell_type": "markdown", + "source": [ + "모델 훈련 예시 코드" + ], + "metadata": { + "id": "jPoJ84OKaffO" + } + }, + { + "cell_type": "markdown", + "source": [ + "```python\n", + "for epoxh in range(100):\n", + " yhat = model(x_train)\n", + " loss = criterion(yhat, y_train)\n", + " optimizer.zero_grad() #오차 중첩적으로 쌓이지 않도록 초기화\n", + " loss.backward()\n", + " optimizer.step()\n", + "```" + ], + "metadata": { + "id": "9y3e07M9syAb" + } + }, + { + "cell_type": "markdown", + "source": [ + "# 2.2.6 모델 평가\n", + "\n" + ], + "metadata": { + "id": "cT_stiiFbNip" + } + }, + { + "cell_type": "code", + "source": [ + "pip install torchmetrics" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "xYKIrV7nboIl", + "outputId": "02544618-e0c6-4c73-b965-389e6ee5c14c" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Requirement already satisfied: torchmetrics in /usr/local/lib/python3.13/dist-packages (1.9.0)\n", + "Requirement already satisfied: numpy>1.20.0 in /usr/local/lib/python3.13/dist-packages (from torchmetrics) (2.1.3)\n", + "Requirement already satisfied: packaging>17.1 in /usr/local/lib/python3.13/dist-packages (from torchmetrics) (26.3)\n", + "Requirement already satisfied: torch>=2.0.0 in /usr/local/lib/python3.13/dist-packages (from torchmetrics) (2.11.0+cu128)\n", + "Requirement already satisfied: lightning-utilities>=0.15.3 in /usr/local/lib/python3.13/dist-packages (from torchmetrics) (0.15.3)\n", + "Requirement already satisfied: typing_extensions in /usr/local/lib/python3.13/dist-packages (from lightning-utilities>=0.15.3->torchmetrics) (4.16.0)\n", + "Requirement already satisfied: filelock in /usr/local/lib/python3.13/dist-packages (from torch>=2.0.0->torchmetrics) (3.32.4)\n", + "Requirement already satisfied: setuptools<82 in /usr/local/lib/python3.13/dist-packages (from torch>=2.0.0->torchmetrics) (75.2.0)\n", + "Requirement already satisfied: sympy>=1.13.3 in /usr/local/lib/python3.13/dist-packages (from torch>=2.0.0->torchmetrics) (1.14.0)\n", + "Requirement already satisfied: networkx>=2.5.1 in /usr/local/lib/python3.13/dist-packages (from torch>=2.0.0->torchmetrics) (3.6.1)\n", + "Requirement already satisfied: jinja2 in /usr/local/lib/python3.13/dist-packages (from torch>=2.0.0->torchmetrics) (3.1.6)\n", + "Requirement already satisfied: fsspec>=0.8.5 in /usr/local/lib/python3.13/dist-packages (from torch>=2.0.0->torchmetrics) (2025.3.0)\n", + "Requirement already satisfied: cuda-toolkit==12.8.1 in /usr/local/lib/python3.13/dist-packages (from cuda-toolkit[cublas,cudart,cufft,cufile,cupti,curand,cusolver,cusparse,nvjitlink,nvrtc,nvtx]==12.8.1; platform_system == \"Linux\"->torch>=2.0.0->torchmetrics) (12.8.1)\n", + "Requirement already satisfied: cuda-bindings<13,>=12.9.4 in /usr/local/lib/python3.13/dist-packages (from torch>=2.0.0->torchmetrics) (12.9.7)\n", + "Requirement already satisfied: nvidia-cudnn-cu12==9.19.0.56 in /usr/local/lib/python3.13/dist-packages (from torch>=2.0.0->torchmetrics) (9.19.0.56)\n", + "Requirement already satisfied: nvidia-cusparselt-cu12==0.7.1 in /usr/local/lib/python3.13/dist-packages (from torch>=2.0.0->torchmetrics) (0.7.1)\n", + "Requirement already satisfied: nvidia-nccl-cu12==2.28.9 in /usr/local/lib/python3.13/dist-packages (from torch>=2.0.0->torchmetrics) (2.28.9)\n", + "Requirement already satisfied: nvidia-nvshmem-cu12==3.4.5 in /usr/local/lib/python3.13/dist-packages (from torch>=2.0.0->torchmetrics) (3.4.5)\n", + "Requirement already satisfied: triton==3.6.0 in /usr/local/lib/python3.13/dist-packages (from torch>=2.0.0->torchmetrics) (3.6.0)\n", + "Requirement already satisfied: nvidia-cublas-cu12==12.8.4.1.* in /usr/local/lib/python3.13/dist-packages (from cuda-toolkit[cublas,cudart,cufft,cufile,cupti,curand,cusolver,cusparse,nvjitlink,nvrtc,nvtx]==12.8.1; platform_system == \"Linux\"->torch>=2.0.0->torchmetrics) (12.8.4.1)\n", + "Requirement already satisfied: nvidia-cuda-runtime-cu12==12.8.90.* in /usr/local/lib/python3.13/dist-packages (from cuda-toolkit[cublas,cudart,cufft,cufile,cupti,curand,cusolver,cusparse,nvjitlink,nvrtc,nvtx]==12.8.1; platform_system == \"Linux\"->torch>=2.0.0->torchmetrics) (12.8.90)\n", + "Requirement already satisfied: nvidia-cufft-cu12==11.3.3.83.* in /usr/local/lib/python3.13/dist-packages (from cuda-toolkit[cublas,cudart,cufft,cufile,cupti,curand,cusolver,cusparse,nvjitlink,nvrtc,nvtx]==12.8.1; platform_system == \"Linux\"->torch>=2.0.0->torchmetrics) (11.3.3.83)\n", + "Requirement already satisfied: nvidia-cufile-cu12==1.13.1.3.* in /usr/local/lib/python3.13/dist-packages (from cuda-toolkit[cublas,cudart,cufft,cufile,cupti,curand,cusolver,cusparse,nvjitlink,nvrtc,nvtx]==12.8.1; platform_system == \"Linux\"->torch>=2.0.0->torchmetrics) (1.13.1.3)\n", + "Requirement already satisfied: nvidia-cuda-cupti-cu12==12.8.90.* in /usr/local/lib/python3.13/dist-packages (from cuda-toolkit[cublas,cudart,cufft,cufile,cupti,curand,cusolver,cusparse,nvjitlink,nvrtc,nvtx]==12.8.1; platform_system == \"Linux\"->torch>=2.0.0->torchmetrics) (12.8.90)\n", + "Requirement already satisfied: nvidia-curand-cu12==10.3.9.90.* in /usr/local/lib/python3.13/dist-packages (from cuda-toolkit[cublas,cudart,cufft,cufile,cupti,curand,cusolver,cusparse,nvjitlink,nvrtc,nvtx]==12.8.1; platform_system == \"Linux\"->torch>=2.0.0->torchmetrics) (10.3.9.90)\n", + "Requirement already satisfied: nvidia-cusolver-cu12==11.7.3.90.* in /usr/local/lib/python3.13/dist-packages (from cuda-toolkit[cublas,cudart,cufft,cufile,cupti,curand,cusolver,cusparse,nvjitlink,nvrtc,nvtx]==12.8.1; platform_system == \"Linux\"->torch>=2.0.0->torchmetrics) (11.7.3.90)\n", + "Requirement already satisfied: nvidia-cusparse-cu12==12.5.8.93.* in /usr/local/lib/python3.13/dist-packages (from cuda-toolkit[cublas,cudart,cufft,cufile,cupti,curand,cusolver,cusparse,nvjitlink,nvrtc,nvtx]==12.8.1; platform_system == \"Linux\"->torch>=2.0.0->torchmetrics) (12.5.8.93)\n", + "Requirement already satisfied: nvidia-nvjitlink-cu12==12.8.93.* in /usr/local/lib/python3.13/dist-packages (from cuda-toolkit[cublas,cudart,cufft,cufile,cupti,curand,cusolver,cusparse,nvjitlink,nvrtc,nvtx]==12.8.1; platform_system == \"Linux\"->torch>=2.0.0->torchmetrics) (12.8.93)\n", + "Requirement already satisfied: nvidia-cuda-nvrtc-cu12==12.8.93.* in /usr/local/lib/python3.13/dist-packages (from cuda-toolkit[cublas,cudart,cufft,cufile,cupti,curand,cusolver,cusparse,nvjitlink,nvrtc,nvtx]==12.8.1; platform_system == \"Linux\"->torch>=2.0.0->torchmetrics) (12.8.93)\n", + "Requirement already satisfied: nvidia-nvtx-cu12==12.8.90.* in /usr/local/lib/python3.13/dist-packages (from cuda-toolkit[cublas,cudart,cufft,cufile,cupti,curand,cusolver,cusparse,nvjitlink,nvrtc,nvtx]==12.8.1; platform_system == \"Linux\"->torch>=2.0.0->torchmetrics) (12.8.90)\n", + "Requirement already satisfied: cuda-pathfinder~=1.1 in /usr/local/lib/python3.13/dist-packages (from cuda-bindings<13,>=12.9.4->torch>=2.0.0->torchmetrics) (1.7.0)\n", + "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /usr/local/lib/python3.13/dist-packages (from sympy>=1.13.3->torch>=2.0.0->torchmetrics) (1.3.0)\n", + "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.13/dist-packages (from jinja2->torch>=2.0.0->torchmetrics) (3.0.3)\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "\n", + "\n", + "* 함수를 이용하여 모델을 평가하는 코드\n", + "\n" + ], + "metadata": { + "id": "MZZvg84Tbi5J" + } + }, + { + "cell_type": "markdown", + "source": [ + "```python\n", + "import torch\n", + "import torchmetrics\n", + "\n", + "preds = torch.randn(10, 5).softmax(dim=1)\n", + "target = torch.randint(5, (10,))\n", + "acc = torchmetrics.functional.accuaracy(preds, target)\n", + "```" + ], + "metadata": { + "id": "87YA6-gts1Bf" + } + }, + { + "cell_type": "markdown", + "source": [ + "\n", + "\n", + "* 모듈을 이용하여 모델을 평가하는 코드\n", + "\n" + ], + "metadata": { + "id": "Z4zP5u0YbnqU" + } + }, + { + "cell_type": "markdown", + "source": [ + "``` python\n", + "import torch\n", + "import torchmetrics\n", + "metric = torchmetrics.Accuracy() #모델 평가(정확도) 초기화\n", + "\n", + "n_batches = 10\n", + "for i in range(n_batches):\n", + " preds = torch.randn(10, 5).sofrmax(dim=-1)\n", + " target = torch.randint(5, (10,))\n", + " acc = metric(preds, target)\n", + " print(f\"Accuracy on batch {i}: {acc}\") #현재 배치에서 모델 평가(정확도)\n", + "acc = metric.compute()\n", + "print(f\"Accuracy on all data: {acc}\") #모든 배치에서 모델 평가(정확도)\n", + "```" + ], + "metadata": { + "id": "RoMPckEBs5ax" + } + }, + { + "cell_type": "markdown", + "source": [ + "# 2.2.7 훈련 과정 모니터링\n", + "\n", + "\n", + "* 텐서보드 이용하면 학습 과정에서 각종 파라미터 값의 변화 과정을 시각화하여 살펴볼 수 있고, 성능 추적 또는 평가 용도로 사용 가능하다\n", + "\n" + ], + "metadata": { + "id": "qiF4vyDt3GiP" + } + }, + { + "cell_type": "markdown", + "source": [ + "\n", + "\n", + "1. 텐서보드를 설정(set up)한다\n", + "2. 텐서보드에 기록(write)한다\n", + "3. 텐서보드를 사용하여 모델 구조 살펴본다\n" + ], + "metadata": { + "id": "eQMXyPfn3YEa" + } + }, + { + "cell_type": "code", + "source": [ + "pip install tensorboard" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "oJyD1tSS3Mss", + "outputId": "20fab2ed-3a49-47f5-e7fa-27f5d5e42102" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Requirement already satisfied: tensorboard in /usr/local/lib/python3.13/dist-packages (2.20.0)\n", + "Requirement already satisfied: absl-py>=0.4 in /usr/local/lib/python3.13/dist-packages (from tensorboard) (1.4.0)\n", + "Requirement already satisfied: grpcio>=1.48.2 in /usr/local/lib/python3.13/dist-packages (from tensorboard) (1.83.0)\n", + "Requirement already satisfied: markdown>=2.6.8 in /usr/local/lib/python3.13/dist-packages (from tensorboard) (3.10.3)\n", + "Requirement already satisfied: numpy>=1.12.0 in /usr/local/lib/python3.13/dist-packages (from tensorboard) (2.1.3)\n", + "Requirement already satisfied: packaging in /usr/local/lib/python3.13/dist-packages (from tensorboard) (26.3)\n", + "Requirement already satisfied: pillow in /usr/local/lib/python3.13/dist-packages (from tensorboard) (11.3.0)\n", + "Requirement already satisfied: protobuf!=4.24.0,>=3.19.6 in /usr/local/lib/python3.13/dist-packages (from tensorboard) (5.29.6)\n", + "Requirement already satisfied: setuptools>=41.0.0 in /usr/local/lib/python3.13/dist-packages (from tensorboard) (75.2.0)\n", + "Requirement already satisfied: tensorboard-data-server<0.8.0,>=0.7.0 in /usr/local/lib/python3.13/dist-packages (from tensorboard) (0.7.2)\n", + "Requirement already satisfied: werkzeug>=1.0.1 in /usr/local/lib/python3.13/dist-packages (from tensorboard) (3.1.8)\n", + "Requirement already satisfied: typing-extensions~=4.12 in /usr/local/lib/python3.13/dist-packages (from grpcio>=1.48.2->tensorboard) (4.16.0)\n", + "Requirement already satisfied: markupsafe>=2.1.1 in /usr/local/lib/python3.13/dist-packages (from werkzeug>=1.0.1->tensorboard) (3.0.3)\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "```python\n", + "import torch\n", + "from torch.utils.tensorboard import SummaryWriter\n", + "writer = SummaryWriter(\"../chap02/tensorboard\") #모니터링에 필요한 값들이 저장될 위치\n", + "for epoch in range(num_epochs):\n", + " model.train() #학습 모드로 전환(dropout=True)\n", + " batch_loss = 0.0\n", + " for i, (x, y) in enumerate(dataloader):\n", + " x, y = x.to(device).float(), y.to(device).float()\n", + " outputs = model(x)\n", + " loss = criterion(outputs, y)\n", + " writer.add_scalar(\"Loss\", loss, epoch) #스칼라 값(오차)을 기록\n", + " optimizer.zero_grad()\n", + " loss.backward()\n", + " optimizer.step()\n", + " writer.close() #SummaryWriter가 더 이상 필요하지 않으면 close() 메서드 호출\n", + "```" + ], + "metadata": { + "id": "kXT2MkG8s9Ua" + } + }, + { + "cell_type": "markdown", + "source": [ + "```python\n", + "tensorboard --logdir=../chap02/tensorboard --port=6006\n", + "```" + ], + "metadata": { + "id": "qcBbr5XdtAnk" + } + }, + { + "cell_type": "markdown", + "source": [ + "* model.train(): 훈련 데이터셋에 사용. 모델 훈련 진행될 것을 알림. 이때 dropout 활성화됨\n", + "* model.eval(): 모델 평가할 때 모든 노드를 사용하겠다는 의미. 검증과 테스트 데이터셋에 사용" + ], + "metadata": { + "id": "qJwat2KY62x1" + } + }, + { + "cell_type": "markdown", + "source": [ + "model.eval() 사용 예시" + ], + "metadata": { + "id": "nvXzGXQ07JnH" + } + }, + { + "cell_type": "markdown", + "source": [ + "```python\n", + "model.eval() #검증 모드로 전환(dropout=False)\n", + "with torch.no_grad():\n", + " valid_loss = 0\n", + " for x, y in valid_dataloader:\n", + " outputs = model(x)\n", + " loss = F.cross_entropy(outputs, y.long().squeeze())\n", + " valid_loss += float(loss)\n", + " y_hat += [outputs]\n", + "valid_loss = valid_loss / len(valid_loader)\n", + "```" + ], + "metadata": { + "id": "vhJXKULhtE63" + } + }, + { + "cell_type": "markdown", + "source": [ + "# 2.4 파이토치 코드 맛보기\n", + "\n", + "\n", + "* 라이브러리(혹은 패키지) 호출" + ], + "metadata": { + "id": "V_jgQHhy8pY6" + } + }, + { + "cell_type": "code", + "source": [ + "import torch\n", + "import torch.nn as nn\n", + "import numpy as np #벡터 및 행렬 연산에서 편리한 기능 제공\n", + "import pandas as pd #데이터 처리\n", + "import matplotlib.pyplot as plt\n", + "import seaborn as sns\n", + "%matplotlib inline" + ], + "metadata": { + "id": "N7Iq3hc280m2" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "\n", + "\n", + "* 데이터 호출\n" + ], + "metadata": { + "id": "2jSqObAo9Pyj" + } + }, + { + "cell_type": "code", + "source": [ + "dataset = pd.read_csv('../chap02/data/car_evaluation.csv')\n", + "dataset.head()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 226 + }, + "id": "P0DIGk9n9OFi", + "outputId": "c388f40f-f723-45e5-c020-9dd3908f9d73" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " price maint doors persons lug_capacity safety output\n", + "0 vhigh vhigh 2 2 small low unacc\n", + "1 vhigh vhigh 2 2 small med unacc\n", + "2 vhigh vhigh 2 2 small high unacc\n", + "3 vhigh vhigh 2 2 med low unacc\n", + "4 vhigh vhigh 2 2 med med unacc" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
pricemaintdoorspersonslug_capacitysafetyoutput
0vhighvhigh22smalllowunacc
1vhighvhigh22smallmedunacc
2vhighvhigh22smallhighunacc
3vhighvhigh22medlowunacc
4vhighvhigh22medmedunacc
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + "
\n" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "dataframe", + "variable_name": "dataset", + "summary": "{\n \"name\": \"dataset\",\n \"rows\": 1728,\n \"fields\": [\n {\n \"column\": \"price\",\n \"properties\": {\n \"dtype\": \"category\",\n \"num_unique_values\": 4,\n \"samples\": [\n \"high\",\n \"low\",\n \"vhigh\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"maint\",\n \"properties\": {\n \"dtype\": \"category\",\n \"num_unique_values\": 4,\n \"samples\": [\n \"high\",\n \"low\",\n \"vhigh\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"doors\",\n \"properties\": {\n \"dtype\": \"category\",\n \"num_unique_values\": 4,\n \"samples\": [\n \"3\",\n \"5more\",\n \"2\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"persons\",\n \"properties\": {\n \"dtype\": \"category\",\n \"num_unique_values\": 3,\n \"samples\": [\n \"2\",\n \"4\",\n \"more\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"lug_capacity\",\n \"properties\": {\n \"dtype\": \"category\",\n \"num_unique_values\": 3,\n \"samples\": [\n \"small\",\n \"med\",\n \"big\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"safety\",\n \"properties\": {\n \"dtype\": \"category\",\n \"num_unique_values\": 3,\n \"samples\": [\n \"low\",\n \"med\",\n \"high\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"output\",\n \"properties\": {\n \"dtype\": \"category\",\n \"num_unique_values\": 4,\n \"samples\": [\n \"acc\",\n \"good\",\n \"unacc\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}" + } + }, + "metadata": {}, + "execution_count": 132 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "* 예제 데이터셋 분포" + ], + "metadata": { + "id": "SDJMlaSrh6kO" + } + }, + { + "cell_type": "code", + "source": [ + "fig_size = plt.rcParams[\"figure.figsize\"]\n", + "fig_size[0] = 8\n", + "fig_size[1] = 6\n", + "plt.rcParams[\"figure.figsize\"] = fig_size\n", + "dataset.output.value_counts().plot(kind='pie', autopct='%0.05f%%',\n", + " colors=['lightblue', 'lightgreen', 'orange', 'pink'], explode=(0.05, 0.05, 0.05, 0.05))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 504 + }, + "id": "uty0zJb4MtEH", + "outputId": "c4997455-311b-4bfc-8508-0d6ac3bc465f" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ] + }, + "metadata": {}, + "execution_count": 133 + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "
" + ], + "image/png": "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3vj4sGHDWLRoEWVlZc1eP2zYMD799NP2Klek1RQYRESiKDMzk+zsbJ566ilWr17NZ599xtVXX934+FlnnUV+fj4nn3wyc+fOZe3atbzxxhvMmzcPgFtvvZWXX36ZW2+9lWXLlrFkyRLuvfdeoz4ckUYKDCIiUWQ2m3nllVdYsGABQ4cO5fe//z33339/4+N2u53Zs2eTm5vLcccdx0EHHcQ999yDxWIBYPLkybz22mu88847jBgxgilTpvDtt98a9eGINNIuCREREWmRRhhERESkRQoMIiIi0iIFBhEREWmRAoOIiIi0SIFBREREWqTAICIiIi1SYBAREZEWKTCIiIhIixQYREREpEUKDCIiItIiBQYRERFpkQKDiIiItEiBQURERFqkwCAiIiItUmAQERGRFikwiIiISIsUGERERKRFCgwiIiLSIgUGERERaZECg4iIiLRIgUFERERapMAgIiIiLVJgEBERkRYpMIiIiEiLFBhERESkRQoMIiIi0iIFBhEREWmRAoOIiIi0SIFBREREWqTAICIiIi1SYBAREZEWKTCIiIhIixQYREREpEUKDCIiItIiBQYRERFpkQKDiIiItEiBQURERFqkwCAiIiItUmAQERGRFv0//gQjxv718/0AAAAASUVORK5CYII=\n" + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "\n", + "\n", + "데이터 전처리\n", + "* astype() 메서드 이용하여 범주 특성 갖는 데이터를 범주형 타입으로 변환\n", + "* 파이토치를 이용한 모델 학습 위해 범주형 타입을 센서로 변환\n", + "* 범주형 데이터 -> dataset[category] -> 넘파이 배열(NumPy array) -> 텐서(Tensor)\n", + "\n" + ], + "metadata": { + "id": "zWGtx0RbiJEW" + } + }, + { + "cell_type": "code", + "source": [ + "categorical_columns = ['price', 'maint', 'doors', 'persons', 'lug_capacity', 'safety'] #예제 데이터셋 칼럼들의 목록\n", + "for category in categorical_columns:\n", + " dataset[category] = dataset[category].astype('category') #astype()메서드 이용하여 데이터를 범주형으로 변환\n", + "price = dataset['price'].cat.codes.values\n", + "maint = dataset['maint'].cat.codes.values\n", + "doors = dataset['doors'].cat.codes.values\n", + "persons = dataset['persons'].cat.codes.values\n", + "lug_capacity = dataset['lug_capacity'].cat.codes.values\n", + "safety = dataset['safety'].cat.codes.values\n", + "\n", + "categorical_data = np.stack([price, maint, doors, persons, lug_capacity, safety], 1)\n", + "categorical_data[:10] #합친 넘파이 배열 중 열 개의 행을 출력하여 보여줌" + ], + "metadata": { + "id": "rkMUIYmBNLGc", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "808e4354-6487-4363-dbfc-54599b7e26dd" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "array([[3, 3, 0, 0, 2, 1],\n", + " [3, 3, 0, 0, 2, 2],\n", + " [3, 3, 0, 0, 2, 0],\n", + " [3, 3, 0, 0, 1, 1],\n", + " [3, 3, 0, 0, 1, 2],\n", + " [3, 3, 0, 0, 1, 0],\n", + " [3, 3, 0, 0, 0, 1],\n", + " [3, 3, 0, 0, 0, 2],\n", + " [3, 3, 0, 0, 0, 0],\n", + " [3, 3, 0, 1, 2, 1]], dtype=int8)" + ] + }, + "metadata": {}, + "execution_count": 134 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "\n", + "\n", + "np.stack & np.concatenate\n", + "* 둘 다 넘파이 객체 합칠 때 사용하는 메서드\n", + "* 차원의 유지 여부에 대한 차이 \n", + "np.concatenate - 선택한 축(axis) 기준으로 두 개의 배열 연결 \n", + "np.stack - 배열들을 새로운 축으로 합침(두 배열의 차원 동일해야 함)\n", + "\n" + ], + "metadata": { + "id": "KU7WDRvni6sC" + } + }, + { + "cell_type": "code", + "source": [ + "a = np.array([[1, 2], [3, 4]]) #a.shape=(2,2)\n", + "b = np.array([[5, 6], [7, 8]]) #b.shape=(2,2)\n", + "c = np.array([[5, 6], [7, 8], [9, 10]]) #c.shape=(3,2)\n", + "print(np.concatenate((a,b), axis=0)) #shape=(4,2)\n", + "print('---------------------')\n", + "print(np.stack((a, b), axis=0)) #shape=(2,2,2)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "06_AjiBuOm0q", + "outputId": "9e7c548b-778a-4da4-f75a-449af62d6208" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[[1 2]\n", + " [3 4]\n", + " [5 6]\n", + " [7 8]]\n", + "---------------------\n", + "[[[1 2]\n", + " [3 4]]\n", + "\n", + " [[5 6]\n", + " [7 8]]]\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "print(np.concatenate((a, c), axis=0)) #shape=(5,2)\n" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "sBjPXZ2_P6Ea", + "outputId": "9f403d1f-dcdf-4632-8ce1-a0f51a9a5278" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[[ 1 2]\n", + " [ 3 4]\n", + " [ 5 6]\n", + " [ 7 8]\n", + " [ 9 10]]\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "print(np.stack(a, c), axis=0)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 344 + }, + "id": "qPwwk65ZjmA5", + "outputId": "46eacac1-792c-4c3e-f1c4-b57e47c3c319" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "error", + "ename": "TypeError", + "evalue": "only integer scalar arrays can be converted to a scalar index", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m/tmp/ipykernel_1728/1635794774.py\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstack\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mc\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0maxis\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m/usr/local/lib/python3.13/dist-packages/numpy/_core/shape_base.py\u001b[0m in \u001b[0;36mstack\u001b[0;34m(arrays, axis, out, dtype, casting)\u001b[0m\n\u001b[1;32m 458\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 459\u001b[0m \u001b[0mresult_ndim\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0marrays\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mndim\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 460\u001b[0;31m \u001b[0maxis\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnormalize_axis_index\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0maxis\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mresult_ndim\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 461\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 462\u001b[0m \u001b[0msl\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mslice\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0maxis\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0m_nx\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnewaxis\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mTypeError\u001b[0m: only integer scalar arrays can be converted to a scalar index" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "np.stack은 합치려는 두 넘파이 배열 차원이 다르므로 오류 발생" + ], + "metadata": { + "id": "5txc740TZiNF" + } + }, + { + "cell_type": "markdown", + "source": [ + "\n", + "\n", + "* 배열을 텐서로 변환\n", + "\n" + ], + "metadata": { + "id": "r-W6bmxYkGEP" + } + }, + { + "cell_type": "code", + "source": [ + "categorical_data = torch.tensor(categorical_data, dtype=torch.int64)\n", + "categorical_data[:10]" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "eT0V3Dm9QITq", + "outputId": "be70bb25-facd-4125-e876-4df78c55749b" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "tensor([[3, 3, 0, 0, 2, 1],\n", + " [3, 3, 0, 0, 2, 2],\n", + " [3, 3, 0, 0, 2, 0],\n", + " [3, 3, 0, 0, 1, 1],\n", + " [3, 3, 0, 0, 1, 2],\n", + " [3, 3, 0, 0, 1, 0],\n", + " [3, 3, 0, 0, 0, 1],\n", + " [3, 3, 0, 0, 0, 2],\n", + " [3, 3, 0, 0, 0, 0],\n", + " [3, 3, 0, 1, 2, 1]])" + ] + }, + "metadata": {}, + "execution_count": 138 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "\n", + "\n", + "* 레이블로 사용할 칼럼을 텐서로 변환\n", + "\n" + ], + "metadata": { + "id": "fb1dU4ttkO1G" + } + }, + { + "cell_type": "code", + "source": [ + "outputs = pd.get_dummies(dataset.output) #get_dummies: 가변수로 만들어주는 함수 = 문자 -> 숫자 (0,1)\n", + "outputs = outputs.values\n", + "outputs = torch.tensor(outputs).flatten() #1차원 텐서로 변환\n", + "print(categorical_data.shape)\n", + "print(outputs.shape)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "BaK0VNO8QOt_", + "outputId": "583cc9ac-6fac-4907-8cf8-8634ea4394f5" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "torch.Size([1728, 6])\n", + "torch.Size([6912])\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "data = {\n", + " 'gender' : ['male', 'female', 'male'],\n", + " 'weight' : [72,55,68],\n", + " 'nation' : ['Japan', 'Korea', 'Australia']\n", + "}\n", + "df = pd.DataFrame(data)\n", + "print(df)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "eLuriq1dkkRZ", + "outputId": "58f0450b-8ce8-44d8-a89e-709e8ac310e1" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + " gender weight nation\n", + "0 male 72 Japan\n", + "1 female 55 Korea\n", + "2 male 68 Australia\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "pd.get_dummies(df)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 163 + }, + "id": "rIBfC_ARk54e", + "outputId": "203f4648-c33d-4390-e316-e8f0dc713776" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " weight gender_female gender_male nation_Australia nation_Japan \\\n", + "0 72 False True False True \n", + "1 55 True False False False \n", + "2 68 False True True False \n", + "\n", + " nation_Korea \n", + "0 False \n", + "1 True \n", + "2 False " + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
weightgender_femalegender_malenation_Australianation_Japannation_Korea
072FalseTrueFalseTrueFalse
155TrueFalseFalseFalseTrue
268FalseTrueTrueFalseFalse
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + "
\n" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "dataframe", + "summary": "{\n \"name\": \"pd\",\n \"rows\": 3,\n \"fields\": [\n {\n \"column\": \"weight\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 8,\n \"min\": 55,\n \"max\": 72,\n \"num_unique_values\": 3,\n \"samples\": [\n 72,\n 55,\n 68\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"gender_female\",\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n true,\n false\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"gender_male\",\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n false,\n true\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"nation_Australia\",\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n true,\n false\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"nation_Japan\",\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n false,\n true\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"nation_Korea\",\n \"properties\": {\n \"dtype\": \"boolean\",\n \"num_unique_values\": 2,\n \"samples\": [\n true,\n false\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}" + } + }, + "metadata": {}, + "execution_count": 142 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "\n", + "\n", + "ravel() & reshape() & flatten()\n", + "* 텐서의 차원 바꿀 때 사용\n", + "* 2차원 텐서 -> 1차원\n", + "\n", + "\n" + ], + "metadata": { + "id": "Z0Py0Q_mk_Bj" + } + }, + { + "cell_type": "code", + "source": [ + "a = np.array([[1, 2],\n", + " [3, 4]])\n", + "print(a.ravel())\n", + "print(a.reshape(-1))\n", + "print(a.flatten())" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "LDIqCc4fTFkq", + "outputId": "66dd5796-2fe6-4089-bd89-01a7c7d63215" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[1 2 3 4]\n", + "[1 2 3 4]\n", + "[1 2 3 4]\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "워드 임베딩: 유사한 단어끼리 유사하게 인코딩되도록 표현하는 방법 \n", + "\n", + "\n", + "* 높은 차원의 임베딩일수록 단어 간의 세부적인 관계를 잘 파악할 수 있다\n", + "* 단일 숫자로 변환된 넘파이 배열을 N차원으로 변경하여 사용\n", + "* 보통 임베딩 크기 - 칼럼의 고유 값 수 / 2\n", + "\n", + "\n", + "\n" + ], + "metadata": { + "id": "Py0cEGMulT65" + } + }, + { + "cell_type": "markdown", + "source": [ + "\n", + "\n", + "* 범주형 칼럼을 N차원으로 변환\n", + "\n" + ], + "metadata": { + "id": "92odGLdWl6fA" + } + }, + { + "cell_type": "code", + "source": [ + "categorical_column_sizes = [len(dataset[column].cat.categories) for column in\n", + " categorical_columns]\n", + "categorical_embedding_sizes = [(col_size, min(550, (col_size+1)//2)) for col_size in\n", + " categorical_column_sizes]\n", + "print(categorical_embedding_sizes)\n" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "EfHugCmpTRCZ", + "outputId": "d636d25e-726e-46b9-a6a9-663b8ff1c4bb" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[(4, 2), (4, 2), (4, 2), (3, 2), (3, 2), (3, 2)]\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "\n", + "\n", + "* 데이터셋 분리(훈련과 테스트 용도로)\n", + "\n" + ], + "metadata": { + "id": "J1nQz-BQmAdb" + } + }, + { + "cell_type": "code", + "source": [ + "total_records = 1728\n", + "test_records = int(total_records * .2) #전체 데이터 중 20%를 테스트 용도로 사용\n", + "categorical_train_data = categorical_data[:total_records - test_records]\n", + "categorical_test_data = categorical_data[total_records - test_records:total_records]\n", + "train_outputs = outputs[:total_records - test_records]\n", + "test_outputs = outputs[total_records - test_records:total_records]" + ], + "metadata": { + "id": "Y49NaIBtTzJH" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "\n", + "\n", + "* 데이터셋 분리 확인\n", + "\n" + ], + "metadata": { + "id": "WERY4sIZmFNC" + } + }, + { + "cell_type": "code", + "source": [ + "print(len(categorical_train_data))\n", + "print(len(train_outputs))\n", + "print(len(categorical_test_data))\n", + "print(len(test_outputs))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "vduhN-ukaIx7", + "outputId": "00956bbc-fafc-4a70-9a4e-cb2589672670" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "1383\n", + "1383\n", + "345\n", + "345\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "\n", + "\n", + "* 모델의 네트워크 생성 \n", + "클래스 형태로 구현되는 모델은 nn.Module을 상속받는다 \n", + "__init__() - 모델에서 사용될 파라미터와 신경망 초기화 용도(객체 생성시 자동으로 호출) \n", + "super().__init__(): 부모 클래스에 접근할 때 사용. super는 self 사용 X \n", + "모델의 네트워크 계층 구축 위해 for문. 각 계층을 all_layers 목록에 추가 \n", + "forward() 함수 - 학습 데이터를 입력받아서 연산 진행. 모델 객체를 데이터와 함께 호출하면 자동으로 실행\n" + ], + "metadata": { + "id": "qqlS3f-lmIz1" + } + }, + { + "cell_type": "code", + "source": [ + "class Model(nn.Module):\n", + " def __init__(self, embedding_size, output_size, layers, p=0.4):\n", + " #(자기자신, 범주형 칼럼의 임베딩 크기, 출력층의 크기, 모든 계층에 대한 목록, 드롭아웃(기본값은 0.5))\n", + " super().__init__()\n", + " self.all_embeddings = nn.ModuleList([nn.Embedding(ni, nf) for ni,\n", + " nf in embedding_size])\n", + " self.embedding_dropout = nn.Dropout(p)\n", + " all_layers = []\n", + " num_categorical_cols = sum((nf for ni, nf in embedding_size))\n", + " input_size = num_categorical_cols\n", + "\n", + " for i in layers:\n", + " all_layers.append(nn.Linear(input_size, i)) #선형 계층 - 입력 데이터에 선형 변환을 진행 y = Wx + b\n", + " all_layers.append(nn.ReLU(inplace=True)) #활성화 함수로 사용\n", + " all_layers.append(nn.BatchNorm1d(i)) #배치 정규화 용도로 사용\n", + " all_layers.append(nn.Dropout(p)) #과적합 방지에 사용\n", + " input_size = i\n", + "\n", + " all_layers.append(nn.Linear(layers[-1], output_size))\n", + " self.layers = nn.Sequential(*all_layers)\n", + " def forward(self, x_categorical):\n", + " embeddings = []\n", + " for i,e in enumerate(self.all_embeddings):\n", + " embeddings.append(e(x_categorical[:,i]))\n", + " x = torch.cat(embeddings, 1)\n", + " x = self.embedding_dropout(x)\n", + " x = self.layers(x)\n", + " return x" + ], + "metadata": { + "id": "OX4oYL2Oa428" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "* Model 클래스의 객체 생성 \n", + "-> 모델에 대한 구조(네트워크)를 보여줌" + ], + "metadata": { + "id": "bsy36aupoQj8" + } + }, + { + "cell_type": "code", + "source": [ + "model = Model(categorical_embedding_sizes, 4, [200,100,50], p=0.4)\n", + "print(model)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "mGZQsKwwcLbI", + "outputId": "111bdec0-5593-467a-d6ff-661ad8adc00a" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Model(\n", + " (all_embeddings): ModuleList(\n", + " (0-2): 3 x Embedding(4, 2)\n", + " (3-5): 3 x Embedding(3, 2)\n", + " )\n", + " (embedding_dropout): Dropout(p=0.4, inplace=False)\n", + " (layers): Sequential(\n", + " (0): Linear(in_features=12, out_features=200, bias=True)\n", + " (1): ReLU(inplace=True)\n", + " (2): BatchNorm1d(200, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (3): Dropout(p=0.4, inplace=False)\n", + " (4): Linear(in_features=200, out_features=100, bias=True)\n", + " (5): ReLU(inplace=True)\n", + " (6): BatchNorm1d(100, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (7): Dropout(p=0.4, inplace=False)\n", + " (8): Linear(in_features=100, out_features=50, bias=True)\n", + " (9): ReLU(inplace=True)\n", + " (10): BatchNorm1d(50, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (11): Dropout(p=0.4, inplace=False)\n", + " (12): Linear(in_features=50, out_features=4, bias=True)\n", + " )\n", + ")\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "\n", + "\n", + "* 모델의 파라미터 정의 \n", + "모델 훈련 전에 손실 함수와 옵티마이저에 대해 정의해야함 \n", + "여기에서는 데이터 분류로 크로스 엔트로피(cross entropy) 손실 함수 사용 \n", + "옵티마이저로는 아담(Adam) 사용\n", + "\n" + ], + "metadata": { + "id": "sgzp-hsFoZZS" + } + }, + { + "cell_type": "code", + "source": [ + "loss_function = nn.CrossEntropyLoss()\n", + "optimizer = torch.optim.Adam(model.parameters(), lr=0.001)" + ], + "metadata": { + "id": "phsCHitmcwW4" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "* CPU/GPU 사용 지정" + ], + "metadata": { + "id": "6NuP5nASo_Sj" + } + }, + { + "cell_type": "code", + "source": [ + "if torch.cuda.is_available():\n", + " device = torch.device('cuda') #GPU가 있다면 GPU 사용\n", + "else:\n", + " device = torch.device('cpu') #GPU가 없다면 CPU 사용" + ], + "metadata": { + "id": "9nf-goqic5-n" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "* 모델 학습" + ], + "metadata": { + "id": "qq7KRN0OpIBz" + } + }, + { + "cell_type": "code", + "source": [ + "epochs = 500\n", + "aggregated_losses = []\n", + "train_outputs = train_outputs.to(device=device, dtype=torch.int64)\n", + "for i in range(epochs): #500회 반복, 각 반복마다 손실함수가 오차 계산\n", + " i += 1\n", + " y_pred = model(categorical_train_data).to(device)\n", + " single_loss = loss_function(y_pred, train_outputs)\n", + " aggregated_losses.append(single_loss) #오차를 aggregated_losses에 추가\n", + " if i%25 == 1:\n", + " print(f'epoch: {i:3} loss: {single_loss.item():10.8f}')\n", + " optimizer.zero_grad()\n", + " single_loss.backward() #손실함수의 backward() 호출하여 가중치 업데이트\n", + " optimizer.step() #옵티마이저 함수의 step() 이용하여 기울기 업데이트\n", + "print(f'epoch: {i:3} loss: {single_loss.item():10.10f}') #오차가 25 에포크마다 출력" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "D9sdkapqdC68", + "outputId": "c04167af-c694-463d-8416-4e4a02cd1914" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "epoch: 1 loss: 1.63961422\n", + "epoch: 26 loss: 1.43742585\n", + "epoch: 51 loss: 1.35185540\n", + "epoch: 76 loss: 1.22745800\n", + "epoch: 101 loss: 1.07764256\n", + "epoch: 126 loss: 0.95250309\n", + "epoch: 151 loss: 0.83041352\n", + "epoch: 176 loss: 0.76717067\n", + "epoch: 201 loss: 0.69741708\n", + "epoch: 226 loss: 0.66913635\n", + "epoch: 251 loss: 0.65270364\n", + "epoch: 276 loss: 0.61363751\n", + "epoch: 301 loss: 0.60089952\n", + "epoch: 326 loss: 0.58936447\n", + "epoch: 351 loss: 0.58841324\n", + "epoch: 376 loss: 0.58674276\n", + "epoch: 401 loss: 0.59102541\n", + "epoch: 426 loss: 0.57606286\n", + "epoch: 451 loss: 0.57803738\n", + "epoch: 476 loss: 0.57410473\n", + "epoch: 500 loss: 0.5719131827\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "* 테스트 데이터셋으로 모델 예측" + ], + "metadata": { + "id": "EeXw3RfqpgOz" + } + }, + { + "cell_type": "code", + "source": [ + "test_outputs = test_outputs.to(device=device, dtype=torch.int64)\n", + "with torch.no_grad():\n", + " y_val = model(categorical_test_data).to(device)\n", + " loss = loss_function(y_val, test_outputs)\n", + "print(f'Loss: {loss:.8f}')" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ET79VJW-dsvc", + "outputId": "243032d9-19a1-4827-be91-be47e4f762ff" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Loss: 0.56769222\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "* 모델이 예측 확인" + ], + "metadata": { + "id": "DUXLlMV0p1Mi" + } + }, + { + "cell_type": "code", + "source": [ + "print(y_val[:5])" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ZUoZKWpId1fc", + "outputId": "3b1934e8-aa44-4023-dd26-bf31043f2c62" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "tensor([[ 2.1429, 1.1846, -2.7685, -2.5937],\n", + " [ 2.1687, 0.8663, -3.1958, -3.1995],\n", + " [ 2.0608, 1.2439, -2.5505, -2.4137],\n", + " [ 3.3007, 2.3240, -3.3861, -3.1999],\n", + " [ 2.5353, 1.5597, -3.3905, -3.4588]], device='cuda:0')\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "* 가장 큰 값을 갖는 인덱스 확인" + ], + "metadata": { + "id": "XXUS9qW-p5By" + } + }, + { + "cell_type": "code", + "source": [ + "y_val = np.argmax(y_val.cpu().numpy(), axis=1)\n", + "test_outputs = test_outputs.cpu().numpy()\n", + "print(y_val[:5])" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "IaTafIkXfm5e", + "outputId": "096f6007-f5da-46ea-ea56-31d2cf2b4306" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[0 0 0 0 0]\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "* 테스트 데이터셋을 이용한 정확도 확인" + ], + "metadata": { + "id": "JaubngCvp-rd" + } + }, + { + "cell_type": "code", + "source": [ + "from sklearn.metrics import classification_report, confusion_matrix, accuracy_score\n", + "print(confusion_matrix(test_outputs, y_val))\n", + "print(classification_report(test_outputs, y_val))\n", + "print(accuracy_score(test_outputs, y_val))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "glNJ2MvZfsFO", + "outputId": "d0b03171-631f-419c-bd16-9adc813496ec" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[[259 0]\n", + " [ 86 0]]\n", + " precision recall f1-score support\n", + "\n", + " 0 0.75 1.00 0.86 259\n", + " 1 0.00 0.00 0.00 86\n", + "\n", + " accuracy 0.75 345\n", + " macro avg 0.38 0.50 0.43 345\n", + "weighted avg 0.56 0.75 0.64 345\n", + "\n", + "0.7507246376811594\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "/usr/local/lib/python3.13/dist-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning: Precision is ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior.\n", + " _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n", + "/usr/local/lib/python3.13/dist-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning: Precision is ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior.\n", + " _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n", + "/usr/local/lib/python3.13/dist-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning: Precision is ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior.\n", + " _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n" + ] + } + ] + } + ] +} \ No newline at end of file diff --git "a/Week1_\354\230\210\354\212\265\352\263\274\354\240\234_\354\247\204\354\230\210\354\233\220.pdf" "b/Week1_\354\230\210\354\212\265\352\263\274\354\240\234_\354\247\204\354\230\210\354\233\220.pdf" new file mode 100644 index 0000000..87ecc43 Binary files /dev/null and "b/Week1_\354\230\210\354\212\265\352\263\274\354\240\234_\354\247\204\354\230\210\354\233\220.pdf" differ