{"cells":[{"metadata":{},"cell_type":"markdown","source":"# 2019 3rd ML month with KaKR\n### 자동차 이미지 데이터셋을 이용한 자동차 차종 분류\n\n## Introduction\n\n안녕하세요. Titanic으로 kaggle에 입문하자마자 무작정 3차 대회에 뛰어든 캐글 초보자입니다. <br>\n제가 이렇게 Notebook을 올리는 이유는 다른 고수분들의 Notebook과 비교하면 부끄러운 수준이지만 제 스스로 대회 기간동안 고민하면서 공부한 과정들을 \n복기하기 위함입니다. <br>\n또한 딥러닝 지식이 전무하다시피 했던 저의 경험을 공유하면서 추후에 대회를 시작하는 초보자분들께 조금이나마 도움이 됐으면 하는 바람입니다.\n\n\n<br>\n\n\n## Reference\n- Image Segmentation Crop (Steve Jang님 notebook) <br>\n  https://www.kaggle.com/cruiserx/3rd-ml-month-car-image-segmentation-crop\n  \n- Keras / Xception (vh1981님 notebook) <br>\n  https://www.kaggle.com/vh1981/kakr-3rd-keras-xception \n  \n- Pretrained Model (Daehun Gwak님 notebook) <br>\n  https://www.kaggle.com/daehungwak/keras-how-to-use-pretrained-model\n  \n- Xception (Jang님 notebook) <br>\n  https://www.kaggle.com/janged/3rd-ml-month-car-model-classification-xception\n\n\nReference Notebook을 간단하게 4개로 추려서 올렸지만 다른 분들께서 올려주신 Notebook, Discussion들을 전부 봤다고해도 과언이 아닐 정도로\n꾸준히 참고하면서 많은 도움을 받았습니다. 공유해주신 모든 분들께 감사드립니다!\n\n\n<br>\n\n\n## Process\n\n제가 대회를 준비하면서 겪은 과정들입니다.\n\n### 1. 필사하기.\n - 태진님께서 올려주신 Baseline을 기반으로 필사하면서 안에 담긴 이론들과 Keras를 공부했습니다.\n - Jang님께서 올려주신 Xception 모델을 사용한 Notebook을 필사하면서 Pre-Trained Model, Transfer Learning을 공부했습니다.\n - 동시에 Daehun Gwak님께서 올려주신 How to use pretrained model Notebook도 많이 참고했습니다.\n\n### 2. 나만의 Baseline을 만들기.\n - 여태까지 필사하면서 공부한 자료로 Xception 모델을 사용한 Baseline을 만들었습니다.\n - 그 때의 Public Score는 0.90003이었고 점수를 올리기 위해 Stratified KFold, 앙상블 기법을 사용해야겠다고 생각했습니다.\n - 또한 Private Score를 잘 받기 위해서는 Overfitting을 막기 위한 일반화가 필수라는 것을 배웠습니다.\n \n### 3. 최종 Modeling\n - Stratified KFold를 사용해서 5개의 fold를 만들었고 이를 각각의 Notebook으로 불러와서 model을 만드는 법을 배웠습니다.\n - 또한 Steve Jang님의 Segmentation crop방법을 이용한 dataset을 사용했습니다. (bounding box로 crop한 dataset보다 더 좋은 성능이 나올 것이라고 예상했습니다.)\n - vh1981님의 https://www.kaggle.com/vh1981/kakr-3rd-keras-xception 을 기반으로 만들었습니다.\n - 위에서 만든 Segmentation dataset과 5개의 fold file, model들을 하나의 커널로 불러왔고 Stacking을 통한 앙상블 기법 중 하나로 weight average model을 사용했습니다.\n\n\n<br>\n\n\n## Result\n\n저는 Private LB 기준으로 0.93668의 Score를 받아 61등으로 대회를 마쳤는데요, 개인적으로는 공부만 하다보니 제출을 많이 못했던 점이 아쉬웠습니다. <br>\nSteve Jang님의 Solution Notebook을 보시면 Segmentation crop Dataset을 사용했을때 점수가 오히려 안나왔다고 말씀해주셨는데요. <br>\n이를 보고 저도 Dataset을 허태명님께서 올려주신 Bounding box로 crop한 dataset을 사용해서 다시 시도해봤더니 0.93668에서 0.94930로 오른 것을 확인할 수 있었습니다. 이 점도 아쉽긴 하지만 좋은 접근이었다고 생각합니다."},{"metadata":{},"cell_type":"markdown","source":"---"},{"metadata":{},"cell_type":"markdown","source":"## Load library"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import os\nimport warnings\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\nimport matplotlib.pyplot as plt\n\nfrom keras import backend as K\nwarnings.filterwarnings(action='ignore')\n\nK.image_data_format()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## File load"},{"metadata":{"trusted":true},"cell_type":"code","source":"# path 목록\nMODEL_PATH = '../input/kakr3rdxception/kakr-3rd-xception'\nFOLD_DATA_PATH = '../input/3rd-ml-df-folds/3rd_ml_df_folds/'\nDATA_PATH = '../input/carimagesegcrop/car-image-segcropping'\n\n# CSV 파일 경로\ndf_train = pd.read_csv(os.path.join(DATA_PATH, 'train.csv'))\ndf_test = pd.read_csv(os.path.join(DATA_PATH, 'test.csv'))\ndf_class = pd.read_csv(os.path.join(DATA_PATH, 'class.csv'))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### 사용할 constant 변수들을 정의해줍니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"# Xception : (299, 299)\nIMAGE_WIDTH, IMAGE_HEIGHT = (299, 299)\nclasses = df_class['id'].values.astype('str').tolist()\n\nBATCH_SIZE = 32\nEPOCHS = 30\nK_FOLDS = 5\nPATIENCE = 6","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"epochs = EPOCHS\nbatch_size = BATCH_SIZE\n\ndef get_total_batch(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":{},"cell_type":"markdown","source":"#### 이번 competition의 평가 지표가 F1 Score이므로 이를 정의해줍니다.\n#### http://blog.naver.com/PostView.nhn?blogId=wideeyed&logNo=221226716255 의 글을 참고하시면 좋을것 같습니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"def recall(y_target, y_pred):\n    # clip(t, clip_value_min, clip_value_max) : clip_value_min~clip_value_max 이외 가장자리를 깎아 낸다\n    # round : 반올림한다\n    y_target_yn = K.round(K.clip(y_target, 0, 1)) # 실제값을 0(Negative) 또는 1(Positive)로 설정한다\n    y_pred_yn = K.round(K.clip(y_pred, 0, 1)) # 예측값을 0(Negative) 또는 1(Positive)로 설정한다\n\n    # True Positive는 실제 값과 예측 값이 모두 1(Positive)인 경우이다\n    count_true_positive = K.sum(y_target_yn * y_pred_yn) \n\n    # (True Positive + False Negative) = 실제 값이 1(Positive) 전체\n    count_true_positive_false_negative = K.sum(y_target_yn)\n\n    # Recall =  (True Positive) / (True Positive + False Negative)\n    # K.epsilon()는 'divide by zero error' 예방차원에서 작은 수를 더한다\n    recall = count_true_positive / (count_true_positive_false_negative + K.epsilon())\n\n    # return a single tensor value\n    return recall\n\n\ndef precision(y_target, y_pred):\n    # clip(t, clip_value_min, clip_value_max) : clip_value_min~clip_value_max 이외 가장자리를 깎아 낸다\n    # round : 반올림한다\n    y_pred_yn = K.round(K.clip(y_pred, 0, 1)) # 예측값을 0(Negative) 또는 1(Positive)로 설정한다\n    y_target_yn = K.round(K.clip(y_target, 0, 1)) # 실제값을 0(Negative) 또는 1(Positive)로 설정한다\n\n    # True Positive는 실제 값과 예측 값이 모두 1(Positive)인 경우이다\n    count_true_positive = K.sum(y_target_yn * y_pred_yn) \n\n    # (True Positive + False Positive) = 예측 값이 1(Positive) 전체\n    count_true_positive_false_positive = K.sum(y_pred_yn)\n\n    # Precision = (True Positive) / (True Positive + False Positive)\n    # K.epsilon()는 'divide by zero error' 예방차원에서 작은 수를 더한다\n    precision = count_true_positive / (count_true_positive_false_positive + K.epsilon())\n\n    # return a single tensor value\n    return precision\n\n\ndef f1score(y_target, y_pred):\n    _recall = recall(y_target, y_pred)\n    _precision = precision(y_target, y_pred)\n    # K.epsilon()는 'divide by zero error' 예방차원에서 작은 수를 더한다\n    _f1score = ( 2 * _recall * _precision) / (_recall + _precision+ K.epsilon())\n    \n    # return a single tensor value\n    return _f1score","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### 전체 데이터에서의 class 간 비율을 fold에서도 동일하게 유지하기 위해 StratifiedKFold를 사용합니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.model_selection import StratifiedKFold, KFold\nskfold = StratifiedKFold(n_splits=K_FOLDS, random_state=1993)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### train / validation에 사용할 ImageDataGenerator를 생성해줍니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.applications.resnet50 import ResNet50, preprocess_input\nfrom keras.preprocessing.image import ImageDataGenerator\n\ndatagen_train = ImageDataGenerator(\n    rescale = 1./255,               \n    featurewise_center = False,              # set input mean to 0 over the dataset\n    samplewise_center = False,               # set each sample mean to 0\n    featurewise_std_normalization = False,   # divide inputs by std of the dataset\n    samplewise_std_normalization = False,    # divide each input by its std\n    zca_whitening = False,                   # apply ZCA whitening\n    rotation_range = 20,                     # randomly rotate images in the range (degrees, 0 to 180)\n    zoom_range = 0.1,                        # randomly zoom range\n    width_shift_range = 0.1,                 # randomly zoom images horizontally (fraction of total width)\n    height_shift_range = 0.1,                # randomly shift images vertically (fraction of total height)\n    horizontal_flip = True,                  # randomly flip images\n    vertical_flip = False,                   # randomly flip images\n    preprocessing_function = preprocess_input\n)\n\n# validation, test셋 이미지는 augmentation을 적용하지 않습니다.\ndatagen_val = ImageDataGenerator(\n    rescale = 1./255     \n#     featurewise_center = False,              # set input mean to 0 over the dataset\n#     samplewise_center = False,               # set each sample mean to 0\n#     featurewise_std_normalization = False,   # divide inputs by std of the dataset\n#     samplewise_std_normalization = False,    # divide each input by its std\n#     zca_whitening = False,                   # apply ZCA whitening\n#     rotation_range = 20,                     # randomly rotate images in the range (degrees, 0 to 180)\n#     zoom_range = 0.1,                        # randomly zoom range\n#     width_shift_range = 0.1,                 # randomly zoom images horizontally (fraction of total width)\n#     height_shift_range = 0.1,                # randomly shift images vertically (fraction of total height)\n#     horizontal_flip = True,                  # randomly flip images\n#     vertical_flip = False,                   # randomly flip images\n#     preprocessing_function = preprocess_input\n)\n\ndf_train['class'] = df_train['class'].astype('str')   # 안해주면 Error 발생.","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Model 정의"},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.applications.resnet50 import ResNet50, preprocess_input\nfrom keras.applications.xception import Xception\nfrom keras.models import Sequential, Model\nfrom keras.layers import Dense, Dropout, Flatten, Activation, Conv2D, GlobalAveragePooling2D\n\nmodels = {}\n\ndef get_model(base_model):\n    base_model = base_model(weights='imagenet', include_top = False, input_shape=(IMAGE_WIDTH, IMAGE_HEIGHT, 3))\n    \n    model = Sequential()\n    model.add(base_model)\n    model.add(GlobalAveragePooling2D())\n    model.add(Dense(196, activation='softmax', kernel_initializer='he_normal'))          # 196개의 class를 분류해야하므로 활성화 함수로는 softmax, \n    model.summary()\n    \n    model.compile(optimizer = 'adam', loss = 'categorical_crossentropy', metrics = ['acc', f1score])\n    \n    return model\n\n__models = {\"Xception\" : Xception}","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Model 훈련시 사용할 callback 목록을 정의합니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau, LambdaCallback\n\ndef get_callbacks(model_save_filename, patience):\n    # 더 이상 개선의 여지가 없을 때 학습을 종료시키는 역할.\n    es = EarlyStopping(monitor = 'val_f1score', \n                       min_delta = 0,                   # 개선되고 있다고 판단하기 위한 최소 변화량.\n                       patience = patience, \n                       verbose = 1,                     # 진행사항 출력여부 표시.\n                       mode = 'max'                     # 관찰하고 있는 항목이 증가되는 것을 멈출 때 종료합니다.\n                       )  \n    \n    # 모델의 정확도가 향상되지 않는 경우, learning rate를 줄여주는 역할.\n    rr = ReduceLROnPlateau(monitor = 'val_f1score', \n                           factor = 0.5,                # 콜백이 호출되면 학습률을 줄이는 정도.\n                           patience = patience / 2,\n                           min_lr = 0.000001,\n                           verbose = 1,\n                           mode = 'max'\n                           )\n    \n    # Keras에서 모델을 학습할 때마다 중간중간에 콜백 형태로 알려주는 역할.\n    mc = ModelCheckpoint(filepath = model_save_filename, \n                         monitor = 'val_f1score', \n                         verbose = 1, \n                         save_best_only = True,         # 모델의 정확도가 최고값을 갱신했을 때만 저장하도록 하는 옵션.\n                         mode = 'max'\n                         )\n    \n    return [es, rr, mc]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Model을 train 해줍니다. (아래와 같은 과정으로 Model을 train했고 이를 불러옴)"},{"metadata":{"trusted":true},"cell_type":"code","source":"import ssl\nfrom keras.models import model_from_json\n\n# ssl 에러를 해결하기 위함.\nssl._create_default_https_context = ssl._create_unverified_context\nhistory_list = {}\nfor _m in __models:\n    print(\"Model : \", _m)\n    \n    # 미리 fold를 나누어 생성해 둔 dataframe 파일을 사용합니다.\n    for fold_index in range(K_FOLDS):\n        os.system(\"training : model %s fold %d\" % (_m, fold_index))\n\n        # Model 생성.\n        model = get_model(__models[_m])\n        \n        # 마찬가지로 미리 생성해둔 weight 파일을 불러와서 MODEL_PATH에 저장합니다.\n        model_save_filename = (\"%s_%d.h5\" % (_m , fold_index))\n        model_save_filepath = os.path.join(MODEL_PATH, model_save_filename)\n        \n        # 나눠진 dataframe을 load.\n        df_train_filename = (\"fold_%d_train.csv\" % fold_index)\n        df_val_filename = (\"fold_%d_val.csv\" % fold_index)\n\n        dataframe_train = pd.read_csv(os.path.join(FOLD_DATA_PATH, df_train_filename))\n        dataframe_val = pd.read_csv(os.path.join(FOLD_DATA_PATH, df_val_filename))\n        \n        # 아래 안해주면 에러남. categorical이어서 기준 col이 숫자값이면 안되는 것인듯.\n        dataframe_train['class'] = dataframe_train['class'].astype('str')\n        dataframe_val['class'] = dataframe_val['class'].astype('str')\n\n        # ImageDataGenerator 생성(train/val)\n        datagen_train_flow = datagen_train.flow_from_dataframe(dataframe=dataframe_train,\n                                                   directory=os.path.join(DATA_PATH, \"train_segcrop\"),\n                                                   x_col='img_file',\n                                                   y_col=\"class\",\n                                                   classes = classes,\n                                                   target_size=(IMAGE_WIDTH, IMAGE_HEIGHT),\n                                                   color_mode='rgb',\n                                                   class_mode='categorical',\n                                                   batch_size=batch_size,\n                                                   seed=1993)\n\n        datagen_val_flow = datagen_val.flow_from_dataframe(dataframe=dataframe_val,\n                                                   directory=os.path.join(DATA_PATH, \"train_segcrop\"),\n                                                   x_col='img_file',\n                                                   y_col=\"class\",\n                                                   classes = classes,\n                                                   target_size=(IMAGE_WIDTH, IMAGE_HEIGHT),\n                                                   color_mode='rgb',\n                                                   class_mode='categorical',\n                                                   batch_size=batch_size,\n                                                   seed=1993)\n        \n        # 동일 이름의 weight 파일이 있으면 넘어간다는 의미.\n        if os.path.exists(model_save_filepath) == True:\n            print(\">>>\", model_save_filepath, \" already trained... skip!\")\n            continue\n        \n        train_steps = get_total_batch(dataframe_train.shape[0], batch_size)\n        val_steps = get_total_batch(dataframe_val.shape[0], batch_size)\n            \n        history = model.fit_generator(datagen_train_flow,\n            epochs=epochs,\n            steps_per_epoch = train_steps,\n            validation_data = datagen_val_flow,\n            validation_steps = val_steps,\n            callbacks = get_callbacks(model_save_filepath, PATIENCE),\n            verbose=1)\n        \n        history_list[model_save_filename] = history","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### training history 과정에 대해 그래프를 그려보는 과정입니다. (이 커널에서는 weight 파일을 불러왔으므로 빈 그림이 나옴)"},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, ax = plt.subplots(1, 1, figsize=(12,8 * len(__models)))\nfrom cycler import cycler\n\n# set color cycle : 그래프 색깔을 알아서 cycling해준다.\nx = np.linspace(0, 1, 10)\nnumber = 5\ncmap = plt.get_cmap('gnuplot')\ncolors = [cmap(i) for i in np.linspace(0, 1, number)]\nax.set_prop_cycle(cycler('color', colors))\n\nfor hname in history_list:\n    history = history_list[hname]\n    plot_label = \"val_score : \" + hname\n    ax.plot(history.history['val_f1score'], label=plot_label)        \nax.legend()        \nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Model Stacking을 통한 ensemble & submission - weight averaged model\n\nvh1951님께서 설명해주신 내용입니다.\n\nhttps://inspiringpeople.github.io/data%20analysis/Ensemble_Stacking/ 를 참고했습니다.\n\n<br>\n\nk-fold로 여러 다른 데이터로 train한 K개 만큼의 모델을 얻은 후, test 데이터를 각각 모델로 돌린 결과값을 평균해서 사용할 수도 있지만, 각각의 모델의 출력을 입력으로 하는 별도의 모델을 만들어서 predict한 결과를 만드는 방법을 weight averaged model이라고 합니다.\n(각각의 모델의 predict 결과물을 동등하지 않게 weight를 부여하여 사용하는 방법).\n\n- meta-learner 모델을 생성.\n- 각각의 fold 모델로 train 데이터를 predict한 것을 합쳐서 dataset1을 만든다.\n- dataset1으로 meta-learner 모델을 train한다. (train 데이터이므로 label이 있어서 훈련 가능.)\n- 각각의 fold 모델로 test 데이터를 predict해서 dataset2을 만든다.\n- meta-learner에 dataset2를 입력으로 해서 나온 결과를 제출한다."},{"metadata":{"trusted":true},"cell_type":"code","source":"datagen_submit = ImageDataGenerator(preprocessing_function=preprocess_input)\n\n# 앞서 저장한 5개의 sub-model을 loading하는 함수.\ndef load_sub_models():\n    sub_models = []\n    for _m in __models:\n        print(\"Model \", _m, \" : \")\n        for _, _, filenames in os.walk(MODEL_PATH):\n            for fname in filenames:\n                if fname.find(_m) >= 0 and fname.find(\".h5\") >= 0:        \n                    print(\">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> loading \", fname)\n                    model = get_model(__models[_m])\n\n                    # model에 데이터파일을 올린다.\n                    # model 명이 있는 것들 중에서만 올린다.\n                    fpath = os.path.join(MODEL_PATH, fname)\n                    print(\"model weight fpath:\", fpath)\n                    model.load_weights(fpath)\n\n                    sub_models.append(model)\n    return sub_models\n\nsubmodels = load_sub_models()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from numpy import dstack     # 각 sub-model의 예측 결과를 dataset으로 생성하기 위한 dstack.\n\ndef make_meta_learner_dataset(submodels, df, imgdirname):\n    datagen_submit = ImageDataGenerator(preprocessing_function=preprocess_input)\n    stackX = None\n    for model in submodels:\n\n        # make prediction\n        datagen_metalearner_flow = datagen_submit.flow_from_dataframe(\n            dataframe=df,\n            directory=os.path.join(DATA_PATH, imgdirname),\n            x_col='img_file',\n            y_col=None,\n            target_size= (IMAGE_WIDTH, IMAGE_HEIGHT),\n            color_mode='rgb',\n            class_mode=None,\n            batch_size=batch_size,\n            shuffle=False)\n\n        datagen_metalearner_flow.reset()\n        pred = model.predict_generator(generator = datagen_metalearner_flow,\n                                       steps = get_total_batch(df.shape[0], batch_size),\n                                       verbose=1)\n        \n        if stackX is None:\n            stackX = pred\n        else:\n            stackX = dstack((stackX, pred))\n   \n    print(\"stackX.shape = \", stackX.shape)\n        \n    # flatten predictions to [rows, members x probabilities]\n    stackX = stackX.reshape((stackX.shape[0], stackX.shape[1]*stackX.shape[2]))\n    \n    return stackX\n    # 5개의 sub-model에 대해 각 예측 결과를 dstack하면 (9960, 196, 5)의 shape를 가지게 된다.\n    # 이를 meta learner의 training dataset으로 사용하기 위해 (9960, 196*5) 모양으로 reshape한다.","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import keras\nfrom keras import layers, models\n\ndef make_meta_learner_model(input_shape, output_class_count, dropout):\n    print(input_shape)\n    print(output_class_count)\n    print(dropout)\n    print(input_shape[1] * 2)\n    \n    model = Sequential()\n    model.add(layers.Dense(units=input_shape[1] * 2, activation='relu', kernel_initializer='he_normal'))\n    model.add(layers.Dropout(dropout))\n    \n    print(\"01\")\n    \n    model.add(layers.Dense(units=int(input_shape[1] / 2), activation='relu', kernel_initializer='he_normal'))\n    model.add(layers.Dropout(dropout))\n    \n    print(\"02\")\n    \n    model.add(layers.Dense(units=output_class_count, activation='softmax', kernel_initializer='he_normal'))\n    \n    print(\"03\")\n    \n    #print(model.summary())\n\n    model.compile(loss=\"categorical_crossentropy\", optimizer='adam', metrics=['accuracy', f1score])\n    \n    return model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.utils import to_categorical\n\nprint(\"Build dataset for meta-learner...\")\nmeta_train_X = make_meta_learner_dataset(submodels, df_train, \"train_segcrop\")\nprint(\"meta_train_X.shape=\", meta_train_X.shape)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# 모델이 훈련된 label값에 맞게 Y값을 만들어야 한다.\n\nlabels = (datagen_train_flow.class_indices)\nmeta_train_Y = df_train['class'].values\nmeta_train_Y = [labels[x] for x in meta_train_Y]\nmeta_train_Y = to_categorical(meta_train_Y)\nprint(\"meta_train_Y.shape=\", meta_train_Y.shape)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def train_meta_learner(X, Y):\n    \n    print(\"Training meta-learner model...\")\n    meta_learner_model = make_meta_learner_model(X.shape, len(classes), dropout=0.2)\n    meta_learner_model.fit(X, Y, epochs=5, verbose=1, batch_size=128)\n\n    return meta_learner_model\n\nmeta_learner_model = train_meta_learner(meta_train_X, meta_train_Y)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### train이 완료된 모델로 test dataset을 predict해서 submission을 만든다."},{"metadata":{"trusted":true},"cell_type":"code","source":"meta_submit_X = make_meta_learner_dataset(submodels, df_test, \"test_segcrop\")\npred = meta_learner_model.predict(meta_submit_X, batch_size=32)\n\nsubmit_Y = np.argmax(pred, axis=1)\nlabels = (datagen_train_flow.class_indices)\nlabels = dict((v,k) for k, v in labels.items())\n\npredictions = [labels[k] for k in submit_Y]\n\nsubmission = pd.DataFrame()\nsubmission['img_file'] = df_test['img_file']\nsubmission[\"class\"] = predictions\nsubmission.to_csv(\"submission.csv\", index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from IPython.display import FileLinks\nFileLinks('.') # input argument is specified folder","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":1}