{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":7327,"databundleVersionId":861871,"sourceType":"competition"}],"dockerImageVersionId":31040,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# 워크플로우 개요\n- 데이터 확인 및 샘플 시각화\n- 데이터셋 나누기(학습/검증)\n- 이미지와 라벨(품종) 매핑\n- 전이학습(사전학습된 CNN, 예: ResNet) 분류기 만들기\n- 훈련\n- 정확도 평가/결과 시각화\n\n","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport matplotlib.pyplot as plt\nfrom PIL import Image","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-04T09:29:00.158560Z","iopub.execute_input":"2025-06-04T09:29:00.159245Z","iopub.status.idle":"2025-06-04T09:29:00.512445Z","shell.execute_reply.started":"2025-06-04T09:29:00.159218Z","shell.execute_reply":"2025-06-04T09:29:00.511511Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 라벨 데이터 읽기","metadata":{}},{"cell_type":"code","source":"labels = pd.read_csv('/kaggle/input/dog-breed-identification/labels.csv')\nprint(labels.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-04T09:29:00.513992Z","iopub.execute_input":"2025-06-04T09:29:00.514337Z","iopub.status.idle":"2025-06-04T09:29:00.572506Z","shell.execute_reply.started":"2025-06-04T09:29:00.514317Z","shell.execute_reply":"2025-06-04T09:29:00.571577Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 품종별 데이터 개수 시각화","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(15,5))\nlabels['breed'].value_counts().plot(kind='bar')\nplt.title('Number of Images by Breed')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-04T09:29:00.573474Z","iopub.execute_input":"2025-06-04T09:29:00.573793Z","iopub.status.idle":"2025-06-04T09:29:01.843434Z","shell.execute_reply.started":"2025-06-04T09:29:00.573759Z","shell.execute_reply":"2025-06-04T09:29:01.842481Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 샘플 이미지 보기","metadata":{}},{"cell_type":"code","source":"sample = labels.sample(1).iloc[0]\nimg_path = f\"/kaggle/input/dog-breed-identification/train/{sample['id']}.jpg\"\nimg = Image.open(img_path)\nplt.imshow(img)\nplt.title(sample['breed'])\nplt.axis('off')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-04T09:29:01.845670Z","iopub.execute_input":"2025-06-04T09:29:01.845999Z","iopub.status.idle":"2025-06-04T09:29:02.065262Z","shell.execute_reply.started":"2025-06-04T09:29:01.845972Z","shell.execute_reply":"2025-06-04T09:29:02.064416Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 분류 AI 실습","metadata":{}},{"cell_type":"markdown","source":"### 라벨 인코딩 & 데이터 분할","metadata":{}},{"cell_type":"code","source":"from sklearn.preprocessing import LabelEncoder\nfrom sklearn.model_selection import train_test_split\n\nle = LabelEncoder()\nlabels['breed_idx'] = le.fit_transform(labels['breed'])\n\n# 학습/검증 셋 분리 (10%는 검증용)\ntrain_df, valid_df = train_test_split(labels, test_size=0.1, stratify=labels['breed_idx'], random_state=42)\nprint(f\"Train: {len(train_df)}, Valid: {len(valid_df)}\")\n\n#  'filename' 컬럼 각각 추가\ntrain_df = train_df.copy()    # 혹시 warning 방지\nvalid_df = valid_df.copy()\ntrain_df['filename'] = train_df['id'] + '.jpg'\nvalid_df['filename'] = valid_df['id'] + '.jpg'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-04T09:29:02.066125Z","iopub.execute_input":"2025-06-04T09:29:02.066375Z","iopub.status.idle":"2025-06-04T09:29:02.922429Z","shell.execute_reply.started":"2025-06-04T09:29:02.066355Z","shell.execute_reply":"2025-06-04T09:29:02.921511Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 이미지 제너레이터 준비","metadata":{}},{"cell_type":"code","source":"import tensorflow as tf\n\nimg_size = 128\nbatch_size = 32\n\ntrain_datagen = tf.keras.preprocessing.image.ImageDataGenerator(\n    rescale=1./255,\n    horizontal_flip=True,\n    zoom_range=0.1\n)\nvalid_datagen = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1./255)\n\ntrain_gen = train_datagen.flow_from_dataframe(\n    train_df,\n    directory='/kaggle/input/dog-breed-identification/train/',\n    x_col='filename',\n    y_col='breed',\n    target_size=(img_size, img_size),\n    class_mode='categorical',\n    batch_size=batch_size,\n    shuffle=True\n)\n\nvalid_gen = valid_datagen.flow_from_dataframe(\n    valid_df,\n    directory='/kaggle/input/dog-breed-identification/train/',\n    x_col='filename',\n    y_col='breed',\n    target_size=(img_size, img_size),\n    class_mode='categorical',\n    batch_size=batch_size,\n    shuffle=False\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-04T09:29:02.923543Z","iopub.execute_input":"2025-06-04T09:29:02.924076Z","iopub.status.idle":"2025-06-04T09:29:49.517479Z","shell.execute_reply.started":"2025-06-04T09:29:02.924043Z","shell.execute_reply":"2025-06-04T09:29:49.516718Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 전이학습(Transfer Learning) 기본 모델 만들기","metadata":{}},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow.keras import layers, models\n\n# MobileNetV2: 빠르고 성능 좋은 사전학습 이미지 분류 모델\nbase_model = tf.keras.applications.MobileNetV2(\n    input_shape=(img_size, img_size, 3),\n    include_top=False,      # 마지막 분류 레이어 빼고 feature만 사용\n    weights='imagenet'      # ImageNet 데이터로 사전학습된 가중치 사용\n)\nbase_model.trainable = False   # 처음에는 base_model을 고정(학습하지 않음)\n\n# 우리 데이터에 맞는 분류 레이어 쌓기\nmodel = models.Sequential([\n    base_model,                             # feature 추출\n    layers.GlobalAveragePooling2D(),        # feature map을 1차원으로 만듦\n    layers.Dense(256, activation='relu'),   # 중간 레이어(자유롭게 수정 가능)\n    layers.Dropout(0.3),                    # 과적합 방지\n    layers.Dense(120, activation='softmax') # 120개 품종 분류 (클래스 수 꼭 맞추기!)\n])\n\nmodel.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])\nmodel.summary()  # 모델 구조 한눈에 보기\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-04T09:29:49.518506Z","iopub.execute_input":"2025-06-04T09:29:49.519109Z","iopub.status.idle":"2025-06-04T09:29:51.201704Z","shell.execute_reply.started":"2025-06-04T09:29:49.519084Z","shell.execute_reply":"2025-06-04T09:29:51.201024Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 모델 학습 훈련하기","metadata":{}},{"cell_type":"code","source":"# 모델 학습 (fit)\nhistory = model.fit(\n    train_gen,\n    epochs=5,  # 빠른 실습은 5, 더 길게 하고 싶으면 10~20도 OK\n    validation_data=valid_gen\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-04T09:29:51.202715Z","iopub.execute_input":"2025-06-04T09:29:51.203062Z","iopub.status.idle":"2025-06-04T09:40:10.797593Z","shell.execute_reply.started":"2025-06-04T09:29:51.203034Z","shell.execute_reply":"2025-06-04T09:40:10.796631Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# 학습 기록(history)에 저장된 정확도/손실 정보 시각화\nplt.figure(figsize=(12, 5))\n\n# 정확도\nplt.subplot(1, 2, 1)\nplt.plot(history.history['accuracy'], label='train_acc')\nplt.plot(history.history['val_accuracy'], label='val_acc')\nplt.title('Accuracy Over Epochs')\nplt.xlabel('Epoch')\nplt.ylabel('Accuracy')\nplt.legend()\n\n# 손실(Loss)\nplt.subplot(1, 2, 2)\nplt.plot(history.history['loss'], label='train_loss')\nplt.plot(history.history['val_loss'], label='val_loss')\nplt.title('Loss Over Epochs')\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\nplt.legend()\n\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-04T09:40:10.799178Z","iopub.execute_input":"2025-06-04T09:40:10.799606Z","iopub.status.idle":"2025-06-04T09:40:11.260731Z","shell.execute_reply.started":"2025-06-04T09:40:10.799574Z","shell.execute_reply":"2025-06-04T09:40:11.259748Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 예측","metadata":{}},{"cell_type":"code","source":"import numpy as np\nfrom PIL import Image\n\n# 1. 샘플 이미지 선택 (검증셋에서 한 장)\nsample = valid_df.sample(1).iloc[0]\nsample_img_path = f\"/kaggle/input/dog-breed-identification/train/{sample['filename']}\"\n\n# 2. 이미지 불러와서 전처리\nimg = Image.open(sample_img_path).convert('RGB').resize((img_size, img_size))\nimg_array = np.array(img) / 255.0  # 정규화\nimg_tensor = np.expand_dims(img_array, axis=0)  # 배치 차원 추가\n\n# 3. 예측\npreds = model.predict(img_tensor)\npred_idx = np.argmax(preds, axis=1)[0]\npred_breed = le.inverse_transform([pred_idx])[0]\n\n# 4. 실제 품종과 예측 품종 비교\nplt.imshow(img)\nplt.axis('off')\nplt.title(f\"Actual: {sample['breed']}\\nPredicted: {pred_breed}\")\nplt.show()\n\nprint(f\"실제 품종: {sample['breed']}\")\nprint(f\"예측 품종: {pred_breed}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-04T09:43:52.315602Z","iopub.execute_input":"2025-06-04T09:43:52.315954Z","iopub.status.idle":"2025-06-04T09:43:52.597015Z","shell.execute_reply.started":"2025-06-04T09:43:52.315933Z","shell.execute_reply":"2025-06-04T09:43:52.596079Z"}},"outputs":[],"execution_count":null}]}