{"cells":[{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"import warnings\nimport seaborn as sns\nimport PIL\nfrom keras import backend as K\nfrom keras.preprocessing.image import ImageDataGenerator\nfrom keras.applications import Xception, InceptionResNetV2, InceptionV3, NASNetLarge, NASNetMobile\nfrom keras_applications.resnext import ResNeXt101\nfrom keras.callbacks import EarlyStopping, ReduceLROnPlateau, ModelCheckpoint\nfrom keras import layers, models, optimizers, utils\nfrom keras.models import Sequential, load_model\nfrom keras.layers import Dense\nfrom keras.layers import Dropout\nfrom keras.layers import Flatten\nfrom keras.layers.convolutional import Conv2D\nfrom keras.layers.convolutional import MaxPooling2D, ZeroPadding2D\nfrom keras.optimizers import SGD\n\nimport os\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\n%matplotlib inline\n\nfrom IPython.display import SVG\nfrom keras.utils.vis_utils import model_to_dot\n\nimport cv2\n\nwarnings.filterwarnings('ignore')\n\nimage_size = 224\napplication = ResNeXt101\nbatch_size = 8","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"DATA_PATH = '../input'\nTRAIN_IMG_PATH = os.path.join(DATA_PATH, 'train')\nTEST_IMG_PATH = os.path.join(DATA_PATH, 'test')\n\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":{"trusted":true},"cell_type":"code","source":"df_train.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(15,6))\nsns.countplot('class', data=df_train)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_train['class'].value_counts()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"119번 유형이 84개로 가장 많고, 136번 유형이 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":{"trusted":true},"cell_type":"code","source":"def crop_boxing_img(img_name, margin=16, 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":"잘라낸 사진들을 디렉토리에 각 클래스별로 저장을 합니다.\n저장할때 훈련데이터 디렉토리와 검증 디렉토리, 테스트용 디렉터리 분류하여 저장합니다.\n훈련용과 검증용 개수는 7:3 입니다.\n\n디렉토리에 저장한 이유는 이미지 파일을 PC에서 바로 열어보기 편하기 때문이고, 다른 뜻 은 없습니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"nb_train_sample = df_train.shape[0] * 0.7\nnb_validation_sample = df_train.shape[0] - nb_train_sample\nnb_test_sample = df_test.shape[0]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"TRAIN_CROPPED_PATH = '../cropped_train'\nVALID_CROPPED_PATH = '../cropped_valid'\nTEST_CROPPED_PATH = '../cropped_test'","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"훈련디렉토리 / 검증 디렉토리 / 테스트 디렉토리 생성"},{"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(VALID_CROPPED_PATH) == False):\n    os.mkdir(VALID_CROPPED_PATH)\n\nif (os.path.isdir(TEST_CROPPED_PATH) == False):\n    os.mkdir(TEST_CROPPED_PATH)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"클래스별 디렉토리를 만들기 위해서 class 형을 문자열로 변경"},{"metadata":{"trusted":true},"cell_type":"code","source":"df_train['class'] = df_train['class'].astype('str')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for i, row in df_train.iterrows():\n    cropped = crop_boxing_img(row['img_file'])\n    \n    if ( i < nb_train_sample):\n        class_path = os.path.join(TRAIN_CROPPED_PATH, df_train['class'][i])\n        if(os.path.isdir(class_path) == False):\n            os.mkdir(class_path)\n\n        cropped.save(os.path.join(class_path, row['img_file']))\n    else:\n        class_path = os.path.join(VALID_CROPPED_PATH, df_train['class'][i])\n        if(os.path.isdir(class_path) == False):\n            os.mkdir(class_path)\n\n        cropped.save(os.path.join(class_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":"keras의 ImageDataGenerator를 이용하여 데이터 증식을 합니다.\n - rotation_range : 랜덤하게 사진을 회전시킬 각도 범위\n - width_shift_range : 사진 수평이동 비율\n - height_shift_range : 사진 수직이동 비율\n - horizontal_flip : 사진 수평으로 뒤집기\n - zoom_range : 랜덤하게 사진 확대\n - fill_mode : 사진을 회전/이동한후에 새롭게 생성해야할 픽셀을 채울 방법"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_datagen = ImageDataGenerator(\n    rotation_range=30,\n    width_shift_range=0.3,\n    height_shift_range=0.3,\n    horizontal_flip=True,\n    vertical_flip=False,\n    zoom_range=0.2,\n    fill_mode='nearest'\n    )\n\nvalid_datagen = ImageDataGenerator()\ntest_datagen = ImageDataGenerator()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_generator = train_datagen.flow_from_directory(\n    TRAIN_CROPPED_PATH,\n    target_size=(image_size, image_size),\n    batch_size=batch_size,\n    class_mode='categorical',\n    seed=2019,\n    color_mode='rgb'\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"validation_generator = valid_datagen.flow_from_directory(\n    VALID_CROPPED_PATH,\n    target_size=(image_size,image_size),\n    batch_size=batch_size,\n    class_mode='categorical',\n    seed=2019,\n    color_mode='rgb'\n)","execution_count":null,"outputs":[]},{"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":"def get_model():\n    base_model = application(weights='imagenet', input_shape=(image_size,image_size,3), include_top=False,\n                             backend = K, layers = layers, models = models, utils = utils)\n    #base_model.trainable = False\n\n    model = models.Sequential()\n    model.add(base_model)\n    model.add(layers.GlobalAveragePooling2D())\n    model.add(layers.Dense(1024, activation='relu'))\n    model.add(layers.Dropout(0.55))\n\n    model.add(layers.Dense(196, activation='softmax'))\n    model.summary()\n\n    #optimizer = optimizers.SGD(lr=1e-4, decay=1e-6, momentum=0.9, nesterov=True)\n    optimizer = optimizers.RMSprop(lr=0.0001)\n    model.compile(loss='categorical_crossentropy', optimizer=optimizer, metrics=['acc'])\n\n    return model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = get_model()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model_path = '../model/'\nif not os.path.exists(model_path):\n    os.mkdir(model_path)\n    \nmodel_path = model_path + 'best_model.hdf5'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"patient = 5\ncallbacks1 = [\n    EarlyStopping(monitor='val_loss', patience=patient, mode='min', verbose=1),\n    ReduceLROnPlateau(monitor = 'val_loss', factor = 0.5, patience = patient / 2, min_lr=0.00001, verbose=1, mode='min'),\n    ModelCheckpoint(filepath=model_path, monitor='val_loss', verbose=1, save_best_only=True, mode='min'),\n    ]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def 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":"history = model.fit_generator(\n    train_generator,\n    steps_per_epoch=get_steps(nb_train_sample, batch_size),\n    epochs=100,\n    validation_data=validation_generator,\n    validation_steps=get_steps(nb_validation_sample, batch_size),\n    verbose=1,\n    callbacks = callbacks1\n)\n\n\n## 1024 relu, 512 relu, 196 softmax\n# InceptionV3, Dropout 0.55 -> 0.47786\n# InceptionV3, Dropout 0.45 -> 0.60999 (val acc 0.88)\n\n# InceptionResNetV2, Dropout 0.65 -> 0.66045 (val acc 0.84, submit 0.88)\n# InceptionResNetV2, Dropout 0.55 -> 0.46816 (Submit 0.90510)\n# InceptionResNetV2, Dropout 0.50 -> 0.54456\n# InceptionResNetV2, Dropout 0.45 -> 0.50621","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"acc = history.history['acc']\nval_acc = history.history['val_acc']\nloss = history.history['loss']\nval_loss = history.history['val_loss']\n\nepochs = range(1, len(acc) + 1)\n\nplt.plot(epochs, acc, 'bo', label='Training Acc')\nplt.plot(epochs, val_acc, 'b', label='Validation Acc')\nplt.title('Training and validation accuracy')\nplt.legend()\nplt.show()\n\nplt.plot(epochs, loss, 'bo', label='Traing loss')\nplt.plot(epochs, val_loss, 'b', label='Validation loss')\nplt.title('Trainging and validation loss')\nplt.legend()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.load_weights(model_path)\ntest_generator.reset()\n\nprediction = model.predict_generator(\n    generator=test_generator,\n    steps = get_steps(nb_test_sample, 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(DATA_PATH, 'sample_submission.csv'))\nsubmission[\"class\"] = predictions\nsubmission.to_csv(\"submission.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":4}