{"cells":[{"metadata":{},"cell_type":"markdown","source":"# <a id='0'>3rd ML Month - Car Model Classification</a>"},{"metadata":{},"cell_type":"markdown","source":"## <a>Introduction</a>\n\n이번 컴페티션에서는, 주어진 자동차 이미지를 총 196개의 클래스로 분류 예측하는 것이 목표입니다.  \nsample_submission에 매핑된 테스트 이미지의 클래스를 예측하여 제출하시면 됩니다.\n\n많은 내용은 [김태진님의 [3rd ML Month] Car Model Classification Baseline](https://www.kaggle.com/fulrose/3rd-ml-month-car-model-classification-baseline)을 참고하였습니다. 부족하거나 틀린 부분있으면 댓글 남겨주시면 감사하겠습니다 :)\n\n### Contents\n\n1. Data exploration\n2. Image Classification"},{"metadata":{},"cell_type":"markdown","source":"### Set up"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport os\nimport time\nimport gc\nprint(os.listdir(\"../input/\"))\n\nimport PIL\nimport cv2","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"#데이터 path\npath = '../input/2019-3rd-ml-month-with-kakr/'\ntrain_img_path = os.path.join(path, 'train')\ntest_img_path = os.path.join(path, 'test')\n\n#seed\nseed = 119","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# <a id='1'>Data exploration</a>"},{"metadata":{},"cell_type":"markdown","source":"## 1. Csv data"},{"metadata":{},"cell_type":"markdown","source":"CSV 파일에는 파일명과 class명, bounding_box의 좌표값이 들어있습니다.\n\n* img_file - 데이터 셋의 각 로우와 연결되는 이미지 파일 이름\n* bbox_x1 - 바운딩 박스 x1 좌표 (좌상단 x)\n* bbox_y1 - 바운딩 박스 y1 좌표 (좌상단 y)\n* bbox_x2 - 바운딩 박스 x2 좌표 (우하단 x)\n* bbox_y2 - 바운딩 박스 y2 좌표 (우하단 y)\n* class - 예측하려는 차종(Target)\n* id - 각 데이터 셋에 기입 되어 있는 클래스 id\n* name - 클래스 id에 대응되는 실제 차종 레이블"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df = pd.read_csv(os.path.join(path, 'train.csv'))\ntest_df = pd.read_csv(os.path.join(path, 'test.csv'))\nclass_df = pd.read_csv(os.path.join(path, 'class.csv'))\nprint(train_df.head())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"데이터는 총 196개의 class로 이루어진 10016장의 Trianing 이미지, 6169장의 Test 이미지입니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"print('Total class is ', len(class_df))\nprint(class_df.head())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"assert len(train_df) == len(os.listdir(train_img_path)) #파일 수와 리스트 수가 동일한지 확인\nprint('Train Image is : ', len(train_df))\n\nassert len(test_df) == len(os.listdir(test_img_path))\nprint('Test Image is : ', len(test_df))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"클래스의 분포를 확인한 결과, class 당 평균 51장의 이미지가 있으며 대체적으로 고르게 이미지가 분포되어 있습니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"#클래스 분포 확인\nplt.figure(figsize=(12, 6))\nsns.countplot(train_df[\"class\"], order=train_df[\"class\"].value_counts(ascending=True).index)\nplt.title(\"Number of data per each class\")\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df['class'].value_counts().describe()[['min', 'mean', 'max']]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 2. Image data\n\n전체 이미지 중 일부를 출력해서 이미지를 확인해보도록하겠습니다. 노랑색 영역은 Bounding box로 추출했을 때의 영역입니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"#이미지 출력함수 정의\ndef plot_img(img_list, path=train_img_path):\n    plt.figure(figsize=(20,20))\n    \n    for num, (index, img) in enumerate(img_list.iterrows()):\n        image = cv2.imread(os.path.join(train_img_path, img.img_file))   #image 파일 load\n        image = cv2.rectangle(image, (img.bbox_x1, img.bbox_y1), (img.bbox_x2, img.bbox_y2), color=(255,255,0), thickness= 5) #bounding box 생성\n        height, width = image.shape[:2] #image의 shape 저장\n        plt.subplot(5, 5, num+1) # 10개의 subplot 중 (num+1) 번쨰 subplot 지정\n        plt.imshow(image)\n        plt.title('%s, (%i * %i)' % (img.img_file, width, height))\n        plt.axis('off')\n        \nimg_list = train_df[:10]\nplot_img(img_list)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#이미지의 width와 height을 저장하는 함수\ndef width_height_img(train_df):\n    start = time.time()\n    w = []  #width를 저장하는 list\n    h = []  #height를 저장하는 list\n\n    for num, df in train_df.iterrows():\n        image = cv2.imread(os.path.join(train_img_path, df.img_file))\n        height, width = image.shape[:2]\n        w.append(width)\n        h.append(height)\n\n    train_df['width'] = w\n    train_df['height'] = h\n    train_df['pixel'] = train_df['width'] * train_df['height']\n    print(time.time() -start)\n    return train_df\n\ntrain_df = width_height_img(train_df)\nprint(train_df.head())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"픽셀 기준으로 살펴보았을 때, 가장 작은 이미지의 크기는 `78 * 58` 이며 가장 큰 이미지는 `5616 * 3744`입니다. "},{"metadata":{"trusted":true},"cell_type":"code","source":"plot_img(train_df.loc[train_df.pixel == min(train_df.pixel)])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plot_img(train_df.loc[train_df.pixel == max(train_df.pixel)])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"하나의 class 이미지만 살펴보았습니다. 차의 정면, 측면, 후면까지 다양한 모습이 저장되어 있으며, 특히 차량의 색이 달라도 같은 class로 저장되어 있는 점을 볼 수 있습니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"temp = train_df.loc[train_df['class'] == 115]\nplot_img(temp[:15])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"이미지의 종횡비를 살펴보면 종횡비가 1 이상의 수치로 분포되는 것으로 보아, 대부분 이미지의 width의 길이가 더 긴 것을 볼 수 있습니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"ratio = train_df['width'] / train_df['height']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.hist(ratio, bins=20)\nplt.xlim(0.5, 2.5)\nplt.ylim(0, 8000)\nplt.title('Ratio histogram')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Crop Image\n\n학습 데이터의 정화도를 높이기위해 주어진 Boundind box 좌표를 이용해서 해당 영역의 이미지만을 사용하도록 하겠습니다. Bounding box 영역을 뽑아내는 방법은 단순히 image에서 해당 좌표영역만 선택하면 됩니다.\n\n* csv 안에 있는 파일명을 그대로 사용하기위해 수정 후 이미지파일명은 따로 변경하지 않겠습니다.\n* [허태명님의 [3rd ML Month] Car Image Cropping](https://www.kaggle.com/tmheo74/3rd-ml-month-car-image-cropping)의 output file을 가져와서 사용하면 이 과정을 스킵해도 됩니다!"},{"metadata":{"trusted":true},"cell_type":"code","source":"#Bounding box image를 저장하는 함수\ndef save_cropped_img(train_df=train_df, path=path, save_path = None):\n    for num, df in train_df.iterrows():\n        img = cv2.imread(os.path.join(path, df.img_file))\n        #print(img.shape)\n        img = img[df.bbox_y1 : df.bbox_y2, df.bbox_x1: df.bbox_x2]\n        #print(img.shape)\n        name = save_path + df.img_file\n        cv2.imwrite(name, img)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"tt = train_df[:1]\n\nfor num, df in tt.iterrows():\n    print(df.img_file)\n    img = cv2.imread(os.path.join(train_img_path, df.img_file))\n    print(img.shape)\n    img = img[df.bbox_y1 : df.bbox_y2, df.bbox_x1: df.bbox_x2]\n    plt.imshow(img)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#새로 만든 이미지를 저장할 path를 생성\n'''%%time\n!mkdir /crop_train\n#crop_train_path = '/crop_train/'\nsave_cropped_img(train_df, path=train_img_path, save_path=crop_train_path)'''","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"'''#모든 이미지가 복사됬는지 확인\nassert len(os.listdir(train_img_path)) == len(os.listdir(crop_train_path))\nprint(len(os.listdir(crop_train_path)))'''","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Test 이미지도 저장합니다.\n'''%%time\n!mkdir /crop_test\ncrop_test_path = '/crop_test/'\nsave_cropped_img(test_df, path=test_img_path, save_path=crop_test_path)'''","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"파일을 직접 받아서 사용하는 경우, 파일의 수가 서로 맞는지 확인합니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"crop_train_path = '../input/2019-3rd-ml-month-with-kakr/train/'\nassert len(os.listdir('../input/crop-image/train_crop/')) == len(os.listdir(crop_train_path))\nprint('Train images : ', len(os.listdir('../input/crop-image/train_crop/')))\n\ncrop_test_path = '../input/2019-3rd-ml-month-with-kakr/test/'\nassert len(os.listdir('../input/crop-image/test_crop//')) == len(os.listdir(crop_test_path))\nprint(\"Test images : \", len(os.listdir('../input/crop-image/test_crop//')))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# <a id='2'>Image classification</a>"},{"metadata":{},"cell_type":"markdown","source":"image classification 과정은 다음과 같이 진행합니다.\n\n    1. 데이터 전처리\n    2. 모델선택 및 완성(구조, optimizer, loss 등)\n    3. 모델 학습\n    4. 예측\n"},{"metadata":{},"cell_type":"markdown","source":"## 1. Data preprocessing\n\n전처리 과정에서는 모델에 넣을 이미지의 크기(input size)와 RGB 채널, Augmentation 사용 여부를 지정합니다. Keras에서 제공하는 모델구조를 쓸 경우에는 `preprocess_input`를 import하여 쉽게 데이터 전처리를 할 수 있습니다. 또한 제너레이터(generator)를 사용한다면 이 과정을 제너레이터 생성 파라미터로 넣을 수 있습니다.\n\n여기서는 주어진 csv 파일의 데이터를 이용해서 제너레이터를 생성하여 이미지를 불러오는 방법을 사용하겠습니다. 제너레이터에 대한 설명 및 코드는  [[3rd ML Month] Car Model Classification Baseline](https://www.kaggle.com/fulrose/3rd-ml-month-car-model-classification-baseline)에 자세히 나와있네요. baseline 커널에도 언급되어 있듯이 kaggle 커널에서 전체 이미지를 메모리에 올릴수 없으므로 꼭 있어야합니다!\n\n* dataframe으로 불러온 csv 파일을 이용하기 위해서는 `flow_from_dataframe` 메서드를 사용해서 제너레이터를 만듭니다.\n* 이때 사용할 `class` 데이터 type이 string 이여야합니다. 따라서 dataframe에서 class의 데이터 타입을 확인하고 string이 아니라면 변경하도록 하겠습니다.\n* 제너레이터 클래스를 정의하는 과정에서 agumentation 방법 설정은 [김태영의 케라스 imagegenerator](https://tykimos.github.io/2017/06/10/CNN_Data_Augmentation/)에 상세히 나와있습니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"#'class' 가 int64 type으로 저장되어 있습니다.\ntrain_df.info()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#type 변환\ntrain_df['class'] = train_df['class'].astype('str')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"다음으로 validation dataset을 생성합니다. \n\n간혹 validation dataset에 모든 class가 포함되지 않으면 밑에서 에러가 발생하게 됩니다. 이 문제를 방지하기 위해 `train_test_split`의 `stratify` 파라미터 값에 target값(class)를 넣으면 target 값이 고르게 뽑힌 데이터셋을 얻을 수 있습니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\ntr_data, val_data = train_test_split(train_df[['img_file', 'class']], train_size=0.9, random_state=seed, stratify=train_df['class'])\ntest_data = test_df[['img_file']]\nprint(len(tr_data), len(val_data))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"assert tr_data['class'].nunique() == val_data['class'].nunique()\nprint('All class image in val_data')\nsns.countplot(val_data['class'])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"제너레이터를 생성합니다. 데이터셋 별로 나눠서 생성하면 됩니다.\n\n** base_line 커널의 코드를 그대로 사용하는 경우, test image를 순서대로 불러와야 결과값이 제대로 저장되기 때문에 `shuffle=False`를 반드시 사용해야 합니다. "},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.applications.mobilenet import preprocess_input #사용할 모델명\nfrom keras.preprocessing.image import ImageDataGenerator","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Hyper Parameter\ninput_size = (224,224) \nbatch_size = 32\nepochs = 22","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#preprocessing만 변경해서 사용할 수 있도록 함수로 정의\ndef make_generator(input_size= input_size, batch_size=batch_size, preprocessing_function=preprocess_input):\n    #ImageDataGenerator 설정을 정의합니다.\n    train_datagen = ImageDataGenerator(horizontal_flip=True,    #수평 반전\n                                       zoom_range= 0.15,        #확대 & 축소\n                                       width_shift_range= 0.1,  #수평방향 이동\n                                       height_shift_range=0.1,  #수직방향 이동\n                                       preprocessing_function= preprocessing_function  \n                                      )\n\n    val_datagen = ImageDataGenerator(preprocessing_function=preprocess_input)\n    test_datagen = ImageDataGenerator(preprocessing_function=preprocess_input)\n\n    #Train Generator를 생성합니다.\n    train_generator = train_datagen.flow_from_dataframe(dataframe=tr_data, \n                                                        directory=crop_train_path,  #path는 맞게 수정\n                                                        x_col='img_file', \n                                                        y_col='class', \n                                                        target_size=input_size, \n                                                        color_mode='rgb',  #RGB라면 'rgb'\n                                                        class_mode='categorical', \n                                                        batch_size=batch_size, \n                                                        #shuffe = True,\n                                                        seed=seed)\n\n    #Valid Generator를 생성합니다.\n    valid_generator = val_datagen.flow_from_dataframe(dataframe=val_data, \n                                                     directory=crop_train_path, #path는 맞게 수정\n                                                     x_col= 'img_file',\n                                                     y_col= 'class',\n                                                     target_size= input_size,\n                                                     color_mode= 'rgb',\n                                                     class_mode= 'categorical',\n                                                     batch_size= batch_size,\n                                                     shuffle=True,\n                                                     seed = seed)\n\n    #Test Generator를 생성합니다.\n    test_generator = test_datagen.flow_from_dataframe(dataframe=test_data, \n                                                        directory=crop_test_path, #path는 맞게 수정\n                                                        x_col='img_file',\n                                                        y_col=None,              #없는 데이터이므로 None\n                                                        target_size=input_size, \n                                                        color_mode='rgb', \n                                                        class_mode=None, \n                                                        batch_size=batch_size,\n                                                        shuffle=False,\n                                                        seed=seed)\n    \n    return train_generator, valid_generator, test_generator","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_generator, valid_generator, test_generator = make_generator()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 2. Create Model\n\n데이터를 준비했다면 모델을 만들어봅시다. 모델구조는 직접 만들수도 있지만 Keras에서 대표적인 몇가지 모델을 이용할 수 있도록 제공하고 있습니다. 모델의 종류는 [Keras application documnent](https://keras.io/applications/)에서 볼 수 있습니다. 구조뿐만 아니라 imagenet 데이터셋으로 미리 학습시킨 모델의 weight를 사용할 수도 있습니다. \n\n여기서는 Mobilenet이라는 구조를 사용하며, pretrained weight을 불러와서 모델을 만들도록 하겠습니다. \n* 모델을 데이터(class 수)에 맞추기 위해 모델의 끝부분은 가져오지 않습니다.\n* 모델을 만들고 학습시킬(업데이트할) weight 영역을 지정합니다.\n* Optimizer와 loss, metrics를 설정합니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.applications import MobileNet\nfrom keras.layers import Dense, GlobalAveragePooling2D #, ZeroPadding2D, Conv2D\nfrom keras.models import Model\nfrom keras.optimizers import Adam\nfrom keras.callbacks import ReduceLROnPlateau, ModelCheckpoint, EarlyStopping","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#기본 모델을 불러옵니다.\nbase_model = MobileNet(input_shape=(224,224,3), \n                         weights='imagenet', #imagenet pretrained weight 불러오기\n                         include_top=False) #include_top로 끝부분 미포함","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#모델의 마지막 부분을 완성합니다. fully_connected layer인 Dense안에는 분류하고자하는 전체 class 수의 넣습니다.\n#activation은 class의 수가 여러개(multi) 일떄는 softmax를, 이진분류(binary)인 경우는 sigmoid를 사용합니다.\nx = base_model.output\nx = GlobalAveragePooling2D()(x)\npred = Dense(196, activation='softmax', kernel_initializer='he_normal')(x)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#최종 모델 생성\nmodel = Model(inputs=base_model.input, output=pred)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"pretrained weight 부분을 학습시킬지 설정합니다. 기본값은 `True`이므로 전체모델을 학습할 떄는 설정할 필요가 없지만, 특정 부분의 weight를 고정하고 싶으면 `False`로 바꿔줘야합니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"'''for layer in base_model.layers:\n    layer.trainable = False\n    \n#마지막 Dense layer의 파라미터만 학습됩니다.\nprint(model.trainable_weights)'''","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"마지막으로 optimizer와 loss, metrics을 정해서 모델을 완성합니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"model.compile(optimizer=Adam(lr=0.001, epsilon=1e-08), loss='categorical_crossentropy', metrics=['acc'])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 3. Model training \n\n이제 모델을 학습하도록 하겠습니다. 위에서 제너레이터를 사용하므로 `fit_generator`로 학습을 진행합니다. 이때`callback`을 설정해서 학습 진행중에 learning rate를 변경하거나 earlystopping 을 설정할 수 있습니다. "},{"metadata":{"trusted":true},"cell_type":"code","source":"#steps_per_epoch 설정 함수\ndef get_steps(num_samples, batch_size):\n    if (num_samples % batch_size) > 0 :\n        return (num_samples // batch_size) + 1\n    else :\n        return num_samples // batch_size","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#callback 설정\nlearning_rate_reduction = ReduceLROnPlateau(monitor='val_loss', patience=3,\n                                           verbose=1, factor=0.5, min_lr=0.0001)\nfilepath = 'model_{val_acc:.2f}_{val_loss:.2f}.h5'\nmodel_ckpt = ModelCheckpoint(filepath, monitor='val_acc', verbose=1, save_best_only=True)\n#es = EarlyStopping(monitor='val_acc', min_delta=0, patience=3, verbose=1, mode='auto')\n\ncallbacks = [learning_rate_reduction, model_ckpt]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#history로 학습과정 데이터를 저장합니다.\nhistory = model.fit_generator(train_generator, \n                              steps_per_epoch=get_steps(len(tr_data), batch_size), \n                              validation_data= valid_generator,\n                              validation_steps= get_steps(len(val_data), batch_size),\n                              epochs=epochs,\n                              callbacks=callbacks, \n                              verbose=1)\ngc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Plot training & validation accuracy values\nfig, ax = plt.subplots()\nplt.plot(history.history['acc'])\nplt.plot(history.history['val_acc'])\nplt.title('Model accuracy')\nplt.ylabel('Accuracy')\nplt.xlabel('Epoch')\nplt.legend(['Train', 'Val'], loc='upper left')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 4. Prediction\n\n학습한 모델로 test 이미지를 넣어 예측값을 구하고 결과값을 저장해서 `submission` 파일을 만듭니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"test_generator.reset()    #Generator 초기화\nprediction = model.predict_generator(\n    generator = test_generator,\n    steps = get_steps(len(test_data), batch_size),\n    verbose=1\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"predicted_class_indices=np.argmax(prediction, axis=1)\n\n# Generator class dictionary mapping\nlabels = (train_generator.class_indices)\nlabels = dict((v,k) for k,v in labels.items())\npredictions = [labels[k] for k in predicted_class_indices]\n\nsubmission = pd.read_csv(os.path.join(path, 'sample_submission.csv'))\nsubmission[\"class\"] = predictions\nsubmission.to_csv(\"submission.csv\", index=False)\nsubmission.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### **번외 1. Xception 모델 with cross validation\n\n단순하게 train, validation dataset를 다시 구성하는 방법을 추가하여 모델 학습을 진행합니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.applications.xception import Xception\nfrom keras.applications.xception import preprocess_input","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"base_model_2 = Xception(input_shape=(224,224,3), weights= 'imagenet', include_top=False) \nx = base_model_2.output\nx = GlobalAveragePooling2D()(x)\npred = Dense(196, activation='softmax', kernel_initializer='he_normal')(x)\n\nmodel_2 = Model(inputs=base_model_2.input, output=pred)\nmodel_2.compile(optimizer=Adam(lr=0.001, epsilon=1e-08), loss='categorical_crossentropy', metrics=['acc'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#callback 설정\nlearning_rate_reduction = ReduceLROnPlateau(monitor='val_loss', patience=3,\n                                           verbose=1, factor=0.5, min_lr=0.0001)\nfilepath = 'model_2_{val_acc:.2f}_{val_loss:.2f}.h5'\nmodel_ckpt = ModelCheckpoint(filepath, monitor='val_acc', verbose=1, save_best_only=True)\n#es = EarlyStopping(monitor='val_acc', min_delta=0, patience=3, verbose=1, mode='auto')\n\ncallbacks = [learning_rate_reduction, model_ckpt]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from random import randint\n\nhistory_2 = []\nfor i in range(6):\n    seed = randint(0, 200)\n    tr_data, val_data = train_test_split(train_df[['img_file', 'class']], train_size=0.9, random_state=seed, stratify=train_df['class'])\n    train_generator, valid_generator, test_generator = make_generator(preprocessing_function=preprocess_input)\n    \n    history = model_2.fit_generator(train_generator, \n                                      steps_per_epoch=get_steps(len(tr_data), batch_size), \n                                      validation_data= valid_generator,\n                                      validation_steps= get_steps(len(val_data), batch_size),\n                                      epochs=4,\n                                      callbacks=callbacks, \n                                      verbose=1)\n    history_2.append(history)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"acc_list = []\nval_acc_list = []\nfor _, i in enumerate(history_2):\n    acc_list.append(i.history['acc'])\n    val_acc_list.append(i.history['val_acc'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, ax = plt.subplots()\nplt.plot(acc_list)\nplt.plot(val_acc_list)\nplt.title('Model accuracy')\nplt.ylabel('Accuracy')\nplt.xlabel('Epoch')\nplt.legend(['Train', 'val'], loc='upper left')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_generator.reset()\nprediction = model_2.predict_generator(\n    generator = test_generator,\n    steps = get_steps(len(test_df), batch_size),\n    verbose=1\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"predicted_class_indices=np.argmax(prediction, axis=1)\n\n# Generator class dictionary mapping\nlabels = (train_generator.class_indices)\nlabels = dict((v,k) for k,v in labels.items())\npredictions = [labels[k] for k in predicted_class_indices]\n\nsubmission = pd.read_csv(os.path.join(path, 'sample_submission.csv'))\nsubmission[\"class\"] = predictions\nsubmission.to_csv(\"submission2.csv\", index=False)\nsubmission.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### **번외 2. grayscale 적용하기 with MoblieNet\n\nKeras application의 모델 구조는 input으로 rgb 값이 있는 이미지만 받을 수 있습니다. 단순히 grayscale만 적용할 경우, input에서 에러가 발생하게 됩니다. 해결방법 중 하나로 이미지를 grayscale로 바꾼 후 같은 값을 복사하여 3채널의 이미지처럼 만들어 모델을 학습시켜보도록 하겠습니다.\n\n** 혹시 다른 방법이 있거나, 모델의 input layer 구조만 변경하는 방법을 알고 계신분은 댓글 남겨주시면 감사하겠습니다!\n\n\n* 결과치\n    - 기본 모델 : 0.87315\n    - grayscale 변경 모델 : 0.85407"},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.applications import MobileNet\nfrom keras.applications.mobilenet import preprocess_input","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"`preprocessing_function` 부분에서 grayscale 변환 후 복사하도록 수정"},{"metadata":{"trusted":true},"cell_type":"code","source":"def gray_preprocess(img):\n    img = preprocess_input(img)\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n    img = np.repeat(img[:,:, np.newaxis], 3, axis=2)\n    return img","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"seed = 119\ntr_data, val_data = train_test_split(train_df[['img_file', 'class']], train_size=0.9, random_state=seed, stratify=train_df['class'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_generator, valid_generator, test_generator = make_generator(preprocessing_function=gray_preprocess)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for i in train_generator:\n    print(i[0][0].shape)\n    plt.imshow(i[0][0])\n    break","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"base_model_3 = MobileNet(input_shape=(224,224,3), weights='imagenet', include_top=False)\n\nx = base_model_3.output\nx = GlobalAveragePooling2D()(x)\npred = Dense(196, activation='softmax')(x)\n\nmodel_3 = Model(inputs=base_model_3.input, output=pred)\nmodel_3.compile(optimizer=Adam(lr=0.001, epsilon=1e-08), loss='categorical_crossentropy', metrics=['accuracy'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#callback 설정\nlearning_rate_reduction = ReduceLROnPlateau(monitor='val_loss', patience=3,\n                                           verbose=1, factor=0.5, min_lr=0.0001)\nfilepath = 'model_3_{val_acc:.2f}_{val_loss:.2f}.h5'\nmodel_ckpt = ModelCheckpoint(filepath, monitor='val_acc', verbose=1, save_best_only=True)\n#es = EarlyStopping(monitor='val_acc', min_delta=0, patience=3, verbose=1, mode='auto')\n\ncallbacks = [learning_rate_reduction, model_ckpt]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#history로 학습과정 데이터를 저장합니다.\nhistory_3 = model_3.fit_generator(train_generator, \n                              steps_per_epoch=get_steps(len(tr_data), batch_size), \n                              validation_data= valid_generator,\n                              validation_steps= get_steps(len(val_data), batch_size),\n                              epochs=epochs,\n                              callbacks=callbacks, \n                              verbose=1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Plot training & validation accuracy values\nfig, ax = plt.subplots()\nplt.plot(history_3.history['acc'])\nplt.plot(history_3.history['val_acc'])\nplt.title('Model accuracy')\nplt.ylabel('Accuracy')\nplt.xlabel('Epoch')\nplt.legend(['Train', 'Val'], loc='upper left')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_generator.reset()\nprediction = model_3.predict_generator(\n    generator = test_generator,\n    steps = get_steps(len(test_df), batch_size),\n    verbose=1\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"predicted_class_indices=np.argmax(prediction, axis=1)\n\n# Generator class dictionary mapping\nlabels = (train_generator.class_indices)\nlabels = dict((v,k) for k,v in labels.items())\npredictions = [labels[k] for k in predicted_class_indices]\n\nsubmission = pd.read_csv(os.path.join(path, 'sample_submission.csv'))\nsubmission[\"class\"] = predictions\nsubmission.to_csv(\"submission3.csv\", index=False)\nsubmission.head()","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.4","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}