{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "vscode": {
          "languageId": "plaintext"
        }
      },
      "outputs": [],
      "source": [
        "# Part 7: 딥러닝 기초 실습 (PyTorch)\n",
        "\n",
        "이 노트북에서는 **PyTorch**를 사용하여 딥러닝의 기본 개념과 신경망 구현을 실습합니다.\n",
        "\n",
        "## 학습 목표\n",
        "- PyTorch 기본 사용법 이해\n",
        "- 신경망(Neural Network) 구조 학습\n",
        "- 순전파(Forward Pass)와 역전파(Backward Pass) 이해\n",
        "- 딥러닝 모델 훈련 과정 체험\n",
        "\n",
        "## 실습 내용\n",
        "1. 데이터 준비 및 전처리\n",
        "2. 신경망 모델 정의\n",
        "3. 손실함수와 옵티마이저 설정\n",
        "4. 모델 훈련 및 평가\n",
        "5. 결과 분석\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 필요한 라이브러리 import\n",
        "# pip install torch torchvision scikit-learn\n",
        "\n",
        "import torch\n",
        "import torch.nn as nn\n",
        "import torch.optim as optim\n",
        "from torch.utils.data import DataLoader, TensorDataset\n",
        "from sklearn.model_selection import train_test_split\n",
        "from sklearn.preprocessing import StandardScaler\n",
        "from sklearn.datasets import load_iris\n",
        "from sklearn.metrics import accuracy_score\n",
        "import numpy as np\n",
        "import matplotlib.pyplot as plt\n",
        "\n",
        "print(f\"PyTorch 버전: {torch.__version__}\")\n",
        "print(f\"CUDA 사용 가능: {torch.cuda.is_available()}\")\n",
        "print(\"라이브러리 import 완료!\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "## 1. 데이터 준비 및 전처리\n",
        "\n",
        "머신러닝 파트와 동일한 붓꽃 데이터셋을 사용하여 딥러닝 모델을 구축해보겠습니다.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 데이터 로드 및 전처리\n",
        "iris = load_iris()\n",
        "X = iris.data\n",
        "y = iris.target\n",
        "\n",
        "# 훈련/테스트 데이터 분리\n",
        "X_train, X_test, y_train, y_test = train_test_split(\n",
        "    X, y, test_size=0.2, random_state=42, stratify=y\n",
        ")\n",
        "\n",
        "# 데이터 스케일링\n",
        "scaler = StandardScaler()\n",
        "X_train_scaled = scaler.fit_transform(X_train)\n",
        "X_test_scaled = scaler.transform(X_test)\n",
        "\n",
        "# PyTorch 텐서로 변환\n",
        "X_train_tensor = torch.FloatTensor(X_train_scaled)\n",
        "y_train_tensor = torch.LongTensor(y_train)\n",
        "X_test_tensor = torch.FloatTensor(X_test_scaled)\n",
        "y_test_tensor = torch.LongTensor(y_test)\n",
        "\n",
        "print(\"=== 데이터 전처리 완료 ===\")\n",
        "print(f\"훈련 데이터 텐서 크기: {X_train_tensor.shape}\")\n",
        "print(f\"훈련 타겟 텐서 크기: {y_train_tensor.shape}\")\n",
        "print(f\"테스트 데이터 텐서 크기: {X_test_tensor.shape}\")\n",
        "print(f\"테스트 타겟 텐서 크기: {y_test_tensor.shape}\")\n",
        "\n",
        "# DataLoader 생성 (미니배치 학습을 위해)\n",
        "train_dataset = TensorDataset(X_train_tensor, y_train_tensor)\n",
        "train_loader = DataLoader(dataset=train_dataset, batch_size=16, shuffle=True)\n",
        "\n",
        "print(f\"DataLoader 생성 완료 (배치 크기: 16)\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "## 2. 신경망 모델 정의\n",
        "\n",
        "간단한 **다층 퍼셉트론(Multi-Layer Perceptron, MLP)**을 구현해보겠습니다.\n",
        "\n",
        "### 모델 구조\n",
        "- **입력층**: 4개 특성 (꽃받침/꽃잎 길이/너비)\n",
        "- **은닉층**: 32개 뉴런 + ReLU 활성화 함수\n",
        "- **출력층**: 3개 뉴런 (3개 클래스)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 신경망 모델 클래스 정의\n",
        "class SimpleNN(nn.Module):\n",
        "    def __init__(self, input_size, hidden_size, num_classes):\n",
        "        super(SimpleNN, self).__init__()\n",
        "        # 첫 번째 완전연결층 (입력층 → 은닉층)\n",
        "        self.fc1 = nn.Linear(input_size, hidden_size)\n",
        "        # ReLU 활성화 함수\n",
        "        self.relu = nn.ReLU()\n",
        "        # 두 번째 완전연결층 (은닉층 → 출력층)\n",
        "        self.fc2 = nn.Linear(hidden_size, num_classes)\n",
        "    \n",
        "    def forward(self, x):\n",
        "        # 순전파 과정\n",
        "        out = self.fc1(x)  # 입력 → 은닉층\n",
        "        out = self.relu(out)  # ReLU 활성화\n",
        "        out = self.fc2(out)  # 은닉층 → 출력층\n",
        "        return out\n",
        "\n",
        "# 모델 하이퍼파라미터 설정\n",
        "input_size = X_train.shape[1]  # 특성의 수: 4\n",
        "hidden_size = 32  # 은닉층 뉴런 수\n",
        "num_classes = len(np.unique(y_train))  # 클래스 수: 3\n",
        "learning_rate = 0.01\n",
        "num_epochs = 100\n",
        "\n",
        "# 모델 인스턴스 생성\n",
        "model = SimpleNN(input_size, hidden_size, num_classes)\n",
        "\n",
        "print(\"=== 모델 정보 ===\")\n",
        "print(f\"모델 구조:\")\n",
        "print(model)\n",
        "print(f\"입력 크기: {input_size}\")\n",
        "print(f\"은닉층 크기: {hidden_size}\")\n",
        "print(f\"출력 크기: {num_classes}\")\n",
        "print(f\"학습률: {learning_rate}\")\n",
        "print(f\"에포크 수: {num_epochs}\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "## 3. 손실함수와 옵티마이저 설정\n",
        "\n",
        "딥러닝 모델 훈련에 필요한 핵심 구성요소들을 설정합니다:\n",
        "- **손실함수**: CrossEntropyLoss (다중 분류)\n",
        "- **옵티마이저**: Adam (적응적 학습률)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 손실함수와 옵티마이저 정의\n",
        "criterion = nn.CrossEntropyLoss()  # 다중 분류를 위한 크로스엔트로피 손실\n",
        "optimizer = optim.Adam(model.parameters(), lr=learning_rate)  # Adam 옵티마이저\n",
        "\n",
        "print(\"=== 훈련 설정 ===\")\n",
        "print(f\"손실함수: {criterion}\")\n",
        "print(f\"옵티마이저: {optimizer}\")\n",
        "print(f\"옵티마이저 파라미터 수: {sum(p.numel() for p in model.parameters() if p.requires_grad)}\")\n",
        "\n",
        "# 손실 기록을 위한 리스트\n",
        "train_losses = []\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "## 4. 모델 훈련\n",
        "\n",
        "이제 신경망 모델을 훈련해보겠습니다. 훈련 과정에서 손실값의 변화를 관찰할 수 있습니다.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 모델 훈련\n",
        "print(\"=== 모델 훈련 시작 ===\")\n",
        "model.train()  # 훈련 모드로 설정\n",
        "\n",
        "for epoch in range(num_epochs):\n",
        "    epoch_loss = 0.0\n",
        "    \n",
        "    for i, (features, labels) in enumerate(train_loader):\n",
        "        # 1. 순전파 (Forward Pass)\n",
        "        outputs = model(features)\n",
        "        loss = criterion(outputs, labels)\n",
        "        \n",
        "        # 2. 역전파 (Backward Pass)\n",
        "        optimizer.zero_grad()  # 이전 그래디언트 초기화\n",
        "        loss.backward()  # 그래디언트 계산\n",
        "        optimizer.step()  # 가중치 업데이트\n",
        "        \n",
        "        epoch_loss += loss.item()\n",
        "    \n",
        "    # 에포크별 평균 손실 계산\n",
        "    avg_loss = epoch_loss / len(train_loader)\n",
        "    train_losses.append(avg_loss)\n",
        "    \n",
        "    # 10 에포크마다 손실 출력\n",
        "    if (epoch + 1) % 10 == 0:\n",
        "        print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {avg_loss:.4f}')\n",
        "\n",
        "print(\"모델 훈련 완료!\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "## 5. 모델 평가 및 시각화\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 모델 평가\n",
        "print(\"=== 모델 평가 ===\")\n",
        "model.eval()  # 평가 모드로 설정\n",
        "\n",
        "# 테스트 데이터로 예측\n",
        "with torch.no_grad():  # 그래디언트 계산 비활성화\n",
        "    test_outputs = model(X_test_tensor)\n",
        "    _, predicted = torch.max(test_outputs.data, 1)  # 가장 높은 확률의 클래스 선택\n",
        "\n",
        "# 정확도 계산\n",
        "accuracy = accuracy_score(y_test_tensor.numpy(), predicted.numpy())\n",
        "print(f\"테스트 데이터 정확도: {100 * accuracy:.2f}%\")\n",
        "\n",
        "# 예측 결과 일부 확인\n",
        "print(\"\\n=== 예측 결과 샘플 ===\")\n",
        "print(f\"실제 값: {y_test_tensor[:10].numpy()}\")\n",
        "print(f\"예측 값: {predicted[:10].numpy()}\")\n",
        "print(f\"클래스명: {[iris.target_names[i] for i in y_test_tensor[:10].numpy()]}\")\n",
        "print(f\"예측 클래스명: {[iris.target_names[i] for i in predicted[:10].numpy()]}\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 손실 함수 시각화\n",
        "plt.figure(figsize=(10, 6))\n",
        "plt.plot(range(1, num_epochs + 1), train_losses, 'b-', linewidth=2)\n",
        "plt.title('훈련 손실(Training Loss) 변화', fontsize=14)\n",
        "plt.xlabel('에포크(Epoch)', fontsize=12)\n",
        "plt.ylabel('손실(Loss)', fontsize=12)\n",
        "plt.grid(True, alpha=0.3)\n",
        "plt.show()\n",
        "\n",
        "print(f\"최종 훈련 손실: {train_losses[-1]:.4f}\")\n",
        "print(f\"초기 훈련 손실: {train_losses[0]:.4f}\")\n",
        "print(f\"손실 감소량: {train_losses[0] - train_losses[-1]:.4f}\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "## 요약 및 결론\n",
        "\n",
        "이번 실습에서 다음 내용을 학습했습니다:\n",
        "\n",
        "### 주요 학습 내용\n",
        "1. **PyTorch 기초**: 텐서, DataLoader 사용법\n",
        "2. **신경망 구조**: Multi-Layer Perceptron 구현\n",
        "3. **훈련 과정**: 순전파, 역전파, 가중치 업데이트\n",
        "4. **모델 평가**: 정확도 측정 및 시각화\n",
        "\n",
        "### 딥러닝 vs 머신러닝 비교\n",
        "- **복잡성**: 딥러닝은 더 복잡한 모델 구조\n",
        "- **학습 과정**: 그래디언트 기반 최적화\n",
        "- **성능**: 복잡한 패턴 학습 가능\n",
        "\n",
        "### 추가 실습 제안\n",
        "- 더 깊은 신경망 구현 (3층 이상)\n",
        "- 다른 활성화 함수 실험 (Sigmoid, Tanh)\n",
        "- 정규화 기법 적용 (Dropout, Batch Normalization)\n",
        "- 다른 데이터셋으로 실습\n",
        "\n",
        "### 다음 단계\n",
        "Part 7의 하위 파트들에서 **RNN, Transformer, GNN, 강화학습** 등 고급 딥러닝 기법을 학습하겠습니다!\n"
      ]
    }
  ],
  "metadata": {
    "language_info": {
      "name": "python"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 2
}
