{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "98a9c9ab-77bd-44a6-88ef-af5efe6e7981",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np  \n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "e019af77-4426-44a6-86eb-168e1700b606",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 'mnist.npz' 파일을 강의자료 게시판에서 다운로드 받자!   \n",
    "# 구글 드라이브에 업로드하고, 마운트 코드를 추가하자!  \n",
    "\n",
    "mnist = np.load('mnist.npz')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "84e5da05-0dba-40e2-a1e3-30b4dd4efa6c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(60000, 28, 28) (60000,) (10000, 28, 28) (10000,)\n"
     ]
    }
   ],
   "source": [
    "x_train = (mnist['x_train'] - np.mean(mnist['x_train'])) / np.std(mnist['x_train'])\n",
    "y_train = mnist['y_train']\n",
    "x_test = (mnist['x_test'] - np.mean(mnist['x_train'])) / np.std(mnist['x_train']) \n",
    "y_test = mnist['y_test']\n",
    "print(x_train.shape, y_train.shape, x_test.shape, y_test.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "f364d7fc-1cc0-4259-bb3a-8411ca812072",
   "metadata": {},
   "outputs": [],
   "source": [
    "def softmax(x):\n",
    "    '''\n",
    "    본 함수의 코드는 주석 코드와 동일한 계산을 수행\n",
    "    주석 코드에서 추가적으로 오버플로우 방지 코드가 추가되어 있음\n",
    "    def softmax(x):\n",
    "        exp_x = np.exp(x)\n",
    "        for i in range(len(x)):\n",
    "            exp_x[i] /= np.sum(exp_x[i]) \n",
    "        return exp_x\n",
    "    '''\n",
    "    exp_x = np.exp(x - np.max(x)) # 오버플로우 방지 코드\n",
    "    for i in range(len(x)): \n",
    "        exp_x[i] /= np.sum(exp_x[i])\n",
    "    return exp_x\n",
    "\n",
    "def hypothesis(w, x, b):\n",
    "    return softmax(x.dot(w) + b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "91fc84dd-89f6-48f3-81da-eb47f8c81382",
   "metadata": {},
   "outputs": [],
   "source": [
    "def cross_entropy(y_true, y_pred):\n",
    "    # one-hot vector -> label\n",
    "    # [1, 0, 0, 0, 0, 0, 0, 0, 0, 0] -> 0\n",
    "    # [0, 0, 0, 0, 0, 1, 0, 0, 0, 0] -> 5\n",
    "    y_true = np.argmax(y_true, axis=-1)\n",
    "    # 레이블에 해당하는 y_pred만을 가져옴\n",
    "    y_pred = y_pred[np.arange(y_true.shape[0]), y_true]\n",
    "    \n",
    "    return -np.mean(np.log(y_pred + 1e-8))\n",
    "    \n",
    "\n",
    "def to_onehot(labels, num_classes):\n",
    "    # label을 onehot vector로 변환\n",
    "    # 0 -> [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n",
    "    # 5 -> [0, 0, 0, 0, 0, 1, 0, 0, 0, 0]\n",
    "    # 9 -> [0, 0, 0, 0, 0, 0, 0, 0, 0, 1]\n",
    "    return np.eye(num_classes)[labels]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "cc4437d6-efd9-46f0-870c-206dc0f714b6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(60000, 10) (10000, 10)\n"
     ]
    }
   ],
   "source": [
    "# onehot vector의 형태로 변환함\n",
    "y_train_onehot = to_onehot(y_train, 10) # 클래스는 0~9까지 10개 \n",
    "y_test_onehot = to_onehot(y_test, 10) # 클래스는 0~9까지 10개\n",
    "print(y_train_onehot.shape, y_test_onehot.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "97322fac-d83a-4a6a-ad25-39f9291f1fca",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(60000, 49) (10000, 49)\n"
     ]
    }
   ],
   "source": [
    "# (28, 28) 크기의 이미지를 (7, 7) 크기의 이미지로 down-sizing (계산 효율을 위해서)\n",
    "# (7, 7) 크기를 (49,) 크기로 reshape\n",
    "x_train_small = x_train[:, ::4, ::4].reshape(-1, 7*7) \n",
    "x_test_small = x_test[:, ::4, ::4].reshape(-1, 7*7)\n",
    "print(x_train_small.shape, x_test_small.shape)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "e11b7f7c-d3ef-4e00-891e-e3e158db2f5f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(49, 10) (10,)\n"
     ]
    }
   ],
   "source": [
    "w = np.random.rand(7*7, 10) \n",
    "b = np.random.rand(10,) \n",
    "print(w.shape, b.shape)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "f6c8a1f9-f2f9-4dda-8779-611dcc1c43f6",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 하이퍼파라메타를 설정하자!\n",
    "epoch = 1000\n",
    "alpha = 0.05\n",
    "# 총 학습데이터 숫자 (60000)\n",
    "sample_num = x_train_small.shape[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "a41fb1c1-5824-4521-bf75-cd636f137e83",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[Epoch :   0] Loss : 1.0917487544\n",
      "[Epoch :  50] Loss : 1.0304729184\n",
      "[Epoch : 100] Loss : 0.9894486915\n",
      "[Epoch : 150] Loss : 0.9602406298\n",
      "[Epoch : 200] Loss : 0.9384731504\n",
      "[Epoch : 250] Loss : 0.9216663298\n",
      "[Epoch : 300] Loss : 0.9083158087\n",
      "[Epoch : 350] Loss : 0.8974595206\n",
      "[Epoch : 400] Loss : 0.8884559496\n",
      "[Epoch : 450] Loss : 0.8808625711\n",
      "[Epoch : 500] Loss : 0.8743653642\n",
      "[Epoch : 550] Loss : 0.8687360323\n",
      "[Epoch : 600] Loss : 0.8638050555\n",
      "[Epoch : 650] Loss : 0.8594441796\n",
      "[Epoch : 700] Loss : 0.8555547343\n",
      "[Epoch : 750] Loss : 0.8520596568\n",
      "[Epoch : 800] Loss : 0.8488979342\n",
      "[Epoch : 850] Loss : 0.8460206587\n",
      "[Epoch : 900] Loss : 0.8433881808\n",
      "[Epoch : 950] Loss : 0.8409680227\n"
     ]
    }
   ],
   "source": [
    "total_loss = []\n",
    "for i in range(epoch):\n",
    "    h = hypothesis(w, x_train_small, b) \n",
    "    loss = cross_entropy(y_train_onehot, h)\n",
    "    \n",
    "    # 이 부분에 코드를 완성하자!!! \n",
    "    # 경사하강법을 구현해보자!!!\n",
    "    gradient_w = np.dot(x_train_small.T, (h - y_train_onehot)) / len(x_train_small)\n",
    "    gradient_b = np.sum(h - y_train_onehot) / len(x_train_small)\n",
    "    \n",
    "    # Update the weights and bias using gradient descent\n",
    "    w -= alpha * gradient_w\n",
    "    b -= alpha * gradient_b\n",
    "    \n",
    "    if i % 50 == 0:\n",
    "        print(f\"[Epoch : {i:3d}] Loss : {loss:.10f}\") \n",
    "    total_loss.append(loss)\n",
    "total_loss = np.array(total_loss)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "301bda26-ab8f-4809-9a5d-9b36eda254f3",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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LjNSUlKMmpoaR5vbbrvNSE5ONpYsWWKsX7/eGDlypDFq1CjH9aamJqNv377GxIkTjU2bNhmfffaZER0dbTz00ENmfCSXt27dOqNbt25G//79jbvvvttxnn5uHxUVFUbXrl2Nm2++2Vi7dq2xZ88e44svvjDy8vIcbZ588kkjLCzMWLBggbF582bjiiuuMFJTU40jR4442kyePNkYMGCAsWbNGmPlypVG9+7djWuvvdaMj+SSnnjiCSMqKspYuHChsXfvXmP+/PlGcHCw8eyzzzra0M9t99lnnxkPP/yw8eGHHxqSjI8++qjV9fbo06qqKiMuLs6YMWOGsXXrVuO9994zAgICjFdeeeW86ye8nIXhw4cbd9xxh+Pn5uZmIzEx0ZgzZ46JVbm30tJSQ5KxfPlywzAMo7Ky0vDx8THmz5/vaLNjxw5DkpGVlWUYRst/bFar1SguLna0eemll4zQ0FCjvr6+Yz+Ai6uurjZ69OhhLF682Bg7dqwjvNDP7efBBx80Ro8efcrrdrvdiI+PN55++mnHucrKSsPPz8947733DMMwjO3btxuSjG+//dbR5vPPPzcsFotRWFjovOLdyJQpU4xf/OIXrc5dddVVxowZMwzDoJ/bww/DS3v16YsvvmhERES0+rvx4IMPGr169TrvmrltdAYNDQ3asGGDJk6c6DhntVo1ceJEZWVlmViZe6uqqpIkRUZGSpI2bNigxsbGVv2ckZGhlJQURz9nZWWpX79+iouLc7SZNGmSbDabtm3b1oHVu7477rhDU6ZMadWfEv3cnj755BMNHTpU06dPV2xsrAYNGqR//OMfjut79+5VcXFxq74OCwvTiBEjWvV1eHi4hg4d6mgzceJEWa1WrV27tuM+jAsbNWqUlixZop07d0qSNm/erFWrVunSSy+VRD87Q3v1aVZWli666CL5+vo62kyaNEm5ubk6fPjwedXocRsztreysjI1Nze3+kMuSXFxccrJyTGpKvdmt9t1zz336MILL1Tfvn0lScXFxfL19VV4eHirtnFxcSouLna0Odm/h+PX0GLevHnauHGjvv322xOu0c/tZ8+ePXrppZd033336Te/+Y2+/fZb/frXv5avr69uuukmR1+drC+/39exsbGtrnt7eysyMpK+Pmb27Nmy2WzKyMiQl5eXmpub9cQTT2jGjBmSRD87QXv1aXFxsVJTU0/4HcevRUREnHONhBd0uDvuuENbt27VqlWrzC7F4xQUFOjuu+/W4sWL5e/vb3Y5Hs1ut2vo0KH64x//KEkaNGiQtm7dqpdfflk33XSTydV5jn/9619655139O677+qCCy5Qdna27rnnHiUmJtLPnRi3jc4gOjpaXl5eJ6zGKCkpUXx8vElVua8777xTCxcu1LJly5SUlOQ4Hx8fr4aGBlVWVrZq//1+jo+PP+m/h+PX0HJbqLS0VIMHD5a3t7e8vb21fPly/f3vf5e3t7fi4uLo53aSkJCgPn36tDrXu3dv5efnS/pvX53ub0d8fLxKS0tbXW9qalJFRQV9fcwDDzyg2bNn65prrlG/fv10ww036N5779WcOXMk0c/O0F596sy/JYSXM/D19dWQIUO0ZMkSxzm73a4lS5YoMzPTxMrci2EYuvPOO/XRRx9p6dKlJwwlDhkyRD4+Pq36OTc3V/n5+Y5+zszM1HfffdfqP5jFixcrNDT0hC+RzmrChAn67rvvlJ2d7TiGDh2qGTNmOP6Zfm4fF1544QnL/Xfu3KmuXbtKklJTUxUfH9+qr202m9auXduqrysrK7VhwwZHm6VLl8put2vEiBEd8ClcX11dnazW1l9VXl5estvtkuhnZ2ivPs3MzNSKFSvU2NjoaLN48WL16tXrvG4ZSWKp9NmYN2+e4efnZ7z++uvG9u3bjVtuucUIDw9vtRoDp3f77bcbYWFhxtdff20cPHjQcdTV1Tna3HbbbUZKSoqxdOlSY/369UZmZqaRmZnpuH58Ce8ll1xiZGdnG4sWLTJiYmJYwnsG319tZBj0c3tZt26d4e3tbTzxxBPGrl27jHfeeccIDAw03n77bUebJ5980ggPDzc+/vhjY8uWLcaVV1550uWmgwYNMtauXWusWrXK6NGjR6dewvtDN910k9GlSxfHUukPP/zQiI6ONv73f//X0YZ+brvq6mpj06ZNxqZNmwxJxl//+ldj06ZNxv79+w3DaJ8+raysNOLi4owbbrjB2Lp1qzFv3jwjMDCQpdId6bnnnjNSUlIMX19fY/jw4caaNWvMLsmtSDrp8dprrznaHDlyxPjVr35lREREGIGBgcZPfvIT4+DBg61+z759+4xLL73UCAgIMKKjo43777/faGxs7OBP415+GF7o5/bz6aefGn379jX8/PyMjIwMY+7cua2u2+1245FHHjHi4uIMPz8/Y8KECUZubm6rNuXl5ca1115rBAcHG6GhocbPf/5zo7q6uiM/hkuz2WzG3XffbaSkpBj+/v5GWlqa8fDDD7dafks/t92yZctO+jf5pptuMgyj/fp08+bNxujRow0/Pz+jS5cuxpNPPtku9VsM43uPKQQAAHBxzHkBAABuhfACAADcCuEFAAC4FcILAABwK4QXAADgVggvAADArRBeAACAWyG8AAAAt0J4AQAAboXwAgAA3ArhBQAAuBXCCwAAcCv/HyqRg++lrpApAAAAAElFTkSuQmCC",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(10.0 * np.log(total_loss / (np.max(total_loss + 1e-5)))) \n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "e9ff71cb-3ac2-443b-80ff-983352de6577",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.7435\n"
     ]
    }
   ],
   "source": [
    "from sklearn.metrics import accuracy_score\n",
    "\n",
    "y_pred_onehot = hypothesis(w, x_test_small, b) \n",
    "y_pred = np.argmax(y_pred_onehot, axis=-1)\n",
    "\n",
    "acc = accuracy_score(y_test, y_pred) \n",
    "print(acc)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4521c653-4fbc-4341-ac16-a0ab7be3bc1d",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "prac",
   "language": "python",
   "name": "prac"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
