{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "0e10e872",
   "metadata": {},
   "source": [
    "**⬅️ 이전 시간: [Part 5.5: NumPy로 배우는 선형대수학](../05.5_linear_algebra_with_numpy/part_5.5_linear_algebra_with_numpy.md)**\n",
    "**➡️ 다음 시간: [Part 6: 머신러닝 모델링과 평가](../06_machine_learning/part_6_machine_learning.md)**\n",
    "\n",
    "---\n",
    "\n",
    "# Part 5.6: 머신러닝/딥러닝을 위한 미적분 - 직관적 이해 중심\n",
    "\n",
    "**⬅️ 이전 시간: [Part 5.5: NumPy로 배우는 선형대수학](../05.5_linear_algebra_with_numpy/part_5.5_linear_algebra_with_numpy.md)**\n",
    "**➡️ 다음 시간: [Part 6: 머신러닝 모델링과 평가](../06_machine_learning/part_6_machine_learning.md)**\n",
    "\n",
    "---\n",
    "\n",
    "## 🗺️ 학습 로드맵: 선형대수 + 미적분 = 머신러닝의 완성\n",
    "\n",
    "### 🎯 이번 파트의 목적\n",
    "선형대수를 통해 **\"무엇을 최적화할 것인가?\"**를 배웠다면, 이제 미적분을 통해 **\"어떻게 최적화할 것인가?\"**를 배워보겠습니다. 기울기 하강법은 모든 머신러닝 모델의 핵심입니다.\n",
    "\n",
    "### 🔗 이전 파트와의 연결점\n",
    "- **Part 5.5 (Linear Algebra)**: 벡터와 행렬로 데이터를 표현하고 변환하는 방법을 배웠습니다\n",
    "- **이번 파트 (Calculus)**: 그 변환된 데이터를 **최적화**하는 방법을 배웁니다\n",
    "- **다음 파트 (Machine Learning)**: 선형대수 + 미적분을 결합하여 실제 AI 모델을 만듭니다\n",
    "\n",
    "### 🚀 기울기 하강법과 미적분의 연결점\n",
    "- **미분**: 함수의 기울기를 계산하여 어느 방향으로 가야 할지 결정\n",
    "- **기울기 하강법**: 미분값을 이용해 최적의 파라미터를 찾아가는 과정\n",
    "- **편미분**: 여러 변수가 있을 때 각각의 기울기를 계산\n",
    "- **연쇄법칙**: 신경망에서 오차를 뒤로 전파하는 핵심 원리\n",
    "\n",
    "### 📈 학습 진행도\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8e32755c",
   "metadata": {},
   "outputs": [],
   "source": [
    "Part 5: AI Core Libraries ✅\n",
    "Part 5.5: Linear Algebra ✅\n",
    "Part 5.6: Calculus 🔄 (현재)\n",
    "Part 6: Machine Learning ⏳"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "64e10f14",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 🎯 이제 할 수 있는 것들\n",
    "- **신경망 가중치 최적화**: 기울기 하강법으로 모델 학습\n",
    "- **함수 최적화**: 복잡한 함수의 최솟값/최댓값 찾기\n",
    "- **변화율 분석**: 데이터의 변화 패턴 분석\n",
    "- **자동 미분**: PyTorch, TensorFlow의 핵심 기능 이해\n",
    "\n",
    "---\n",
    "\n",
    "<br>\n",
    "\n",
    "> ## 🎯 학습 목표 (Learning Objectives)\n",
    ">\n",
    "> 이번 파트가 끝나면, 여러분은 다음을 할 수 있게 됩니다.\n",
    "> \n",
    "> - **직관적으로 이해하기**: 복잡한 수식보다는 시각화와 비유를 통해 미적분의 핵심 개념을 직관적으로 이해할 수 있습니다.\n",
    "> - **실습 중심 학습**: Python을 사용하여 미분과 기울기 하강법을 직접 구현하고 시각화할 수 있습니다.\n",
    "> - **실제 활용**: 머신러닝에서 미적분이 어떻게 활용되는지 구체적인 예제를 통해 이해할 수 있습니다.\n",
    "\n",
    "<br>\n",
    "\n",
    "> ## 🔑 핵심 접근법 (Key Approach)\n",
    "> \n",
    "> **\"왜 이걸 배워야 하는가?\"** 에 집중합니다.\n",
    "> - 미분 = \"순간 변화율\", \"기울기\"\n",
    "> - 기울기 하강법 = \"산에서 내려오는 사람\"\n",
    "> - 편미분 = \"여러 변수 중 하나만 바꾸기\"\n",
    "> - 연쇄법칙 = \"파이프라인을 통해 변화 전파\"\n",
    "\n",
    "<br>\n",
    "\n",
    "> ## 🛠️ 사용 도구 (Tools)\n",
    "> - **Python + NumPy**: 실제 계산과 구현\n",
    "> - **Matplotlib**: 시각화로 직관적 이해\n",
    "> - **Jupyter Notebook**: 대화형 학습\n",
    "> - **실제 예제**: 선형 회귀, 신경망 등\n",
    "\n",
    "---\n",
    "\n",
    "<br>\n",
    "\n",
    "## 1. 미분: 순간 변화율과 기울기 📈\n",
    "\n",
    "### 1-1. 미분이란 무엇인가? (직관적 이해)\n",
    "\n",
    "> **💡 비유**: 미분은 \"순간 변화율\"입니다!\n",
    "> - **기울기**: 곡선의 특정 점에서 접선의 기울기\n",
    "> - **변화율**: 아주 짧은 순간에 얼마나 빨리 변하는지\n",
    "> - **방향**: 어느 방향으로 변하고 있는지\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c08d4d51",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# 미분의 직관적 이해: 기울기 시각화\n",
    "def plot_function_with_tangent(x, y, tangent_x, tangent_y, slope, title):\n",
    "    \"\"\"함수와 접선을 시각화\"\"\"\n",
    "    plt.figure(figsize=(10, 6))\n",
    "    \n",
    "    # 함수 그리기\n",
    "    plt.plot(x, y, 'b-', linewidth=2, label='함수 f(x)')\n",
    "    \n",
    "    # 접선 그리기\n",
    "    plt.plot(tangent_x, tangent_y, 'r--', linewidth=2, label=f'접선 (기울기: {slope:.2f})')\n",
    "    \n",
    "    # 접점 표시\n",
    "    plt.scatter(tangent_x[len(tangent_x)//2], tangent_y[len(tangent_y)//2], \n",
    "               color='red', s=100, zorder=5, label='접점')\n",
    "    \n",
    "    plt.grid(True, alpha=0.3)\n",
    "    plt.xlabel('x')\n",
    "    plt.ylabel('y')\n",
    "    plt.title(title)\n",
    "    plt.legend()\n",
    "    plt.show()\n",
    "\n",
    "# 예제: f(x) = x² 함수의 미분\n",
    "x = np.linspace(-3, 3, 100)\n",
    "y = x**2\n",
    "\n",
    "# x = 1에서의 접선\n",
    "x0 = 1\n",
    "y0 = x0**2\n",
    "slope = 2 * x0  # f'(x) = 2x\n",
    "\n",
    "# 접선 그리기\n",
    "tangent_x = np.linspace(x0 - 1, x0 + 1, 50)\n",
    "tangent_y = slope * (tangent_x - x0) + y0\n",
    "\n",
    "plot_function_with_tangent(x, y, tangent_x, tangent_y, slope, \n",
    "                          f\"f(x) = x²의 미분: x = {x0}에서 기울기 = {slope}\")\n",
    "\n",
    "print(f\"f(x) = x²\")\n",
    "print(f\"f'({x0}) = {slope}\")\n",
    "print(f\"x = {x0}에서 함수가 {slope}만큼 빠르게 증가하고 있습니다!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1d354a84",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 1-2. 미분의 기하학적 의미\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a144ba49",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 여러 점에서의 기울기 시각화\n",
    "def visualize_derivatives():\n",
    "    \"\"\"여러 점에서의 미분값을 시각화\"\"\"\n",
    "    x = np.linspace(-3, 3, 100)\n",
    "    y = x**2\n",
    "    \n",
    "    # 여러 점에서의 접선\n",
    "    points = [-2, -1, 0, 1, 2]\n",
    "    \n",
    "    plt.figure(figsize=(12, 8))\n",
    "    plt.plot(x, y, 'b-', linewidth=3, label='f(x) = x²')\n",
    "    \n",
    "    colors = ['red', 'orange', 'green', 'blue', 'purple']\n",
    "    \n",
    "    for i, x0 in enumerate(points):\n",
    "        y0 = x0**2\n",
    "        slope = 2 * x0\n",
    "        \n",
    "        # 접선 그리기\n",
    "        tangent_x = np.linspace(x0 - 0.5, x0 + 0.5, 20)\n",
    "        tangent_y = slope * (tangent_x - x0) + y0\n",
    "        \n",
    "        plt.plot(tangent_x, tangent_y, '--', color=colors[i], linewidth=2,\n",
    "                label=f'x = {x0}, 기울기 = {slope}')\n",
    "        plt.scatter(x0, y0, color=colors[i], s=100, zorder=5)\n",
    "    \n",
    "    plt.grid(True, alpha=0.3)\n",
    "    plt.xlabel('x')\n",
    "    plt.ylabel('y')\n",
    "    plt.title('f(x) = x²의 여러 점에서의 기울기')\n",
    "    plt.legend()\n",
    "    plt.show()\n",
    "    \n",
    "    print(\"미분의 직관적 의미:\")\n",
    "    print(\"- 음수 기울기: 함수가 감소하고 있음\")\n",
    "    print(\"- 0 기울기: 함수가 변하지 않음 (극점)\")\n",
    "    print(\"- 양수 기울기: 함수가 증가하고 있음\")\n",
    "\n",
    "visualize_derivatives()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "522a1c3b",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 1-3. 수치 미분 구현하기\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ff83a5c4",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 수치 미분의 직관적 이해\n",
    "def numerical_derivative(func, x, h=1e-4):\n",
    "    \"\"\"수치 미분: 미분의 정의를 이용한 근사\"\"\"\n",
    "    return (func(x + h) - func(x - h)) / (2 * h)\n",
    "\n",
    "def compare_numerical_analytical():\n",
    "    \"\"\"수치 미분과 해석적 미분 비교\"\"\"\n",
    "    # 테스트 함수들\n",
    "    functions = {\n",
    "        'f(x) = x²': (lambda x: x**2, lambda x: 2*x),\n",
    "        'f(x) = x³': (lambda x: x**3, lambda x: 3*x**2),\n",
    "        'f(x) = sin(x)': (lambda x: np.sin(x), lambda x: np.cos(x))\n",
    "    }\n",
    "    \n",
    "    x_test = 1.0\n",
    "    \n",
    "    print(\"수치 미분 vs 해석적 미분 비교:\")\n",
    "    print(\"-\" * 50)\n",
    "    \n",
    "    for name, (func, derivative) in functions.items():\n",
    "        numerical = numerical_derivative(func, x_test)\n",
    "        analytical = derivative(x_test)\n",
    "        \n",
    "        print(f\"{name}:\")\n",
    "        print(f\"  수치 미분: f'({x_test}) ≈ {numerical:.6f}\")\n",
    "        print(f\"  해석적 미분: f'({x_test}) = {analytical:.6f}\")\n",
    "        print(f\"  오차: {abs(numerical - analytical):.2e}\")\n",
    "        print()\n",
    "\n",
    "compare_numerical_analytical()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "543c9a88",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "---\n",
    "\n",
    "<br>\n",
    "\n",
    "## 2. 기울기 하강법: 산에서 내려오는 사람 🏔️\n",
    "\n",
    "### 2-1. 기울기 하강법의 직관적 이해\n",
    "\n",
    "> **💡 비유**: 기울기 하강법은 \"산에서 내려오는 사람\"입니다!\n",
    "> - **현재 위치**: 함수의 특정 점\n",
    "> - **기울기**: 어느 방향이 내리막인지\n",
    "> - **한 걸음**: 학습률만큼 이동\n",
    "> - **목표**: 가장 낮은 지점(최솟값) 찾기\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "34fe76b8",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 기울기 하강법 시각화\n",
    "def gradient_descent_visualization():\n",
    "    \"\"\"기울기 하강법의 과정을 시각화\"\"\"\n",
    "    # 목표 함수: f(x) = x² + 2\n",
    "    def f(x):\n",
    "        return x**2 + 2\n",
    "    \n",
    "    def df(x):\n",
    "        return 2*x\n",
    "    \n",
    "    # 기울기 하강법\n",
    "    x = 3.0  # 시작점\n",
    "    learning_rate = 0.1\n",
    "    steps = 20\n",
    "    \n",
    "    x_history = [x]\n",
    "    y_history = [f(x)]\n",
    "    \n",
    "    for i in range(steps):\n",
    "        gradient = df(x)\n",
    "        x = x - learning_rate * gradient\n",
    "        x_history.append(x)\n",
    "        y_history.append(f(x))\n",
    "    \n",
    "    # 시각화\n",
    "    x_plot = np.linspace(-4, 4, 100)\n",
    "    y_plot = f(x_plot)\n",
    "    \n",
    "    plt.figure(figsize=(12, 8))\n",
    "    \n",
    "    # 함수 그리기\n",
    "    plt.plot(x_plot, y_plot, 'b-', linewidth=2, label='f(x) = x² + 2')\n",
    "    \n",
    "    # 기울기 하강 과정\n",
    "    plt.plot(x_history, y_history, 'ro-', linewidth=2, markersize=8, label='기울기 하강 경로')\n",
    "    \n",
    "    # 화살표로 이동 방향 표시\n",
    "    for i in range(len(x_history) - 1):\n",
    "        dx = x_history[i+1] - x_history[i]\n",
    "        dy = y_history[i+1] - y_history[i]\n",
    "        plt.arrow(x_history[i], y_history[i], dx, dy, \n",
    "                 head_width=0.05, head_length=0.1, fc='red', ec='red', alpha=0.7)\n",
    "    \n",
    "    plt.grid(True, alpha=0.3)\n",
    "    plt.xlabel('x')\n",
    "    plt.ylabel('f(x)')\n",
    "    plt.title('기울기 하강법: 산에서 내려오는 과정')\n",
    "    plt.legend()\n",
    "    plt.show()\n",
    "    \n",
    "    print(\"기울기 하강법 과정:\")\n",
    "    for i, (x_val, y_val) in enumerate(zip(x_history, y_history)):\n",
    "        print(f\"단계 {i}: x = {x_val:.4f}, f(x) = {y_val:.4f}\")\n",
    "\n",
    "gradient_descent_visualization()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e58be0cf",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 2-2. 학습률의 중요성\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dfbf650b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 학습률에 따른 기울기 하강법 비교\n",
    "def compare_learning_rates():\n",
    "    \"\"\"다양한 학습률로 기울기 하강법 비교\"\"\"\n",
    "    def f(x):\n",
    "        return x**2 + 2\n",
    "    \n",
    "    def df(x):\n",
    "        return 2*x\n",
    "    \n",
    "    learning_rates = [0.01, 0.1, 0.5, 1.0]\n",
    "    colors = ['blue', 'green', 'orange', 'red']\n",
    "    \n",
    "    plt.figure(figsize=(15, 10))\n",
    "    \n",
    "    for i, lr in enumerate(learning_rates):\n",
    "        # 기울기 하강법\n",
    "        x = 3.0\n",
    "        steps = 20\n",
    "        x_history = [x]\n",
    "        y_history = [f(x)]\n",
    "        \n",
    "        for step in range(steps):\n",
    "            gradient = df(x)\n",
    "            x = x - lr * gradient\n",
    "            x_history.append(x)\n",
    "            y_history.append(f(x))\n",
    "        \n",
    "        # 경로 그리기\n",
    "        plt.subplot(2, 2, i+1)\n",
    "        x_plot = np.linspace(-4, 4, 100)\n",
    "        y_plot = f(x_plot)\n",
    "        \n",
    "        plt.plot(x_plot, y_plot, 'b-', linewidth=2, label='f(x) = x² + 2')\n",
    "        plt.plot(x_history, y_history, 'o-', color=colors[i], linewidth=2, \n",
    "                markersize=6, label=f'학습률 = {lr}')\n",
    "        \n",
    "        plt.grid(True, alpha=0.3)\n",
    "        plt.xlabel('x')\n",
    "        plt.ylabel('f(x)')\n",
    "        plt.title(f'학습률 = {lr}')\n",
    "        plt.legend()\n",
    "    \n",
    "    plt.tight_layout()\n",
    "    plt.show()\n",
    "    \n",
    "    print(\"학습률의 영향:\")\n",
    "    print(\"- 너무 작음 (0.01): 천천히 수렴\")\n",
    "    print(\"- 적당함 (0.1): 빠르게 수렴\")\n",
    "    print(\"- 너무 큼 (0.5, 1.0): 진동하거나 발산\")\n",
    "\n",
    "compare_learning_rates()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8fc38007",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "---\n",
    "\n",
    "<br>\n",
    "\n",
    "## 3. 편미분: 여러 변수 중 하나만 바꾸기 🔧\n",
    "\n",
    "### 3-1. 편미분의 직관적 이해\n",
    "\n",
    "> **💡 비유**: 편미분은 \"여러 변수 중 하나만 바꾸기\"입니다!\n",
    "> - **다변수 함수**: 여러 입력을 받는 함수\n",
    "> - **편미분**: 한 변수만 조금 바꿨을 때 함수가 얼마나 변하는지\n",
    "> - **그래디언트**: 모든 변수에 대한 편미분을 모은 벡터\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fc4bd1f0",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 편미분의 시각화\n",
    "def visualize_partial_derivatives():\n",
    "    \"\"\"2변수 함수의 편미분 시각화\"\"\"\n",
    "    # 예제 함수: f(x, y) = x² + y²\n",
    "    def f(x, y):\n",
    "        return x**2 + y**2\n",
    "    \n",
    "    def df_dx(x, y):\n",
    "        return 2*x\n",
    "    \n",
    "    def df_dy(x, y):\n",
    "        return 2*y\n",
    "    \n",
    "    # 3D 시각화\n",
    "    from mpl_toolkits.mplot3d import Axes3D\n",
    "    \n",
    "    x = np.linspace(-3, 3, 50)\n",
    "    y = np.linspace(-3, 3, 50)\n",
    "    X, Y = np.meshgrid(x, y)\n",
    "    Z = f(X, Y)\n",
    "    \n",
    "    fig = plt.figure(figsize=(15, 5))\n",
    "    \n",
    "    # 3D 표면\n",
    "    ax1 = fig.add_subplot(131, projection='3d')\n",
    "    surf = ax1.plot_surface(X, Y, Z, cmap='viridis', alpha=0.8)\n",
    "    ax1.set_title('f(x, y) = x² + y²')\n",
    "    ax1.set_xlabel('x')\n",
    "    ax1.set_ylabel('y')\n",
    "    ax1.set_zlabel('f(x, y)')\n",
    "    \n",
    "    # x에 대한 편미분 (y = 1에서의 단면)\n",
    "    ax2 = fig.add_subplot(132)\n",
    "    y_fixed = 1\n",
    "    x_slice = np.linspace(-3, 3, 100)\n",
    "    z_slice = f(x_slice, y_fixed)\n",
    "    ax2.plot(x_slice, z_slice, 'b-', linewidth=2, label=f'f(x, {y_fixed})')\n",
    "    \n",
    "    # 접선 그리기 (x = 1에서)\n",
    "    x0 = 1\n",
    "    z0 = f(x0, y_fixed)\n",
    "    slope = df_dx(x0, y_fixed)\n",
    "    tangent_x = np.linspace(x0 - 1, x0 + 1, 20)\n",
    "    tangent_z = slope * (tangent_x - x0) + z0\n",
    "    ax2.plot(tangent_x, tangent_z, 'r--', linewidth=2, label=f'∂f/∂x = {slope}')\n",
    "    \n",
    "    ax2.scatter(x0, z0, color='red', s=100)\n",
    "    ax2.grid(True, alpha=0.3)\n",
    "    ax2.set_xlabel('x')\n",
    "    ax2.set_ylabel('f(x, y)')\n",
    "    ax2.set_title(f'∂f/∂x (y = {y_fixed})')\n",
    "    ax2.legend()\n",
    "    \n",
    "    # y에 대한 편미분 (x = 1에서의 단면)\n",
    "    ax3 = fig.add_subplot(133)\n",
    "    x_fixed = 1\n",
    "    y_slice = np.linspace(-3, 3, 100)\n",
    "    z_slice = f(x_fixed, y_slice)\n",
    "    ax3.plot(y_slice, z_slice, 'g-', linewidth=2, label=f'f({x_fixed}, y)')\n",
    "    \n",
    "    # 접선 그리기 (y = 1에서)\n",
    "    y0 = 1\n",
    "    z0 = f(x_fixed, y0)\n",
    "    slope = df_dy(x_fixed, y0)\n",
    "    tangent_y = np.linspace(y0 - 1, y0 + 1, 20)\n",
    "    tangent_z = slope * (tangent_y - y0) + z0\n",
    "    ax3.plot(tangent_y, tangent_z, 'r--', linewidth=2, label=f'∂f/∂y = {slope}')\n",
    "    \n",
    "    ax3.scatter(y0, z0, color='red', s=100)\n",
    "    ax3.grid(True, alpha=0.3)\n",
    "    ax3.set_xlabel('y')\n",
    "    ax3.set_ylabel('f(x, y)')\n",
    "    ax3.set_title(f'∂f/∂y (x = {x_fixed})')\n",
    "    ax3.legend()\n",
    "    \n",
    "    plt.tight_layout()\n",
    "    plt.show()\n",
    "    \n",
    "    print(\"편미분의 직관적 의미:\")\n",
    "    print(f\"∂f/∂x = {df_dx(1, 1)}: x를 조금 바꾸면 함수가 {df_dx(1, 1)}배만큼 변함\")\n",
    "    print(f\"∂f/∂y = {df_dy(1, 1)}: y를 조금 바꾸면 함수가 {df_dy(1, 1)}배만큼 변함\")\n",
    "\n",
    "visualize_partial_derivatives()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5c9d574d",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 3-2. 그래디언트 벡터 시각화\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7a31fd92",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 그래디언트 벡터의 시각화\n",
    "def visualize_gradient():\n",
    "    \"\"\"그래디언트 벡터를 시각화\"\"\"\n",
    "    def f(x, y):\n",
    "        return x**2 + y**2\n",
    "    \n",
    "    def gradient(x, y):\n",
    "        return np.array([2*x, 2*y])\n",
    "    \n",
    "    # 등고선 그리기\n",
    "    x = np.linspace(-3, 3, 50)\n",
    "    y = np.linspace(-3, 3, 50)\n",
    "    X, Y = np.meshgrid(x, y)\n",
    "    Z = f(X, Y)\n",
    "    \n",
    "    plt.figure(figsize=(10, 8))\n",
    "    \n",
    "    # 등고선\n",
    "    contours = plt.contour(X, Y, Z, levels=10, colors='blue', alpha=0.6)\n",
    "    plt.clabel(contours, inline=True, fontsize=8)\n",
    "    \n",
    "    # 그래디언트 벡터들\n",
    "    x_points = np.linspace(-2, 2, 8)\n",
    "    y_points = np.linspace(-2, 2, 8)\n",
    "    \n",
    "    for i, x_val in enumerate(x_points):\n",
    "        for j, y_val in enumerate(y_points):\n",
    "            grad = gradient(x_val, y_val)\n",
    "            # 벡터 크기 정규화\n",
    "            grad_norm = grad / np.linalg.norm(grad) * 0.3\n",
    "            plt.quiver(x_val, y_val, grad_norm[0], grad_norm[1], \n",
    "                      color='red', alpha=0.7, scale=20)\n",
    "    \n",
    "    plt.xlabel('x')\n",
    "    plt.ylabel('y')\n",
    "    plt.title('그래디언트 벡터: 가장 가파르게 증가하는 방향')\n",
    "    plt.grid(True, alpha=0.3)\n",
    "    plt.axis('equal')\n",
    "    plt.show()\n",
    "    \n",
    "    print(\"그래디언트의 의미:\")\n",
    "    print(\"- 방향: 함수가 가장 가파르게 증가하는 방향\")\n",
    "    print(\"- 크기: 그 방향으로의 증가율\")\n",
    "    print(\"- 기울기 하강법: 그래디언트의 반대 방향으로 이동\")\n",
    "\n",
    "visualize_gradient()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9312810a",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "---\n",
    "\n",
    "<br>\n",
    "\n",
    "## 4. 연쇄법칙: 파이프라인을 통해 변화 전파 🔗\n",
    "\n",
    "### 4-1. 연쇄법칙의 직관적 이해\n",
    "\n",
    "> **💡 비유**: 연쇄법칙은 \"파이프라인을 통해 변화 전파\"입니다!\n",
    "> - **합성 함수**: 여러 함수가 연결된 구조\n",
    "> - **변화 전파**: 출력의 변화가 입력으로 거꾸로 전파\n",
    "> - **역전파**: 딥러닝의 핵심 원리\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8d96c670",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 연쇄법칙의 시각화\n",
    "def chain_rule_visualization():\n",
    "    \"\"\"연쇄법칙의 직관적 이해\"\"\"\n",
    "    # 예제: f(x) = sin(x²)\n",
    "    def f(x):\n",
    "        return np.sin(x**2)\n",
    "    \n",
    "    def df_dx(x):\n",
    "        # 연쇄법칙: d/dx[sin(x²)] = cos(x²) * 2x\n",
    "        return np.cos(x**2) * 2*x\n",
    "    \n",
    "    x = np.linspace(-2, 2, 100)\n",
    "    y = f(x)\n",
    "    \n",
    "    # 수치 미분으로 검증\n",
    "    def numerical_derivative(func, x, h=1e-4):\n",
    "        return (func(x + h) - func(x - h)) / (2 * h)\n",
    "    \n",
    "    x_test = 1.0\n",
    "    analytical = df_dx(x_test)\n",
    "    numerical = numerical_derivative(f, x_test)\n",
    "    \n",
    "    plt.figure(figsize=(12, 8))\n",
    "    \n",
    "    # 함수와 미분\n",
    "    plt.subplot(2, 1, 1)\n",
    "    plt.plot(x, y, 'b-', linewidth=2, label='f(x) = sin(x²)')\n",
    "    plt.grid(True, alpha=0.3)\n",
    "    plt.xlabel('x')\n",
    "    plt.ylabel('f(x)')\n",
    "    plt.title('합성 함수: f(x) = sin(x²)')\n",
    "    plt.legend()\n",
    "    \n",
    "    # 미분\n",
    "    plt.subplot(2, 1, 2)\n",
    "    dy_dx = df_dx(x)\n",
    "    plt.plot(x, dy_dx, 'r-', linewidth=2, label=\"f'(x) = cos(x²) * 2x\")\n",
    "    plt.grid(True, alpha=0.3)\n",
    "    plt.xlabel('x')\n",
    "    plt.ylabel(\"f'(x)\")\n",
    "    plt.title('연쇄법칙을 이용한 미분')\n",
    "    plt.legend()\n",
    "    \n",
    "    plt.tight_layout()\n",
    "    plt.show()\n",
    "    \n",
    "    print(\"연쇄법칙의 직관적 의미:\")\n",
    "    print(f\"f(x) = sin(x²)에서 x = {x_test}일 때:\")\n",
    "    print(f\"  해석적 미분: f'({x_test}) = {analytical:.6f}\")\n",
    "    print(f\"  수치 미분: f'({x_test}) ≈ {numerical:.6f}\")\n",
    "    print(f\"  오차: {abs(analytical - numerical):.2e}\")\n",
    "    print()\n",
    "    print(\"연쇄법칙: d/dx[sin(x²)] = cos(x²) * d/dx[x²] = cos(x²) * 2x\")\n",
    "\n",
    "chain_rule_visualization()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9ace6622",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 4-2. 간단한 신경망의 역전파\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "83db7669",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 간단한 신경망으로 역전파 이해하기\n",
    "def simple_neural_network():\n",
    "    \"\"\"간단한 신경망의 역전파 시각화\"\"\"\n",
    "    # 간단한 신경망: 입력 -> 은닉층 -> 출력\n",
    "    # f(x) = w2 * sigmoid(w1 * x + b1) + b2\n",
    "    \n",
    "    def sigmoid(x):\n",
    "        return 1 / (1 + np.exp(-x))\n",
    "    \n",
    "    def sigmoid_derivative(x):\n",
    "        return sigmoid(x) * (1 - sigmoid(x))\n",
    "    \n",
    "    # 가중치 초기화\n",
    "    w1, b1 = 2.0, 1.0\n",
    "    w2, b2 = 3.0, 0.5\n",
    "    \n",
    "    def forward(x):\n",
    "        \"\"\"순전파\"\"\"\n",
    "        z1 = w1 * x + b1\n",
    "        a1 = sigmoid(z1)\n",
    "        z2 = w2 * a1 + b2\n",
    "        return z2, a1, z1\n",
    "    \n",
    "    def backward(x, target, learning_rate=0.1):\n",
    "        \"\"\"역전파 (연쇄법칙)\"\"\"\n",
    "        # 순전파\n",
    "        output, a1, z1 = forward(x)\n",
    "        \n",
    "        # 오차\n",
    "        error = output - target\n",
    "        \n",
    "        # 역전파 (연쇄법칙)\n",
    "        # ∂E/∂w2 = ∂E/∂output * ∂output/∂w2\n",
    "        dE_dw2 = error * a1\n",
    "        \n",
    "        # ∂E/∂w1 = ∂E/∂output * ∂output/∂a1 * ∂a1/∂z1 * ∂z1/∂w1\n",
    "        dE_dw1 = error * w2 * sigmoid_derivative(z1) * x\n",
    "        \n",
    "        # ∂E/∂b1 = ∂E/∂output * ∂output/∂a1 * ∂a1/∂z1 * ∂z1/∂b1\n",
    "        dE_db1 = error * w2 * sigmoid_derivative(z1)\n",
    "        \n",
    "        # ∂E/∂b2 = ∂E/∂output * ∂output/∂b2\n",
    "        dE_db2 = error\n",
    "        \n",
    "        return dE_dw1, dE_db1, dE_dw2, dE_db2\n",
    "    \n",
    "    # 학습 과정 시각화\n",
    "    x_train = np.array([0.5, 1.0, 1.5])\n",
    "    y_train = np.array([2.0, 3.0, 4.0])\n",
    "    \n",
    "    print(\"간단한 신경망 학습 과정:\")\n",
    "    print(\"-\" * 50)\n",
    "    \n",
    "    for epoch in range(5):\n",
    "        total_error = 0\n",
    "        for x, target in zip(x_train, y_train):\n",
    "            output, _, _ = forward(x)\n",
    "            error = output - target\n",
    "            total_error += error**2\n",
    "            \n",
    "            # 그래디언트 계산\n",
    "            dw1, db1, dw2, db2 = backward(x, target)\n",
    "            \n",
    "            print(f\"입력: {x}, 목표: {target}, 출력: {output:.4f}, 오차: {error:.4f}\")\n",
    "            print(f\"  그래디언트: dw1={dw1:.4f}, db1={db1:.4f}, dw2={dw2:.4f}, db2={db2:.4f}\")\n",
    "        \n",
    "        print(f\"에포크 {epoch+1}: 평균 오차 = {total_error/len(x_train):.4f}\")\n",
    "        print()\n",
    "\n",
    "simple_neural_network()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "99f3f722",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "---\n",
    "\n",
    "<br>\n",
    "\n",
    "## 5. 실제 활용: 선형 회귀와 경사 하강법 📊\n",
    "\n",
    "### 5-1. 선형 회귀 구현하기\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b77b4df4",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 선형 회귀의 경사 하강법 구현\n",
    "def linear_regression_gradient_descent():\n",
    "    \"\"\"선형 회귀를 경사 하강법으로 학습\"\"\"\n",
    "    np.random.seed(42)\n",
    "    \n",
    "    # 데이터 생성\n",
    "    X = np.random.rand(100, 1) * 10\n",
    "    y = 3 * X + 2 + np.random.randn(100, 1) * 0.5\n",
    "    \n",
    "    # 모델: y = w * x + b\n",
    "    w, b = 0.0, 0.0\n",
    "    learning_rate = 0.01\n",
    "    epochs = 100\n",
    "    \n",
    "    # 학습 과정 기록\n",
    "    w_history = [w]\n",
    "    b_history = [b]\n",
    "    loss_history = []\n",
    "    \n",
    "    for epoch in range(epochs):\n",
    "        # 예측\n",
    "        y_pred = w * X + b\n",
    "        \n",
    "        # 손실 함수 (MSE)\n",
    "        loss = np.mean((y_pred - y)**2)\n",
    "        loss_history.append(loss)\n",
    "        \n",
    "        # 그래디언트 계산\n",
    "        dw = np.mean(2 * (y_pred - y) * X)\n",
    "        db = np.mean(2 * (y_pred - y))\n",
    "        \n",
    "        # 파라미터 업데이트\n",
    "        w = w - learning_rate * dw\n",
    "        b = b - learning_rate * db\n",
    "        \n",
    "        w_history.append(w)\n",
    "        b_history.append(b)\n",
    "    \n",
    "    # 결과 시각화\n",
    "    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))\n",
    "    \n",
    "    # 데이터와 학습된 선\n",
    "    ax1.scatter(X, y, alpha=0.6, label='데이터')\n",
    "    X_line = np.linspace(0, 10, 100).reshape(-1, 1)\n",
    "    y_line = w * X_line + b\n",
    "    ax1.plot(X_line, y_line, 'r-', linewidth=2, label=f'학습된 선: y = {w:.2f}x + {b:.2f}')\n",
    "    ax1.set_xlabel('x')\n",
    "    ax1.set_ylabel('y')\n",
    "    ax1.set_title('선형 회귀 결과')\n",
    "    ax1.legend()\n",
    "    ax1.grid(True, alpha=0.3)\n",
    "    \n",
    "    # 손실 함수 변화\n",
    "    ax2.plot(loss_history, 'b-', linewidth=2)\n",
    "    ax2.set_xlabel('에포크')\n",
    "    ax2.set_ylabel('손실 (MSE)')\n",
    "    ax2.set_title('손실 함수 수렴')\n",
    "    ax2.grid(True, alpha=0.3)\n",
    "    \n",
    "    plt.tight_layout()\n",
    "    plt.show()\n",
    "    \n",
    "    print(\"선형 회귀 학습 결과:\")\n",
    "    print(f\"학습된 가중치: w = {w:.4f}\")\n",
    "    print(f\"학습된 편향: b = {b:.4f}\")\n",
    "    print(f\"실제 값: w = 3.0, b = 2.0\")\n",
    "    print(f\"최종 손실: {loss_history[-1]:.6f}\")\n",
    "\n",
    "linear_regression_gradient_descent()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "99541a25",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 5-2. 다양한 최적화 알고리즘 비교\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f162b6de",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 다양한 최적화 알고리즘 비교\n",
    "def compare_optimizers():\n",
    "    \"\"\"다양한 최적화 알고리즘 비교\"\"\"\n",
    "    def objective_function(x):\n",
    "        return x**2 + 2*x + 1\n",
    "    \n",
    "    def gradient(x):\n",
    "        return 2*x + 2\n",
    "    \n",
    "    # 최적화 알고리즘들\n",
    "    def gradient_descent(x0, learning_rate=0.1, epochs=50):\n",
    "        x = x0\n",
    "        history = [x]\n",
    "        for _ in range(epochs):\n",
    "            x = x - learning_rate * gradient(x)\n",
    "            history.append(x)\n",
    "        return history\n",
    "    \n",
    "    def momentum_gradient_descent(x0, learning_rate=0.1, momentum=0.9, epochs=50):\n",
    "        x = x0\n",
    "        velocity = 0\n",
    "        history = [x]\n",
    "        for _ in range(epochs):\n",
    "            velocity = momentum * velocity - learning_rate * gradient(x)\n",
    "            x = x + velocity\n",
    "            history.append(x)\n",
    "        return history\n",
    "    \n",
    "    # 최적화 실행\n",
    "    x0 = 5.0\n",
    "    gd_history = gradient_descent(x0)\n",
    "    mgd_history = momentum_gradient_descent(x0)\n",
    "    \n",
    "    # 시각화\n",
    "    x_plot = np.linspace(-1, 6, 100)\n",
    "    y_plot = objective_function(x_plot)\n",
    "    \n",
    "    plt.figure(figsize=(12, 8))\n",
    "    \n",
    "    # 목적 함수\n",
    "    plt.subplot(2, 1, 1)\n",
    "    plt.plot(x_plot, y_plot, 'b-', linewidth=2, label='목적 함수: f(x) = x² + 2x + 1')\n",
    "    plt.plot(gd_history, [objective_function(x) for x in gd_history], 'ro-', \n",
    "             markersize=6, label='기본 경사 하강법')\n",
    "    plt.plot(mgd_history, [objective_function(x) for x in mgd_history], 'go-', \n",
    "             markersize=6, label='모멘텀 경사 하강법')\n",
    "    plt.xlabel('x')\n",
    "    plt.ylabel('f(x)')\n",
    "    plt.title('최적화 과정 비교')\n",
    "    plt.legend()\n",
    "    plt.grid(True, alpha=0.3)\n",
    "    \n",
    "    # 수렴 과정\n",
    "    plt.subplot(2, 1, 2)\n",
    "    plt.plot([objective_function(x) for x in gd_history], 'r-', linewidth=2, label='기본 경사 하강법')\n",
    "    plt.plot([objective_function(x) for x in mgd_history], 'g-', linewidth=2, label='모멘텀 경사 하강법')\n",
    "    plt.xlabel('에포크')\n",
    "    plt.ylabel('f(x)')\n",
    "    plt.title('손실 함수 수렴 비교')\n",
    "    plt.legend()\n",
    "    plt.grid(True, alpha=0.3)\n",
    "    \n",
    "    plt.tight_layout()\n",
    "    plt.show()\n",
    "    \n",
    "    print(\"최적화 알고리즘 비교:\")\n",
    "    print(f\"기본 경사 하강법 최종값: {gd_history[-1]:.6f}\")\n",
    "    print(f\"모멘텀 경사 하강법 최종값: {mgd_history[-1]:.6f}\")\n",
    "    print(f\"실제 최솟값: -1.0\")\n",
    "\n",
    "compare_optimizers()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8f7f3991",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 5-3. 실습: 직접 해보기 🛠️\n",
    "\n",
    "#### 🎯 과제 1: 미분 계산기 구현하기\n",
    "다양한 함수의 미분을 계산하는 클래스를 구현해보세요:\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "206facb8",
   "metadata": {},
   "outputs": [],
   "source": [
    "class DerivativeCalculator:\n",
    "    \"\"\"미분 계산기 클래스\"\"\"\n",
    "    \n",
    "    def __init__(self, h=1e-6):\n",
    "        self.h = h\n",
    "    \n",
    "    def numerical_derivative(self, func, x):\n",
    "        \"\"\"수치 미분\"\"\"\n",
    "        # TODO: 수치 미분 공식을 사용하여 미분값을 계산하세요!\n",
    "        # 힌트: f'(x) ≈ (f(x+h) - f(x-h)) / (2h)\n",
    "        pass\n",
    "    \n",
    "    def analytical_derivative(self, func_name, x):\n",
    "        \"\"\"해석적 미분 (기본 함수들)\"\"\"\n",
    "        # TODO: 기본 함수들의 해석적 미분을 구현하세요!\n",
    "        # 지원할 함수: 'x^2', 'sin(x)', 'cos(x)', 'exp(x)', 'log(x)'\n",
    "        pass\n",
    "    \n",
    "    def compare_methods(self, func, x, func_name=None):\n",
    "        \"\"\"수치 미분과 해석적 미분 비교\"\"\"\n",
    "        numerical = self.numerical_derivative(func, x)\n",
    "        analytical = self.analytical_derivative(func_name, x) if func_name else None\n",
    "        \n",
    "        print(f\"x = {x}에서의 미분값:\")\n",
    "        print(f\"  수치 미분: {numerical:.6f}\")\n",
    "        if analytical is not None:\n",
    "            print(f\"  해석적 미분: {analytical:.6f}\")\n",
    "            print(f\"  오차: {abs(numerical - analytical):.2e}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "590163ab",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "#### 🎯 과제 2: 기울기 하강법 시각화 도구\n",
    "기울기 하강법의 과정을 애니메이션으로 시각화하는 도구를 만들어보세요:\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "97f530be",
   "metadata": {},
   "outputs": [],
   "source": [
    "def animated_gradient_descent(func, gradient, x0, learning_rate=0.1, epochs=100):\n",
    "    \"\"\"애니메이션 기울기 하강법\"\"\"\n",
    "    import matplotlib.animation as animation\n",
    "    \n",
    "    # TODO: 기울기 하강법 과정을 애니메이션으로 표현하세요!\n",
    "    # 힌트: matplotlib.animation.FuncAnimation을 사용하여 실시간으로 최적화 과정을 보여주세요\n",
    "    pass\n",
    "\n",
    "def plot_optimization_landscape(func, x_range, y_range, optimizer_history):\n",
    "    \"\"\"최적화 경관을 3D로 시각화\"\"\"\n",
    "    # TODO: 3D 그래프로 목적 함수의 표면과 최적화 경로를 그려보세요!\n",
    "    # 힌트: ax.plot_surface()와 ax.plot()을 사용하세요\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "93aaf3ae",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "#### 🎯 과제 3: 다변수 함수 최적화\n",
    "여러 변수를 가진 함수의 최적화를 구현해보세요:\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f4217e71",
   "metadata": {},
   "outputs": [],
   "source": [
    "def multivariate_gradient_descent(objective_func, gradient_func, x0, learning_rate=0.01, epochs=1000):\n",
    "    \"\"\"다변수 함수의 기울기 하강법\"\"\"\n",
    "    # TODO: 다변수 함수의 기울기 하강법을 구현하세요!\n",
    "    # 힌트: 각 변수에 대해 편미분을 계산하고 동시에 업데이트하세요\n",
    "    pass\n",
    "\n",
    "def rosenbrock_function(x, y):\n",
    "    \"\"\"Rosenbrock 함수 (최적화 테스트용)\"\"\"\n",
    "    return (1 - x)**2 + 100 * (y - x**2)**2\n",
    "\n",
    "def rosenbrock_gradient(x, y):\n",
    "    \"\"\"Rosenbrock 함수의 그래디언트\"\"\"\n",
    "    # TODO: Rosenbrock 함수의 편미분을 계산하세요!\n",
    "    # ∂f/∂x = -2(1-x) - 400x(y-x²)\n",
    "    # ∂f/∂y = 200(y-x²)\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "515b2c9a",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 5-4. 난이도별 실습 과제\n",
    "\n",
    "#### 🟢 초급 과제: 기본 개념 이해 및 구현\n",
    "1. **미분 계산기**: 다양한 함수의 수치 미분을 계산하는 함수 구현\n",
    "2. **기울기 시각화**: 함수와 그 미분을 동시에 그래프로 표현\n",
    "3. **단순 최적화**: 1변수 함수의 최솟값을 기울기 하강법으로 찾기\n",
    "\n",
    "#### 🟡 중급 과제: 실제 데이터 적용\n",
    "1. **선형 회귀 구현**: 실제 데이터셋으로 선형 회귀 모델 학습\n",
    "2. **학습률 스케줄링**: 다양한 학습률 스케줄링 기법 구현 및 비교\n",
    "3. **조기 종료**: 과적합을 방지하는 조기 종료 기법 구현\n",
    "\n",
    "#### 🔴 고급 과제: 창의적 활용 및 확장\n",
    "1. **신경망 역전파**: 간단한 신경망의 역전파 알고리즘 직접 구현\n",
    "2. **적응적 최적화**: Adam, RMSprop 등 고급 최적화 알고리즘 구현\n",
    "3. **제약 최적화**: 라그랑주 승수법을 사용한 제약 조건이 있는 최적화\n",
    "\n",
    "---\n",
    "\n",
    "<br>\n",
    "\n",
    "## 6. 마무리: 미적분의 핵심 메시지 🎯\n",
    "\n",
    "### 6-1. 우리가 배운 것들\n",
    "\n",
    "1. **미분**: 순간 변화율과 기울기 → 함수의 변화 방향과 속도\n",
    "2. **기울기 하강법**: 산에서 내려오는 사람 → 최적화의 핵심 알고리즘\n",
    "3. **편미분**: 여러 변수 중 하나만 바꾸기 → 다변수 함수의 최적화\n",
    "4. **연쇄법칙**: 파이프라인을 통해 변화 전파 → 딥러닝의 역전파\n",
    "\n",
    "### 6-2. 머신러닝에서의 활용\n",
    "\n",
    "- **모델 학습**: 손실 함수의 최소화\n",
    "- **파라미터 업데이트**: 그래디언트 기반 최적화\n",
    "- **신경망 훈련**: 역전파 알고리즘\n",
    "- **특성 선택**: 그래디언트 기반 특성 중요도\n",
    "\n",
    "### 6-3. 다음 단계\n",
    "\n",
    "이제 선형대수와 미적분을 모두 배웠으니, **실제 머신러닝 모델**을 만들어보겠습니다!\n",
    "\n",
    "> **💡 핵심 메시지**: \n",
    "> 미적분은 머신러닝의 \"학습 엔진\"입니다. \n",
    "> 복잡한 수식보다는 **직관적 이해**와 **실제 구현**에 집중하세요!\n",
    "\n",
    "---\n",
    "\n",
    "## ✅ Part 5.6 완료 체크리스트\n",
    "\n",
    "### 🎯 달성 목표 확인\n",
    "다음 항목들을 모두 완료했다면 미적분의 핵심을 이해한 것입니다!\n",
    "\n",
    "#### 📈 미분 이해도\n",
    "- [ ] 미분의 기하학적 의미 이해\n",
    "- [ ] 순간 변화율의 직관적 개념 파악\n",
    "- [ ] 수치 미분과 해석적 미분의 차이 이해\n",
    "- [ ] Python으로 미분 계산 구현 가능\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",
    "- **자동 미분을 활용한 모델 학습**: PyTorch, TensorFlow의 핵심 기능 이해\n",
    "\n",
    "### 💼 포트폴리오 프로젝트 아이디어\n",
    "- **자동 미분을 활용한 신경망 구현**: 역전파 알고리즘 직접 구현\n",
    "- **최적화 알고리즘 비교 분석**: 다양한 최적화 기법 성능 비교\n",
    "- **실시간 데이터 변화율 모니터링 시스템**: 센서 데이터 분석\n",
    "- **함수 근사화 도구**: 복잡한 함수를 단순한 함수로 근사\n",
    "\n",
    "### 🔬 실무 적용 예시\n",
    "- **금융**: 포트폴리오 최적화, 리스크 관리\n",
    "- **의료**: 약물 투여량 최적화, 진단 모델 학습\n",
    "- **제조업**: 생산 공정 최적화, 품질 관리\n",
    "- **물류**: 경로 최적화, 재고 관리\n",
    "\n",
    "> **💡 체크리스트 완료 후**: \n",
    "> 80% 이상 완료했다면 다음 파트(Part 6: 머신러닝)로 진행하세요!\n",
    "> 미완료 항목이 있다면 해당 섹션을 다시 학습하고 실습해보세요.\n",
    "\n",
    "---\n",
    "\n",
    "**⬅️ 이전 시간: [Part 5.5: NumPy로 배우는 선형대수학](../05.5_linear_algebra_with_numpy/part_5.5_linear_algebra_with_numpy.md)**\n",
    "**➡️ 다음 시간: [Part 6: 머신러닝 모델링과 평가](../06_machine_learning/part_6_machine_learning.md)** "
   ]
  }
 ],
 "metadata": {},
 "nbformat": 4,
 "nbformat_minor": 5
}
