{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":77476,"databundleVersionId":8462666,"sourceType":"competition"}],"dockerImageVersionId":30699,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# CNN을 활용해서 옷 분류하기 [2024 ver]","metadata":{}},{"cell_type":"markdown","source":"# 1. Import Library","metadata":{}},{"cell_type":"code","source":"import sys\nimport os\n\nfrom keras.datasets import mnist\nfrom keras.utils import to_categorical # 원-핫 인코딩. np_utils.to_categorical(클래스, 클래스의 개수)\nfrom keras.models import Sequential\nfrom keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPooling2D\nfrom keras.callbacks import ModelCheckpoint, EarlyStopping\n\nimport pandas as pd\nimport numpy as np\nimport tensorflow as tf\nimport matplotlib.pyplot as plt\nimport seaborn as sns","metadata":{"execution":{"iopub.status.busy":"2024-05-16T08:12:13.405920Z","iopub.execute_input":"2024-05-16T08:12:13.406271Z","iopub.status.idle":"2024-05-16T08:12:25.576391Z","shell.execute_reply.started":"2024-05-16T08:12:13.406240Z","shell.execute_reply":"2024-05-16T08:12:25.575589Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 2. 데이터 분석","metadata":{}},{"cell_type":"markdown","source":"## 2-1. Load data and check","metadata":{}},{"cell_type":"code","source":"train = pd.read_csv('/kaggle/input/2024-smarcle-ks-3-fashion-mnist2/train.csv')\ntest = pd.read_csv('/kaggle/input/2024-smarcle-ks-3-fashion-mnist2/test.csv')","metadata":{"execution":{"iopub.status.busy":"2024-05-16T08:12:25.577879Z","iopub.execute_input":"2024-05-16T08:12:25.578349Z","iopub.status.idle":"2024-05-16T08:12:33.043578Z","shell.execute_reply.started":"2024-05-16T08:12:25.578323Z","shell.execute_reply":"2024-05-16T08:12:33.042620Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train.head()","metadata":{"execution":{"iopub.status.busy":"2024-05-16T08:12:33.044794Z","iopub.execute_input":"2024-05-16T08:12:33.045122Z","iopub.status.idle":"2024-05-16T08:12:33.072985Z","shell.execute_reply.started":"2024-05-16T08:12:33.045094Z","shell.execute_reply":"2024-05-16T08:12:33.072048Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train.info()","metadata":{"execution":{"iopub.status.busy":"2024-05-16T08:12:33.075179Z","iopub.execute_input":"2024-05-16T08:12:33.075510Z","iopub.status.idle":"2024-05-16T08:12:33.131204Z","shell.execute_reply.started":"2024-05-16T08:12:33.075483Z","shell.execute_reply":"2024-05-16T08:12:33.130153Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test.head()","metadata":{"execution":{"iopub.status.busy":"2024-05-16T08:12:33.132417Z","iopub.execute_input":"2024-05-16T08:12:33.132724Z","iopub.status.idle":"2024-05-16T08:12:33.152233Z","shell.execute_reply.started":"2024-05-16T08:12:33.132697Z","shell.execute_reply":"2024-05-16T08:12:33.151144Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 2-2. Handle train, test data","metadata":{}},{"cell_type":"markdown","source":"* train에 라벨 컬럼을 지우자.\n* test는 원래 없으니까 따로 만들자.","metadata":{}},{"cell_type":"code","source":"# 학습 데이터와 라벨 분리\nX_train = train.drop([\"label\"], axis=1)\nY_train = train[\"label\"]","metadata":{"execution":{"iopub.status.busy":"2024-05-16T08:12:33.153611Z","iopub.execute_input":"2024-05-16T08:12:33.153957Z","iopub.status.idle":"2024-05-16T08:12:33.273385Z","shell.execute_reply.started":"2024-05-16T08:12:33.153928Z","shell.execute_reply":"2024-05-16T08:12:33.272282Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 테스트 데이터의 경우 라벨이 제공되지 않는다면 이를 무시\nX_test = test","metadata":{"execution":{"iopub.status.busy":"2024-05-16T08:12:33.274743Z","iopub.execute_input":"2024-05-16T08:12:33.275079Z","iopub.status.idle":"2024-05-16T08:12:33.279495Z","shell.execute_reply.started":"2024-05-16T08:12:33.275050Z","shell.execute_reply":"2024-05-16T08:12:33.278428Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 2-3. 그림 살펴보기","metadata":{}},{"cell_type":"markdown","source":"* X_train, X_test는 dataFrame 형태인데, imshow를 하려면 2차원 배열을 만들어야한다.\n* 2차원 배열을 만드려면 reshape를 써야하는데 numpy형태만 가능하다.\n* 그래서 형태를 변환하고, reshape를 통해 그림을 나타내보자","metadata":{}},{"cell_type":"code","source":"# DataFrame을 numpy 배열로 변환 및 전처리\nX_train = X_train.to_numpy().reshape(X_train.shape[0], 28, 28, 1).astype('float32') / 255\nX_test = X_test.to_numpy().reshape(X_test.shape[0], 28, 28, 1).astype('float32') / 255","metadata":{"execution":{"iopub.status.busy":"2024-05-16T08:12:33.280937Z","iopub.execute_input":"2024-05-16T08:12:33.281517Z","iopub.status.idle":"2024-05-16T08:12:33.402685Z","shell.execute_reply.started":"2024-05-16T08:12:33.281480Z","shell.execute_reply":"2024-05-16T08:12:33.401803Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.imshow(X_train[0].reshape(28,28),cmap ='gray')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-05-16T08:12:33.404183Z","iopub.execute_input":"2024-05-16T08:12:33.404699Z","iopub.status.idle":"2024-05-16T08:12:33.623915Z","shell.execute_reply.started":"2024-05-16T08:12:33.404660Z","shell.execute_reply":"2024-05-16T08:12:33.622924Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 2-4. 데이터 파악하기","metadata":{}},{"cell_type":"markdown","source":"10개의 class로 이루어짐.\n* 0: T-shirt/top\n* 1: Trouser\n* 2: Pullover\n* 3: Dress\n* 4: Coat\n* 5: Sandal\n* 6: Shirt\n* 7: Sneaker\n* 8: Bag\n* 9: Ankle boot","metadata":{}},{"cell_type":"markdown","source":"### 클래스 별로 옷 구분하기","metadata":{}},{"cell_type":"code","source":"class_names = ['T_shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', \n               'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']\n# class_names는 각 클래스에 해당하는 의류 아이템 이름을 문자열 리스트로 정의\n\nplt.figure(figsize=(20, 20))\n# 출력 이미지의 크기를 20x20 인치로 설정\n\nfor k in range(10): # 10개의 클래스 각각에 대해 반복 (k는 클래스 레이블)\n    X_data = X_train[Y_train == k] # X_train에서 클래스 k에 해당하는 데이터만 선택\n    Y_data = Y_train[Y_train == k].reset_index()['label'] # Y_train에서 클래스 k에 해당하는 라벨만 선택하고, 인덱스를 리셋하여 'label' 열을 선택\n    for i in range(10): # 각 클래스에서 10개의 샘플 이미지를 반복 (i는 샘플 인덱스) \n        plt.subplot(10, 10, k*10 + i + 1) # 10x10 그리드에서 현재 subplot을 선택 (총 100개 그리드)\n        \n        # x축과 y축의 눈금을 숨김\n        plt.xticks([])\n        plt.yticks([])\n        \n        # 그리드 라인을 숨김\n        plt.grid(False)\n        \n        plt.imshow(X_data[i].reshape(28, 28), cmap='gray') # 현재 샘플 이미지를 28x28 크기로 재구성하여 회색조로 표시\n        \n        label_index = int(Y_data[i]) # 현재 샘플의 라벨 인덱스를 정수로 변환\n        \n        # 현재 subplot의 제목을 클래스 레이블과 클래스 이름으로 설정\n        plt.title('{}: {}'.format(k, class_names[label_index]))\n\nplt.show() # 모든 subplot을 화면에 표시","metadata":{"execution":{"iopub.status.busy":"2024-05-16T08:12:33.628353Z","iopub.execute_input":"2024-05-16T08:12:33.628682Z","iopub.status.idle":"2024-05-16T08:12:39.667598Z","shell.execute_reply.started":"2024-05-16T08:12:33.628655Z","shell.execute_reply":"2024-05-16T08:12:39.666543Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 2-5. One-hot encoding","metadata":{}},{"cell_type":"code","source":"Y_train","metadata":{"execution":{"iopub.status.busy":"2024-05-16T08:12:39.668769Z","iopub.execute_input":"2024-05-16T08:12:39.669066Z","iopub.status.idle":"2024-05-16T08:12:39.676410Z","shell.execute_reply.started":"2024-05-16T08:12:39.669041Z","shell.execute_reply":"2024-05-16T08:12:39.675411Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 라벨을 원-핫 인코딩\nY_train = to_categorical(Y_train, 10)","metadata":{"execution":{"iopub.status.busy":"2024-05-16T08:12:39.677589Z","iopub.execute_input":"2024-05-16T08:12:39.677953Z","iopub.status.idle":"2024-05-16T08:12:39.685277Z","shell.execute_reply.started":"2024-05-16T08:12:39.677925Z","shell.execute_reply":"2024-05-16T08:12:39.684315Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"Y_train","metadata":{"execution":{"iopub.status.busy":"2024-05-16T08:12:39.686676Z","iopub.execute_input":"2024-05-16T08:12:39.687065Z","iopub.status.idle":"2024-05-16T08:12:39.694667Z","shell.execute_reply.started":"2024-05-16T08:12:39.687037Z","shell.execute_reply":"2024-05-16T08:12:39.693578Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 3. 컨볼루션 신경망 설정 후 모델링","metadata":{}},{"cell_type":"markdown","source":"* ModelCheckpoint와 EarlyStopping 콜백을 설정하여 모델 학습 중에 최적의 모델을 저장하고, 조기 종료를 구현","metadata":{}},{"cell_type":"markdown","source":"#### ModelCheckpoint\nModelCheckpoint는 Keras의 콜백 클래스 중 하나로, 모델 학습 과정에서 특정 조건이 충족될 때마다 모델을 저장.\n이 콜백은 학습 도중 모델이 개선될 때마다 모델의 현재 상태를 파일로 저장하는 데 유용.","metadata":{}},{"cell_type":"markdown","source":"#### EarlyStopping\nEarlyStopping은 Keras의 콜백 클래스 중 하나로, 모델 학습 도중 지정된 성능 지표가 개선되지 않으면 학습을 조기에 중지. \n이는 과적합(overfitting)을 방지하고, 불필요하게 긴 학습 시간을 줄일 때 유용","metadata":{}},{"cell_type":"code","source":"# 모델 최적화 설정\nMODEL_DIR = './model/'\nif not os.path.exists(MODEL_DIR):\n    os.mkdir(MODEL_DIR)\n\nmodelpath = './model/{epoch:02d}-{val_loss:.4f}.keras'\n\n# ModelCheckpoint 콜백을 설정하여 검증 손실('val_loss')을 모니터링\n# 모델 파일을 지정된 경로에 저장하고, 'verbose=1'로 설정하여 저장 시 메시지 출력\n# 'save_best_only=True'로 설정하여 최상의 검증 손실을 가진 모델만 저장\ncheckpointer = ModelCheckpoint(filepath=modelpath, monitor='val_loss', verbose=1, save_best_only=True)\n\n# EarlyStopping 콜백을 설정하여 검증 손실('val_loss')을 모니터링\n# 'patience=10'으로 설정하여 10 에포크 동안 검증 손실이 개선되지 않으면 학습을 중지\nearly_stopping_callback = EarlyStopping(monitor='val_loss', patience=10)","metadata":{"execution":{"iopub.status.busy":"2024-05-16T08:12:39.696099Z","iopub.execute_input":"2024-05-16T08:12:39.696458Z","iopub.status.idle":"2024-05-16T08:12:39.703109Z","shell.execute_reply.started":"2024-05-16T08:12:39.696430Z","shell.execute_reply":"2024-05-16T08:12:39.702145Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* model = Sequential(): Sequential 클래스를 사용하여 신경망 모델을 초기화합니다.\n* Conv2D: 2D 컨볼루션 레이어로, 필터 수, 필터 크기, 입력 형태, 활성화 함수 등을 지정합니다.\n* MaxPooling2D: 최대 풀링 레이어로, 공간적 크기를 줄여 주요 특징을 추출합니다.\n* Dropout: 드롭아웃 레이어로, 학습 중 일부 뉴런을 무작위로 비활성화하여 과적합을 방지합니다.\n* Flatten: 평탄화 레이어로, 다차원 출력을 1차원 벡터로 변환합니다.\n* Dense: 완전 연결 레이어로, 지정된 수의 뉴런과 활성화 함수를 사용합니다.\n* model.compile: 모델 컴파일 함수로, 옵티마이저, 손실 함수, 평가 지표 등을 지정합니다.","metadata":{}},{"cell_type":"code","source":"# 컨볼루션 신경망 설정\n\nmodel = Sequential()\n# Sequential 클래스를 사용하여 신경망 모델을 초기화합니다.\n\n# 첫번째 Conv2D 레이터\nmodel.add(Conv2D(32, kernel_size=(3, 3), input_shape=(28, 28, 1), activation='relu'))\n# 32개의 3x3 필터를 사용하여 입력 이미지(28x28, 단일 채널)에서 특징을 추출\n\n# 두 번째 Conv2D 레이어\nmodel.add(Conv2D(64, (3, 3), activation='relu'))\n# 64개의 3x3 필터를 사용하여 이전 레이어의 출력을 처리\n\n# MaxPooling2D 레이어\nmodel.add(MaxPooling2D(pool_size=(2, 2)))\n# 2x2 풀링 윈도우를 사용하여 공간적 크기를 줄이고, 가장 중요한 특징을 추출\n\n# Dropout 레이어\nmodel.add(Dropout(0.25))\n# 학습 중 무작위로 뉴런의 25%를 비활성화하여 과적합을 방지.\n\n# Flatten 레이어\nmodel.add(Flatten())\n# 3차원 출력을 1차원 벡터로 변환하여 Dense 레이어에 전달\n\n# Dense 레이어\nmodel.add(Dense(128, activation='relu'))\n# 128개의 뉴런을 가지고 있으며, ReLU 활성화 함수를 사용하여 비선형성 추가\n\n# 두 번째 Dropout 레이어\nmodel.add(Dropout(0.5))\n# 학습 중 무작위로 뉴런의 50%를 비활성화하여 과적합 방지\n\n# 출력 Dense 레이어\nmodel.add(Dense(10, activation='softmax'))\n# 10개의 뉴런을 가지고 있으며, Softmax 활성화 함수를 사용하여 출력 확률 분포 생성\n# 이는 10개의 클래스 중 하나를 선택하기 위해 사용\n\n# 모델을 컴파일\nmodel.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])\n# optimizer='adam'은 Adam 옵티마이저를 사용하여 학습 속도를 조절\n# loss='categorical_crossentropy'는 다중 클래스 분류 문제를 위한 손실 함수\n# metrics=['accuracy']는 모델의 성능을 평가하기 위해 정확도를 사용\n","metadata":{"execution":{"iopub.status.busy":"2024-05-16T08:12:39.704659Z","iopub.execute_input":"2024-05-16T08:12:39.705486Z","iopub.status.idle":"2024-05-16T08:12:40.552910Z","shell.execute_reply.started":"2024-05-16T08:12:39.705441Z","shell.execute_reply":"2024-05-16T08:12:40.552115Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.summary()","metadata":{"execution":{"iopub.status.busy":"2024-05-16T08:12:40.554006Z","iopub.execute_input":"2024-05-16T08:12:40.554307Z","iopub.status.idle":"2024-05-16T08:12:40.579117Z","shell.execute_reply.started":"2024-05-16T08:12:40.554281Z","shell.execute_reply":"2024-05-16T08:12:40.578256Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"history = model.fit(\n    X_train,                  # 입력 데이터 (특징)\n    Y_train,                  # 타겟 데이터 (라벨)\n    validation_split=0.2,     # 학습 데이터의 20%를 검증 데이터로 사용\n    epochs=20,                # 총 학습 반복 횟수 (에포크 수)\n    batch_size=200,           # 배치 크기. 한 번에 처리할 데이터 샘플의 수\n    verbose=1,                # 학습 과정의 출력 여부 (0: 출력 없음, 1: 출력 있음, 2: 에포크당 한 줄 출력)\n    callbacks=[early_stopping_callback, checkpointer]  # 콜백 함수 리스트 (조기 종료와 체크포인트 저장)\n)","metadata":{"execution":{"iopub.status.busy":"2024-05-16T08:12:40.580099Z","iopub.execute_input":"2024-05-16T08:12:40.580353Z","iopub.status.idle":"2024-05-16T08:13:27.234558Z","shell.execute_reply.started":"2024-05-16T08:12:40.580330Z","shell.execute_reply":"2024-05-16T08:13:27.233687Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 4.그래프 그리기","metadata":{}},{"cell_type":"code","source":"history.history.keys()","metadata":{"execution":{"iopub.status.busy":"2024-05-16T08:13:27.235921Z","iopub.execute_input":"2024-05-16T08:13:27.236237Z","iopub.status.idle":"2024-05-16T08:13:27.242426Z","shell.execute_reply.started":"2024-05-16T08:13:27.236211Z","shell.execute_reply":"2024-05-16T08:13:27.241302Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 4-1. 정확도 & 학습 손실 그래프 그리기","metadata":{}},{"cell_type":"code","source":"# 학습 손실 및 정확도 시각화\nplt.figure(figsize=(12, 4))\nplt.subplot(1, 2, 1)\nplt.plot(history.history['loss'], label='loss')\nplt.plot(history.history['val_loss'], label='val_loss')\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\nplt.legend()\nplt.subplot(1, 2, 2)\nplt.plot(history.history['accuracy'], label='accuracy')\nplt.plot(history.history['val_accuracy'], label='val_accuracy')\nplt.xlabel('Epoch')\nplt.ylabel('Accuracy')\nplt.legend()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-05-16T08:13:27.243738Z","iopub.execute_input":"2024-05-16T08:13:27.244016Z","iopub.status.idle":"2024-05-16T08:13:27.720020Z","shell.execute_reply.started":"2024-05-16T08:13:27.243993Z","shell.execute_reply":"2024-05-16T08:13:27.718993Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 4-2. 예측 수행 및 저장","metadata":{}},{"cell_type":"code","source":"# 테스트 데이터에 대한 예측 수행\npredictions = model.predict(X_test)\n# np.argmax : 주어진 배열에서 가장 큰 값을 가지는 요소의 인덱스를 반환\npredicted_labels = np.argmax(predictions, axis=1)","metadata":{"execution":{"iopub.status.busy":"2024-05-16T08:13:27.721256Z","iopub.execute_input":"2024-05-16T08:13:27.721636Z","iopub.status.idle":"2024-05-16T08:13:29.581546Z","shell.execute_reply.started":"2024-05-16T08:13:27.721609Z","shell.execute_reply":"2024-05-16T08:13:29.580420Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 결과를 submission.csv 파일로 저장\nsubmission = pd.DataFrame({'ImageId': np.arange(1, len(predicted_labels) + 1), 'Label': predicted_labels})\nsubmission.to_csv('submission.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2024-05-16T08:13:29.592163Z","iopub.execute_input":"2024-05-16T08:13:29.592544Z","iopub.status.idle":"2024-05-16T08:13:29.618787Z","shell.execute_reply.started":"2024-05-16T08:13:29.592511Z","shell.execute_reply":"2024-05-16T08:13:29.617965Z"},"trusted":true},"execution_count":null,"outputs":[]}]}