{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"# import libraries\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# import data visualization\nimport matplotlib.pyplot as plt\nfrom matplotlib.font_manager import FontProperties","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Pivot EDA might hint why cutmix works.\n\nAs you will see each root-consonant-vowel combination has roughly 150 images, so full classes are well balanced. However, (root-consonant), (root-vowel) and (consonant-vowel) are constructed in a peculiar way. For isntance, less popular consonants are mostly mixed with more popular vowels and the other way around. I am not sure if this have been done on purpose, but this is a good hint for any model to overfit on combinations given in the training set. \n\nFor instance, consonant_3 is exclusively paired to vowel_0, so it is likely to use vowel_0 presence as a feature for consonant_3. Apparently cutmix works well because it may \"construct\" combinations that are not present in the training set. \n\nA crossvalidation/augumentation strategy must focus on preventing the model from overfitting on combinations given in the train set, becasue test set is likely to conatin significantly different combinations. (which goes inline with the purpose of the competition i.e. \"focusing the problem on the grapheme components rather than on recognizing whole graphemes should make it possible to assemble a Bengali OCR system without handwriting samples for all 10,000 graphemes.\")"},{"metadata":{"trusted":true},"cell_type":"code","source":"# setup the input data folder\nDATA_PATH = '../input/bengaliai-cv19/'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# load the dataframes with labels\ntrain_labels = pd.read_csv(DATA_PATH + 'train.csv')\ntest_labels = pd.read_csv(DATA_PATH + 'test.csv')\nclass_map = pd.read_csv(DATA_PATH + 'class_map.csv')\nsample_submission = pd.read_csv(DATA_PATH + 'sample_submission.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# count the number of images for each grapheme_root\nroot_img = train_labels.groupby(by=['grapheme_root']).count().reset_index()[['grapheme_root', 'image_id']]\nroot_img.sort_values(by=['image_id'], ascending=False, inplace=True)\nroot_img.rename(columns={'grapheme_root':'root','image_id': 'root_count'}, inplace=True)\n\n# count the number of images for each grapheme_root\nvowel_img = train_labels.groupby(by=['vowel_diacritic']).count().reset_index()[['vowel_diacritic', 'image_id']]\nvowel_img.sort_values(by=['image_id'], ascending=False, inplace=True)\nvowel_img.rename(columns={'image_id': 'vowel_count'}, inplace=True)\n\n# count the number of images for each grapheme_root\nconsonant_img = train_labels.groupby(by=['consonant_diacritic']).count().reset_index()[['consonant_diacritic', 'image_id']]\nconsonant_img.sort_values(by=['image_id'], ascending=False, inplace=True)\nconsonant_img.rename(columns={'image_id': 'consonant_count'}, inplace=True)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Pivots\n### Root - consonants\nAs you can see least populated columns have roughly 150 images each.\nA bit more populated columns have around 300 images, for example columns 82, 37, 19, and 34"},{"metadata":{"trusted":true},"cell_type":"code","source":"root_consonant = pd.pivot_table(train_labels, values='image_id', index=['consonant_diacritic'],\n                                    columns=['grapheme_root'], aggfunc='count')\n\nroot_consonant = root_consonant.reindex(consonant_img.index, columns=root_img.index).fillna(0)\nroot_consonant.iloc[:,120:150]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Root - vowels\nIf we look at columns 82,37,19,34 again, we note that they have about 300 images each because they have two image sets for two vowel diacritics. Hence, every root-vowel-consonant set has roughly 150 images."},{"metadata":{"trusted":true},"cell_type":"code","source":"root_vowel = pd.pivot_table(train_labels, values='image_id', index=['vowel_diacritic'],\n                                    columns=['grapheme_root'], aggfunc='count')\n\nroot_vowel = root_vowel.reindex(vowel_img.index, columns=root_img.index).fillna(0)\nroot_vowel.iloc[:,120:150]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 150 images per combination\nEvery combination has roughly 150 images, so 'full' classes are well balanced."},{"metadata":{"trusted":true},"cell_type":"code","source":"train_labels['image'] = train_labels['grapheme_root'].astype(str)\\\n        + '_' + train_labels['vowel_diacritic'].astype(str)\\\n        + '_' + train_labels['consonant_diacritic'].astype(str)\n\nimage = train_labels.groupby(by=['image']).count().reset_index()[['image', 'image_id']]\nimage.sort_values(by=['image_id'], ascending=False, inplace=True)\nimage.rename(columns={'image_id': 'image_count'}, inplace=True)\nimage['image_count'].describe()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Consonant - Vowels:\nAs you can see the matrix is diogonal, so less popular consonants are mostly mixed with more popular vowels and the other way around. I am not sure if this have been done on purpose, but this is a good hint for model to overfit on training set. For instance, consonant_3 never only appears with vowel_0.\n\nA crossvalidation/augumentation strategy must focus on preventing the model from overfitting on given combinations. Apparently cutmix works well because it \"constructs\" additional combinations."},{"metadata":{"trusted":true},"cell_type":"code","source":"consonant_vowel = pd.pivot_table(train_labels, values='image_id', index=['consonant_diacritic'],\n                                    columns=['vowel_diacritic'], aggfunc='count')\n\nconsonant_vowel = consonant_vowel.reindex(consonant_img.index, columns=vowel_img.index).fillna(0)\nconsonant_vowel","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"One example of validation strategy might be using combinations on the diogonal for validation. Or random subsets of combinations from full combination set."}],"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":4,"nbformat_minor":1}