{"cells":[{"metadata":{},"cell_type":"markdown","source":"다음 Descripstion에 어떤 방법을 어떤 이유로 선택했는지 작성함."},{"metadata":{"trusted":true},"cell_type":"code","source":"#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"\nDescripstion(어떤 방법을 어떤 이유로 선택했는지)\n\nI used the local computer without using the kernel of the Kaggle.\nSince I thought the kernel lacked resources such as RAM and disks.\n\nCurrently, this code has only been enabled to execute. \n\nRemove the Args object and Modify main function to run it in a local computer.\n\nModel:\n    I implemented basic CNN model introduced Quick, Draw! Doodle Recognition paper from scratch.\n    And I used Transfer Learning Models like Densenet, Mobilnet, Xception.\n\n    These models such as ResNet are \"large\" deep learning model. \n    These models require a big image size. \n    So, I can't use these models since I don't have large resources.\n    \n    Mobilenet is shallow deep learning model.\n    It didn't come out as much as I expected.\n\n    DenseNet201 doesn't requires a big image size.\n\n    I used a range of models and hyperparameters.\n\n    Best performance is the result of DenseNet201 + FCN(512, 512).\n\nMulti GPUs:\n    This code was implemented for using multi gpus.\n    If you use multi gpus model, you must use multi gpus in test.\n\nData:\n    I used only simplified train data because my server computer's hard disk capacity was insufficient.\n    Anyway, I employed Beluga's pre-processing method.\n\n\"\"\"\n\n###########\n# imports #\n###########\n\nimport os\nimport json\nimport datetime as dt\nimport cv2\nimport numpy as np\nimport pandas as pd\nimport tensorflow as tf\nfrom argparse import ArgumentParser, Namespace\nfrom tensorflow.keras.metrics import categorical_accuracy, top_k_categorical_accuracy, categorical_crossentropy\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.applications import MobileNet, Xception, DenseNet201\nfrom tensorflow.keras.callbacks import ReduceLROnPlateau, ModelCheckpoint\n\n\nDATA_DIR = '../input/shuffle-csvs/'\nINPUT_DIR = '../input/quickdraw-doodle-recognition'\n\nBASE_SIZE = 256\nnum_csvs = 100\nnum_classes = 340\n\nlw = 1\nchannel = 3\nborder = 2  \n\n\n#############\n# Converter #\n#############\n\ndef f2cat(filename):\n    return filename.split('.')[0]\n\ndef list_all_categories():\n    files = os.listdir(os.path.join(INPUT_DIR, 'train_simplified'))\n    return sorted([f2cat(f) for f in files], key=str.lower)\n\ndef preds2catids(predictions):\n\treturn pd.DataFrame(np.argsort(-predictions, axis=1)[:, :3], columns=['a', 'b', 'c'])\n\n\n###########\n# Metrics #\n###########\n\ndef apk(actual, predicted, k=3):\n    \"\"\"ref: https://github.com/benhamner/Metrics/blob/master/Python/ml_metrics/average_precision.py\n    \"\"\"\n    print('actual:', actual, 'predicted:', predicted)\n    if len(predicted) > k:\n        predicted = predicted[:k]\n    score = 0.0\n    num_hits = 0.0\n    for i, p in enumerate(predicted):\n        if p in actual and p not in predicted[:i]:\n            num_hits += 1.0\n            score += num_hits / (i + 1.0)\n    if not actual:\n        return 0.0\n    return score / min(len(actual), k)\n\ndef mapk(actual, predicted, k=3):\n    \"\"\"ref: https://github.com/benhamner/Metrics/blob/master/Python/ml_metrics/average_precision.py\n    \"\"\"\n    return np.mean([apk(a, p, k) for a, p in zip(actual, predicted)])\n\ndef top_3_accuracy(y_true, y_pred):\n    return top_k_categorical_accuracy(y_true, y_pred, k=3)\n\n\n#############\n# Generator #\n#############\n\n\nshift_colors = (\n        (255, 0, 0),\n        (255, 128, 0),\n        (255, 255, 0),\n        (128, 255, 0),\n        (0, 255, 0),\n        (0, 255, 128),\n        (0, 255, 255),\n        (0, 128, 255),\n        (0, 0, 255),\n        (128, 0, 255),\n        (255, 0, 255),\n        (255, 0, 128)\n)\n\ndef list2drawing(raw_strokes, img_size):\n    img = np.zeros((img_size, img_size, channel), np.uint8)\n    coef = (img_size - 2 * lw - 2 * border) / (BASE_SIZE - 1)\n    nb_stokes = len(raw_strokes)\n    for t, stroke in enumerate(raw_strokes[::-1]):\n        rgb = shift_colors[(nb_stokes-t-1)%12]\n\n        for i in range(len(stroke[0]) - 1):\n            p1 = (int(coef * stroke[0][i] + lw + border), int(coef * stroke[1][i] + lw+ border))\n            p2 = (int(coef * stroke[0][i + 1] + lw + border), int(coef * stroke[1][i + 1] + lw + border))\n            _ = cv2.line(img, p1, p2, rgb, lw, cv2.LINE_AA)\n    if img_size != BASE_SIZE:\n        return cv2.resize(img, (img_size, img_size))\n    else:\n        return img\n\ndef draw_cv2(raw_strokes, img_size=256, lw=lw, time_color=True):\n    \"\"\"Ref: https://www.kaggle.com/gaborfodor/greyscale-mobilenet-lb-0-892\n    \"\"\"\n    img = np.zeros((BASE_SIZE, BASE_SIZE), np.uint8)\n    for t, stroke in enumerate(raw_strokes):\n        for i in range(len(stroke[0]) - 1):\n            color = 255 - min(t, 10) * 13 if time_color else 255\n            _ = cv2.line(img, (stroke[0][i], stroke[1][i]),\n                         (stroke[0][i + 1], stroke[1][i + 1]), color, lw)\n    if img_size != BASE_SIZE:\n        return cv2.resize(img, (img_size, img_size))\n    else:\n        return img\n\ndef image_generator_test(img_size, batch_size, model_name, lw=lw, time_color=True):\n    while True:\n        filename = os.path.join(INPUT_DIR, 'test_simplified.csv') \n        for df in pd.read_csv(filename, chunksize=batch_size):\n            df['drawing'] = df['drawing'].apply(json.loads)\n            x = np.zeros((len(df), img_size, img_size, channel))\n            for i, raw_strokes in enumerate(df.drawing.values):\n                x[i, :, :, 0] = draw_cv2(raw_strokes, img_size=img_size, lw=lw,\n                                        time_color=time_color)\n#                     x[i, :, :, :] = list2drawing(raw_strokes, img_size=img_size)\n            if model_name == 'mobilenet':\n                x = tf.keras.applications.mobilenet.preprocess_input(x).astype(np.float32)\n            elif model_name =='xception':\n                x = tf.keras.applications.xception.preprocess_input(x).astype(np.float32)\n            elif model_name == 'densenet201':\n                x = tf.keras.applications.densenet.preprocess_input(x).astype(np.float32)\n            else:\n                x /= 255.\n                x.astype(np.float32)\n            yield x\n\ndef image_generator_xd(img_size, batch_size, ks, model_name, lw=lw, time_color=True):\n    while True:\n        for k in np.random.permutation(ks):\n            filename = os.path.join(DATA_DIR, 'train_k{}.csv.gz'.format(k))\n            for df in pd.read_csv(filename, chunksize=batch_size):\n                df['drawing'] = df['drawing'].apply(json.loads)\n                x = np.zeros((len(df), img_size, img_size, channel))\n                for i, raw_strokes in enumerate(df.drawing.values):\n                    x[i, :, :, 0] = draw_cv2(raw_strokes, img_size=img_size, lw=lw,\n                                            time_color=time_color)\n#                     x[i, :, :, :] = list2drawing(raw_strokes, img_size=img_size)\n                if model_name == 'mobilenet':\n                    x = tf.keras.applications.mobilenet.preprocess_input(x).astype(np.float32)\n                elif model_name =='xception':\n                    x = tf.keras.applications.xception.preprocess_input(x).astype(np.float32)\n                elif model_name == 'densenet201':\n                    x = tf.keras.applications.densenet.preprocess_input(x).astype(np.float32)\n                else:\n                    x /= 255.\n                    x.astype(np.float32)\n                y = tf.keras.utils.to_categorical(df.y, num_classes=num_classes)\n                yield x, y\n                \n                \ndef df_to_image_array_xd(df, img_size, model_name, lw=lw, time_color=True):\n    df['drawing'] = df['drawing'].apply(json.loads)\n    x = np.zeros((len(df), img_size, img_size, channel))\n    for i, raw_strokes in enumerate(df.drawing.values):\n        x[i, :, :, 0] = draw_cv2(raw_strokes, img_size=img_size, lw=lw, time_color=time_color)\n#         x[i, :, :, :] = list2drawing(raw_strokes, img_size=img_size)\n    if model_name == 'mobilenet':\n        x = tf.keras.applications.mobilenet.preprocess_input(x).astype(np.float32)\n    elif model_name =='xception':\n        x = tf.keras.applications.xception.preprocess_input(x).astype(np.float32)\n    elif model_name == 'densenet201':\n        x = tf.keras.applications.densenet.preprocess_input(x).astype(np.float32)\n    else:\n        x /= 255.\n        x.astype(np.float32)\n    return x\n\n\n#########\n# Model #\n#########\n\nclass StanfordCNN(tf.keras.layers.Layer):\n    \"\"\"ref: http://cs229.stanford.edu/proj2018/report/98.pdf\n    It is an CNN architectural model introduced in the present paper.\n    \"\"\"\n    def __init__(self, filters, kernel_size, pool_size, num_classes):\n        super(StanfordCNN, self).__init__()\n        \n        self.conv1 = tf.keras.layers.Conv2D(filters, kernel_size, padding='same')\n        self.conv2 = tf.keras.layers.Conv2D(filters, kernel_size, padding='same')\n        self.conv3 = tf.keras.layers.Conv2D(filters, kernel_size, padding='same')\n        self.maxpool = tf.keras.layers.MaxPool2D(pool_size)\n        \n        self.fcn = FCN([700,500,400], num_classes)\n\n    def call(self, x):\n        x = self.conv1(x)\n        x = self.conv2(x)\n        x = self.conv3(x)\n        x = self.maxpool(x)\n        \n        x = self.fcn(x)\n\n        return x\n\nclass FCN(tf.keras.layers.Layer):\n    def __init__(self, num_unit, num_classes):\n        super(FCN, self).__init__()\n        self.layers = []\n\n        num_layer = len(num_unit)\n\n        self.flatten = tf.keras.layers.Flatten()\n\n        for i in range(num_layer):\n            W = tf.keras.layers.Dense(num_unit[i], activation='relu')\n            self.layers.append(W)\n\n        self.W_o = tf.keras.layers.Dense(num_classes, activation='softmax')\n\n    def call(self, x):\n        x = self.flatten(x)\n        for layer in self.layers:\n            x = layer(x)\n        return self.W_o(x)\n\n\ndef create_model(model_name, input_shape, num_gpu, learning_rate):\n\n    if model_name.lower()=='mobilenet':\n        model = MobileNet(input_shape=input_shape, weights=None, classes=num_classes)\n\n    elif model_name.lower() == 'xception':\n        xcep = Xception(input_shape=input_shape, weights=None, include_top=False)\n        fcn = FCN([512, 512], num_classes)\n\n        output = fcn(xcep.output)\n        model = tf.keras.Model(inputs=xcep.input, outputs=output)\n\n    elif model_name.lower() =='densenet201':\n        dense = DenseNet201(input_shape=input_shape, weights='imagenet', include_top=False) \n        x = dense.output\n        x = tf.keras.layers.GlobalAveragePooling2D()(x)\n        fcn = FCN([1024, 512], num_classes)\n\n        output = fcn(dense.output)\n        model = tf.keras.Model(inputs=dense.input, outputs=output)\n\n    elif model_name.lower() == 'resnet50':\n        resnet = tf.keras.applications.ResNet50(input_shape=input_shape, weights=None, include_top=False, pooling='avg')\n        fcn = FCN([512, 512], num_classes)\n\n        output = fcn(resnet.output)\n        model = tf.keras.Model(inputs=resnet.input, outputs=output)\n\n    elif model_name.lower() == 'base':\n        stanCNN = StanfordCNN(5, (3,3), (2,2), num_classes)\n        input_img = tf.keras.layers.Input(shape=input_shape)\n        output = stanCNN(input_img)\n        model = tf.keras.Model(inputs=input_img, outputs=output)\n\n    elif model_name.lower() == 'inception':\n        incep = tf.keras.applications.inception_resnet_v2.InceptionResNetV2(input_shape=input_shape, weights=None, include_top=False, pooling='avg')\n        fcn = FCN([512], num_classes)\n\n        output = fcn(incep.output) \n        model = tf.keras.Model(inputs=incep.input, outputs=output)\n\n\n\n    if num_gpu != 1:\n        model = tf.keras.utils.multi_gpu_model(model, gpus=num_gpu)\n\n    model.compile(optimizer=Adam(lr=learning_rate), loss='categorical_crossentropy',\n                  metrics=[categorical_crossentropy, categorical_accuracy, top_3_accuracy])\n\n    print(model.summary())\n\n    return model\n\n\n############\n# Executor #\n############\n\ndef test(args: Namespace, model=None):\n\n    img_size = args.img_size \n    input_shape = (img_size, img_size, channel)\n    model_name = args.model_name\n\n    test = pd.read_csv(os.path.join(INPUT_DIR, 'test_simplified.csv'))\n#     x_test = df_to_image_array_xd(test, img_size, model_name)\n#     print(test.shape, x_test.shape)\n#     print('Test array memory {:.2f} GB'.format(x_test.nbytes / 2.**30))\n\n    test_datagen = image_generator_test(img_size=img_size, batch_size=128, model_name=model_name)\n\n\n    if model == None:\n        print('Loading saved model')\n        model = create_model(model_name, input_shape, args.num_gpu, args.learning_rate)\n        model.load_weights('model.h5')\n\n#         valid_df = pd.read_csv(os.path.join(DATA_DIR, 'train_k{}.csv.gz'.format(num_csvs - 1)), nrows=3000)\n#         x_valid = df_to_image_array_xd(valid_df, img_size, model_name)\n#         y_valid = tf.keras.utils.to_categorical(valid_df.y, num_classes=num_classes)\n#         print(x_valid.shape, y_valid.shape)\n#         print('Validation array memory {:.2f} GB'.format(x_valid.nbytes / 2.**30 ))\n#         valid_predictions = model.predict(x_valid, batch_size=128, verbose=1)\n#         map3 = mapk(valid_df[['y']].values, preds2catids(valid_predictions).values)\n#         print('Map3: {:.3f}'.format(map3))\n\n#     test_predictions = model.predict(x_test, batch_size=128, verbose=1)\n    test_predictions = model.predict_generator(test_datagen, verbose=1, steps=len(test)//128)\n\n    top3 = preds2catids(test_predictions)\n\n    cats = list_all_categories()\n    id2cat = {k: cat.replace(' ', '_') for k, cat in enumerate(cats)}\n    top3cats = top3.replace(id2cat)\n\n    test['word'] = top3cats['a'] + ' ' + top3cats['b'] + ' ' + top3cats['c']\n    submission = test[['key_id', 'word']]\n    now_time = dt.datetime.now().strftime(\"%m%d%H%M\")\n    submission.to_csv('my_submission_{}.csv'.format(now_time), index=False)\n    print('submission shape:', submission.shape)\n\ndef train(args: Namespace):\n    start = dt.datetime.now()\n\n    num_gpu = args.num_gpu\n    epochs = args.epoch \n    batch_size = args.batch_size * num_gpu\n    if args.steps == -1:\n        steps_per_epoch = (49209839) // batch_size \n    else: \n        steps_per_epoch = args.steps \n    learning_rate = args.learning_rate\n    img_size = args.img_size\n    input_shape = (img_size, img_size, channel)\n    model_name = args.model_name\n\n    model = create_model(model_name, input_shape, num_gpu, learning_rate) \n\n    valid_df = pd.read_csv(os.path.join(DATA_DIR, 'train_k{}.csv.gz'.format(num_csvs - 1)), nrows=3000)\n    x_valid = df_to_image_array_xd(valid_df, img_size, model_name)\n    y_valid = tf.keras.utils.to_categorical(valid_df.y, num_classes=num_classes)\n    print(x_valid.shape, y_valid.shape)\n    print('Validation array memory {:.2f} GB'.format(x_valid.nbytes / 2.**30 ))\n\n    train_datagen = image_generator_xd(img_size=img_size, batch_size=batch_size, ks=range(num_csvs - 1), model_name=model_name)\n\n    callbacks = [\n        ReduceLROnPlateau(monitor='val_top_3_accuracy', factor=0.75, patience=3, min_delta=0.001,\n                              mode='max', min_lr=1e-5, verbose=1),\n        ModelCheckpoint('model.h5', monitor='val_top_3_accuracy', mode='max', save_best_only=True,\n                        save_weights_only=True),\n    ]\n\n    model.fit_generator(\n        train_datagen, steps_per_epoch=steps_per_epoch, epochs=epochs, verbose=1,\n        validation_data=(x_valid, y_valid),\n        callbacks = callbacks\n    )\n\n    valid_predictions = model.predict(x_valid, batch_size=128, verbose=1)\n    map3 = mapk(valid_df[['y']].values, preds2catids(valid_predictions).values)\n    print('Map3: {:.3f}'.format(map3))\n\n    end = dt.datetime.now()\n    print('Total time {}s'.format((end - start).seconds))\n\n    return model\n\nclass Args():\n    \"\"\"This Object is for using Kaggle Kernel.\n    \"\"\"\n    def __init__(self):\n        self.mode = 'train'\n        self.model_name = 'densenet201'\n        self.img_size = 75\n        self.batch_size = 256\n        self.epoch = 15\n        self.steps = 500\n        self.learning_rate = 0.002\n        self.num_gpu = 1\n        \ndef main():\n    \n    \"\"\"Remove the following annotations if you want to use local resources.\"\"\"\n\n    # parser = ArgumentParser(description='train model from data')\n\n    # parser.add_argument('--mode', help='train or test <default: train>', metavar='STRING', default='train')\n    # parser.add_argument('--model-name', help='select a model <default: mobilenet>', metavar='STRING', default='mobilenet')\n    # parser.add_argument('--img-size', help='image size <default: 70>', metavar='INT', \n    #                     type=int, default=75)\n\n    # parser.add_argument('--batch-size', help='batch size <default: 256>', metavar='INT', \n    #                     type=int, default=256)\n    # parser.add_argument('--epoch', help='epoch number <default: 3>', metavar='INT', \n    #                     type=int, default=3)\n    # parser.add_argument('--steps', help='steps <default: -1>', metavar='INT', \n    #                     type=int, default=-1)\n    # parser.add_argument('--learning-rate', help='learning_rate <default: 1>', \n    #                     metavar='REAL', type=float, default=0.002)\n\n    # parser.add_argument('--num-gpu', help='the number of GPUs to use <default: 1>', \n    #                     metavar='INT', type=int, default=1)\n\n    # args = parser.parse_args()\n    \n    \"\"\"Remove this object if you want to use local resources.\"\"\"\n    args = Args() \n    \n\n#     if args.mode=='train':\n#         train(args)\n#     elif args.mode=='test':\n#         test(args)\n\n    model = train(args)\n#     model = None\n    \n    test(args, model)\n    \n\nif __name__=='__main__':\n    main()\n","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}