{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPython 3.5.5\n",
      "IPython 6.2.1\n",
      "\n",
      "numpy 1.14.1\n",
      "scipy 1.0.0\n",
      "sklearn 0.19.1\n",
      "pandas 0.22.0\n",
      "matplotlib 2.2.2\n"
     ]
    }
   ],
   "source": [
    "%load_ext watermark\n",
    "%watermark -v -p numpy,scipy,sklearn,pandas,matplotlib"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**8장 – 차원 축소**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "_이 노트북은 8장에 있는 모든 샘플 코드와 연습문제 해답을 가지고 있습니다._"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 설정"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "파이썬 2와 3을 모두 지원합니다. 공통 모듈을 임포트하고 맷플롯립 그림이 노트북 안에 포함되도록 설정하고 생성한 그림을 저장하기 위한 함수를 준비합니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 파이썬 2와 파이썬 3 지원\n",
    "from __future__ import division, print_function, unicode_literals\n",
    "\n",
    "# 공통\n",
    "import numpy as np\n",
    "import os\n",
    "\n",
    "# 일관된 출력을 위해 유사난수 초기화\n",
    "np.random.seed(42)\n",
    "\n",
    "# 맷플롯립 설정\n",
    "%matplotlib inline\n",
    "import matplotlib\n",
    "import matplotlib.pyplot as plt\n",
    "plt.rcParams['axes.labelsize'] = 14\n",
    "plt.rcParams['xtick.labelsize'] = 12\n",
    "plt.rcParams['ytick.labelsize'] = 12\n",
    "\n",
    "# 한글출력\n",
    "plt.rcParams['font.family'] = 'NanumBarunGothic'\n",
    "plt.rcParams['axes.unicode_minus'] = False\n",
    "\n",
    "# 그림을 저장할 폴더\n",
    "PROJECT_ROOT_DIR = \".\"\n",
    "CHAPTER_ID = \"dim_reduction\"\n",
    "\n",
    "def save_fig(fig_id, tight_layout=True):\n",
    "    path = os.path.join(PROJECT_ROOT_DIR, \"images\", CHAPTER_ID, fig_id + \".png\")\n",
    "    if tight_layout:\n",
    "        plt.tight_layout()\n",
    "    plt.savefig(path, format='png', dpi=300)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 투영 방법\n",
    "3D 데이터셋을 만듭니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.random.seed(4)\n",
    "m = 60\n",
    "w1, w2 = 0.1, 0.3\n",
    "noise = 0.1\n",
    "\n",
    "angles = np.random.rand(m) * 3 * np.pi / 2 - 0.5\n",
    "X = np.empty((m, 3))\n",
    "X[:, 0] = np.cos(angles) + np.sin(angles)/2 + noise * np.random.randn(m) / 2\n",
    "X[:, 1] = np.sin(angles) * 0.7 + noise * np.random.randn(m) / 2\n",
    "X[:, 2] = X[:, 0] * w1 + X[:, 1] * w2 + noise * np.random.randn(m)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## SVD 분해를 사용한 PCA"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "노트: `svd()` 함수는 `U`, `s` 그리고 `Vt`를 반환합니다. 여기에서 `Vt`는 행렬 $\\mathbf{V}$의 전치인 $\\mathbf{V}^T$입니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_centered = X - X.mean(axis=0)\n",
    "U, s, Vt = np.linalg.svd(X_centered)\n",
    "c1 = Vt.T[:, 0]\n",
    "c2 = Vt.T[:, 1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "m, n = X.shape\n",
    "\n",
    "S = np.zeros(X_centered.shape)\n",
    "S[:n, :n] = np.diag(s)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.allclose(X_centered, U.dot(S).dot(Vt))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "W2 = Vt.T[:, :2]\n",
    "X2D = X_centered.dot(W2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "X2D_using_svd = X2D"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 사이킷런을 사용한 PCA"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "사이킷런에서는 PCA가 아주 간단합니다. 데이터셋에서 평균을 빼는 작업도 대신 처리해 줍니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.decomposition import PCA\n",
    "\n",
    "pca = PCA(n_components = 2)\n",
    "X2D = pca.fit_transform(X)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 1.26203346,  0.42067648],\n",
       "       [-0.08001485, -0.35272239],\n",
       "       [ 1.17545763,  0.36085729],\n",
       "       [ 0.89305601, -0.30862856],\n",
       "       [ 0.73016287, -0.25404049]])"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X2D[:5]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-1.26203346, -0.42067648],\n",
       "       [ 0.08001485,  0.35272239],\n",
       "       [-1.17545763, -0.36085729],\n",
       "       [-0.89305601,  0.30862856],\n",
       "       [-0.73016287,  0.25404049]])"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X2D_using_svd[:5]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "데이터셋을 조금 다르게해서 PCA를 실행하면 결과가 달라질 것입니다. 일반적으로 달라지는 것은 일부 축이 반대로 바뀌는 정도입니다. 이 예에서 사이킷런의 PCA는 두 축이 반대로 뒤집힌 것외에는 SVD 방식을 사용한 것과 통일한 투영 결과를 만듭니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.allclose(X2D, -X2D_using_svd)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "평면(PCA 2D 부분공간)에 투영된 3D 포인트를 복원합니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "X3D_inv = pca.inverse_transform(X2D)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "물론, 투영 단게에서 일부 정보를 잃어버리기 때문에 복원된 3D 포인트가 원본 3D 포인트와 완전히 똑같지는 않습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.allclose(X3D_inv, X)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "재구성 오차를 계산합니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.010170337792848549"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.mean(np.sum(np.square(X3D_inv - X), axis=1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "SVD 방식의 역변환은 다음과 같습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "X3D_inv_using_svd = X2D_using_svd.dot(Vt[:2, :])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "사이킷런의 `PCA` 클래스는 자동으로 평균을 뺏던 것을 복원해주기 때문에 두 방식의 재구성 오차가 동일하지는 않습니다. 하지만 평균을 빼면 동일한 재구성을 얻을 수 있습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.allclose(X3D_inv_using_svd, X3D_inv - pca.mean_)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`PCA` 객체를 사용하여 계산된 주성분을 참조할 수 있습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-0.93636116, -0.29854881, -0.18465208],\n",
       "       [ 0.34027485, -0.90119108, -0.2684542 ]])"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pca.components_"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "SVD 방법으로 계산된 처음 두 개의 주성분과 비교해 보겠습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0.93636116,  0.29854881,  0.18465208],\n",
       "       [-0.34027485,  0.90119108,  0.2684542 ]])"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Vt[:2]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "축이 뒤집힌 것을 알 수 있습니다."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "이제 설명된 분산 비율을 확인해 보겠습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0.84248607, 0.14631839])"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pca.explained_variance_ratio_"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "첫 번째 차원이 84.2%의 분산을 포함하고 있고 두 번째는 14.6%의 분산을 설명합니다."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "2D로 투영했기 때문에 분산의 1.1%을 잃었습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.011195535570688975"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "1 - pca.explained_variance_ratio_.sum()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "SVD 방식을 사용했을 때 설명된 분산의 비율을 계산하는 방법은 다음과 같습니다(`s`는 행렬 `S`의 대각 성분입니다):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0.84248607, 0.14631839, 0.01119554])"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.square(s) / np.square(s).sum()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "이를 그래프로 멋지게 그려보죠! :)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "3D 화살표를 그래기 위한 유틸리티 클래스입니다(http://stackoverflow.com/questions/11140163 에서 복사했습니다)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "from matplotlib.patches import FancyArrowPatch\n",
    "from mpl_toolkits.mplot3d import proj3d\n",
    "\n",
    "class Arrow3D(FancyArrowPatch):\n",
    "    def __init__(self, xs, ys, zs, *args, **kwargs):\n",
    "        FancyArrowPatch.__init__(self, (0,0), (0,0), *args, **kwargs)\n",
    "        self._verts3d = xs, ys, zs\n",
    "\n",
    "    def draw(self, renderer):\n",
    "        xs3d, ys3d, zs3d = self._verts3d\n",
    "        xs, ys, zs = proj3d.proj_transform(xs3d, ys3d, zs3d, renderer.M)\n",
    "        self.set_positions((xs[0],ys[0]),(xs[1],ys[1]))\n",
    "        FancyArrowPatch.draw(self, renderer)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "x와 y의 함수로 평면을 표현합니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "axes = [-1.8, 1.8, -1.3, 1.3, -1.0, 1.0]\n",
    "\n",
    "x1s = np.linspace(axes[0], axes[1], 10)\n",
    "x2s = np.linspace(axes[2], axes[3], 10)\n",
    "x1, x2 = np.meshgrid(x1s, x2s)\n",
    "\n",
    "C = pca.components_\n",
    "R = C.T.dot(C)\n",
    "z = (R[0, 2] * x1 + R[1, 2] * x2) / (1 - R[2, 2])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "3D 데이터셋, 평면 그리고 이 평면으로의 투영을 그립니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 65448 characters]\n",
      "text/plain": [
       "<Figure size 432x273.6 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from mpl_toolkits.mplot3d import Axes3D\n",
    "\n",
    "fig = plt.figure(figsize=(6, 3.8))\n",
    "ax = fig.add_subplot(111, projection='3d')\n",
    "\n",
    "X3D_above = X[X[:, 2] > X3D_inv[:, 2]]\n",
    "X3D_below = X[X[:, 2] <= X3D_inv[:, 2]]\n",
    "\n",
    "ax.plot(X3D_below[:, 0], X3D_below[:, 1], X3D_below[:, 2], \"bo\", alpha=0.5)\n",
    "\n",
    "ax.plot_surface(x1, x2, z, alpha=0.2, color=\"k\")\n",
    "np.linalg.norm(C, axis=0)\n",
    "ax.add_artist(Arrow3D([0, C[0, 0]],[0, C[0, 1]],[0, C[0, 2]], mutation_scale=15, lw=1, arrowstyle=\"-|>\", color=\"k\"))\n",
    "ax.add_artist(Arrow3D([0, C[1, 0]],[0, C[1, 1]],[0, C[1, 2]], mutation_scale=15, lw=1, arrowstyle=\"-|>\", color=\"k\"))\n",
    "ax.plot([0], [0], [0], \"k.\")\n",
    "\n",
    "for i in range(m):\n",
    "    if X[i, 2] > X3D_inv[i, 2]:\n",
    "        ax.plot([X[i][0], X3D_inv[i][0]], [X[i][1], X3D_inv[i][1]], [X[i][2], X3D_inv[i][2]], \"k-\")\n",
    "    else:\n",
    "        ax.plot([X[i][0], X3D_inv[i][0]], [X[i][1], X3D_inv[i][1]], [X[i][2], X3D_inv[i][2]], \"k-\", color=\"#505050\")\n",
    "    \n",
    "ax.plot(X3D_inv[:, 0], X3D_inv[:, 1], X3D_inv[:, 2], \"k+\")\n",
    "ax.plot(X3D_inv[:, 0], X3D_inv[:, 1], X3D_inv[:, 2], \"k.\")\n",
    "ax.plot(X3D_above[:, 0], X3D_above[:, 1], X3D_above[:, 2], \"bo\")\n",
    "ax.set_xlabel(\"$x_1$\", fontsize=18, labelpad=7)\n",
    "ax.set_ylabel(\"$x_2$\", fontsize=18, labelpad=7)\n",
    "ax.set_zlabel(\"$x_3$\", fontsize=18, labelpad=4)\n",
    "ax.set_xlim(axes[0:2])\n",
    "ax.set_ylim(axes[2:4])\n",
    "ax.set_zlim(axes[4:6])\n",
    "\n",
    "save_fig(\"dataset_3d_plot\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 9112 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig = plt.figure()\n",
    "ax = fig.add_subplot(111, aspect='equal')\n",
    "\n",
    "ax.plot(X2D[:, 0], X2D[:, 1], \"k+\")\n",
    "ax.plot(X2D[:, 0], X2D[:, 1], \"k.\")\n",
    "ax.plot([0], [0], \"ko\")\n",
    "ax.arrow(0, 0, 0, 1, head_width=0.05, length_includes_head=True, head_length=0.1, fc='k', ec='k')\n",
    "ax.arrow(0, 0, 1, 0, head_width=0.05, length_includes_head=True, head_length=0.1, fc='k', ec='k')\n",
    "ax.set_xlabel(\"$z_1$\", fontsize=18)\n",
    "ax.set_ylabel(\"$z_2$\", fontsize=18, rotation=0)\n",
    "ax.axis([-1.5, 1.3, -1.2, 1.2])\n",
    "ax.grid(True)\n",
    "save_fig(\"dataset_2d_plot\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 매니폴드 학습\n",
    "스위스 롤:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.datasets import make_swiss_roll\n",
    "X, t = make_swiss_roll(n_samples=1000, noise=0.2, random_state=42)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 97260 characters]\n",
      "text/plain": [
       "<Figure size 432x360 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "axes = [-11.5, 14, -2, 23, -12, 15]\n",
    "\n",
    "fig = plt.figure(figsize=(6, 5))\n",
    "ax = fig.add_subplot(111, projection='3d')\n",
    "\n",
    "ax.scatter(X[:, 0], X[:, 1], X[:, 2], c=t, cmap=plt.cm.hot)\n",
    "ax.view_init(10, -70)\n",
    "ax.set_xlabel(\"$x_1$\", fontsize=18, labelpad=7)\n",
    "ax.set_ylabel(\"$x_2$\", fontsize=18, labelpad=7)\n",
    "ax.set_zlabel(\"$x_3$\", fontsize=18)\n",
    "ax.set_xlim(axes[0:2])\n",
    "ax.set_ylim(axes[2:4])\n",
    "ax.set_zlim(axes[4:6])\n",
    "\n",
    "save_fig(\"swiss_roll_plot\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 199892 characters]\n",
      "text/plain": [
       "<Figure size 792x288 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(11, 4))\n",
    "\n",
    "plt.subplot(121)\n",
    "plt.scatter(X[:, 0], X[:, 1], c=t, cmap=plt.cm.hot)\n",
    "plt.axis(axes[:4])\n",
    "plt.xlabel(\"$x_1$\", fontsize=18)\n",
    "plt.ylabel(\"$x_2$\", fontsize=18, rotation=0, labelpad=10)\n",
    "plt.grid(True)\n",
    "\n",
    "plt.subplot(122)\n",
    "plt.scatter(t, X[:, 1], c=t, cmap=plt.cm.hot)\n",
    "plt.axis([4, 15, axes[2], axes[3]])\n",
    "plt.xlabel(\"$z_1$\", fontsize=18)\n",
    "plt.grid(True)\n",
    "\n",
    "save_fig(\"squished_swiss_roll_plot\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 62112 characters]\n",
      "text/plain": [
       "<Figure size 432x360 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 33752 characters]\n",
      "text/plain": [
       "<Figure size 360x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 47072 characters]\n",
      "text/plain": [
       "<Figure size 432x360 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 34612 characters]\n",
      "text/plain": [
       "<Figure size 360x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from matplotlib import gridspec\n",
    "\n",
    "axes = [-11.5, 14, -2, 23, -12, 15]\n",
    "\n",
    "x2s = np.linspace(axes[2], axes[3], 10)\n",
    "x3s = np.linspace(axes[4], axes[5], 10)\n",
    "x2, x3 = np.meshgrid(x2s, x3s)\n",
    "\n",
    "fig = plt.figure(figsize=(6, 5))\n",
    "ax = plt.subplot(111, projection='3d')\n",
    "\n",
    "positive_class = X[:, 0] > 5\n",
    "X_pos = X[positive_class]\n",
    "X_neg = X[~positive_class]\n",
    "ax.view_init(10, -70)\n",
    "ax.plot(X_neg[:, 0], X_neg[:, 1], X_neg[:, 2], \"y^\")\n",
    "ax.plot_wireframe(5, x2, x3, alpha=0.5)\n",
    "ax.plot(X_pos[:, 0], X_pos[:, 1], X_pos[:, 2], \"gs\")\n",
    "ax.set_xlabel(\"$x_1$\", fontsize=18, labelpad=7)\n",
    "ax.set_ylabel(\"$x_2$\", fontsize=18, labelpad=7)\n",
    "ax.set_zlabel(\"$x_3$\", fontsize=18)\n",
    "ax.set_xlim(axes[0:2])\n",
    "ax.set_ylim(axes[2:4])\n",
    "ax.set_zlim(axes[4:6])\n",
    "\n",
    "save_fig(\"manifold_decision_boundary_plot1\")\n",
    "plt.show()\n",
    "\n",
    "fig = plt.figure(figsize=(5, 4))\n",
    "ax = plt.subplot(111)\n",
    "\n",
    "plt.plot(t[positive_class], X[positive_class, 1], \"gs\")\n",
    "plt.plot(t[~positive_class], X[~positive_class, 1], \"y^\")\n",
    "plt.axis([4, 15, axes[2], axes[3]])\n",
    "plt.xlabel(\"$z_1$\", fontsize=18)\n",
    "plt.ylabel(\"$z_2$\", fontsize=18, rotation=0, labelpad=7)\n",
    "plt.grid(True)\n",
    "\n",
    "save_fig(\"manifold_decision_boundary_plot2\")\n",
    "plt.show()\n",
    "\n",
    "fig = plt.figure(figsize=(6, 5))\n",
    "ax = plt.subplot(111, projection='3d')\n",
    "\n",
    "positive_class = 2 * (t[:] - 4) > X[:, 1]\n",
    "X_pos = X[positive_class]\n",
    "X_neg = X[~positive_class]\n",
    "ax.view_init(10, -70)\n",
    "ax.plot(X_neg[:, 0], X_neg[:, 1], X_neg[:, 2], \"y^\")\n",
    "ax.plot(X_pos[:, 0], X_pos[:, 1], X_pos[:, 2], \"gs\")\n",
    "ax.set_xlabel(\"$x_1$\", fontsize=18, labelpad=7)\n",
    "ax.set_ylabel(\"$x_2$\", fontsize=18, labelpad=7)\n",
    "ax.set_zlabel(\"$x_3$\", fontsize=18)\n",
    "ax.set_xlim(axes[0:2])\n",
    "ax.set_ylim(axes[2:4])\n",
    "ax.set_zlim(axes[4:6])\n",
    "\n",
    "save_fig(\"manifold_decision_boundary_plot3\")\n",
    "plt.show()\n",
    "\n",
    "fig = plt.figure(figsize=(5, 4))\n",
    "ax = plt.subplot(111)\n",
    "\n",
    "plt.plot(t[positive_class], X[positive_class, 1], \"gs\")\n",
    "plt.plot(t[~positive_class], X[~positive_class, 1], \"y^\")\n",
    "plt.plot([4, 15], [0, 22], \"b-\", linewidth=2)\n",
    "plt.axis([4, 15, axes[2], axes[3]])\n",
    "plt.xlabel(\"$z_1$\", fontsize=18)\n",
    "plt.ylabel(\"$z_2$\", fontsize=18, rotation=0, labelpad=7)\n",
    "plt.grid(True)\n",
    "\n",
    "save_fig(\"manifold_decision_boundary_plot4\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# PCA"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 36480 characters]\n",
      "text/plain": [
       "<Figure size 576x288 with 4 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "angle = np.pi / 5\n",
    "stretch = 5\n",
    "m = 200\n",
    "\n",
    "np.random.seed(3)\n",
    "X = np.random.randn(m, 2) / 10\n",
    "X = X.dot(np.array([[stretch, 0],[0, 1]])) # stretch\n",
    "X = X.dot([[np.cos(angle), np.sin(angle)], [-np.sin(angle), np.cos(angle)]]) # rotate\n",
    "\n",
    "u1 = np.array([np.cos(angle), np.sin(angle)])\n",
    "u2 = np.array([np.cos(angle - 2 * np.pi/6), np.sin(angle - 2 * np.pi/6)])\n",
    "u3 = np.array([np.cos(angle - np.pi/2), np.sin(angle - np.pi/2)])\n",
    "\n",
    "X_proj1 = X.dot(u1.reshape(-1, 1))\n",
    "X_proj2 = X.dot(u2.reshape(-1, 1))\n",
    "X_proj3 = X.dot(u3.reshape(-1, 1))\n",
    "\n",
    "plt.figure(figsize=(8,4))\n",
    "plt.subplot2grid((3,2), (0, 0), rowspan=3)\n",
    "plt.plot([-1.4, 1.4], [-1.4*u1[1]/u1[0], 1.4*u1[1]/u1[0]], \"k-\", linewidth=1)\n",
    "plt.plot([-1.4, 1.4], [-1.4*u2[1]/u2[0], 1.4*u2[1]/u2[0]], \"k--\", linewidth=1)\n",
    "plt.plot([-1.4, 1.4], [-1.4*u3[1]/u3[0], 1.4*u3[1]/u3[0]], \"k:\", linewidth=2)\n",
    "plt.plot(X[:, 0], X[:, 1], \"bo\", alpha=0.5)\n",
    "plt.axis([-1.4, 1.4, -1.4, 1.4])\n",
    "plt.arrow(0, 0, u1[0], u1[1], head_width=0.1, linewidth=5, length_includes_head=True, head_length=0.1, fc='k', ec='k')\n",
    "plt.arrow(0, 0, u3[0], u3[1], head_width=0.1, linewidth=5, length_includes_head=True, head_length=0.1, fc='k', ec='k')\n",
    "plt.text(u1[0] + 0.1, u1[1] - 0.05, r\"$\\mathbf{c_1}$\", fontsize=22)\n",
    "plt.text(u3[0] + 0.1, u3[1], r\"$\\mathbf{c_2}$\", fontsize=22)\n",
    "plt.xlabel(\"$x_1$\", fontsize=18)\n",
    "plt.ylabel(\"$x_2$\", fontsize=18, rotation=0)\n",
    "plt.grid(True)\n",
    "\n",
    "plt.subplot2grid((3,2), (0, 1))\n",
    "plt.plot([-2, 2], [0, 0], \"k-\", linewidth=1)\n",
    "plt.plot(X_proj1[:, 0], np.zeros(m), \"bo\", alpha=0.3)\n",
    "plt.gca().get_yaxis().set_ticks([])\n",
    "plt.gca().get_xaxis().set_ticklabels([])\n",
    "plt.axis([-2, 2, -1, 1])\n",
    "plt.grid(True)\n",
    "\n",
    "plt.subplot2grid((3,2), (1, 1))\n",
    "plt.plot([-2, 2], [0, 0], \"k--\", linewidth=1)\n",
    "plt.plot(X_proj2[:, 0], np.zeros(m), \"bo\", alpha=0.3)\n",
    "plt.gca().get_yaxis().set_ticks([])\n",
    "plt.gca().get_xaxis().set_ticklabels([])\n",
    "plt.axis([-2, 2, -1, 1])\n",
    "plt.grid(True)\n",
    "\n",
    "plt.subplot2grid((3,2), (2, 1))\n",
    "plt.plot([-2, 2], [0, 0], \"k:\", linewidth=2)\n",
    "plt.plot(X_proj3[:, 0], np.zeros(m), \"bo\", alpha=0.3)\n",
    "plt.gca().get_yaxis().set_ticks([])\n",
    "plt.axis([-2, 2, -1, 1])\n",
    "plt.xlabel(\"$z_1$\", fontsize=18)\n",
    "plt.grid(True)\n",
    "\n",
    "save_fig(\"pca_best_projection\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# MNIST 압축"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "from six.moves import urllib\n",
    "from sklearn.datasets import fetch_mldata\n",
    "mnist = fetch_mldata('MNIST original')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.model_selection import train_test_split\n",
    "\n",
    "X = mnist[\"data\"]\n",
    "y = mnist[\"target\"]\n",
    "\n",
    "X_train, X_test, y_train, y_test = train_test_split(X, y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [],
   "source": [
    "pca = PCA()\n",
    "pca.fit(X_train)\n",
    "cumsum = np.cumsum(pca.explained_variance_ratio_)\n",
    "d = np.argmax(cumsum >= 0.95) + 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "154"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "d"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [],
   "source": [
    "pca = PCA(n_components=0.95)\n",
    "X_reduced = pca.fit_transform(X_train)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "154"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pca.n_components_"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.9503623084769206"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.sum(pca.explained_variance_ratio_)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [],
   "source": [
    "pca = PCA(n_components = 154)\n",
    "X_reduced = pca.fit_transform(X_train)\n",
    "X_recovered = pca.inverse_transform(X_reduced)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_digits(instances, images_per_row=5, **options):\n",
    "    size = 28\n",
    "    images_per_row = min(len(instances), images_per_row)\n",
    "    images = [instance.reshape(size,size) for instance in instances]\n",
    "    n_rows = (len(instances) - 1) // images_per_row + 1\n",
    "    row_images = []\n",
    "    n_empty = n_rows * images_per_row - len(instances)\n",
    "    images.append(np.zeros((size, size * n_empty)))\n",
    "    for row in range(n_rows):\n",
    "        rimages = images[row * images_per_row : (row + 1) * images_per_row]\n",
    "        row_images.append(np.concatenate(rimages, axis=1))\n",
    "    image = np.concatenate(row_images, axis=0)\n",
    "    plt.imshow(image, cmap = matplotlib.cm.binary, **options)\n",
    "    plt.axis(\"off\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 61244 characters]\n",
      "text/plain": [
       "<Figure size 504x288 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(7, 4))\n",
    "plt.subplot(121)\n",
    "plot_digits(X_train[::2100])\n",
    "plt.title(\"원본\", fontsize=16)\n",
    "plt.subplot(122)\n",
    "plot_digits(X_recovered[::2100])\n",
    "plt.title(\"압축 후 복원\", fontsize=16)\n",
    "\n",
    "save_fig(\"mnist_compression_plot\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_reduced_pca = X_reduced"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 점진적 PCA"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "...................................................................................................."
     ]
    }
   ],
   "source": [
    "from sklearn.decomposition import IncrementalPCA\n",
    "\n",
    "n_batches = 100\n",
    "inc_pca = IncrementalPCA(n_components=154)\n",
    "for X_batch in np.array_split(X_train, n_batches):\n",
    "    print(\".\", end=\"\") # not shown in the book\n",
    "    inc_pca.partial_fit(X_batch)\n",
    "\n",
    "X_reduced = inc_pca.transform(X_train)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_recovered_inc_pca = inc_pca.inverse_transform(X_reduced)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 59588 characters]\n",
      "text/plain": [
       "<Figure size 504x288 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(7, 4))\n",
    "plt.subplot(121)\n",
    "plot_digits(X_train[::2100])\n",
    "plt.subplot(122)\n",
    "plot_digits(X_recovered_inc_pca[::2100])\n",
    "plt.tight_layout()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_reduced_inc_pca = X_reduced"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "일반 PCA와 점진적 PCA로 MNIST 데이터를 변환한 결과를 비교해 보겠습니다. 먼저 평균이 같은지 확인합니다: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.allclose(pca.mean_, inc_pca.mean_)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "하지만 결과는 완전히 동일하지 않습니다. 점진적 PCA는 아주 훌륭한 근사치를 제공하지만 완벽하지는 않습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 48,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.allclose(X_reduced_pca, X_reduced_inc_pca)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### `memmap()` 사용하기"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`memmap()` 구조를 만들고 MNIST 데이터를 복사합니다. 이는 일반적으로 별도의 프로그램에서 먼저 수행됩니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [],
   "source": [
    "filename = \"my_mnist.data\"\n",
    "m, n = X_train.shape\n",
    "\n",
    "X_mm = np.memmap(filename, dtype='float32', mode='write', shape=(m, n))\n",
    "X_mm[:] = X_train"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "이제 `memmap()` 객체를 삭제하면 파이썬 종결자(finalizer)를 호출해서 데이터를 디스크에 저장하게 됩니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [],
   "source": [
    "del X_mm"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "다음에 다른 프로그램에서 데이터를 로드하여 훈련에 사용합니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "IncrementalPCA(batch_size=525, copy=True, n_components=154, whiten=False)"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X_mm = np.memmap(filename, dtype=\"float32\", mode=\"readonly\", shape=(m, n))\n",
    "\n",
    "batch_size = m // n_batches\n",
    "inc_pca = IncrementalPCA(n_components=154, batch_size=batch_size)\n",
    "inc_pca.fit(X_mm)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [],
   "source": [
    "rnd_pca = PCA(n_components=154, svd_solver=\"randomized\", random_state=42)\n",
    "X_reduced = rnd_pca.fit_transform(X_train)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 시간 복잡도"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "주성분 개수를 바꾸어가며 점진적 PCA와 랜덤 PCA에 비해 일반 PCA 시간을 재어보겠습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "n_components = 2\n",
      "    PCA: 0.8 seconds\n",
      "    IncrementalPCA: 4.5 seconds\n",
      "    PCA: 0.8 seconds\n",
      "n_components = 10\n",
      "    PCA: 0.9 seconds\n",
      "    IncrementalPCA: 4.6 seconds\n",
      "    PCA: 1.0 seconds\n",
      "n_components = 154\n",
      "    PCA: 2.3 seconds\n",
      "    IncrementalPCA: 6.1 seconds\n",
      "    PCA: 2.3 seconds\n"
     ]
    }
   ],
   "source": [
    "import time\n",
    "\n",
    "for n_components in (2, 10, 154):\n",
    "    print(\"n_components =\", n_components)\n",
    "    regular_pca = PCA(n_components=n_components)\n",
    "    inc_pca = IncrementalPCA(n_components=n_components, batch_size=500)\n",
    "    rnd_pca = PCA(n_components=n_components, random_state=42, svd_solver=\"randomized\")\n",
    "\n",
    "    for pca in (regular_pca, inc_pca, rnd_pca):\n",
    "        t1 = time.time()\n",
    "        pca.fit(X_train)\n",
    "        t2 = time.time()\n",
    "        print(\"    {}: {:.1f} seconds\".format(pca.__class__.__name__, t2 - t1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "이번에는 데이터셋의 크기(샘플의 수)를 바꾸어가며 일반 PCA와 랜덤 PCA를 비교해 보겠습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5,1,'PCA와 랜덤 PCA의 시간 복잡도')"
      ]
     },
     "execution_count": 54,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 19572 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "times_rpca = []\n",
    "times_pca = []\n",
    "sizes = [1000, 10000, 20000, 30000, 40000, 50000, 70000, 100000, 200000, 500000]\n",
    "for n_samples in sizes:\n",
    "    X = np.random.randn(n_samples, 5)\n",
    "    pca = PCA(n_components = 2, svd_solver=\"randomized\", random_state=42)\n",
    "    t1 = time.time()\n",
    "    pca.fit(X)\n",
    "    t2 = time.time()\n",
    "    times_rpca.append(t2 - t1)\n",
    "    pca = PCA(n_components = 2)\n",
    "    t1 = time.time()\n",
    "    pca.fit(X)\n",
    "    t2 = time.time()\n",
    "    times_pca.append(t2 - t1)\n",
    "\n",
    "plt.plot(sizes, times_rpca, \"b-o\", label=\"RPCA\")\n",
    "plt.plot(sizes, times_pca, \"r-s\", label=\"PCA\")\n",
    "plt.xlabel(\"n_samples\")\n",
    "plt.ylabel(\"훈련 시간\")\n",
    "plt.legend(loc=\"upper left\")\n",
    "plt.title(\"PCA와 랜덤 PCA의 시간 복잡도\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "이번에는 특성의 개수를 달리하면서 2,000 샘플이 있는 데이터셋에서 성능을 비교해 보겠습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5,1,'PCA와 Randomized PCA의 시간 복잡도')"
      ]
     },
     "execution_count": 55,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 20736 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "times_rpca = []\n",
    "times_pca = []\n",
    "sizes = [1000, 2000, 3000, 4000, 5000, 6000]\n",
    "for n_features in sizes:\n",
    "    X = np.random.randn(2000, n_features)\n",
    "    pca = PCA(n_components = 2, random_state=42, svd_solver=\"randomized\")\n",
    "    t1 = time.time()\n",
    "    pca.fit(X)\n",
    "    t2 = time.time()\n",
    "    times_rpca.append(t2 - t1)\n",
    "    pca = PCA(n_components = 2)\n",
    "    t1 = time.time()\n",
    "    pca.fit(X)\n",
    "    t2 = time.time()\n",
    "    times_pca.append(t2 - t1)\n",
    "\n",
    "plt.plot(sizes, times_rpca, \"b-o\", label=\"RPCA\")\n",
    "plt.plot(sizes, times_pca, \"r-s\", label=\"PCA\")\n",
    "plt.xlabel(\"n_features\")\n",
    "plt.ylabel(\"훈련 시간\")\n",
    "plt.legend(loc=\"upper left\")\n",
    "plt.title(\"PCA와 Randomized PCA의 시간 복잡도\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 커널 PCA"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [],
   "source": [
    "X, t = make_swiss_roll(n_samples=1000, noise=0.2, random_state=42)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.decomposition import KernelPCA\n",
    "\n",
    "rbf_pca = KernelPCA(n_components = 2, kernel=\"rbf\", gamma=0.04)\n",
    "X_reduced = rbf_pca.fit_transform(X)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 123304 characters]\n",
      "text/plain": [
       "<Figure size 792x288 with 3 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from sklearn.decomposition import KernelPCA\n",
    "\n",
    "lin_pca = KernelPCA(n_components = 2, kernel=\"linear\", fit_inverse_transform=True)\n",
    "rbf_pca = KernelPCA(n_components = 2, kernel=\"rbf\", gamma=0.0433, fit_inverse_transform=True)\n",
    "sig_pca = KernelPCA(n_components = 2, kernel=\"sigmoid\", gamma=0.001, coef0=1, fit_inverse_transform=True)\n",
    "\n",
    "y = t > 6.9\n",
    "\n",
    "plt.figure(figsize=(11, 4))\n",
    "for subplot, pca, title in ((131, lin_pca, \"선형 커널\"), (132, rbf_pca, \"RBF 커널, $\\gamma=0.04$\"), (133, sig_pca, \"시그모이드 커널, $\\gamma=10^{-3}, r=1$\")):\n",
    "    X_reduced = pca.fit_transform(X)\n",
    "    if subplot == 132:\n",
    "        X_reduced_rbf = X_reduced\n",
    "    \n",
    "    plt.subplot(subplot)\n",
    "    #plt.plot(X_reduced[y, 0], X_reduced[y, 1], \"gs\")\n",
    "    #plt.plot(X_reduced[~y, 0], X_reduced[~y, 1], \"y^\")\n",
    "    plt.title(title, fontsize=14)\n",
    "    plt.scatter(X_reduced[:, 0], X_reduced[:, 1], c=t, cmap=plt.cm.hot)\n",
    "    plt.xlabel(\"$z_1$\", fontsize=18)\n",
    "    if subplot == 131:\n",
    "        plt.ylabel(\"$z_2$\", fontsize=18, rotation=0)\n",
    "    plt.grid(True)\n",
    "\n",
    "save_fig(\"kernel_pca_plot\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 62000 characters]\n",
      "text/plain": [
       "<Figure size 432x360 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(6, 5))\n",
    "\n",
    "X_inverse = rbf_pca.inverse_transform(X_reduced_rbf)\n",
    "\n",
    "ax = plt.subplot(111, projection='3d')\n",
    "ax.view_init(10, -70)\n",
    "ax.scatter(X_inverse[:, 0], X_inverse[:, 1], X_inverse[:, 2], c=t, cmap=plt.cm.hot, marker=\"x\")\n",
    "ax.set_xlabel(\"\")\n",
    "ax.set_ylabel(\"\")\n",
    "ax.set_zlabel(\"\")\n",
    "ax.set_xticklabels([])\n",
    "ax.set_yticklabels([])\n",
    "ax.set_zticklabels([])\n",
    "\n",
    "save_fig(\"preimage_plot\", tight_layout=False)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 59504 characters]\n",
      "text/plain": [
       "<Figure size 792x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "X_reduced = rbf_pca.fit_transform(X)\n",
    "\n",
    "plt.figure(figsize=(11, 4))\n",
    "plt.subplot(132)\n",
    "plt.scatter(X_reduced[:, 0], X_reduced[:, 1], c=t, cmap=plt.cm.hot, marker=\"x\")\n",
    "plt.xlabel(\"$z_1$\", fontsize=18)\n",
    "plt.ylabel(\"$z_2$\", fontsize=18, rotation=0)\n",
    "plt.grid(True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "GridSearchCV(cv=3, error_score='raise',\n",
       "       estimator=Pipeline(memory=None,\n",
       "     steps=[('kpca', KernelPCA(alpha=1.0, coef0=1, copy_X=True, degree=3, eigen_solver='auto',\n",
       "     fit_inverse_transform=False, gamma=None, kernel='linear',\n",
       "     kernel_params=None, max_iter=None, n_components=2, n_jobs=1,\n",
       "     random_state=None, remove_zero_eig=False, tol=0)), ('log_reg', LogisticRegre...ty='l2', random_state=None, solver='liblinear', tol=0.0001,\n",
       "          verbose=0, warm_start=False))]),\n",
       "       fit_params=None, iid=True, n_jobs=1,\n",
       "       param_grid=[{'kpca__gamma': array([0.03   , 0.03222, 0.03444, 0.03667, 0.03889, 0.04111, 0.04333,\n",
       "       0.04556, 0.04778, 0.05   ]), 'kpca__kernel': ['rbf', 'sigmoid']}],\n",
       "       pre_dispatch='2*n_jobs', refit=True, return_train_score='warn',\n",
       "       scoring=None, verbose=0)"
      ]
     },
     "execution_count": 61,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from sklearn.model_selection import GridSearchCV\n",
    "from sklearn.linear_model import LogisticRegression\n",
    "from sklearn.pipeline import Pipeline\n",
    "\n",
    "clf = Pipeline([\n",
    "        (\"kpca\", KernelPCA(n_components=2)),\n",
    "        (\"log_reg\", LogisticRegression())\n",
    "    ])\n",
    "\n",
    "param_grid = [{\n",
    "        \"kpca__gamma\": np.linspace(0.03, 0.05, 10),\n",
    "        \"kpca__kernel\": [\"rbf\", \"sigmoid\"]\n",
    "    }]\n",
    "\n",
    "grid_search = GridSearchCV(clf, param_grid, cv=3)\n",
    "grid_search.fit(X, y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'kpca__gamma': 0.043333333333333335, 'kpca__kernel': 'rbf'}\n"
     ]
    }
   ],
   "source": [
    "print(grid_search.best_params_)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {},
   "outputs": [],
   "source": [
    "rbf_pca = KernelPCA(n_components = 2, kernel=\"rbf\", gamma=0.0433,\n",
    "                    fit_inverse_transform=True)\n",
    "X_reduced = rbf_pca.fit_transform(X)\n",
    "X_preimage = rbf_pca.inverse_transform(X_reduced)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "32.78630879576615"
      ]
     },
     "execution_count": 64,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from sklearn.metrics import mean_squared_error\n",
    "\n",
    "mean_squared_error(X, X_preimage)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# LLE"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {},
   "outputs": [],
   "source": [
    "X, t = make_swiss_roll(n_samples=1000, noise=0.2, random_state=41)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.manifold import LocallyLinearEmbedding\n",
    "\n",
    "lle = LocallyLinearEmbedding(n_components=2, n_neighbors=10, random_state=42)\n",
    "X_reduced = lle.fit_transform(X)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 57176 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.title(\"LLE를 사용하여 펼쳐진 스위스 롤\", fontsize=14)\n",
    "plt.scatter(X_reduced[:, 0], X_reduced[:, 1], c=t, cmap=plt.cm.hot)\n",
    "plt.xlabel(\"$z_1$\", fontsize=18)\n",
    "plt.ylabel(\"$z_2$\", fontsize=18)\n",
    "plt.axis([-0.065, 0.055, -0.1, 0.12])\n",
    "plt.grid(True)\n",
    "\n",
    "save_fig(\"lle_unrolling_plot\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# MDS, Isomap 그리고 t-SNE"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.manifold import MDS\n",
    "\n",
    "mds = MDS(n_components=2, random_state=42)\n",
    "X_reduced_mds = mds.fit_transform(X)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.manifold import Isomap\n",
    "\n",
    "isomap = Isomap(n_components=2)\n",
    "X_reduced_isomap = isomap.fit_transform(X)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.manifold import TSNE\n",
    "\n",
    "tsne = TSNE(n_components=2, random_state=42)\n",
    "X_reduced_tsne = tsne.fit_transform(X)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/haesun/anaconda3/envs/handson-ml/lib/python3.5/site-packages/sklearn/discriminant_analysis.py:388: UserWarning: Variables are collinear.\n",
      "  warnings.warn(\"Variables are collinear.\")\n"
     ]
    }
   ],
   "source": [
    "from sklearn.discriminant_analysis import LinearDiscriminantAnalysis\n",
    "\n",
    "lda = LinearDiscriminantAnalysis(n_components=2)\n",
    "X_mnist = mnist[\"data\"]\n",
    "y_mnist = mnist[\"target\"]\n",
    "lda.fit(X_mnist, y_mnist)\n",
    "X_reduced_lda = lda.transform(X_mnist)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 129200 characters]\n",
      "text/plain": [
       "<Figure size 792x288 with 3 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "titles = [\"MDS\", \"Isomap\", \"t-SNE\"]\n",
    "\n",
    "plt.figure(figsize=(11,4))\n",
    "\n",
    "for subplot, title, X_reduced in zip((131, 132, 133), titles,\n",
    "                                     (X_reduced_mds, X_reduced_isomap, X_reduced_tsne)):\n",
    "    plt.subplot(subplot)\n",
    "    plt.title(title, fontsize=14)\n",
    "    plt.scatter(X_reduced[:, 0], X_reduced[:, 1], c=t, cmap=plt.cm.hot)\n",
    "    plt.xlabel(\"$z_1$\", fontsize=18)\n",
    "    if subplot == 131:\n",
    "        plt.ylabel(\"$z_2$\", fontsize=18, rotation=0)\n",
    "    plt.grid(True)\n",
    "\n",
    "save_fig(\"other_dim_reduction_plot\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "# 연습문제 해답"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. to 8."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "부록 A 참조."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "## 9."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*문제: (3장에서 소개한) MNIST 데이터셋을 로드하고 훈련 세트와 테스트 세트로 분할합니다(처음 60,000개는 훈련을 위한 샘플이고 나머지 10,000개는 테스트용입니다).*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.datasets import fetch_mldata\n",
    "mnist = fetch_mldata('MNIST original')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_train = mnist['data'][:60000]\n",
    "y_train = mnist['target'][:60000]\n",
    "\n",
    "X_test = mnist['data'][60000:]\n",
    "y_test = mnist['target'][60000:]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*문제: 이 데이터셋에 랜덤 포레스트 분류기를 훈련시키고 얼마나 오래 걸리는지 시간을 잰 다음, 테스트 세트로 만들어진 모델을 평가합니다.*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.ensemble import RandomForestClassifier\n",
    "\n",
    "rnd_clf = RandomForestClassifier(random_state=42)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "metadata": {},
   "outputs": [],
   "source": [
    "import time\n",
    "\n",
    "t0 = time.time()\n",
    "rnd_clf.fit(X_train, y_train)\n",
    "t1 = time.time()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "훈련 시간: 2.86s\n"
     ]
    }
   ],
   "source": [
    "print(\"훈련 시간 {:.2f}s\".format(t1 - t0))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.9455"
      ]
     },
     "execution_count": 78,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from sklearn.metrics import accuracy_score\n",
    "\n",
    "y_pred = rnd_clf.predict(X_test)\n",
    "accuracy_score(y_test, y_pred)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*문제: 그런 다음 PCA를 사용해 설명된 분산이 95%가 되도록 차원을 축소합니다.*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.decomposition import PCA\n",
    "\n",
    "pca = PCA(n_components=0.95)\n",
    "X_train_reduced = pca.fit_transform(X_train)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*문제: 이 축소된 데이터셋에 새로운 랜덤 포레스트 분류기를 훈련시키고 얼마나 오래 걸리는지 확인합니다. 훈련 속도가 더 빨라졌나요?*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "metadata": {},
   "outputs": [],
   "source": [
    "rnd_clf2 = RandomForestClassifier(random_state=42)\n",
    "t0 = time.time()\n",
    "rnd_clf2.fit(X_train_reduced, y_train)\n",
    "t1 = time.time()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "훈련 시간: 7.25s\n"
     ]
    }
   ],
   "source": [
    "print(\"훈련 시간 {:.2f}s\".format(t1 - t0))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "이런! 훈련이 두 배 이상 느려졌습니다! 어떻게 이럴 수 있죠? 이 장에서 보았듯이 차원 축소는 언제나 훈련 시간을 줄여주지 못합니다. 데이터셋, 모델, 훈련 알고리즘에 따라 달라집니다. 그림 8-6을 참고하세요. 랜덤 포레스트 분류기 대신 소프트맥스 분류기를 적용하면 PCA를 사용해서 훈련 시간을 3배나 줄일 수 있습니다. 잠시 후에 실제로 한번 해보겠습니다. 하지만 먼저 새로운 랜덤 포레스트 분류기의 정밀도를 확인해 보죠."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*문제: 이제 테스트 세트에서 이 분류기를 평가해보세요. 이전 분류기와 비교해서 어떤가요?*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.8908"
      ]
     },
     "execution_count": 82,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X_test_reduced = pca.transform(X_test)\n",
    "\n",
    "y_pred = rnd_clf2.predict(X_test_reduced)\n",
    "accuracy_score(y_test, y_pred)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "차원 축소를 했을 때 유용한 정보를 일부 잃었기 때문에 성능이 조금 감소되는 것이 일반적입니다. 그렇지만 이 경우에는 성능 감소가 좀 심각한 것 같습니다. PCA가 별로 도움이 되지 않네요. 훈련 시간도 느려지고 성능도 감소했습니다. :(\n",
    "\n",
    "소프트맥스 회귀를 사용하면 도움이 되는지 확인해 보겠습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.linear_model import LogisticRegression\n",
    "\n",
    "log_clf = LogisticRegression(multi_class=\"multinomial\", solver=\"lbfgs\", random_state=42)\n",
    "t0 = time.time()\n",
    "log_clf.fit(X_train, y_train)\n",
    "t1 = time.time()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "훈련 시간: 7.63s\n"
     ]
    }
   ],
   "source": [
    "print(\"훈련 시간: {:.2f}s\".format(t1 - t0))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 85,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.9252"
      ]
     },
     "execution_count": 85,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y_pred = log_clf.predict(X_test)\n",
    "accuracy_score(y_test, y_pred)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "좋네요. 소프트맥스 회귀는 랜덤 포레스트 분류기보다 이 데이터셋에서 훈련하는데 더 많은 시간이 걸리고 테스트 세트에서의 성능도 더 나쁩니다. 하지만 지금 관심 사항은 아닙니다. PCA가 소프트맥스 회귀에 얼마나 도움이 되는지가 궁금합니다. 축소된 데이터셋에 소프트맥스 회귀 모델을 훈련시켜 보겠습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 86,
   "metadata": {},
   "outputs": [],
   "source": [
    "log_clf2 = LogisticRegression(multi_class=\"multinomial\", solver=\"lbfgs\", random_state=42)\n",
    "t0 = time.time()\n",
    "log_clf2.fit(X_train_reduced, y_train)\n",
    "t1 = time.time()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "훈련 시간: 3.81s\n"
     ]
    }
   ],
   "source": [
    "print(\"훈련 시간: {:.2f}s\".format(t1 - t0))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "와우! 차원 축소가 속도를 4배나 빠르게 만들었습니다. :) 모델의 정확도를 확인해 보겠습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.9198"
      ]
     },
     "execution_count": 88,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y_pred = log_clf2.predict(X_test_reduced)\n",
    "accuracy_score(y_test, y_pred)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "성능이 조금 감소되었지만 애플리케이션에 따라서 4배 속도 향상의 댓가로 적절한 것 같습니다."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "여기서 알 수 있는 것: PCA는 속도를 아주 빠르게 만들어 주지만 항상 그런 것은 아니다!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 10."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*문제: t-SNE 알고리즘을 사용해 MNIST 데이터셋을 2차원으로 축소시키고 맷플롯립으로 그래프를 그려보세요. 이미지의 타깃 클래스마다 10가지 색깔로 나타낸 산점도를 그릴 수 있습니다.*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "MNIST 데이터셋을 (다시) 로드합니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 89,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.datasets import fetch_mldata\n",
    "\n",
    "mnist = fetch_mldata('MNIST original')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "전체 60,000개의 이미지에 차원 축소를 하면 매우 오랜 시간이 걸리므로 10,000개의 이미지만 무작위로 선택하여 사용하겠습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 90,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.random.seed(42)\n",
    "\n",
    "m = 10000\n",
    "idx = np.random.permutation(60000)[:m]\n",
    "\n",
    "X = mnist['data'][idx]\n",
    "y = mnist['target'][idx]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "이제 t-SNE를 사용해 2D로 차원을 축소해 그래프로 나타냅니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.manifold import TSNE\n",
    "\n",
    "tsne = TSNE(n_components=2, random_state=42)\n",
    "X_reduced = tsne.fit_transform(X)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "산점도를 그리기 위해 맷플롯립의 `scatter()` 함수를 사용합니다. 각 숫자마다 다른 색깔을 사용합니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 92,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 180904 characters]\n",
      "text/plain": [
       "<Figure size 936x720 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(13,10))\n",
    "plt.scatter(X_reduced[:, 0], X_reduced[:, 1], c=y, cmap=\"jet\")\n",
    "plt.axis('off')\n",
    "plt.colorbar()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "아름답지 않나요? :) 이 그래프는 어떤 숫자가 다른 것과 구분이 쉬운지 알려 줍니다(가령, 0, 6 등이 잘 구분되어 있습니다). 그리고 어떤 숫자가 구분이 어려운지 알려 줍니다(가령, 4, 9입니다)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "많이 겹쳐진 것 같은 숫자 4과 9에 집중해 보겠습니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 93,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 27720 characters]\n",
      "text/plain": [
       "<Figure size 648x648 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(9,9))\n",
    "cmap = matplotlib.cm.get_cmap(\"jet\")\n",
    "for digit in (4, 9):\n",
    "    plt.scatter(X_reduced[y == digit, 0], X_reduced[y == digit, 1], c=cmap(digit / 9))\n",
    "plt.axis('off')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "이 두 개의 숫자에 t-SNE를 실행시켜 더 나은 이미지를 만들 수 있는지 보겠습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 94,
   "metadata": {},
   "outputs": [],
   "source": [
    "idx = (y == 4) | (y == 9)\n",
    "X_subset = X[idx]\n",
    "y_subset = y[idx]\n",
    "\n",
    "tsne_subset = TSNE(n_components=2, random_state=42)\n",
    "X_subset_reduced = tsne_subset.fit_transform(X_subset)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 95,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 65172 characters]\n",
      "text/plain": [
       "<Figure size 648x648 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(9,9))\n",
    "for digit in (4, 9):\n",
    "    plt.scatter(X_subset_reduced[y_subset == digit, 0], X_subset_reduced[y_subset == digit, 1], c=cmap(digit / 9))\n",
    "plt.axis('off')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "훨씬 좋네요. 이제 군집이 덜 겹쳐졌습니다. 하지만 숫자 4가 두 개의 군집으로 나뉘어져 있습니다. 각 군집에 숫자를 몇 개씩 나타내면 훨씬 이해하는기 좋을 것 같습니다. 그렇게 한번 해보죠."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "*문제: 또는 샘플의 위치에 각기 다른 색깔의 숫자를 나타낼 수도 있고, 숫자 이미지 자체의 크기를 줄여서 그릴 수도 있습니다(모든 숫자를 다 그리면 그래프가 너무 복잡해지므로 무작위로 선택한 샘플만 그리거나, 인접한 곳에 다른 샘플이 그려져 있지 않은 경우에만 그립니다). 잘 분리된 숫자의 군집을 시각화할 수 있을 것입니다.*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "산점도와 색깔있는 숫자를 쓰기위해 `plot_digits()` 함수를 만듭니다. 이 숫자 사이의 거리가 최소가 되도록 합니다. 숫자 이미지가 있다면 대신 이를 사용합니다. 이 코드는 사이킷런의 훌륭한 데모([plot_lle_digits](http://scikit-learn.org/stable/auto_examples/manifold/plot_lle_digits.html), 데이터셋은 다릅니다)를 참고했습니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 96,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.preprocessing import MinMaxScaler\n",
    "from matplotlib.offsetbox import AnnotationBbox, OffsetImage\n",
    "\n",
    "def plot_digits(X, y, min_distance=0.05, images=None, figsize=(13, 10)):\n",
    "    # 입력 특성의 스케일을 0에서 1 사이로 만듭니다.\n",
    "    X_normalized = MinMaxScaler().fit_transform(X)\n",
    "    # 그릴 숫자의 좌표 목록을 만듭니다.\n",
    "    # 반복문 아래에서 `if` 문장을 쓰지 않기 위해 시작할 때 이미 그래프가 그려져 있다고 가정합니다.\n",
    "    neighbors = np.array([[10., 10.]])\n",
    "    # 나머지는 이해하기 쉽습니다.\n",
    "    plt.figure(figsize=figsize)\n",
    "    cmap = matplotlib.cm.get_cmap(\"jet\")\n",
    "    digits = np.unique(y)\n",
    "    for digit in digits:\n",
    "        plt.scatter(X_normalized[y == digit, 0], X_normalized[y == digit, 1], c=cmap(digit / 9))\n",
    "    plt.axis(\"off\")\n",
    "    ax = plt.gcf().gca()  # 현재 그래프의 축을 가져옵니다.\n",
    "    for index, image_coord in enumerate(X_normalized):\n",
    "        closest_distance = np.linalg.norm(np.array(neighbors) - image_coord, axis=1).min()\n",
    "        if closest_distance > min_distance:\n",
    "            neighbors = np.r_[neighbors, [image_coord]]\n",
    "            if images is None:\n",
    "                plt.text(image_coord[0], image_coord[1], str(int(y[index])),\n",
    "                         color=cmap(y[index] / 9), fontdict={\"weight\": \"bold\", \"size\": 16})\n",
    "            else:\n",
    "                image = images[index].reshape(28, 28)\n",
    "                imagebox = AnnotationBbox(OffsetImage(image, cmap=\"binary\"), image_coord)\n",
    "                ax.add_artist(imagebox)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "시작해 보죠! 먼저 색깔이 입혀진 숫자를 써 보겠습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 97,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 151252 characters]\n",
      "text/plain": [
       "<Figure size 936x720 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_digits(X_reduced, y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "꽤 좋습니다. 하지만 아름답지는 않네요. 숫자 이미지를 사용해 보겠습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 98,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 486472 characters]\n",
      "text/plain": [
       "<Figure size 2520x1800 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_digits(X_reduced, y, images=X, figsize=(35, 25))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 99,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 216236 characters]\n",
      "text/plain": [
       "<Figure size 1584x1584 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_digits(X_subset_reduced, y_subset, images=X_subset, figsize=(22, 22))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*문제: PCA, LLE, MDS 같은 차원 축소 알고리즘을 적용해보고 시각화 결과를 비교해보세요.*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "PCA부터 시작해 보죠. 얼마나 오래 걸리는지도 재어 보겠습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 100,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "PCA 시간: 0.1s.\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 260672 characters]\n",
      "text/plain": [
       "<Figure size 936x720 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from sklearn.decomposition import PCA\n",
    "import time\n",
    "\n",
    "t0 = time.time()\n",
    "X_pca_reduced = PCA(n_components=2, random_state=42).fit_transform(X)\n",
    "t1 = time.time()\n",
    "print(\"PCA 시간: {:.1f}s.\".format(t1 - t0))\n",
    "plot_digits(X_pca_reduced, y)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "와우, PCA가 아주 빠르네요! 몇 개의 군집이 보이지만 너무 겹쳐져 있습니다. LLE를 사용해 보죠:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 101,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "LLE 시간: 124.9s.\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 71876 characters]\n",
      "text/plain": [
       "<Figure size 936x720 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from sklearn.manifold import LocallyLinearEmbedding\n",
    "\n",
    "t0 = time.time()\n",
    "X_lle_reduced = LocallyLinearEmbedding(n_components=2, random_state=42).fit_transform(X)\n",
    "t1 = time.time()\n",
    "print(\"LLE 시간: {:.1f}s.\".format(t1 - t0))\n",
    "plot_digits(X_lle_reduced, y)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "시간이 좀 걸리고 결과도 아주 좋지는 않습니다. 분산의 95%를 보존하도록 먼저 PCA를 적용하면 어떻게 되는지 보겠습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 102,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "PCA+LLE 시간: 31.3s.\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 65904 characters]\n",
      "text/plain": [
       "<Figure size 936x720 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from sklearn.pipeline import Pipeline\n",
    "\n",
    "pca_lle = Pipeline([\n",
    "    (\"pca\", PCA(n_components=0.95, random_state=42)),\n",
    "    (\"lle\", LocallyLinearEmbedding(n_components=2, random_state=42)),\n",
    "])\n",
    "t0 = time.time()\n",
    "X_pca_lle_reduced = pca_lle.fit_transform(X)\n",
    "t1 = time.time()\n",
    "print(\"PCA+LLE 시간: {:.1f}s.\".format(t1 - t0))\n",
    "plot_digits(X_pca_lle_reduced, y)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "결과는 비슷하지만 걸린 시간은 4배나 줄었습니다."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "MDS를 시도해 보죠. 10,000개 샘플을 적용하면 이 알고리즘은 너무 오래걸리므로 2,000개만 시도해 보겠습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 103,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "MDS 시간: 57.7s (10,000개가 아니고 2,000 MNIST 이미지에서).\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 147400 characters]\n",
      "text/plain": [
       "<Figure size 936x720 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from sklearn.manifold import MDS\n",
    "\n",
    "m = 2000\n",
    "t0 = time.time()\n",
    "X_mds_reduced = MDS(n_components=2, random_state=42).fit_transform(X[:m])\n",
    "t1 = time.time()\n",
    "print(\"MDS 시간: {:.1f}s (10,000개가 아니고 2,000 MNIST 이미지에서).\".format(t1 - t0))\n",
    "plot_digits(X_mds_reduced, y[:m])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "아 이건 좋지 않아 보이네요. 모든 군집이 너무 중복되어 있습니다. 먼저 PCA를 적용하면 빨라질까요?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 104,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "PCA+MDS 시간: 58.2s (on 2,000 MNIST images).\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 147172 characters]\n",
      "text/plain": [
       "<Figure size 936x720 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from sklearn.pipeline import Pipeline\n",
    "\n",
    "pca_mds = Pipeline([\n",
    "    (\"pca\", PCA(n_components=0.95, random_state=42)),\n",
    "    (\"mds\", MDS(n_components=2, random_state=42)),\n",
    "])\n",
    "t0 = time.time()\n",
    "X_pca_mds_reduced = pca_mds.fit_transform(X[:2000])\n",
    "t1 = time.time()\n",
    "print(\"PCA+MDS 시간: {:.1f}s (on 2,000 MNIST images).\".format(t1 - t0))\n",
    "plot_digits(X_pca_mds_reduced, y[:2000])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "같은 결과에 속도도 동일합니다. PCA가 도움이 되지 않네요."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "LDA를 시도해 보죠:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 105,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/haesun/anaconda3/envs/handson-ml/lib/python3.5/site-packages/sklearn/discriminant_analysis.py:388: UserWarning: Variables are collinear.\n",
      "  warnings.warn(\"Variables are collinear.\")\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "LDA 시간: 0.6s.\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 192272 characters]\n",
      "text/plain": [
       "<Figure size 864x864 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from sklearn.discriminant_analysis import LinearDiscriminantAnalysis\n",
    "\n",
    "t0 = time.time()\n",
    "X_lda_reduced = LinearDiscriminantAnalysis(n_components=2).fit_transform(X, y)\n",
    "t1 = time.time()\n",
    "print(\"LDA 시간: {:.1f}s.\".format(t1 - t0))\n",
    "plot_digits(X_lda_reduced, y, figsize=(12,12))\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "매우 빨라 처음엔 괜찮아 보이지만 자세히 보면 몇 개의 군집이 심각하게 중복되어 있습니다."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "아마 이 비교에서 t-SNE가 승자같네요. 시간을 재어 보진 않았으니 한번 해보죠:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 106,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "t-SNE 시간: 264.1s.\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 151252 characters]\n",
      "text/plain": [
       "<Figure size 936x720 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from sklearn.manifold import TSNE\n",
    "\n",
    "t0 = time.time()\n",
    "X_tsne_reduced = TSNE(n_components=2, random_state=42).fit_transform(X)\n",
    "t1 = time.time()\n",
    "print(\"t-SNE 시간: {:.1f}s.\".format(t1 - t0))\n",
    "plot_digits(X_tsne_reduced, y)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "LLE 보다 두 배나 느립니다. 하지만 MDS 보단 훨씬 빠르고 결과물도 아주 좋습니다. PCA가 속도를 높여줄 수 있는지 확인해 보겠습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 107,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "PCA+t-SNE 시간 173.6s.\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 159048 characters]\n",
      "text/plain": [
       "<Figure size 936x720 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "pca_tsne = Pipeline([\n",
    "    (\"pca\", PCA(n_components=0.95, random_state=42)),\n",
    "    (\"tsne\", TSNE(n_components=2, random_state=42)),\n",
    "])\n",
    "t0 = time.time()\n",
    "X_pca_tsne_reduced = pca_tsne.fit_transform(X)\n",
    "t1 = time.time()\n",
    "print(\"PCA+t-SNE 시간 {:.1f}s.\".format(t1 - t0))\n",
    "plot_digits(X_pca_tsne_reduced, y)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "네, 결과물에 영향을 미치지 않으면서 PCA 속도가 34% 정도 향상되었습니다. t-SNE가 제일 좋네요!"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.5"
  },
  "nav_menu": {
   "height": "352px",
   "width": "458px"
  },
  "toc": {
   "navigate_menu": true,
   "number_sections": true,
   "sideBar": true,
   "threshold": 6,
   "toc_cell": false,
   "toc_section_display": "block",
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
