{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Brown University Data Science Initiative\n## Kaggle Competition: Identifying Bengali Letters\n### Written By: Mike Harder, Jason Katz, Naina Wodon, David Kebudi \n"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"from matplotlib import pyplot as plt\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport PIL.Image as Image, PIL.ImageDraw as ImageDraw, PIL.ImageFont as ImageFont\nimport random \nimport os\nimport cv2\nimport gc\nfrom tqdm.auto import tqdm\nimport numpy as np\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import Dense, Dropout, Flatten\nfrom tensorflow.keras.layers import Conv2D, MaxPooling2D\nfrom tensorflow.keras.optimizers import SGD\nfrom tensorflow.keras.layers import BatchNormalization\nfrom tensorflow.keras.layers import LeakyReLU\nfrom tensorflow.keras.models import clone_model\nfrom sklearn.model_selection import train_test_split\nfrom tensorflow.keras.callbacks import ReduceLROnPlateau\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.utils import plot_model\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\nimport datetime as dt\n\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"HEIGHT = 236\nWIDTH = 236\n\ndef get_n(df, field, n, top=True):\n    top_graphemes = df.groupby([field]).size().reset_index(name='counts')['counts'].sort_values(ascending=not top)[:n]\n    top_grapheme_roots = top_graphemes.index\n    top_grapheme_counts = top_graphemes.values\n    top_graphemes = class_map_df[class_map_df['component_type'] == field].reset_index().iloc[top_grapheme_roots]\n    top_graphemes.drop(['component_type', 'label'], axis=1, inplace=True)\n    top_graphemes.loc[:, 'count'] = top_grapheme_counts\n    return top_graphemes\n\ndef image_from_char(char):\n    image = Image.new('RGB', (WIDTH, HEIGHT))\n    draw = ImageDraw.Draw(image)\n    myfont = ImageFont.truetype('/kaggle/input/kalpurush/kalpurush-2.ttf', 120)\n    w, h = draw.textsize(char, font=myfont)\n    draw.text(((WIDTH - w) / 2,(HEIGHT - h) / 3), char, font=myfont)\n\n    return image\n\ndef resize(df, size=64, need_progress_bar=True):\n    resized = {}\n    for i in range(df.shape[0]):\n        image = cv2.resize(df.loc[df.index[i]].values.reshape(137,236),(size,size))\n        resized[df.index[i]] = image.reshape(-1)\n    resized = pd.DataFrame(resized).T\n    return resized","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"train_data = pd.read_csv('/kaggle/input/bengaliai-cv19/train.csv')\nclass_map_df = pd.read_csv('/kaggle/input/bengaliai-cv19/class_map.csv')\ntop_10_vowels = get_n(train_data, 'vowel_diacritic', 5)\ntop_10_roots = get_n(train_data, 'grapheme_root', 5)\ntop_10_consonants = get_n(train_data, 'consonant_diacritic', 5)\ntops = [(top_10_vowels,'vowel_diacritic'), (top_10_roots, 'grapheme_root'), (top_10_consonants, 'consonant_diacritic')]\n\nfor top in tops:\n    f, ax = plt.subplots(1, 5, figsize=(16, 8))\n    ax = ax.flatten()\n    for i in range(5):\n        ax[i].imshow(image_from_char(top[0]['component'].iloc[i]), cmap='Greys')\n        ax[i].set_title(f\"{top[1]}:{i}\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import seaborn as sns\ndef plot_count(feature, title, df, size=1):\n    '''\n    Plot count of classes of selected feature; feature is a categorical value\n    param: feature - the feature for which we present the distribution of classes\n    param: title - title to show in the plot\n    param: df - dataframe \n    param: size - size (from 1 to n), multiplied with 4 - size of plot\n    '''\n    f, ax = plt.subplots(1,1, figsize=(4*size,4))\n    total = float(len(df))\n    g = sns.countplot(df[feature], order = df[feature].value_counts().index[:20], palette='Set3')\n    g.set_title(\"Number and percentage of {}\".format(title))\n    if(size > 2):\n        plt.xticks(rotation=90, size=8)\n    for p in ax.patches:\n        height = p.get_height()\n        ax.text(p.get_x()+p.get_width()/2.,\n                height + 3,\n                '{:1.2f}%'.format(100*height/total),\n                ha=\"center\") \n    plt.show() \n    \nplot_count('grapheme_root', 'grapheme_root (first most frequent 20 values - train)', train_data, size=4)\nplot_count('vowel_diacritic', 'vowel_diacritic (train)', train_data, size=3)\nplot_count('consonant_diacritic', 'consonant_diacritic (train)', train_data, size=3)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import seaborn as sns \ndef plot_count_heatmap(feature1, feature2, df, size=1):   \n    '''\n    Heatmap showing the distribution of couple of features\n    param: feature1 - ex: vowel_diacritic\n    param: feature2 - ex: consonant_diacritic\n    '''\n    tmp = df.groupby([feature1, feature2])['grapheme'].count()\n    df = tmp.reset_index()\n    df\n    df_m = df.pivot(feature1, feature2, \"grapheme\")\n    f, ax = plt.subplots(figsize=(9, size * 4))\n    sns.heatmap(df_m, annot=True, fmt='3.0f', linewidths=.5, ax=ax)\n    \nplot_count_heatmap('grapheme_root','consonant_diacritic', train_data, size=8)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_data =  pd.merge(pd.read_parquet(f'/kaggle/input/bengaliai-cv19/train_image_data_0.parquet'), train_data, on='image_id').drop(['image_id'], axis=1)\ntrain_labels = train_data[['grapheme_root', 'vowel_diacritic', 'consonant_diacritic','grapheme']]\ntrain_data = train_data.drop(['grapheme_root', 'vowel_diacritic', 'consonant_diacritic','grapheme'], axis=1)\ntrain_labels.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_data = resize(train_data)/255\ntrain_data = train_data.values.reshape(-1, 64, 64, 1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model_dict = {\n    'grapheme_root': Sequential(),\n    'vowel_diacritic': Sequential(),\n    'consonant_diacritic': Sequential()\n}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for model_type, model in model_dict.items():\n    model.add(Conv2D(64, 7, activation=\"relu\", padding=\"same\", input_shape=[64, 64, 1]))\n    model.add(layers.BatchNormalization(momentum=0.15))\n    model.add(MaxPooling2D(2))\n    model.add(Conv2D(128, 3, activation=\"relu\", padding=\"same\"))\n    model.add(Conv2D(128, 3, activation=\"relu\", padding=\"same\"))\n    model.add(MaxPooling2D(2))\n    model.add(Conv2D(256, 3, activation=\"relu\", padding=\"same\"))\n    model.add(Conv2D(256, 3, activation=\"relu\", padding=\"same\"))\n    model.add(MaxPooling2D(2))\n    model.add(Flatten())\n    model.add(Dense(1024, activation=\"relu\"))\n    model.add(Dropout(0.5))\n    model.add(Dense(512, activation=\"relu\"))\n    model.add(Dropout(0.5))\n    if model_type == 'grapheme_root':\n        model.add(layers.Dense(168, activation='softmax', name='root_out'))\n    elif model_type == 'vowel_diacritic':\n        model.add(layers.Dense(11, activation='softmax', name='vowel_out'))\n    elif model_type == 'consonant_diacritic':\n        model.add(layers.Dense(7, activation='softmax', name='consonant_out'))\n    model.compile(optimizer=\"adam\", loss=['categorical_crossentropy'], metrics=['accuracy'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from tensorflow.keras.utils import plot_model\nprint('grapheme_root')\nplot_model(model_dict['grapheme_root'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"batch_size = 32\nepochs = 5\nhistory_list = []","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model_types = ['grapheme_root', 'vowel_diacritic', 'consonant_diacritic']\nfor target in model_types:\n    Y_train = train_labels[target]\n    Y_train = pd.get_dummies(Y_train).values\n    x_train, x_test, y_train, y_test = train_test_split(train_data, Y_train, test_size=0.1, random_state=123)\n    datagen = ImageDataGenerator()\n    datagen.fit(x_train)\n    history = model_dict[target].fit_generator(datagen.flow(x_train, y_train, batch_size=batch_size), \n                                               epochs = epochs, validation_data = (x_test, y_test))\n    history_list.append(history)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure()\nfor i in range(3):\n    plt.plot(np.arange(0, epochs), history_list[i].history['accuracy'], label='train_accuracy')\n    plt.plot(np.arange(0, epochs), history_list[i].history['val_accuracy'], label='val_accuracy')\n    plt.title(model_types[i])\n    plt.xlabel('Epoch #')\n    plt.ylabel('Accuracy')\n    plt.legend(loc='lower right')\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"preds_dict = {\n    'grapheme_root': [],\n    'vowel_diacritic': [],\n    'consonant_diacritic': []\n}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"target=[] # model predictions placeholder\nrow_id=[] # row_id place holder\nfor i in range(4):\n    print(\"Parquet: {}\".format(i))\n    df_test_img = pd.read_parquet('/kaggle/input/bengaliai-cv19/test_image_data_{}.parquet'.format(i)) \n    df_test_img.set_index('image_id', inplace=True)\n\n    X_test = resize(df_test_img, need_progress_bar=False)/255\n    X_test = X_test.values.reshape(-1, 64, 64, 1)\n\n    for i, p in preds_dict.items():\n        preds = model_dict[i].predict(X_test)\n        preds_dict[i] = np.argmax(preds, axis=1)\n\n    for k,id in enumerate(df_test_img.index.values):  \n        for i,comp in enumerate(model_types):\n            id_sample=id+'_'+comp\n            row_id.append(id_sample)\n            target.append(preds_dict[comp][k])\n    del df_test_img\n    del X_test\n    gc.collect()\n\ndf_sample = pd.DataFrame(\n    {\n        'row_id': row_id,\n        'target':target\n    },\n    columns = ['row_id','target'] \n)\ndf_sample.to_csv('submission.csv',index=False)\ndf_sample.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"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}