{"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":"markdown","source":"# Data Import & EDA","metadata":{}},{"cell_type":"code","source":"from pathlib import Path\n\nimages_path = Path('/kaggle/input/benetech-making-graphs-accessible/train/images')","metadata":{"execution":{"iopub.status.busy":"2023-06-08T07:12:42.819011Z","iopub.execute_input":"2023-06-08T07:12:42.819956Z","iopub.status.idle":"2023-06-08T07:12:42.850322Z","shell.execute_reply.started":"2023-06-08T07:12:42.819899Z","shell.execute_reply":"2023-06-08T07:12:42.849291Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"extra_data_path = Path('/kaggle/input/b-m-g-a-extra-data')","metadata":{"execution":{"iopub.status.busy":"2023-06-08T07:12:44.393632Z","iopub.execute_input":"2023-06-08T07:12:44.394003Z","iopub.status.idle":"2023-06-08T07:12:44.399804Z","shell.execute_reply.started":"2023-06-08T07:12:44.393969Z","shell.execute_reply":"2023-06-08T07:12:44.398568Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We've prepared a CSV file containing:\n\n1. The file names of the `'line'` and `'vertical_bar'` images.\n2. Whether the x-axis tick labels are rotated or not. (**Note:** 90 degree rotation has been treated as `'non_rotated'`.)\n3. The sources: `'generated'` / `'extracted'`.\n4. The chart types: `'line'` / `'vertical_bar'`.\n5. A column `'is_val'` containing information on whether each image belongs to the training set or the validation set. (**Note:** All `'generated'` images have been put in the training set, and all `'extracted'` images have been put in the validation set.)\n\nLet's read it in.","metadata":{}},{"cell_type":"code","source":"import pandas as pd\n\ndf = pd.read_csv(extra_data_path/'rotated_vs_non_rotated_x_tick_labels.csv')\ndf","metadata":{"execution":{"iopub.status.busy":"2023-06-08T07:12:47.076601Z","iopub.execute_input":"2023-06-08T07:12:47.077121Z","iopub.status.idle":"2023-06-08T07:12:47.210278Z","shell.execute_reply.started":"2023-06-08T07:12:47.077082Z","shell.execute_reply":"2023-06-08T07:12:47.209291Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df['rotation'].value_counts()","metadata":{"execution":{"iopub.status.busy":"2023-06-08T07:12:49.634251Z","iopub.execute_input":"2023-06-08T07:12:49.634792Z","iopub.status.idle":"2023-06-08T07:12:49.655601Z","shell.execute_reply.started":"2023-06-08T07:12:49.634752Z","shell.execute_reply":"2023-06-08T07:12:49.654638Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df['chart_type'].value_counts()","metadata":{"execution":{"iopub.status.busy":"2023-06-08T07:12:50.158853Z","iopub.execute_input":"2023-06-08T07:12:50.159910Z","iopub.status.idle":"2023-06-08T07:12:50.181164Z","shell.execute_reply.started":"2023-06-08T07:12:50.159860Z","shell.execute_reply":"2023-06-08T07:12:50.180213Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df['source'].value_counts()","metadata":{"execution":{"iopub.status.busy":"2023-06-08T07:12:50.682335Z","iopub.execute_input":"2023-06-08T07:12:50.683374Z","iopub.status.idle":"2023-06-08T07:12:50.698016Z","shell.execute_reply.started":"2023-06-08T07:12:50.683322Z","shell.execute_reply":"2023-06-08T07:12:50.696952Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df['is_val'].value_counts()","metadata":{"execution":{"iopub.status.busy":"2023-06-08T07:12:53.303275Z","iopub.execute_input":"2023-06-08T07:12:53.304334Z","iopub.status.idle":"2023-06-08T07:12:53.314716Z","shell.execute_reply.started":"2023-06-08T07:12:53.304292Z","shell.execute_reply":"2023-06-08T07:12:53.313221Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's visualize a few images from each of the two classes.","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt","metadata":{"execution":{"iopub.status.busy":"2023-06-08T07:12:55.925440Z","iopub.execute_input":"2023-06-08T07:12:55.926187Z","iopub.status.idle":"2023-06-08T07:12:55.931234Z","shell.execute_reply.started":"2023-06-08T07:12:55.926146Z","shell.execute_reply":"2023-06-08T07:12:55.929959Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"rotated_sample = df.loc[df['rotation'] == 'rotated'].sample(n=10, replace=False, random_state=42, axis=0, ignore_index=True)\n\nplt.figure(figsize=(24, 10)) # width, height in inches.\nfor i, image_loc in enumerate(rotated_sample['img_file_name']):\n    plt.subplot(2, 5, i + 1) # nrows, ncols, index.\n    img = plt.imread(images_path/image_loc)\n    plt.title(rotated_sample['rotation'][i])\n    plt.imshow(img)","metadata":{"execution":{"iopub.status.busy":"2023-06-08T07:12:56.107425Z","iopub.execute_input":"2023-06-08T07:12:56.108151Z","iopub.status.idle":"2023-06-08T07:12:57.991785Z","shell.execute_reply.started":"2023-06-08T07:12:56.108117Z","shell.execute_reply":"2023-06-08T07:12:57.990574Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"non_rotated_sample = df.loc[df['rotation'] == 'non_rotated'].sample(n=10, replace=False, random_state=42, axis=0, ignore_index=True)\n\nplt.figure(figsize=(24, 10)) # width, height in inches.\nfor i, image_loc in enumerate(non_rotated_sample['img_file_name']):\n    plt.subplot(2, 5, i + 1) # nrows, ncols, index.\n    img = plt.imread(images_path/image_loc)\n    plt.title(non_rotated_sample['rotation'][i])\n    plt.imshow(img)","metadata":{"execution":{"iopub.status.busy":"2023-06-08T07:13:03.302325Z","iopub.execute_input":"2023-06-08T07:13:03.302777Z","iopub.status.idle":"2023-06-08T07:13:05.395506Z","shell.execute_reply.started":"2023-06-08T07:13:03.302735Z","shell.execute_reply":"2023-06-08T07:13:05.394531Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model Training","metadata":{}},{"cell_type":"code","source":"import os\nimport random\nimport numpy as np\nimport torch\n\ndef seed_everything(seed):\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    random.seed(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.cuda.manual_seed_all(seed)\n    torch.backends.cudnn.deterministic = True\n    torch.backends.cudnn.benchmark = False\n\nseed_everything(42)","metadata":{"execution":{"iopub.status.busy":"2023-06-08T07:13:25.837433Z","iopub.execute_input":"2023-06-08T07:13:25.838188Z","iopub.status.idle":"2023-06-08T07:13:28.133677Z","shell.execute_reply.started":"2023-06-08T07:13:25.838146Z","shell.execute_reply":"2023-06-08T07:13:28.132542Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from fastai.vision.all import *\n\ndls = ImageDataLoaders.from_df(\n    df=df, \n    path=images_path, \n    fn_col=0, \n    label_col=1, \n    valid_col=4, \n    item_tfms=Resize(448, ResizeMethod.Pad, pad_mode='zeros'), \n    shuffle=True\n)\ntype(dls)","metadata":{"execution":{"iopub.status.busy":"2023-06-08T07:13:30.043257Z","iopub.execute_input":"2023-06-08T07:13:30.043863Z","iopub.status.idle":"2023-06-08T07:13:41.300231Z","shell.execute_reply.started":"2023-06-08T07:13:30.043823Z","shell.execute_reply":"2023-06-08T07:13:41.298976Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's save the `DataLoaders` object to disk.","metadata":{}},{"cell_type":"code","source":"torch.save(dls, 'rotation_dls.pkl', pickle_protocol=2)","metadata":{"execution":{"iopub.status.busy":"2023-06-08T07:14:17.188477Z","iopub.execute_input":"2023-06-08T07:14:17.189641Z","iopub.status.idle":"2023-06-08T07:14:17.642512Z","shell.execute_reply.started":"2023-06-08T07:14:17.189584Z","shell.execute_reply":"2023-06-08T07:14:17.640361Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dls.vocab","metadata":{"execution":{"iopub.status.busy":"2023-06-08T07:14:19.285627Z","iopub.execute_input":"2023-06-08T07:14:19.286022Z","iopub.status.idle":"2023-06-08T07:14:19.294354Z","shell.execute_reply.started":"2023-06-08T07:14:19.285986Z","shell.execute_reply":"2023-06-08T07:14:19.293186Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"len(dls.train.dataset), len(dls.valid.dataset)","metadata":{"execution":{"iopub.status.busy":"2023-06-08T07:14:22.059441Z","iopub.execute_input":"2023-06-08T07:14:22.059853Z","iopub.status.idle":"2023-06-08T07:14:22.068321Z","shell.execute_reply.started":"2023-06-08T07:14:22.059819Z","shell.execute_reply":"2023-06-08T07:14:22.066065Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"len(dls.train), len(dls.valid)","metadata":{"execution":{"iopub.status.busy":"2023-06-08T07:14:24.767883Z","iopub.execute_input":"2023-06-08T07:14:24.768548Z","iopub.status.idle":"2023-06-08T07:14:24.778017Z","shell.execute_reply.started":"2023-06-08T07:14:24.768482Z","shell.execute_reply":"2023-06-08T07:14:24.776938Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dls.train.show_batch()","metadata":{"execution":{"iopub.status.busy":"2023-06-08T07:14:25.145194Z","iopub.execute_input":"2023-06-08T07:14:25.145592Z","iopub.status.idle":"2023-06-08T07:14:27.191703Z","shell.execute_reply.started":"2023-06-08T07:14:25.145555Z","shell.execute_reply":"2023-06-08T07:14:27.190539Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dls.valid.show_batch()","metadata":{"execution":{"iopub.status.busy":"2023-06-08T07:14:31.909620Z","iopub.execute_input":"2023-06-08T07:14:31.910589Z","iopub.status.idle":"2023-06-08T07:14:33.810416Z","shell.execute_reply.started":"2023-06-08T07:14:31.910533Z","shell.execute_reply":"2023-06-08T07:14:33.809441Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"learn = vision_learner(dls, resnet34, metrics=accuracy)","metadata":{"execution":{"iopub.status.busy":"2023-06-08T07:14:38.159037Z","iopub.execute_input":"2023-06-08T07:14:38.159753Z","iopub.status.idle":"2023-06-08T07:14:39.280512Z","shell.execute_reply.started":"2023-06-08T07:14:38.159706Z","shell.execute_reply":"2023-06-08T07:14:39.279438Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's tell the `Learner` that the models should be saved in `'/kaggle/working/models'`.","metadata":{}},{"cell_type":"code","source":"learn.path = Path('/kaggle/working')\nlearn.model_dir = 'models'","metadata":{"execution":{"iopub.status.busy":"2023-06-08T07:14:42.582139Z","iopub.execute_input":"2023-06-08T07:14:42.582561Z","iopub.status.idle":"2023-06-08T07:14:42.587837Z","shell.execute_reply.started":"2023-06-08T07:14:42.582515Z","shell.execute_reply":"2023-06-08T07:14:42.586346Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"learn.fine_tune(epochs=1)","metadata":{"execution":{"iopub.status.busy":"2023-06-08T07:14:43.619023Z","iopub.execute_input":"2023-06-08T07:14:43.619403Z","iopub.status.idle":"2023-06-08T07:36:49.743445Z","shell.execute_reply.started":"2023-06-08T07:14:43.619367Z","shell.execute_reply":"2023-06-08T07:36:49.739579Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's save the model.","metadata":{}},{"cell_type":"code","source":"learn.save('rotation_model', with_opt=False)","metadata":{"execution":{"iopub.status.busy":"2023-06-08T07:36:55.776172Z","iopub.execute_input":"2023-06-08T07:36:55.776587Z","iopub.status.idle":"2023-06-08T07:36:55.924761Z","shell.execute_reply.started":"2023-06-08T07:36:55.776547Z","shell.execute_reply":"2023-06-08T07:36:55.923565Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's also export the `Learner`.","metadata":{}},{"cell_type":"code","source":"learn.export('rotation_learner.pkl')","metadata":{"execution":{"iopub.status.busy":"2023-06-08T07:36:58.518366Z","iopub.execute_input":"2023-06-08T07:36:58.519359Z","iopub.status.idle":"2023-06-08T07:36:58.896345Z","shell.execute_reply.started":"2023-06-08T07:36:58.519316Z","shell.execute_reply":"2023-06-08T07:36:58.895278Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model Evaluation","metadata":{}},{"cell_type":"code","source":"learn.validate()","metadata":{"execution":{"iopub.status.busy":"2023-06-08T07:37:01.705235Z","iopub.execute_input":"2023-06-08T07:37:01.705730Z","iopub.status.idle":"2023-06-08T07:37:10.351836Z","shell.execute_reply.started":"2023-06-08T07:37:01.705690Z","shell.execute_reply":"2023-06-08T07:37:10.350587Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"learn.show_results()","metadata":{"execution":{"iopub.status.busy":"2023-06-08T07:37:13.049055Z","iopub.execute_input":"2023-06-08T07:37:13.050167Z","iopub.status.idle":"2023-06-08T07:37:15.305415Z","shell.execute_reply.started":"2023-06-08T07:37:13.050108Z","shell.execute_reply":"2023-06-08T07:37:15.304459Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"interp = ClassificationInterpretation.from_learner(learn)\ninterp.plot_confusion_matrix()","metadata":{"execution":{"iopub.status.busy":"2023-06-08T07:37:20.387427Z","iopub.execute_input":"2023-06-08T07:37:20.388180Z","iopub.status.idle":"2023-06-08T07:37:38.377931Z","shell.execute_reply.started":"2023-06-08T07:37:20.388139Z","shell.execute_reply":"2023-06-08T07:37:38.376353Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"interp.plot_top_losses(20, nrows=5, figsize=(20, 20))","metadata":{"execution":{"iopub.status.busy":"2023-06-08T07:37:42.410864Z","iopub.execute_input":"2023-06-08T07:37:42.412013Z","iopub.status.idle":"2023-06-08T07:37:45.434064Z","shell.execute_reply.started":"2023-06-08T07:37:42.411957Z","shell.execute_reply":"2023-06-08T07:37:45.432096Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's profile how much time it takes to run inference on all 880 validation images.","metadata":{}},{"cell_type":"code","source":"%%time\npreds, targs = learn.get_preds()","metadata":{"execution":{"iopub.status.busy":"2023-06-08T07:37:57.850764Z","iopub.execute_input":"2023-06-08T07:37:57.851588Z","iopub.status.idle":"2023-06-08T07:38:06.306376Z","shell.execute_reply.started":"2023-06-08T07:37:57.851538Z","shell.execute_reply":"2023-06-08T07:38:06.304796Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}