{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceType":"competition","sourceId":3649,"databundleVersionId":46718}],"dockerImageVersionId":31329,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom sklearn.model_selection import train_test_split\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers, datasets\nimport warnings\nfrom tensorflow.keras.layers import RandomFlip, RandomRotation, RandomZoom\n\nwarnings.filterwarnings('ignore')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-04-17T07:49:24.686519Z","iopub.execute_input":"2026-04-17T07:49:24.687294Z","iopub.status.idle":"2026-04-17T07:49:24.693346Z","shell.execute_reply.started":"2026-04-17T07:49:24.687260Z","shell.execute_reply":"2026-04-17T07:49:24.692519Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 1. Keras에서 CIFAR-10 데이터 직접 로드","metadata":{}},{"cell_type":"code","source":"(X_train_full, y_train_full), (X_test, y_test) = datasets.cifar10.load_data()\nX_train_full = X_train_full.astype('float32')\nX_test = X_test.astype('float32')\ny_train_full = y_train_full.flatten()\ny_test = y_test.flatten()\nprint(f\"훈련 데이터: {X_train_full.shape}\")  # (50000, 32, 32, 3)\nprint(f\"테스트 데이터: {X_test.shape}\")      # (10000, 32, 32, 3)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-17T07:49:24.704313Z","iopub.execute_input":"2026-04-17T07:49:24.704991Z","iopub.status.idle":"2026-04-17T07:49:27.158900Z","shell.execute_reply.started":"2026-04-17T07:49:24.704959Z","shell.execute_reply":"2026-04-17T07:49:27.158033Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 2. 정규화 (0~255 → 0~1)","metadata":{}},{"cell_type":"code","source":"X_train_full = X_train_full / 255.0\nX_test = X_test / 255.0","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-17T07:49:27.160327Z","iopub.execute_input":"2026-04-17T07:49:27.160624Z","iopub.status.idle":"2026-04-17T07:49:27.383734Z","shell.execute_reply.started":"2026-04-17T07:49:27.160601Z","shell.execute_reply":"2026-04-17T07:49:27.382824Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 3. 훈련/검증 분할 (8:2)","metadata":{}},{"cell_type":"code","source":"# ✅ TODO: train_test_split 사용하여 분할\nX_train, X_val, y_train, y_val = train_test_split(\n    X_train_full, y_train_full,\n    test_size=0.2,\n    random_state=42,\n    stratify=y_train_full  # 클래스 비율 유지\n)\nprint(f\"훈련: {X_train.shape}, 검증: {X_val.shape}\")\nprint(f\"훈련 라벨 분포: {np.bincount(y_train)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-17T07:49:27.384923Z","iopub.execute_input":"2026-04-17T07:49:27.385211Z","iopub.status.idle":"2026-04-17T07:49:28.035296Z","shell.execute_reply.started":"2026-04-17T07:49:27.385190Z","shell.execute_reply":"2026-04-17T07:49:28.034632Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 4. 클래스 이름 정의","metadata":{}},{"cell_type":"code","source":"class_names = ['airplane', 'automobile', 'bird', 'cat', 'deer',\n               'dog', 'frog', 'horse', 'ship', 'truck']\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-17T07:49:28.036923Z","iopub.execute_input":"2026-04-17T07:49:28.037211Z","iopub.status.idle":"2026-04-17T07:49:28.040967Z","shell.execute_reply.started":"2026-04-17T07:49:28.037188Z","shell.execute_reply":"2026-04-17T07:49:28.040406Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 5. 시각화: 각 클래스별 샘플 이미지","metadata":{}},{"cell_type":"code","source":"fig, axes = plt.subplots(5, 2, figsize=(8, 10))\nfor class_idx in range(10):\n    # ✅ TODO: 각 클래스의 이미지 인덱스 찾기\n    indices = np.where(y_train == class_idx)[0]\n    idx = indices[0] if len(indices) > 0 else 0\n    \n    row, col = class_idx // 2, class_idx % 2\n    axes[row, col].imshow(X_train[idx])\n    axes[row, col].set_title(class_names[class_idx])\n    axes[row, col].axis('off')  # 힌트: axis\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-17T07:49:28.042004Z","iopub.execute_input":"2026-04-17T07:49:28.042326Z","iopub.status.idle":"2026-04-17T07:49:28.696920Z","shell.execute_reply.started":"2026-04-17T07:49:28.042293Z","shell.execute_reply":"2026-04-17T07:49:28.696047Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 6. 이미지 샘플 확인 (데이터 형태, 픽셀값 범위)","metadata":{}},{"cell_type":"code","source":"print(f\"이미지 최소값: {X_train.min():.3f}, 최대값: {X_train.max():.3f}\")\nprint(f\"평균: {X_train.mean():.3f}, 표준편차: {X_train.std():.3f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-17T07:49:28.697781Z","iopub.execute_input":"2026-04-17T07:49:28.698022Z","iopub.status.idle":"2026-04-17T07:49:29.116659Z","shell.execute_reply.started":"2026-04-17T07:49:28.698001Z","shell.execute_reply":"2026-04-17T07:49:29.115988Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 1. CNN 모델 구축","metadata":{}},{"cell_type":"code","source":"model = keras.Sequential([\n    layers.Input(shape=(32, 32, 3)),\n    RandomFlip('horizontal'),\n    RandomRotation(0.1),\n    RandomZoom(0.1),\n    \n    layers.Conv2D(64, (3, 3), activation='relu', padding='same'),\n    layers.MaxPooling2D(pool_size=(2, 2)),\n    \n    layers.Conv2D(128, (3, 3), activation='relu', padding='same'),\n    layers.MaxPooling2D(pool_size=(2, 2)),\n    \n    layers.Conv2D(256, (3, 3), activation='relu', padding='same'),\n    layers.MaxPooling2D(pool_size=(2, 2)),\n    \n    layers.Flatten(),\n    layers.Dense(256, activation='relu'),\n    layers.Dropout(0.5),\n    layers.Dense(10, activation='softmax')\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-17T07:49:29.117691Z","iopub.execute_input":"2026-04-17T07:49:29.117985Z","iopub.status.idle":"2026-04-17T07:49:29.174223Z","shell.execute_reply.started":"2026-04-17T07:49:29.117954Z","shell.execute_reply":"2026-04-17T07:49:29.173676Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 2. 모델 컴파일","metadata":{}},{"cell_type":"code","source":"model.compile(\n    optimizer=keras.optimizers.Adam(learning_rate=0.001),\n    # ✅ TODO: 손실함수 선택\n    # 옵션: 'categorical_crossentropy', 'sparse_categorical_crossentropy'\n    loss='sparse_categorical_crossentropy',\n    metrics=['accuracy']\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-17T07:49:29.175234Z","iopub.execute_input":"2026-04-17T07:49:29.175524Z","iopub.status.idle":"2026-04-17T07:49:29.183117Z","shell.execute_reply.started":"2026-04-17T07:49:29.175503Z","shell.execute_reply":"2026-04-17T07:49:29.182345Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 3. 모델 요약","metadata":{}},{"cell_type":"code","source":"model.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-17T07:49:29.184159Z","iopub.execute_input":"2026-04-17T07:49:29.184439Z","iopub.status.idle":"2026-04-17T07:49:29.203535Z","shell.execute_reply.started":"2026-04-17T07:49:29.184407Z","shell.execute_reply":"2026-04-17T07:49:29.202993Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 조기 종료 (과적합 방지)","metadata":{}},{"cell_type":"code","source":"early_stop = keras.callbacks.EarlyStopping(\n    monitor='val_loss',\n    patience=10,\n    restore_best_weights=True\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-17T07:49:29.205354Z","iopub.execute_input":"2026-04-17T07:49:29.205532Z","iopub.status.idle":"2026-04-17T07:49:29.209519Z","shell.execute_reply.started":"2026-04-17T07:49:29.205516Z","shell.execute_reply":"2026-04-17T07:49:29.208637Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 훈련","metadata":{}},{"cell_type":"code","source":"history = model.fit(\n    X_train, y_train,\n    validation_data=(X_val, y_val),\n    epochs=100,\n    batch_size=128,  # CIFAR-10은 작은 이미지라 배치 크기 증가 가능\n    # ✅ TODO: 콜백 전달\n    callbacks=[early_stop],\n    verbose=1\n)\n\nprint(f\"훈련 완료! 총 {len(history.history['loss'])}개 에포크\")\npd.DataFrame(history.history).tail(10)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-17T07:49:29.210449Z","iopub.execute_input":"2026-04-17T07:49:29.210743Z","iopub.status.idle":"2026-04-17T07:52:52.440894Z","shell.execute_reply.started":"2026-04-17T07:49:29.210723Z","shell.execute_reply":"2026-04-17T07:52:52.440347Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 1. 훈련/검증 손실 & 정확도 시각화","metadata":{}},{"cell_type":"code","source":"fig, axes = plt.subplots(1, 2, figsize=(12, 4))\n# 손실\naxes[0].plot(history.history['loss'], label='Train Loss')\naxes[0].plot(history.history['val_loss'], label='Val Loss')\naxes[0].set_xlabel('Epoch')\naxes[0].set_ylabel('Loss')\naxes[0].set_title('Model Loss')\naxes[0].legend()\naxes[0].grid(True)\n# 정확도\naxes[1].plot(history.history['accuracy'], label='Train Acc')\naxes[1].plot(history.history['val_accuracy'], label='Val Acc')\naxes[1].set_xlabel('Epoch')\naxes[1].set_ylabel('Accuracy')\naxes[1].set_title('Model Accuracy')\naxes[1].legend()\naxes[1].grid(True)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-17T07:53:12.246174Z","iopub.execute_input":"2026-04-17T07:53:12.246830Z","iopub.status.idle":"2026-04-17T07:53:12.513578Z","shell.execute_reply.started":"2026-04-17T07:53:12.246799Z","shell.execute_reply":"2026-04-17T07:53:12.512767Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 2. 최종 성능 평가","metadata":{}},{"cell_type":"code","source":"val_loss, val_acc = model.evaluate(X_val, y_val)\nprint(f\"\\n=== 검증 성능 ===\")\nprint(f\"Loss: {val_loss:.4f}\")\nprint(f\"훈련 정확도: {history.history['accuracy'][-1]*100:.2f}%\")\nprint(f\"기존: 75.67% → Data Augmentation 추가 후: {val_acc*100:.2f}%\")\nprint(f\"정확도: {val_acc:.4f} ({val_acc*100:.2f}%)\")\n# 테스트 성능 (최종 평가)\ntest_loss, test_acc = model.evaluate(X_test, y_test)\nprint(f\"\\n=== 테스트 성능 (최종) ===\")\nprint(f\"Loss: {test_loss:.4f}\")\nprint(f\"정확도: {test_acc:.4f} ({test_acc*100:.2f}%)\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-17T07:53:16.219898Z","iopub.execute_input":"2026-04-17T07:53:16.220578Z","iopub.status.idle":"2026-04-17T07:53:19.287508Z","shell.execute_reply.started":"2026-04-17T07:53:16.220548Z","shell.execute_reply":"2026-04-17T07:53:19.286879Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 3. 예측 확률 분석","metadata":{}},{"cell_type":"code","source":"y_val_pred_proba = model.predict(X_val)\n# ✅ TODO: 확률을 클래스 인덱스로 변환\ny_val_pred = np.argmax(y_val_pred_proba, axis=1)\n# 신뢰도 확인\nconfidence = np.max(y_val_pred_proba, axis=1)\nprint(f\"\\n예측 신뢰도:\")\nprint(f\"  평균: {confidence.mean():.4f}\")\nprint(f\"  최소: {confidence.min():.4f}\")\nprint(f\"  최대: {confidence.max():.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-17T07:53:24.863003Z","iopub.execute_input":"2026-04-17T07:53:24.863337Z","iopub.status.idle":"2026-04-17T07:53:26.016126Z","shell.execute_reply.started":"2026-04-17T07:53:24.863314Z","shell.execute_reply":"2026-04-17T07:53:26.015136Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import confusion_matrix, classification_report\nimport seaborn as sns","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-17T07:53:27.913889Z","iopub.execute_input":"2026-04-17T07:53:27.914182Z","iopub.status.idle":"2026-04-17T07:53:27.917975Z","shell.execute_reply.started":"2026-04-17T07:53:27.914157Z","shell.execute_reply":"2026-04-17T07:53:27.917463Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 1. 예측 (10-class)","metadata":{}},{"cell_type":"code","source":"y_val_pred_proba = model.predict(X_val)\n# ✅ TODO: 확률을 클래스 인덱스로 변환\ny_val_pred = np.argmax(y_val_pred_proba, axis=1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-17T07:53:30.035720Z","iopub.execute_input":"2026-04-17T07:53:30.036333Z","iopub.status.idle":"2026-04-17T07:53:31.167098Z","shell.execute_reply.started":"2026-04-17T07:53:30.036305Z","shell.execute_reply":"2026-04-17T07:53:31.166124Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 2. 혼동행렬 (10×10) 계산","metadata":{}},{"cell_type":"code","source":"# ✅ TODO: confusion_matrix 사용\ncm = confusion_matrix(y_val, y_val_pred, labels=range(10))\nplt.figure(figsize=(10, 8))\nsns.heatmap(cm, annot=True, fmt='d', cmap='Blues', cbar=True,\n            xticklabels=class_names,\n            yticklabels=class_names)\nplt.title('Confusion Matrix (Validation Data)')\nplt.ylabel('Actual')\nplt.xlabel('Predicted')\nplt.xticks(rotation=45, ha='right')\nplt.yticks(rotation=0)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-17T07:53:33.668425Z","iopub.execute_input":"2026-04-17T07:53:33.669192Z","iopub.status.idle":"2026-04-17T07:53:34.017726Z","shell.execute_reply.started":"2026-04-17T07:53:33.669161Z","shell.execute_reply":"2026-04-17T07:53:34.017097Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for i in range(10):\n    for j in range(10):\n        if i != j and cm[i, j] > 50:\n            print(f\"{class_names[i]} → {class_names[j]}: {cm[i, j]}건\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-17T07:53:38.329934Z","iopub.execute_input":"2026-04-17T07:53:38.330635Z","iopub.status.idle":"2026-04-17T07:53:38.335633Z","shell.execute_reply.started":"2026-04-17T07:53:38.330603Z","shell.execute_reply":"2026-04-17T07:53:38.334863Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 3. 분류 리포트","metadata":{}},{"cell_type":"code","source":"print(\"\\n분류 리포트:\")\n# ✅ TODO: classification_report 사용\nprint(classification_report(y_val, y_val_pred, target_names=class_names))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-17T07:53:45.006655Z","iopub.execute_input":"2026-04-17T07:53:45.007448Z","iopub.status.idle":"2026-04-17T07:53:45.025997Z","shell.execute_reply.started":"2026-04-17T07:53:45.007421Z","shell.execute_reply":"2026-04-17T07:53:45.025139Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 4. 클래스별 정확도","metadata":{}},{"cell_type":"code","source":"print(f\"\\n클래스별 정확도:\")\nfor i, name in enumerate(class_names):\n    class_mask = y_val == i\n    if class_mask.sum() > 0:\n        # ✅ TODO: 각 클래스 정확도 계산\n        acc = np.sum(y_val_pred[class_mask] == i) / class_mask.sum()\n        print(f\"  {name}: {acc:.4f} ({acc*100:.2f}%)\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-17T07:53:48.405391Z","iopub.execute_input":"2026-04-17T07:53:48.406121Z","iopub.status.idle":"2026-04-17T07:53:48.412342Z","shell.execute_reply.started":"2026-04-17T07:53:48.406043Z","shell.execute_reply":"2026-04-17T07:53:48.411558Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 5. 가장 잘못 분류된 샘플 시각화","metadata":{}},{"cell_type":"code","source":"# 신뢰도가 낮은 샘플 찾기\nconfidence_scores = np.max(y_val_pred_proba, axis=1)\nlow_confidence_idx = np.argsort(confidence_scores)[:9]\nfig, axes = plt.subplots(3, 3, figsize=(10, 10))\nfor i, idx in enumerate(low_confidence_idx):\n    row, col = i // 3, i % 3\n    axes[row, col].imshow(X_val[idx])\n    actual = class_names[y_val[idx]]\n    predicted = class_names[y_val_pred[idx]]\n    conf = confidence_scores[idx]\n    axes[row, col].set_title(f\"실제: {actual}\\n예측: {predicted}\\n신뢰도: {conf:.2f}\")\n    axes[row, col].axis('off')\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-17T07:53:50.332981Z","iopub.execute_input":"2026-04-17T07:53:50.333669Z","iopub.status.idle":"2026-04-17T07:53:50.750569Z","shell.execute_reply.started":"2026-04-17T07:53:50.333642Z","shell.execute_reply":"2026-04-17T07:53:50.749999Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 성능 비교 표","metadata":{}},{"cell_type":"code","source":"comparison = pd.DataFrame({\n    'Dense Network (Day 3)': {\n        '입력': '224×224×3 → 150,528 벡터',\n        '파라미터': '약 77,246,660개',\n        '예상 정확도': '70~78%',\n        '훈련 시간': '2~5분',\n        '메모리': '~2GB'\n    },\n    'CNN (Day 4)': {\n        '입력': '32×32×3 (이미지 그대로)',\n        '파라미터': f'{model.count_params():,}개',\n        '검증 정확도': f'{val_acc*100:.2f}%',\n        '훈련 시간': '3~5분',\n        '메모리': '~500MB'\n    }\n})\nprint(\"\\n=== Dense Network vs CNN 비교 ===\")\nprint(comparison.to_string())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-17T07:53:55.172432Z","iopub.execute_input":"2026-04-17T07:53:55.172982Z","iopub.status.idle":"2026-04-17T07:53:55.180292Z","shell.execute_reply.started":"2026-04-17T07:53:55.172954Z","shell.execute_reply":"2026-04-17T07:53:55.179555Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 핵심 차이점","metadata":{}},{"cell_type":"code","source":"print(\"\\n핵심 차이점:\")\nprint(\"1. 파라미터 수:\")\nprint(f\"   - Dense: 77,246,660개 (약 77M)\")\nprint(f\"   - CNN: {model.count_params():,}개 (약 {model.count_params()/1e6:.1f}M)\")\nprint(f\"   - CNN이 약 {77246660/model.count_params():.0f}배 더 효율적!\")\nprint(\"\\n2. 공간 정보 보존:\")\nprint(\"   - Dense: 이미지를 1D 벡터로 변환 → 공간 정보 손실\")\nprint(\"   - CNN: 2D 구조 유지 → 인접 픽셀 관계 학습\")\nprint(\"\\n3. 특징 학습:\")\nprint(\"   - Dense: 전체 픽셀을 함께 처리\")\nprint(\"   - CNN: 지역 패턴부터 시작 (엣지→텍스처→객체)\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-17T07:53:58.253123Z","iopub.execute_input":"2026-04-17T07:53:58.253770Z","iopub.status.idle":"2026-04-17T07:53:58.259453Z","shell.execute_reply.started":"2026-04-17T07:53:58.253741Z","shell.execute_reply":"2026-04-17T07:53:58.258592Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}