{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"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 in \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 \"../input/\" directory.\n# For example, running this (by clicking run or pressing Shift+Enter) will list the files in the input directory\n\nimport os\nprint(os.listdir(\"../input\"))\n\n# Any results you write to the current directory are saved as output.","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"https://www.kaggle.com/janged/3rd-ml-month-xception-stratifiedkfold-ensemble\n위 커널에서 약간?의 변형만 한 것입니다.\n그런고로 실질적으로 제가 한 건 (제 기준에서지만, 남들도 그렇기를 희망하며)좀 더 다양하게 실험해보기 쉬운 형태로 바꾼 것일 뿐입니다."},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"import warnings\nimport seaborn as sns\nimport matplotlib.pylab as plt\nimport PIL\nfrom sklearn.model_selection import StratifiedKFold, KFold\n\nfrom keras import backend as K\nfrom keras.preprocessing.image import ImageDataGenerator\n\nfrom keras.applications import *\n\nfrom keras.callbacks import EarlyStopping, ReduceLROnPlateau, ModelCheckpoint\nfrom keras import layers, models, optimizers","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"warnings.filterwarnings('ignore')\nK.image_data_format()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_base_model_name(base_model):\n    if(base_model is Xception):\n        return \"Xception\"\n    elif(base_model is VGG16):\n        return \"VGG16\"\n    elif(base_model is VGG19):\n        return \"VGG19\"\n    elif(base_model is ResNet50):\n        return \"ResNet50\"\n    elif(base_model is ResNet101):\n        return \"ResNet101\"\n    elif(base_model is ResNet152):\n        return \"ResNet152\"\n    elif(base_model is ResNet50V2):\n        return \"ResNet50V2\"\n    elif(base_model is ResNet101V2):\n        return \"ResNet101V2\"\n    elif(base_model is ResNet152V2):\n        return \"ResNet152V2\"\n    elif(base_model is ResNeXt50):\n        return \"ResNeXt50\"\n    elif(base_model is ResNeXt101):\n        return \"ResNeXt101\"\n    elif(base_model is InceptionV3):\n        return \"InceptionV3\"\n    elif(base_model is InceptionResNetV2):\n        return \"InceptionResNetV2\"\n    elif(base_model is MobileNet):\n        return \"MobileNet\"\n    elif(base_model is MobileNetV2):\n        return \"MobileNetV2\"\n    elif(base_model is DenseNet121):\n        return \"DenseNet121\"\n    elif(base_model is DenseNet16):\n        return \"DenseNet16\"\n    elif(base_model is DenseNet201):\n        return \"DenseNet201\"\n    elif(base_model is NASNetMobile):\n        return \"NASNetMobile\"\n    elif(base_model is NASNetLarge):\n        return \"NASNetLarge\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_base_model_image_size(base_model):\n    if(base_model in [Xception, InceptionV3, InceptionResNetV2]):\n        return 299\n    elif(base_model in [VGG16, VGG19, ResNet50, ResNet101, ResNet152, ResNet50V2, ResNet101V2, ResNet152V2, ResNeXt50, ResNeXt101, MobileNet, MobileNetV2, DenseNet121, DenseNet16, DenseNet201, NASNetMobile]):\n        return 224\n    elif(base_model in [NASNetLarge]):\n        return 331","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"BATCH_SIZE = 32\nEPOCHS = 100\nk_folds = 5\nPATIENCE = 4\nSEED = 2019\nBASE_MODEL = Xception\nIMAGE_SIZE = get_base_model_image_size(BASE_MODEL)\n\nJUST_FOR_TESTING=True\n#for submission, change JUST_FOR_TRAINING to False","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"위에서 JUST_FOR_TESTING은 빠르게 모델이 오류없이 돌아가는지만 확인하고 싶을 때에 사용하는 것으로, 아래에 서술된(janged님께서 원래 적어주신) 수치는 모두 이것이 False, 즉 전체 데이터셋에 대한 이야기입니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"DATA_PATH = '../input'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"TRAIN_IMG_PATH = os.path.join(DATA_PATH, 'train')\nTEST_IMG_PATH = os.path.join(DATA_PATH, 'test')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_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":{"trusted":true},"cell_type":"code","source":"df_train.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"if(JUST_FOR_TESTING):\n    k_folds=2\n    PATIENCE=2\n    df_train=df_train[:2048]\n    df_test=df_train[:10]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(15,6))\nsns.countplot('class', data=df_train)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-output":true},"cell_type":"code","source":"df_train['class'].value_counts()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"119번 유형이 84개로 제일 많고,\n\n136번 유형이 30개로 가장 적음."},{"metadata":{"trusted":true},"cell_type":"code","source":"df_train['class'].value_counts().mean()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"각 차종별 평균 분포개수는 51개임."},{"metadata":{"trusted":true},"cell_type":"code","source":"df_train['class'].value_counts().describe()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"위의 내용을 요약하여 출력해 보면,\n최대값 84, 최소값 30, 평균51로 편차가 좀 있다고 볼 수도 있습니다.\n그래서 KFold대신 StratifiedKFold를 사용하였습니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"def crop_boxing_img(img_name, margin=0, size=(IMAGE_SIZE,IMAGE_SIZE)):\n    if img_name.split('_')[0] == 'train':\n        PATH = TRAIN_IMG_PATH\n        data = df_train\n    else:\n        PATH = TEST_IMG_PATH\n        data = df_test\n\n    img = PIL.Image.open(os.path.join(PATH, img_name))\n    pos = data.loc[data[\"img_file\"] == img_name, ['bbox_x1', 'bbox_y1', 'bbox_x2', 'bbox_y2']].values.reshape(-1)\n\n    width, height = img.size\n    x1 = max(0, pos[0] - margin)\n    y1 = max(0, pos[1] - margin)\n    x2 = min(pos[2] + margin, width)\n    y2 = min(pos[3] + margin, height)\n\n    return img.crop((x1, y1, x2, y2)).resize(size)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"crop_boxing은 \n\n [Daehun Gwak님 커널](https://www.kaggle.com/daehungwak/keras-how-to-use-pretrained-model)\n\n [허태명님 커널](https://www.kaggle.com/tmheo74/3rd-ml-month-car-image-cropping)\n \n 에서 가져왔습니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"TRAIN_CROPPED_PATH = '../cropped_train'\nTEST_CROPPED_PATH = '../cropped_test'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"if (os.path.isdir(TRAIN_CROPPED_PATH) == False):\n    os.mkdir(TRAIN_CROPPED_PATH)\n\nif (os.path.isdir(TEST_CROPPED_PATH) == False):\n    os.mkdir(TEST_CROPPED_PATH)\n\nfor i, row in df_train.iterrows():\n    cropped = crop_boxing_img(row['img_file'])\n    cropped.save(os.path.join(TRAIN_CROPPED_PATH, row['img_file']))\n\nfor i, row in df_test.iterrows():\n    cropped = crop_boxing_img(row['img_file'])\n    cropped.save(os.path.join(TEST_CROPPED_PATH, row['img_file']))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Cropping한 이미지 저장하기 위한 디렉토리 생성및 Cropping"},{"metadata":{"trusted":true},"cell_type":"code","source":"df_train['class'] = df_train['class'].astype('str')\ndf_train = df_train[['img_file', 'class']]\ndf_test = df_test[['img_file']]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model_path = './'\nif not os.path.exists(model_path):\n    os.mkdir(model_path)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_callback(model_name, patient):\n    ES = EarlyStopping(\n        monitor='val_loss', \n        patience=patient, \n        mode='min', \n        verbose=1)\n    RR = ReduceLROnPlateau(\n        monitor = 'val_loss', \n        factor = 0.5, \n        patience = patient / 2, \n        min_lr=0.000001, \n        verbose=1, \n        mode='min')\n    MC = ModelCheckpoint(\n        filepath=model_name, \n        monitor='val_loss', \n        verbose=1, \n        save_best_only=True, \n        mode='min')\n\n    return [ES, RR, MC]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* EarlyStopping은 인자인 Patient가 예를 들어 10으로 들어온다면, Val_loss가 10회 이상 좋아지지 않으면 Stop.\n* ReduceLROnPlateau는 Patient/2 회만큼 Val_loss가 좋아지지 않으면 learing late를 반으로 줄이는 용도.\n* ModelCheckPoint는 Val_loss가 가장 좋은(가장작은) 경우에 해당 훈련 Weight 파일로 저장."},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_model(model_name, iamge_size):\n    base_model = model_name(weights='imagenet', input_shape=(iamge_size,iamge_size,3), include_top=False)\n    #base_model.trainable = False\n    model = models.Sequential()\n    model.add(base_model)\n    model.add(layers.GlobalAveragePooling2D())\n    model.add(layers.Dense(2048, activation='relu', kernel_initializer='he_normal'))\n    model.add(layers.Dropout(0.15))\n \n    model.add(layers.Dense(196, activation='softmax', kernel_initializer='lecun_normal'))\n    model.summary()\n\n    optimizer = optimizers.Nadam(lr=0.0002)\n    model.compile(loss='categorical_crossentropy', optimizer=optimizer, metrics=['acc'])\n\n    return model","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"사전 훈련모델에 층을 더해서 훈련 시키는 구조로 모델을 구성하였습니다.\nDropout은 과적합을 최대한 줄여볼 용도로 추가했습니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"train_datagen = ImageDataGenerator(\n    rescale=1./255,\n    #featurewise_center= True,  # set input mean to 0 over the dataset\n    #samplewise_center=True,  # set each sample mean to 0\n    #featurewise_std_normalization= True,  # divide inputs by std of the dataset\n    #samplewise_std_normalization=True,  # divide each input by its std\n    rotation_range=20,\n    width_shift_range=0.2,\n    height_shift_range=0.2,\n    horizontal_flip=True,\n    vertical_flip=False,\n    zoom_range=0.2,\n    #shear_range=0.2,\n    #brightness_range=(1, 1.2),\n    #fill_mode='nearest'\n    )\n\nvalid_datagen = ImageDataGenerator(\n    rescale=1./255,\n    #featurewise_center= True,  # set input mean to 0 over the dataset\n    #samplewise_center=True,  # set each sample mean to 0\n    #featurewise_std_normalization= True,  # divide inputs by std of the dataset\n    #samplewise_std_normalization=True  # divide each input by its std\n    )\ntest_datagen = ImageDataGenerator(\n    rescale=1./255,\n    #featurewise_center= True,  # set input mean to 0 over the dataset\n    #samplewise_center=True,  # set each sample mean to 0\n    #featurewise_std_normalization= True,  # divide inputs by std of the dataset\n    #samplewise_std_normalization=True,  # divide each input by its std\n    )","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-output":true},"cell_type":"code","source":"skf = StratifiedKFold(n_splits=k_folds, random_state=SEED)\n#skf = KFold(n_splits=k_folds, random_state=SEED)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-output":true},"cell_type":"code","source":"j = 1\nmodel_names = []\nfor (train_index, valid_index) in skf.split(\n    df_train['img_file'], \n    df_train['class']):\n\n    traindf = df_train.iloc[train_index, :].reset_index()\n    validdf = df_train.iloc[valid_index, :].reset_index()\n\n    print(\"=========================================\")\n    print(\"====== K Fold Validation step => %d/%d =======\" % (j,k_folds))\n    print(\"=========================================\")\n    \n    train_generator = train_datagen.flow_from_dataframe(\n        dataframe=traindf,\n        directory=TRAIN_CROPPED_PATH,\n        x_col='img_file',\n        y_col='class',\n        target_size= (IMAGE_SIZE, IMAGE_SIZE),\n        color_mode='rgb',\n        class_mode='categorical',\n        batch_size=BATCH_SIZE,\n        seed=SEED,\n        shuffle=True\n        )\n    \n    valid_generator = valid_datagen.flow_from_dataframe(\n        dataframe=validdf,\n        directory=TRAIN_CROPPED_PATH,\n        x_col='img_file',\n        y_col='class',\n        target_size= (IMAGE_SIZE, IMAGE_SIZE),\n        color_mode='rgb',\n        class_mode='categorical',\n        batch_size=BATCH_SIZE,\n        seed=SEED,\n        shuffle=True\n        )\n    \n    model_name = model_path + str(j) + '_'+get_base_model_name(BASE_MODEL)+'.hdf5'\n    model_names.append(model_name)\n    \n    model = get_model(BASE_MODEL, IMAGE_SIZE)\n    \n    try:\n        model.load_weights(model_name)\n    except:\n        pass\n        \n    history = model.fit_generator(\n        train_generator,\n        steps_per_epoch=len(traindf.index) / BATCH_SIZE,\n        epochs=EPOCHS,\n        validation_data=valid_generator,\n        validation_steps=len(validdf.index) / BATCH_SIZE,\n        verbose=1,\n        shuffle=False,\n        callbacks = get_callback(model_name, PATIENCE)\n        )\n        \n    j+=1","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"5개의 Fold를 훈련하면서 각 폴드별 Val loss가 가장 작은 model weight를 저장합니다.\n\n훈련이 끝나면 5개의 Weight파일이 생성됩니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"test_generator = test_datagen.flow_from_dataframe(\n    dataframe=df_test,\n    directory=TEST_CROPPED_PATH,\n    x_col='img_file',\n    y_col=None,\n    target_size= (IMAGE_SIZE, IMAGE_SIZE),\n    color_mode='rgb',\n    class_mode=None,\n    batch_size=BATCH_SIZE,\n    shuffle=False\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"prediction = []\nfor i, name in enumerate(model_names):\n    model = get_model(BASE_MODEL, IMAGE_SIZE)\n    model.load_weights(name)\n    \n    test_generator.reset()\n    pred = model.predict_generator(\n        generator=test_generator,\n        steps = len(df_test)/BATCH_SIZE,\n        verbose=1\n    )\n    prediction.append(pred)\n\ny_pred = np.mean(prediction, axis=0)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"5개의 Fold에서 훈련한 결과들(Weight)를 불러들려서 5번 예측하고 평균."},{"metadata":{"trusted":true},"cell_type":"code","source":"preds_class_indices=np.argmax(y_pred, axis=1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"labels = (train_generator.class_indices)\nlabels = dict((v,k) for k,v in labels.items())\nfinal_pred = [labels[k] for k in preds_class_indices]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"len(final_pred)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission = pd.read_csv(os.path.join(DATA_PATH, 'sample_submission.csv'))\nif(JUST_FOR_TESTING):\n    submission=submission[:10]\nsubmission[\"class\"] = final_pred\nsubmission.to_csv(\"submission.csv\", index=False)\nsubmission.head()\n#DO NOT SUBMIT RESULT if JUST_FOR_TESTING is TRUE","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"조만간 F1스코어도 반영해보도록 하겠습니다!"}],"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}