{"cells":[{"metadata":{},"cell_type":"markdown","source":"제가 만든 모델은 \n 1.  1 COLOR CHANNEL 기반으로 CNN 모델\n 2.  1 COLOR CHANNEL 기반의 MOBILENET 모델 \n 2.  3 COLOR CHANNEL 기반으로 CNN 모델 을 만든다.\n 3.  1,2,3에서 얻은 모델을 앙상블한다. \n 4.  앙상블한 결과를 최종 결과로 제출한다. \n\n이렇게 구성되어 있습니다. \n"},{"metadata":{},"cell_type":"markdown","source":"DP_DIR 이라는 PATH 에는 TRAIN DATA의 일부가 VALIDATION SET으로  작용하기 위해 들어있습니다.\nhttps://www.kaggle.com/gaborfodor/shuffle-csvs 이 분의 코드의 결과 값들입니다.\n대략적으로 말하면 각각의 CATEGORY 인 CSV파일에 대해 전부 실시합니다. ( 즉 340개의 CATEGORY에 대해서 ) \n 1. 각 CATEGORY의 한 CSV파일에서 KEY_ID를 고른다. \n 2. 이 때 KEY_ID의 끝에 2자리를 기준으로 같은 것들을 하나의 파일로 묶는다. 어디로? TRAIN_K34 이런식으로. "},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"\n# 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 \nfrom glob import glob\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport ast\nimport re\nimport keras\nimport cv2\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\nfrom tqdm import tqdm\nimport os\nprint(os.listdir(\"../input\"))\nBASE_SIZE = 256\nDP_DIR = '../input/shuffle-csvs/'\nINPUT_DIR = '../input/quickdraw-doodle-recognition/'\nNCSVS = 100\nNCATS = 340","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.preprocessing.image import ImageDataGenerator\nfrom keras.metrics import top_k_categorical_accuracy\nfrom keras.callbacks import ModelCheckpoint, ReduceLROnPlateau, EarlyStopping\nfrom keras.applications.resnet50 import ResNet50, preprocess_input\nfrom keras.layers import Dense, Activation, Flatten, Dropout\nfrom keras.models import Sequential, Model\nfrom keras.optimizers import SGD, Adam\nimport matplotlib.pyplot as plt\n\nfrom keras import backend as K\nfrom keras.layers import Conv2D, MaxPooling2D\nfrom keras.layers import Input, Dense, Activation, Dropout, Flatten\nfrom keras.layers import Reshape, Lambda, BatchNormalization\nfrom keras.layers.merge import add, concatenate\nfrom keras.metrics import top_k_categorical_accuracy","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"먼저 CATEGORY 별로 데이터를 끌어오기 위해서는 CSV 파일의 이름들을 수정할 필요가 있습니다. 그래서 띄어쓰기 대신에 _으로 대체했습니다. \ndraw_cv2_color 라는 함수는 stroke가 주어지면 그것을 색깔이 있는 사진으로 바꾸는 함수입니다.\n이 때 색깔을 칠하는 기준은 stroke를 거듭해나갈 때마다 색깔이 바뀌도록 해놓았습니다. 그래서 stroke의 번째 수 마다 color의 index가 되게끔 했습닏다. \n\n그렇기 때문에 color가 존재하기 때문에 (imgwidth , imgheight, 3) 이러한 형태이어야 합니다. \n그래서 image_generator_color 함수에서 x[i,:,:,:] 이런식으로 initialize를 해주었습니다.\n또한 reshape 해줄때도 (len(df), size, size, 3)) 이런 형태가 되게끔 했습니다."},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"classes_path = os.listdir(INPUT_DIR + 'train_simplified/')\nclasses_path = sorted(classes_path, key=lambda s: s.lower())\nclass_dict = {x[:-4].replace(\" \", \"_\"):i for i, x in enumerate(classes_path)}\nlabels = {x[:-4].replace(\" \", \"_\") for i, x in enumerate(classes_path)}\n\nn_labels = len(labels)\nprint(\"Number of labels: {}\".format(n_labels))\n\nfileList = glob(INPUT_DIR + \"train_simplified/*.csv\")\n\n\nBASE_SIZE = 256\n\nimg_size = 90\nbatchsize = 256\nline_width = 7\n\ncolors = [(255, 0, 0) , (255, 255, 0),  (128, 255, 0),  (0, 255, 0), (0, 255, 128), (0, 255, 255), \n          (0, 128, 255), (0, 0, 255), (128, 0, 255), (255, 0, 255)]\n\ndef draw_cv2_color(raw_strokes, size=256, lw=7, time_color=True):\n    img = np.zeros((BASE_SIZE, BASE_SIZE, 3), np.uint8)\n    for t, stroke in enumerate(raw_strokes):\n        for i in range(len(stroke[0]) - 1):\n            color = colors[min(t, len(colors)-1)]\n            _ = cv2.line(img, (stroke[0][i], stroke[1][i]),\n                         (stroke[0][i + 1], stroke[1][i + 1]), color, lw, lineType=cv2.LINE_AA)\n    if size != BASE_SIZE:\n        return cv2.resize(img, (size, size))\n    else:\n        return img\n\n    \ndef image_generator_color(size, batchsize, ks, lw=6):\n    while True:\n        for k in np.random.permutation(ks):\n            filename = os.path.join(DP_DIR, 'train_k{}.csv.gz'.format(k))\n            for df in pd.read_csv(filename, chunksize=batchsize):\n                df['drawing'] = df['drawing'].apply(ast.literal_eval)\n                x = np.zeros((len(df), size, size,3))\n                for i, raw_strokes in enumerate(df.drawing.values):\n                    x[i,:,:,:] = draw_cv2_color(raw_strokes, size=size, lw=lw)\n                x = x / 255.\n                x = x.reshape((len(df), size, size, 3)).astype(np.float32)\n                y = keras.utils.to_categorical(df.y, num_classes=NCATS)\n                yield x, y\n\ndef df_to_image_array_color(df, size, lw=6):\n    df['drawing'] = df['drawing'].apply(ast.literal_eval)\n    x = np.zeros((len(df), size, size,3))\n    for i, raw_strokes in enumerate(df.drawing.values):\n        x[i,:,:,:] = draw_cv2_color(raw_strokes, size=size, lw=lw)\n    x = x.reshape((len(df), size, size, 3)).astype(np.float32)\n    return x\n\ndef df_to_image_array_color_test(df, size, lw=6):\n    df['drawing'] = df['drawing']\n    x = np.zeros((len(df), size, size,3))\n    for i, raw_strokes in enumerate(df.drawing.values):\n        x[i,:,:,:] = draw_cv2_color(raw_strokes, size=size, lw=lw)\n    x = x.reshape((len(df), size, size, 3)).astype(np.float32)\n    return x\ntrain_datagen_color = image_generator_color(size=img_size, batchsize=batchsize,ks=99)\n\nSTEPS = 1200\nepochs = 15","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"이번에는 color가 없는 monotone 한 image를 만들기 위한 코드입니다. \ndraw_cv2 는 draw_cv2_color 와 다르게 color를 넣는 부분이 없습니다. 그저 stroke를 잇고, color는 255로 동일합니다. \n따라서 initialize할 때도 ((len(df), size, size)) 와 같이 생겼습니다. \nreshape을 해줄 때도 (len(df), size, size, 1)) 에서 color channel이 1이 되게끔 했습니다.  "},{"metadata":{"trusted":true},"cell_type":"code","source":"\ndef draw_cv2(raw_strokes, size=256, lw=6):\n    img = np.zeros((BASE_SIZE, BASE_SIZE), np.uint8)\n    for stroke in raw_strokes:\n        for i in range(len(stroke[0]) - 1):\n            _ = cv2.line(img, (stroke[0][i], stroke[1][i]), (stroke[0][i + 1], stroke[1][i + 1]), 255, lw)\n    if size != BASE_SIZE:\n        return cv2.resize(img, (size, size))\n    else:\n        return img\n    \ndef image_generator(size, batchsize, ks, lw=6):\n    while True:\n        for k in np.random.permutation(ks):\n            filename = os.path.join(DP_DIR, 'train_k{}.csv.gz'.format(k))\n            for df in pd.read_csv(filename, chunksize=batchsize):\n                df['drawing']= df['drawing'].apply(ast.literal_eval)\n                x = np.zeros((len(df), size, size))\n                for i, raw_strokes in enumerate(df.drawing.values):\n                    x[i] = draw_cv2(raw_strokes, size=size, lw=lw)\n                x = x / 255.\n                x = x.reshape((len(df), size, size, 1)).astype(np.float32)\n                y = keras.utils.to_categorical(df.y, num_classes=NCATS)\n                yield x, y\n\ndef df_to_image_array(df, size=img_size, lw=6):\n    df['drawing'] = df['drawing'].apply(ast.literal_eval)\n    x = np.zeros((len(df), size, size))\n    for i, raw_strokes in enumerate(df.drawing.values):\n        x[i] = draw_cv2(raw_strokes, size=size, lw=lw)\n    x = x / 255.\n    x = x.reshape((len(df), size, size, 1)).astype(np.float32)\n    return x\n\n\n\ntrain_datagen = image_generator(size=img_size, batchsize=batchsize,ks=99)\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"아래 코드는 test data에 대해서 그림을 그려보는 코드입니다. 위의 color를 넣어서 stroke를 img화 한 것들을 표현한 것입니다. \n(간단화 시각화)"},{"metadata":{"trusted":true},"cell_type":"code","source":"def test_generator_color(size, batchsize, lw=6):\n    while True:\n        for df in pd.read_csv(os.path.join(INPUT_DIR,\"test_simplified.csv\"), chunksize=batchsize):\n            df['drawing'] = df['drawing'].apply(ast.literal_eval)\n            x = np.zeros((len(df), img_size, img_size, 3))\n            for i, raw_strokes in enumerate(df.drawing.values):\n                x[i, :, :, :] = draw_cv2_color(raw_strokes, size=img_size, lw=lw)\n            yield x, preprocess_input(x).astype(np.float32)\n            \ntest_datagen_color = test_generator_color(img_size, batchsize, line_width)\n\nx, xi = next(test_datagen_color)\nn = 8\nfig, axs = plt.subplots(nrows=n, ncols=n, sharex=True, sharey=True, figsize=(12, 12))\nfor i in range(n**2):\n    ax = axs[i // n, i % n]\n    ax.imshow(x[i])\n    ax.axis('off')\nplt.tight_layout()\n\nplt.show();","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import ast\nimport re\nimport keras\nimport cv2\n\nvalid_df = pd.read_csv(os.path.join(DP_DIR, 'train_k{}.csv.gz'.format(NCSVS - 1)), nrows=30000)\nvalid_df_color = pd.read_csv(os.path.join(DP_DIR, 'train_k{}.csv.gz'.format(NCSVS - 1)), nrows=30000)\nx_valid = df_to_image_array(valid_df, img_size)\nx_valid_color = df_to_image_array_color(valid_df_color, img_size)\ny_valid = keras.utils.to_categorical(valid_df.y, num_classes=NCATS)\n\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"이 모델은 color 가 없는 CNN 모델입니다. 보시다시피 CNN layer 3개를 쌓았고, maxpooling을 하였고, overfitting을 방지하기 위한 dropout을 2,3번째 layer에 주었습니다.\n그리고 나서 fully connected layer를 만들어주었습니다. 이 때 총 class가 340개라서 680개의 히든 노드를 만들고, dropout을 0.5로 했습니다. "},{"metadata":{"trusted":true},"cell_type":"code","source":"cnn_model = Sequential()\ncnn_model.add(Conv2D(32, kernel_size=(3, 3), padding='same', activation='relu', input_shape=(img_size, img_size, 1)))\ncnn_model.add(MaxPooling2D(pool_size=(2, 2)))\n\ncnn_model.add(Conv2D(64, kernel_size=(3, 3), padding='same', activation='relu'))\ncnn_model.add(MaxPooling2D(pool_size=(2, 2)))\ncnn_model.add(Dropout(0.2))\n\ncnn_model.add(Conv2D(64, kernel_size=(3, 3), padding='same', activation='relu'))\ncnn_model.add(MaxPooling2D(pool_size=(2, 2)))\ncnn_model.add(Dropout(0.1))\n\n\ncnn_model.add(Flatten())\ncnn_model.add(Dense(680, activation='relu'))\ncnn_model.add(Dropout(0.5))\ncnn_model.add(Dense(n_labels, activation='softmax'))\ncnn_model.summary()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"위의 모델과 구조적으로 같은 모델인데, input shape을 (img_size, img_size, 3) 으로 함으로써 color input을 받을 수 있게 조정했습니다. "},{"metadata":{"trusted":true},"cell_type":"code","source":"cnn_model_color = Sequential()\ncnn_model_color.add(Conv2D(32, kernel_size=(3, 3), padding='same', activation='relu', input_shape=(img_size, img_size, 3)))\ncnn_model_color.add(MaxPooling2D(pool_size=(2, 2)))\n\ncnn_model_color.add(Conv2D(64, kernel_size=(3, 3), padding='same', activation='relu'))\ncnn_model_color.add(MaxPooling2D(pool_size=(2, 2)))\ncnn_model_color.add(Dropout(0.2))\n\ncnn_model_color.add(Conv2D(64, kernel_size=(3, 3), padding='same', activation='relu'))\ncnn_model_color.add(MaxPooling2D(pool_size=(2, 2)))\ncnn_model_color.add(Dropout(0.1))\n\n\ncnn_model_color.add(Flatten())\ncnn_model_color.add(Dense(680, activation='relu'))\ncnn_model_color.add(Dropout(0.5))\ncnn_model_color.add(Dense(n_labels, activation='softmax'))\ncnn_model_color.summary()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"그래서 위 두 모델을 compile하고 fitting 시켰습니다. 대용량의 파일이라서 generator 로 파일을 읽어왔기 때문에 fit_generator을 사용했습니다. \n또한 두 모델의 input이 color가 있고 없고의 차이가 있기 때문에 ,validation data도 다르게 생성했습니다. image_generator를 통해서 color 가 없는 validation 을, \n그리고 image_generator_color을 통해서 image가 존재하는 validation 을 만들었습니다. \ny_valid는 같기 때문에 그대로 두 모델 공통적으로 사용하였고, 어떤 generator을 사용하느냐에 따라 x_valid와 x_valid_color을 만들고 모델에 맞게 사용했습니다. \n"},{"metadata":{"trusted":true},"cell_type":"code","source":"def top_3_accuracy(x,y): \n    t3 = top_k_categorical_accuracy(x,y, 3)\n    return t3\n\nreduceLROnPlat = ReduceLROnPlateau(monitor='val_categorical_accuracy', factor=0.5, patience=3, \n                                   verbose=1, mode='auto', min_delta=0.005, cooldown=5, min_lr=0.0001)\nearlystop = EarlyStopping(monitor='val_top_3_accuracy', mode='max', patience=5) \ncallbacks = [reduceLROnPlat, earlystop]\n\ncnn_model.compile(loss='categorical_crossentropy',\n              optimizer='adam',\n              metrics=['accuracy', top_3_accuracy])\ncnn_model_color.compile(loss='categorical_crossentropy',\n              optimizer='adam',\n              metrics=['accuracy', top_3_accuracy])\n\ncnn_hist = cnn_model.fit_generator(\n    train_datagen, steps_per_epoch=STEPS, epochs=epochs, verbose=1,\n    validation_data=(x_valid, y_valid),\n    callbacks = callbacks\n)\n\ncnn_color_hist = cnn_model_color.fit_generator(\n    train_datagen_color, steps_per_epoch=STEPS, epochs=epochs, verbose=1,\n    validation_data=(x_valid_color, y_valid),\n    callbacks = callbacks\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"모바일 넷이라는 pretrained 된 모델을 사용해보았습니다. resnet 이라는 모델도 사용해보았으나, mobile net이 더 성능이 좋아서 이 모델을 사용하게 되었습니다.\n또한 mobile net을 color가 있는 channel에서도 사용해보았는데, color가 없을 때 더 성능이 좋았기 때문에 1 channel인 경우에만 mobile net을 사용하게 되었습니다. \n\n모델의 구조는 mobile net의 구조와 학습된 가중치를 이용합니다. 그리고 나서 dense layer를 통해서 마지막 fully connected layer를 구성합니다. \n"},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.applications import MobileNet\n\nbase_model = MobileNet(input_shape=(img_size, img_size, 1), include_top=False, weights=None, classes=n_labels)\n\n# add a global spatial average pooling layer\nx = base_model.output\nx = Flatten()(x)\n# let's add a fully-connected layer\nx = Dense(1024, activation='relu')(x)\npredictions = Dense(n_labels, activation='softmax')(x)\n# this is the model we will train\nmobile_model = Model(inputs=base_model.input, outputs=predictions)\n\nmobile_model.compile(optimizer=Adam(lr=1e-4), loss='categorical_crossentropy',\n              metrics=['accuracy', top_3_accuracy])\n\ncallbacks = [\n    ReduceLROnPlateau(monitor='val_categorical_accuracy', factor=0.5, patience=5,\n                      min_delta=0.005, mode='max', cooldown=3, verbose=1)\n]\n\nmobile_hist = mobile_model.fit_generator(\n    train_datagen, steps_per_epoch=STEPS, epochs=15, verbose=1,\n    validation_data=(x_valid, y_valid),\n    callbacks = callbacks\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"사실 앙상블에 여러종류가 있고 효과적인 앙상블 method가 boosting이라서 boosting을 구현해보고 싶었으나, 각각의 모델들이 딥러닝 모델인 만큼 헤비하기 때문에 본래 boosting의 취지에 어긋나기 때문에 사용하지 않았습니다.\n그래서 stacking 앙상블 모형을 사용하는것도 고려해보았지만, 각각의 딥러닝 모델들의 아웃풋을 고려하여 meta model을 학습시키는 데에도 시간이 오래걸릴 것 같고, meta model을 만들만큼 여러 base model을 \n만들여력이 없었기 때문에 stacking 앙상블은 사용하지 않았습니다. \n\n그래서 그저 단순하게 각각의 모델들이 각 row에 대해 예측한 embedding 값들을 합쳐서 total embedding 벡터를 기준으로 softmax 함수를 적용해보았습니다. \npred_cnn + pred_cnn_color + pred_mobile 을 더하는 행위가 바로 앙상블하는 것입니다. "},{"metadata":{"trusted":true},"cell_type":"code","source":"pred_results = []\nchunksize = 10000\nreader = pd.read_csv(INPUT_DIR + 'test_simplified.csv', chunksize=chunksize)\nfor chunk in tqdm(reader):\n    imgs = df_to_image_array(chunk,img_size)\n    imgs_color = df_to_image_array_color_test(chunk,img_size)\n    pred_cnn = cnn_model.predict(imgs,verbose=1)\n    pred_cnn_color = cnn_model_color.predict(imgs_color,verbose=1)\n    pred_mobile = mobile_model.predict(imgs, verbose=1)\n    top_3 =  np.argsort(-(pred_cnn + pred_mobile+pred_cnn_color))[:, 0:3]  \n    pred_results.append(top_3)\nprint(\"Finished test predictions...\")\n\nreverse_dict = {v: k for k, v in class_dict.items()}\npred_results = np.concatenate(pred_results)\nprint(\"Finished data prep...\")\n\n\n\n'''\n(2199, 340)\n(112199, 3)\n'''\n\npreds_df = pd.DataFrame({'first': pred_results[:,0], 'second': pred_results[:,1], 'third': pred_results[:,2]})\npreds_df = preds_df.replace(reverse_dict)\n\npreds_df['words'] = preds_df['first'] + \" \" + preds_df['second'] + \" \" + preds_df['third']\n\nsub = pd.read_csv(INPUT_DIR + 'sample_submission.csv', index_col=['key_id'])\nsub['word'] = preds_df.words.values\nsub.to_csv('1class_per_label_proto.csv')\nsub.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}