{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"batch_size = 4096\nSTROKE_COUNT = 196\nTRAIN_SAMPLES = 750\nVALID_SAMPLES = 75\nTEST_SAMPLES = 50","metadata":{"_uuid":"8b08fbab2000a563b388f126eac74362641e497c","execution":{"iopub.status.busy":"2023-04-10T16:51:34.662744Z","iopub.execute_input":"2023-04-10T16:51:34.663019Z","iopub.status.idle":"2023-04-10T16:51:34.671066Z","shell.execute_reply.started":"2023-04-10T16:51:34.662968Z","shell.execute_reply":"2023-04-10T16:51:34.67024Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%matplotlib inline\nimport os\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom keras.utils.np_utils import to_categorical\nfrom keras.preprocessing.sequence import pad_sequences\nfrom sklearn.preprocessing import LabelEncoder\nimport pandas as pd\nfrom keras.metrics import top_k_categorical_accuracy\ndef top_3_accuracy(x,y): return top_k_categorical_accuracy(x,y, 3)\nfrom keras.callbacks import ModelCheckpoint, LearningRateScheduler, EarlyStopping, ReduceLROnPlateau\nfrom glob import glob\nimport gc\ngc.enable()\ndef get_available_gpus():\n    from tensorflow.python.client import device_lib\n    local_device_protos = device_lib.list_local_devices()\n    return [x.name for x in local_device_protos if x.device_type == 'GPU']\nbase_dir = os.path.join('..', 'input')\ntest_path = os.path.join(base_dir, 'test_simplified.csv')","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","execution":{"iopub.status.busy":"2023-04-10T16:51:34.672094Z","iopub.execute_input":"2023-04-10T16:51:34.672315Z","iopub.status.idle":"2023-04-10T16:51:35.54251Z","shell.execute_reply.started":"2023-04-10T16:51:34.672267Z","shell.execute_reply":"2023-04-10T16:51:35.541603Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from ast import literal_eval\nALL_TRAIN_PATHS = glob(os.path.join(base_dir, 'train_simplified', '*.csv'))\nCOL_NAMES = ['countrycode', 'drawing', 'key_id', 'recognized', 'timestamp', 'word']\n\ndef _stack_it(raw_strokes):\n    \"\"\"preprocess the string and make \n    a standard Nx3 stroke vector\"\"\"\n    stroke_vec = literal_eval(raw_strokes) # string->list\n    # unwrap the list\n    in_strokes = [(xi,yi,i)  \n     for i,(x,y) in enumerate(stroke_vec) \n     for xi,yi in zip(x,y)]\n    c_strokes = np.stack(in_strokes)\n    # replace stroke id with 1 for continue, 2 for new\n    c_strokes[:,2] = [1]+np.diff(c_strokes[:,2]).tolist()\n    c_strokes[:,2] += 1 # since 0 is no stroke\n    # pad the strokes with zeros\n    return pad_sequences(c_strokes.swapaxes(0, 1), \n                         maxlen=STROKE_COUNT, \n                         padding='post').swapaxes(0, 1)\ndef read_batch(samples=5, \n               start_row=0,\n               max_rows = 1000):\n    \"\"\"\n    load and process the csv files\n    this function is horribly inefficient but simple\n    \"\"\"\n    out_df_list = []\n    for c_path in ALL_TRAIN_PATHS:\n        c_df = pd.read_csv(c_path, nrows=max_rows, skiprows=start_row)\n        c_df.columns=COL_NAMES\n        out_df_list += [c_df.sample(samples)[['drawing', 'word']]]\n    full_df = pd.concat(out_df_list)\n    full_df['drawing'] = full_df['drawing'].\\\n        map(_stack_it)\n    \n    return full_df","metadata":{"_uuid":"7acacf8e960084782425ef1a1a3fd532a240ad48","execution":{"iopub.status.busy":"2023-04-10T16:51:35.5472Z","iopub.execute_input":"2023-04-10T16:51:35.54938Z","iopub.status.idle":"2023-04-10T16:51:35.716174Z","shell.execute_reply.started":"2023-04-10T16:51:35.549319Z","shell.execute_reply":"2023-04-10T16:51:35.715414Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_args = dict(samples=TRAIN_SAMPLES, \n                  start_row=0, \n                  max_rows=int(TRAIN_SAMPLES*1.5))\nvalid_args = dict(samples=VALID_SAMPLES, \n                  start_row=train_args['max_rows']+1, \n                  max_rows=VALID_SAMPLES+25)\ntest_args = dict(samples=TEST_SAMPLES, \n                 start_row=valid_args['max_rows']+train_args['max_rows']+1, \n                 max_rows=TEST_SAMPLES+25)\ntrain_df = read_batch(**train_args)\nvalid_df = read_batch(**valid_args)\ntest_df = read_batch(**test_args)\nword_encoder = LabelEncoder()\nword_encoder.fit(train_df['word'])\nprint('words', len(word_encoder.classes_), '=>', ', '.join([x for x in word_encoder.classes_]))","metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","execution":{"iopub.status.busy":"2023-04-10T16:51:35.720526Z","iopub.execute_input":"2023-04-10T16:51:35.722629Z","iopub.status.idle":"2023-04-10T16:53:50.67293Z","shell.execute_reply.started":"2023-04-10T16:51:35.722573Z","shell.execute_reply":"2023-04-10T16:53:50.671496Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_Xy(in_df):\n    X = np.stack(in_df['drawing'], 0)\n    y = to_categorical(word_encoder.transform(in_df['word'].values))\n    return X, y\ntrain_X, train_y = get_Xy(train_df)\nvalid_X, valid_y = get_Xy(valid_df)\ntest_X, test_y = get_Xy(test_df)\nprint(train_X.shape)","metadata":{"_cell_guid":"ff5ddced-d77e-473f-899d-82cf11ad2bd9","_uuid":"409468f1d5abd17b819482473a4f354a61f8d7ef","execution":{"iopub.status.busy":"2023-04-10T16:53:50.674198Z","iopub.execute_input":"2023-04-10T16:53:50.67448Z","iopub.status.idle":"2023-04-10T16:53:51.865852Z","shell.execute_reply.started":"2023-04-10T16:53:50.674418Z","shell.execute_reply":"2023-04-10T16:53:51.865056Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig, m_axs = plt.subplots(3,3, figsize = (16, 16))\nrand_idxs = np.random.choice(range(train_X.shape[0]), size = 9)\nfor c_id, c_ax in zip(rand_idxs, m_axs.flatten()):\n    test_arr = train_X[c_id]\n    test_arr = test_arr[test_arr[:,2]>0, :] # only keep valid points\n    lab_idx = np.cumsum(test_arr[:,2]-1)\n    for i in np.unique(lab_idx):\n        c_ax.plot(test_arr[lab_idx==i,0], \n                np.max(test_arr[:,1])-test_arr[lab_idx==i,1], '.-')\n    c_ax.axis('off')\n    c_ax.set_title(word_encoder.classes_[np.argmax(train_y[c_id])])","metadata":{"_cell_guid":"56240ed9-42b0-4f62-b3d1-f92017f04e30","_uuid":"5cc79204a0a1da048d1d58ba8dfdafd0af3ebcb8","execution":{"iopub.status.busy":"2023-04-10T16:53:51.866998Z","iopub.execute_input":"2023-04-10T16:53:51.867274Z","iopub.status.idle":"2023-04-10T16:53:52.555176Z","shell.execute_reply.started":"2023-04-10T16:53:51.867225Z","shell.execute_reply":"2023-04-10T16:53:52.553699Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from keras.models import Sequential\nfrom keras.layers import BatchNormalization, Conv1D, LSTM, Dense, Dropout\nif len(get_available_gpus())>0:\n    # https://twitter.com/fchollet/status/918170264608817152?lang=en\n    from keras.layers import CuDNNLSTM as LSTM # this one is about 3x faster on GPU instances\nstroke_read_model = Sequential()\nstroke_read_model.add(BatchNormalization(input_shape = (None,)+train_X.shape[2:]))\n# filter count and length are taken from the script https://github.com/tensorflow/models/blob/master/tutorials/rnn/quickdraw/train_model.py\nstroke_read_model.add(Conv1D(48, (5,)))\nstroke_read_model.add(Dropout(0.3))\nstroke_read_model.add(Conv1D(64, (5,)))\nstroke_read_model.add(Dropout(0.3))\nstroke_read_model.add(Conv1D(96, (3,)))\nstroke_read_model.add(Dropout(0.3))\nstroke_read_model.add(LSTM(128, return_sequences = True))\nstroke_read_model.add(Dropout(0.3))\nstroke_read_model.add(LSTM(128, return_sequences = False))\nstroke_read_model.add(Dropout(0.3))\nstroke_read_model.add(Dense(512))\nstroke_read_model.add(Dropout(0.3))\nstroke_read_model.add(Dense(len(word_encoder.classes_), activation = 'softmax'))\nstroke_read_model.compile(optimizer = 'adam', \n                          loss = 'categorical_crossentropy', \n                          metrics = ['categorical_accuracy', top_3_accuracy])\nstroke_read_model.summary()","metadata":{"_cell_guid":"d65490e7-302e-4232-afe7-4e9499010e31","_uuid":"ba9d55554ba9e4177df5f0645ca1e0f5e4393ca3","execution":{"iopub.status.busy":"2023-04-10T16:53:52.556142Z","iopub.execute_input":"2023-04-10T16:53:52.556377Z","iopub.status.idle":"2023-04-10T16:54:00.962469Z","shell.execute_reply.started":"2023-04-10T16:53:52.556333Z","shell.execute_reply":"2023-04-10T16:54:00.961754Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"weight_path=\"{}_weights.best.hdf5\".format('stroke_lstm_model')\n\ncheckpoint = ModelCheckpoint(weight_path, monitor='val_loss', verbose=1, \n                             save_best_only=True, mode='min', save_weights_only = True)\n\n\nreduceLROnPlat = ReduceLROnPlateau(monitor='val_loss', factor=0.8, patience=10, \n                                   verbose=1, mode='auto', epsilon=0.0001, cooldown=5, min_lr=0.0001)\nearly = EarlyStopping(monitor=\"val_loss\", \n                      mode=\"min\", \n                      patience=5) # probably needs to be more patient, but kaggle time is limited\ncallbacks_list = [checkpoint, early, reduceLROnPlat]","metadata":{"_cell_guid":"2a549512-a9d9-4afd-b748-3e1c3296e193","_uuid":"5fda10b30c47a8cf6ea822ed0a4a1d7cd2c81195","execution":{"iopub.status.busy":"2023-04-10T16:54:00.963875Z","iopub.execute_input":"2023-04-10T16:54:00.964136Z","iopub.status.idle":"2023-04-10T16:54:00.975471Z","shell.execute_reply.started":"2023-04-10T16:54:00.964092Z","shell.execute_reply":"2023-04-10T16:54:00.974596Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from IPython.display import clear_output\nstroke_read_model.fit(train_X, train_y,\n                      validation_data = (valid_X, valid_y), \n                      batch_size = batch_size,\n                      epochs = 50,\n                      callbacks = callbacks_list)\n","metadata":{"_cell_guid":"825b3af8-9451-487b-a1e1-538f2f1489e1","_uuid":"ed2fc26af74aed1a93bbc253d61b72db5a81f5cc","execution":{"iopub.status.busy":"2023-04-10T16:54:00.976708Z","iopub.execute_input":"2023-04-10T16:54:00.977214Z","iopub.status.idle":"2023-04-10T17:14:51.702835Z","shell.execute_reply.started":"2023-04-10T16:54:00.977154Z","shell.execute_reply":"2023-04-10T17:14:51.702125Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"stroke_read_model.load_weights(weight_path)\nlstm_results = stroke_read_model.evaluate(test_X, test_y, batch_size = 4096)\nprint('Accuracy: %2.1f%%, Top 3 Accuracy %2.1f%%' % (100*lstm_results[1], 100*lstm_results[2]))","metadata":{"_cell_guid":"a7eb5b62-cf57-4380-8786-9ddc05be658f","_uuid":"858059b6c16d81f86460bef8fcf595e0d68d12b2","execution":{"iopub.status.busy":"2023-04-10T17:14:51.705643Z","iopub.execute_input":"2023-04-10T17:14:51.705857Z","iopub.status.idle":"2023-04-10T17:14:52.430176Z","shell.execute_reply.started":"2023-04-10T17:14:51.705812Z","shell.execute_reply":"2023-04-10T17:14:52.429438Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# get the training and validation metrics\ntrain_loss = stroke_read_model.history.history['loss']\nvalid_loss = stroke_read_model.history.history['val_loss']\ntrain_acc = stroke_read_model.history.history['categorical_accuracy']\nvalid_acc = stroke_read_model.history.history['val_categorical_accuracy']\ntrain_top3_acc = stroke_read_model.history.history['top_3_accuracy']\nvalid_top3_acc = stroke_read_model.history.history['val_top_3_accuracy']\n\n# plot the training and validation loss\nplt.figure(figsize=(10, 5))\nplt.plot(train_loss, label='Training Loss')\nplt.plot(valid_loss, label='Validation Loss')\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\nplt.title('Training and Validation Loss')\nplt.legend()\nplt.show()\n\n# plot the training and validation top-1 accuracy\nplt.figure(figsize=(10, 5))\nplt.plot(train_acc, label='Training Top-1 Accuracy')\nplt.plot(valid_acc, label='Validation Top-1 Accuracy')\nplt.xlabel('Epoch')\nplt.ylabel('Accuracy')\nplt.title('Training and Validation Top-1 Accuracy')\nplt.legend()\nplt.show()\n\n# plot the training and validation top-3 accuracy\nplt.figure(figsize=(10, 5))\nplt.plot(train_top3_acc, label='Training Top-3 Accuracy')\nplt.plot(valid_top3_acc, label='Validation Top-3 Accuracy')\nplt.xlabel('Epoch')\nplt.ylabel('Accuracy')\nplt.title('Training and Validation Top-3 Accuracy')\nplt.legend()\nplt.show()\n\n","metadata":{"execution":{"iopub.status.busy":"2023-04-10T17:14:52.431373Z","iopub.execute_input":"2023-04-10T17:14:52.432071Z","iopub.status.idle":"2023-04-10T17:14:53.131718Z","shell.execute_reply.started":"2023-04-10T17:14:52.432023Z","shell.execute_reply":"2023-04-10T17:14:53.130918Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from sklearn.metrics import confusion_matrix, classification_report\ntest_cat = np.argmax(test_y, 1)\npred_y = stroke_read_model.predict(test_X, batch_size = 4096)\npred_cat = np.argmax(pred_y, 1)\nplt.matshow(confusion_matrix(test_cat, pred_cat))\nprint(classification_report(test_cat, pred_cat, \n                            target_names = [x for x in word_encoder.classes_]))","metadata":{"_cell_guid":"ee75c585-b134-4ea2-b8f3-219e24efd1f1","_uuid":"6b9cdf52d233de60108d72f540db978801b578c1","execution":{"iopub.status.busy":"2023-04-10T17:14:53.132986Z","iopub.execute_input":"2023-04-10T17:14:53.133493Z","iopub.status.idle":"2023-04-10T17:14:54.09779Z","shell.execute_reply.started":"2023-04-10T17:14:53.133426Z","shell.execute_reply":"2023-04-10T17:14:54.096721Z"},"trusted":true},"execution_count":null,"outputs":[]}]}