{"cells":[{"metadata":{},"cell_type":"markdown","source":"이미지 데이터 처리를 위해 기본적으로 Keras를 사용하여 CNN모델을 생성하는 방향으로 접근하였다. 우선적으로 simplified 데이터를 사용하여 각 좌표값을 선으로 연결하여 이미지로 변환한뒤 생성된 이미지들을 Keras의 MobileNet을 사용하여 학습을 시키는 방식을 사용했다. 처음 시도해보는 방식이고 시간도 부족하여 공개되어있는 코드(https://www.kaggle.com/amneves/quick-draw-keras-cnn-model/data / https://www.kaggle.com/gaborfodor/greyscale-mobilenet-lb-0-892) 를 사용하며 하나씩 공부해가는 식으로 진행했다. 물론 아직 좀 더 공부를 해야하는 부분이 있지만 개인적으로는 짧고 농도있는 학습을 하는 시간이 되었다."},{"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 \nfrom glob import glob\nimport re\nimport ast\nimport cv2\nimport csv\nimport time\nimport ast\nimport urllib\nfrom PIL import Image, ImageDraw\nfrom tqdm import tqdm\nfrom dask import bag, threaded\nimport matplotlib\nimport matplotlib.pyplot as pltc\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport tensorflow as tf\nimport matplotlib.pyplot as plt\nfrom dask import bag, threaded\nfrom tensorflow import keras\nfrom tensorflow.keras.layers import Conv2D, MaxPooling2D\nfrom tensorflow.keras.layers import Dense, Dropout, Flatten, Activation\nfrom tensorflow.keras.metrics import categorical_accuracy, top_k_categorical_accuracy, categorical_crossentropy\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau, ModelCheckpoint\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.applications.nasnet import NASNetMobile\nfrom tensorflow.keras.preprocessing import image\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.layers import Dense, GlobalAveragePooling2D\nfrom tensorflow.keras import backend as K\nfrom tensorflow.keras.applications import MobileNet\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# Any results you write to the current directory are saved as output.","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"BASE_SIZE = 256\nDP_DIR = '../input/shuffle-csvs/'\nINPUT_DIR = '../input/quickdraw-doodle-recognition/'\nNCSVS = 100\nNCATS = 340\nimport os\nprint(os.listdir(\"../input\"))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"startTime = time.time()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"roller coaster와 같이 두 단어로 이루어진 단어들을 하나로 묶어주는 작업. 라벨의 갯수는 340개다."},{"metadata":{"trusted":true},"cell_type":"code","source":"#clean spaces in name\nclasses_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))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"epoch은 16으로 주고 이미지의 크기와 커널 사이즈를 64 * 64로 만들어준다."},{"metadata":{"trusted":true},"cell_type":"code","source":"STEPS = 1000\nbatchsize = 512\nepochs = 16\nsize = 64","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def top_3_accuracy(y_true, y_pred):\n    return top_k_categorical_accuracy(y_true, y_pred, k=3)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"draw_cv2 함수에서는 좌표상에 그려진 storke를 256X256사이즈의 image로 변환시킨다.\nimage_generator 함수를 통해 random permutation을 사용하여 OOM에러를 방지시킨다.\ndf_to_image_array 함수로 이미지의 사이즈를 64X64로 축소, 변환시켜준다."},{"metadata":{"trusted":true},"cell_type":"code","source":"def 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\n#ADD DATA AUGMENTATION TO BOOST\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 = tf.keras.utils.to_categorical(df.y, num_classes=NCATS)\n                yield x, y\n\ndef df_to_image_array(df, size=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","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"30000개의 validation set을 설정하고 x_valid의 shape은 (30000, 64, 64, 1)이 되고, y_valid의 shape은 (30000, 340)이 된다."},{"metadata":{"trusted":true},"cell_type":"code","source":"valid_df = 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, size)\ny_valid = tf.keras.utils.to_categorical(valid_df.y, num_classes=NCATS)\nprint(x_valid.shape, y_valid.shape)\nprint('Validation array memory {:.2f} GB'.format(x_valid.nbytes / 1024.**3 ))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_datagen = image_generator(size=size, batchsize=batchsize, ks=range(NCSVS - 1))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"변환된 이미지를 8 * 8의 형태로 표현하였다."},{"metadata":{"trusted":true},"cell_type":"code","source":"x, y = next(train_datagen)\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    (-x[i]+1)/2\n    ax.imshow((-x[i, :, :, 0] + 1)/2)\n    ax.axis('off')\nplt.tight_layout()\nfig.savefig('gs.png', dpi=300)\nplt.show();","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"MobileNet을 사용하였다. CNN모델을 보다 가볍게 해주기 위해 사용한다. size는 위에서 정한대로 64 * 64의 형태로 만들고 활성함수는 Relu function을, fully-connected 후 예측 모델은 softmax 함수를 사용하였다."},{"metadata":{"trusted":true},"cell_type":"code","source":"base_model = MobileNet(input_shape=(size, 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\nmodel = Model(inputs=base_model.input, outputs=predictions)\n\nmodel.compile(optimizer=Adam(lr=1e-4), loss='categorical_crossentropy',\n              metrics=[categorical_crossentropy, categorical_accuracy, top_3_accuracy])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.summary()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"이후 학습을 진행시켰다, epoch은 16, 한 epoch당 1000step을 주고 검증은 앞서 만든 validation set을 사용한다.\ntraining결과 categorical accuracy가 0.6629, loss값이 1.3482, top3 accuaracy가 0.8373까지 나왔다."},{"metadata":{"trusted":true},"cell_type":"code","source":"callbacks = [\n    ReduceLROnPlateau(monitor='val_categorical_accuracy', factor=0.5, patience=5,\n                      min_delta=0.005, mode='max', cooldown=3, verbose=1)\n]\n\nhist = 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)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def gen_graph(history, title):\n    plt.plot(history.history['categorical_accuracy'])\n    plt.plot(history.history['val_categorical_accuracy'])\n    plt.plot(history.history['top_3_accuracy'])\n    plt.plot(history.history['val_top_3_accuracy'])\n    plt.title('Accuracy ' + title)\n    plt.ylabel('Accuracy')\n    plt.xlabel('Epoch')\n    plt.legend(['train', 'validation', 'Test top 3', 'Validation top 3'], loc='upper left')\n    plt.show()\n    plt.plot(history.history['categorical_crossentropy'])\n    plt.plot(history.history['val_categorical_crossentropy'])\n    plt.title('Loss ' + title)\n    plt.ylabel('MLogLoss')\n    plt.xlabel('Epoch')\n    plt.legend(['train', 'validation'], loc='upper left')\n    plt.show()\n\n\n#plot\ngen_graph(hist, \n              \"Simple net lul\")","execution_count":null,"outputs":[]},{"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)\n    pred = model.predict(imgs, verbose=1)\n    top_3 =  np.argsort(-pred)[:, 0:3]  \n    pred_results.append(top_3)\nprint(\"Finished test predictions...\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#prepare data for saving\nreverse_dict = {v: k for k, v in class_dict.items()}\npred_results = np.concatenate(pred_results)\nprint(\"Finished data prep...\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"preds_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']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sub = 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}