{"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":"none","dataSources":[{"sourceType":"competition","sourceId":3649,"databundleVersionId":46718}],"dockerImageVersionId":31328,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-04-17T08:03:08.078780Z","iopub.execute_input":"2026-04-17T08:03:08.079225Z","iopub.status.idle":"2026-04-17T08:03:08.489443Z","shell.execute_reply.started":"2026-04-17T08:03:08.079176Z","shell.execute_reply":"2026-04-17T08:03:08.488159Z"}},"outputs":[],"execution_count":null},{"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\n\nwarnings.filterwarnings('ignore')\n\n# 1. CIFAR-10 데이터 로드\n(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()\n\n# 2. 정규화 (0~1 범위로)\nX_train_full = X_train_full / 255.0\nX_test = X_test / 255.0\n\n# 3. 훈련/검증 분할 (8:2)\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)\n\n# 4. 클래스 이름\nclass_names = ['airplane', 'automobile', 'bird', 'cat', 'deer',\n               'dog', 'frog', 'horse', 'ship', 'truck']\n\n# 5. 시각화\nfig, axes = plt.subplots(5, 2, figsize=(8, 10))\nfor class_idx in range(10):\n    indices = np.where(y_train == class_idx)[0] # 빈칸 채움: np.where\n    idx = indices[0] if len(indices) > 0 else 0\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-17T08:03:08.491868Z","iopub.execute_input":"2026-04-17T08:03:08.492435Z","iopub.status.idle":"2026-04-17T08:03:55.849239Z","shell.execute_reply.started":"2026-04-17T08:03:08.492400Z","shell.execute_reply":"2026-04-17T08:03:55.847862Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = keras.Sequential([\n    # 합성곱층 1\n    layers.Conv2D(32, (3, 3), activation='relu', padding='same', input_shape=(32, 32, 3)),\n    layers.MaxPooling2D(pool_size=(2, 2)), # 빈칸 채움: MaxPooling2D\n    \n    # 합성곱층 2\n    layers.Conv2D(64, (3, 3), activation='relu', padding='same'),\n    layers.MaxPooling2D(pool_size=(2, 2)), # 빈칸 채움: MaxPooling2D\n    \n    # 합성곱층 3\n    layers.Conv2D(128, (3, 3), activation='relu', padding='same'),\n    layers.MaxPooling2D(pool_size=(2, 2)), # 빈칸 채움: MaxPooling2D\n    \n    layers.Flatten(),\n    layers.Dense(256, activation='relu'),\n    layers.Dropout(0.5), # 빈칸 채움: Dropout\n    \n    # 출력층 (10개 클래스 다중분류)\n    layers.Dense(10, activation='softmax') # 빈칸 채움: softmax\n])\n\nmodel.compile(\n    optimizer=keras.optimizers.Adam(learning_rate=0.001),\n    loss='sparse_categorical_crossentropy', # 빈칸 채움: 라벨이 정수이므로 sparse 사용\n    metrics=['accuracy']\n)\n\nmodel.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-17T08:03:55.850743Z","iopub.execute_input":"2026-04-17T08:03:55.851300Z","iopub.status.idle":"2026-04-17T08:03:56.014592Z","shell.execute_reply.started":"2026-04-17T08:03:55.851269Z","shell.execute_reply":"2026-04-17T08:03:56.013046Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"early_stop = keras.callbacks.EarlyStopping(\n    monitor='val_loss',\n    patience=10,\n    restore_best_weights=True\n)\n\nhistory = model.fit(\n    X_train, y_train,\n    validation_data=(X_val, y_val),\n    epochs=100,\n    batch_size=128,\n    callbacks=[early_stop],\n    verbose=1\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-17T08:03:56.015977Z","iopub.execute_input":"2026-04-17T08:03:56.016427Z","iopub.status.idle":"2026-04-17T08:05:28.102100Z","shell.execute_reply.started":"2026-04-17T08:03:56.016384Z","shell.execute_reply":"2026-04-17T08:05:28.100556Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 1. 훈련/검증 손실 & 정확도 시각화\nfig, axes = plt.subplots(1, 2, figsize=(12, 4))\n\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\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)\n\nplt.tight_layout()\nplt.show()\n\n# 2. 최종 성능 평가\nval_loss, val_acc = model.evaluate(X_val, y_val)\nprint(f\"\\n=== 검증 성능 ===\")\nprint(f\"Loss: {val_loss:.4f}\")\nprint(f\"정확도: {val_acc:.4f} ({val_acc*100:.2f}%)\")\n\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}%)\")\n\n# 3. 예측 확률 분석\ny_val_pred_proba = model.predict(X_val)\n\n# ✅ 빈칸 채움: 확률을 클래스 인덱스로 변환\ny_val_pred = np.argmax(y_val_pred_proba, axis=1)\n\n# ✅ 빈칸 채움: 신뢰도(가장 높은 확률값) 확인\nconfidence = np.max(y_val_pred_proba, axis=1)\n\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-17T08:05:28.103148Z","iopub.status.idle":"2026-04-17T08:05:28.103612Z","shell.execute_reply.started":"2026-04-17T08:05:28.103376Z","shell.execute_reply":"2026-04-17T08:05:28.103402Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import confusion_matrix, classification_report\nimport seaborn as sns\n\n# 1. 예측 (이미 위에서 했으므로 변수 재사용 가능)\n# y_val_pred_proba = model.predict(X_val)\n# y_val_pred = np.argmax(y_val_pred_proba, axis=1)\n\n# 2. 혼동행렬 (10×10) 계산\n# ✅ 빈칸 채움: confusion_matrix 사용\ncm = confusion_matrix(y_val, y_val_pred, labels=range(10))\n\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()\n\n# 3. 분류 리포트\nprint(\"\\n분류 리포트:\")\n# ✅ 빈칸 채움: classification_report 사용\nprint(classification_report(y_val, y_val_pred, target_names=class_names))\n\n# 4. 클래스별 정확도\nprint(f\"\\n클래스별 정확도:\")\nfor i, name in enumerate(class_names):\n    class_mask = (y_val == i)\n    if class_mask.sum() > 0:\n        # ✅ 빈칸 채움: 각 클래스 정확도 계산\n        acc = np.mean(y_val_pred[class_mask] == i) \n        print(f\"  {name}: {acc:.4f} ({acc*100:.2f}%)\")\n\n# 5. 가장 잘못 분류된 샘플 시각화\nconfidence_scores = np.max(y_val_pred_proba, axis=1)\n# 신뢰도가 낮으면서 틀린 것 위주로 정렬\nlow_confidence_idx = np.argsort(confidence_scores)[:9]\n\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-17T08:05:28.106357Z","iopub.status.idle":"2026-04-17T08:05:28.106830Z","shell.execute_reply.started":"2026-04-17T08:05:28.106612Z","shell.execute_reply":"2026-04-17T08:05:28.106638Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 파일 생성 전용 셀\nimport os\n\n# 1. 모델이 있는지 확인\nif 'model' in locals():\n    print(\"모델 확인 완료! 예측을 시작합니다.\")\n    predictions = model.predict(X_test)\n    prediction_indices = np.argmax(predictions, axis=1)\n\n    # 2. 데이터프레임 생성\n    submission = pd.DataFrame({\n        \"id\": np.arange(1, len(prediction_indices) + 1),\n        \"label\": [class_names[i] for i in prediction_indices]\n    })\n\n    # 3. /kaggle/working/ 경로에 확실히 저장\n    output_path = \"/kaggle/working/submission.csv\"\n    submission.to_csv(output_path, index=False)\n\n    if os.path.exists(output_path):\n        print(f\"성공! 파일이 다음 경로에 생성되었습니다: {output_path}\")\n    else:\n        print(\"파일 생성에 실패했습니다.\")\nelse:\n    print(\"에러: 'model' 변수가 없습니다. 모델 훈련 셀을 먼저 실행하세요!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-17T08:05:30.784616Z","iopub.execute_input":"2026-04-17T08:05:30.784944Z","iopub.status.idle":"2026-04-17T08:05:35.247901Z","shell.execute_reply.started":"2026-04-17T08:05:30.784917Z","shell.execute_reply":"2026-04-17T08:05:35.246759Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}