{"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":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-04-01T09:57:29.534979Z","iopub.execute_input":"2023-04-01T09:57:29.535493Z","iopub.status.idle":"2023-04-01T09:57:29.569851Z","shell.execute_reply.started":"2023-04-01T09:57:29.535387Z","shell.execute_reply":"2023-04-01T09:57:29.568806Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nimport sys\nimport tensorflow as tf\nimport os\nimport sys\nimport pandas as pd\n\nfrom keras.models import Sequential\nfrom keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPooling2D\nfrom sklearn.model_selection import train_test_split\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\n\n%matplotlib inline\nimport matplotlib.image as img\nimport matplotlib.pyplot as plt\n\nfrom sklearn.metrics import confusion_matrix\nimport plotly.graph_objects as go\nimport itertools\nimport plotly.express as px\n\nimport zipfile","metadata":{"execution":{"iopub.status.busy":"2023-04-01T09:57:29.57149Z","iopub.execute_input":"2023-04-01T09:57:29.572025Z","iopub.status.idle":"2023-04-01T09:57:42.403657Z","shell.execute_reply.started":"2023-04-01T09:57:29.571992Z","shell.execute_reply":"2023-04-01T09:57:42.402488Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"seed = 0\nnp.random.seed(seed)\ntf.random.set_seed(3)","metadata":{"execution":{"iopub.status.busy":"2023-04-01T09:57:42.405005Z","iopub.execute_input":"2023-04-01T09:57:42.406454Z","iopub.status.idle":"2023-04-01T09:57:42.410692Z","shell.execute_reply.started":"2023-04-01T09:57:42.406413Z","shell.execute_reply":"2023-04-01T09:57:42.409847Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"zip_files = ['test1', 'train']\n\nfor zip_file in zip_files:\n    with zipfile.ZipFile(\"../input/dogs-vs-cats/{}.zip\".format(zip_file),\"r\") as z:\n        z.extractall(\".\")\n        print(\"{} unzipped\".format(zip_file))","metadata":{"execution":{"iopub.status.busy":"2023-04-01T09:57:42.412645Z","iopub.execute_input":"2023-04-01T09:57:42.413744Z","iopub.status.idle":"2023-04-01T09:58:05.333571Z","shell.execute_reply.started":"2023-04-01T09:57:42.413704Z","shell.execute_reply":"2023-04-01T09:58:05.332215Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"IMAGE_FOLDER_PATH = \"../working/train\"\nFILE_NAMES = os.listdir(IMAGE_FOLDER_PATH)\nWIDTH = 150\nHEIGHT = 150\n\nFILE_NAMES[0:5]\n\nlabels = []\nfor i in os.listdir(IMAGE_FOLDER_PATH):\n    labels+=[i]","metadata":{"execution":{"iopub.status.busy":"2023-04-01T09:58:05.336385Z","iopub.execute_input":"2023-04-01T09:58:05.336796Z","iopub.status.idle":"2023-04-01T09:58:05.37698Z","shell.execute_reply.started":"2023-04-01T09:58:05.336765Z","shell.execute_reply":"2023-04-01T09:58:05.375489Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"targets = list()\nfull_paths = list()\ntrain_cats_dir = list()\ntrain_dogs_dir = list()\n\n# finding each file's target\nfor file_name in FILE_NAMES:\n    target = file_name.split(\".\")[0] # target name\n    full_path = os.path.join(IMAGE_FOLDER_PATH, file_name)\n    \n    if(target == \"dog\"):\n        train_dogs_dir.append(full_path)\n    if(target == \"cat\"):\n        train_cats_dir.append(full_path)\n    \n    full_paths.append(full_path)\n    targets.append(target)\n\ndataset = pd.DataFrame()\ndataset['image_path'] = full_paths\ndataset['target'] = targets \n\ndataset.head(10)","metadata":{"execution":{"iopub.status.busy":"2023-04-01T09:58:05.380155Z","iopub.execute_input":"2023-04-01T09:58:05.380922Z","iopub.status.idle":"2023-04-01T09:58:05.502188Z","shell.execute_reply.started":"2023-04-01T09:58:05.380881Z","shell.execute_reply":"2023-04-01T09:58:05.501275Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"total data counts:\", dataset['target'].count())\ncounts = dataset['target'].value_counts()\nprint(counts)","metadata":{"execution":{"iopub.status.busy":"2023-04-01T09:58:05.503338Z","iopub.execute_input":"2023-04-01T09:58:05.504353Z","iopub.status.idle":"2023-04-01T09:58:05.519961Z","shell.execute_reply.started":"2023-04-01T09:58:05.504318Z","shell.execute_reply":"2023-04-01T09:58:05.518425Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Distribution of training data\nfig = go.Figure(go.Bar(\n            x= counts.values,\n            y=counts.index,\n            orientation='h'))\nfig.update_layout(title='Data Distribution in Bars',font_size=15,title_x=0.45)\nfig.show()\n\nfig=px.pie(counts.head(10),values= 'target', names=dataset['target'].unique(),hole=0.425)\nfig.update_layout(title='Data Distribution of Data',font_size=15,title_x=0.45,annotations=[dict(text='Cat vs Dog',font_size=18, showarrow=False,height=800,width=700)])\nfig.update_traces(textfont_size=15,textinfo='percent')\nfig.show()","metadata":{"execution":{"iopub.status.busy":"2023-04-01T09:58:05.523574Z","iopub.execute_input":"2023-04-01T09:58:05.524093Z","iopub.status.idle":"2023-04-01T09:58:07.005049Z","shell.execute_reply.started":"2023-04-01T09:58:05.524043Z","shell.execute_reply":"2023-04-01T09:58:07.004088Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Display cat images\nrows = 4\ncols = 4\naxes = []\nfig=plt.figure(figsize=(10,10))\ni = 0\n\nfor a in range(rows*cols):\n    b = img.imread(train_cats_dir[i])\n    axes.append(fig.add_subplot(rows,cols,a+1))\n    plt.imshow(b)\n    i+=1\nfig.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-04-01T09:58:07.006541Z","iopub.execute_input":"2023-04-01T09:58:07.006925Z","iopub.status.idle":"2023-04-01T09:58:09.221052Z","shell.execute_reply.started":"2023-04-01T09:58:07.00689Z","shell.execute_reply":"2023-04-01T09:58:09.219797Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Display dog images\nrows = 4\ncols = 4\naxes = []\nfig=plt.figure(figsize=(10,10))\ni = 0\n\nfor a in range(rows*cols):\n    b = img.imread(train_dogs_dir[i])\n    axes.append(fig.add_subplot(rows,cols,a+1))\n    plt.imshow(b)\n    i+=1\nfig.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-04-01T09:58:09.224892Z","iopub.execute_input":"2023-04-01T09:58:09.225336Z","iopub.status.idle":"2023-04-01T09:58:11.291807Z","shell.execute_reply.started":"2023-04-01T09:58:09.225298Z","shell.execute_reply":"2023-04-01T09:58:11.290452Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Split the data\ndataset_train, dataset_test = train_test_split(dataset, test_size=0.2, random_state=seed)","metadata":{"execution":{"iopub.status.busy":"2023-04-01T09:58:11.293208Z","iopub.execute_input":"2023-04-01T09:58:11.293611Z","iopub.status.idle":"2023-04-01T09:58:11.307571Z","shell.execute_reply.started":"2023-04-01T09:58:11.293576Z","shell.execute_reply":"2023-04-01T09:58:11.306603Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data distribution of training data","metadata":{}},{"cell_type":"code","source":"class_id_distributionTrain = dataset_train['target'].value_counts()\nclass_id_distributionTrain.head(10)","metadata":{"execution":{"iopub.status.busy":"2023-04-01T09:58:11.309317Z","iopub.execute_input":"2023-04-01T09:58:11.309877Z","iopub.status.idle":"2023-04-01T09:58:11.323064Z","shell.execute_reply.started":"2023-04-01T09:58:11.309841Z","shell.execute_reply":"2023-04-01T09:58:11.321823Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig = go.Figure(go.Bar(\n            x=class_id_distributionTrain.values,\n            y=class_id_distributionTrain.index,\n            orientation='h'))\nfig.update_layout(title='Data Distribution Of Train Data in Bars',font_size=15,title_x=0.45)\nfig.show()","metadata":{"execution":{"iopub.status.busy":"2023-04-01T09:58:11.324909Z","iopub.execute_input":"2023-04-01T09:58:11.326136Z","iopub.status.idle":"2023-04-01T09:58:11.344346Z","shell.execute_reply.started":"2023-04-01T09:58:11.326078Z","shell.execute_reply":"2023-04-01T09:58:11.343022Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig=px.pie(class_id_distributionTrain.head(10),values= 'target', names=dataset_train['target'].unique(),hole=0.425)\nfig.update_layout(title='Data Distribution of Train Data in Pie Chart',font_size=15,title_x=0.45,annotations=[dict(text='Cat vs Dog',font_size=18, showarrow=False,height=800,width=700)])\nfig.update_traces(textfont_size=15,textinfo='percent')\nfig.show()","metadata":{"execution":{"iopub.status.busy":"2023-04-01T09:58:11.345684Z","iopub.execute_input":"2023-04-01T09:58:11.346227Z","iopub.status.idle":"2023-04-01T09:58:11.414218Z","shell.execute_reply.started":"2023-04-01T09:58:11.346193Z","shell.execute_reply":"2023-04-01T09:58:11.412953Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data distribution of test data","metadata":{}},{"cell_type":"code","source":"class_id_distributionTest = dataset_test['target'].value_counts()\nclass_id_distributionTest.head(10)","metadata":{"execution":{"iopub.status.busy":"2023-04-01T09:58:11.416071Z","iopub.execute_input":"2023-04-01T09:58:11.417389Z","iopub.status.idle":"2023-04-01T09:58:11.42864Z","shell.execute_reply.started":"2023-04-01T09:58:11.417336Z","shell.execute_reply":"2023-04-01T09:58:11.427761Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig = go.Figure(go.Bar(\n            x=class_id_distributionTest.values,\n            y=class_id_distributionTest.index,\n            orientation='h'))\n\nfig.update_layout(title='Data Distribution Of Train Data in Bars',font_size=15,title_x=0.45)\n\nfig.show()","metadata":{"execution":{"iopub.status.busy":"2023-04-01T09:58:11.429934Z","iopub.execute_input":"2023-04-01T09:58:11.430402Z","iopub.status.idle":"2023-04-01T09:58:11.450102Z","shell.execute_reply.started":"2023-04-01T09:58:11.430365Z","shell.execute_reply":"2023-04-01T09:58:11.448758Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig=px.pie(class_id_distributionTrain.head(10),values= 'target', names=dataset_train['target'].unique(),hole=0.425)\nfig.update_layout(title='Data Distribution of Train Data in Pie Chart',font_size=15,title_x=0.45,annotations=[dict(text='Cat vs Dog',font_size=18, showarrow=False,height=800,width=700)])\nfig.update_traces(textfont_size=15,textinfo='percent')\nfig.show()\n","metadata":{"execution":{"iopub.status.busy":"2023-04-01T09:58:11.45141Z","iopub.execute_input":"2023-04-01T09:58:11.451759Z","iopub.status.idle":"2023-04-01T09:58:11.513762Z","shell.execute_reply.started":"2023-04-01T09:58:11.45173Z","shell.execute_reply":"2023-04-01T09:58:11.511988Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# ImageDataGenerator to increase the number of data.","metadata":{}},{"cell_type":"code","source":"train_datagen=ImageDataGenerator(\nrotation_range=15,\nrescale=1./255,\nshear_range=0.1,\nzoom_range=0.2,\nhorizontal_flip=True,\nwidth_shift_range=0.1,\nheight_shift_range=0.1)\n\ntrain_datagenerator=train_datagen.flow_from_dataframe(dataframe=dataset_train,\n                                                     x_col=\"image_path\",\n                                                     y_col=\"target\",\n                                                     target_size=(WIDTH, HEIGHT),\n                                                     class_mode=\"binary\",\n                                                     batch_size=150)","metadata":{"execution":{"iopub.status.busy":"2023-04-01T09:58:11.515343Z","iopub.execute_input":"2023-04-01T09:58:11.515815Z","iopub.status.idle":"2023-04-01T09:58:11.748604Z","shell.execute_reply.started":"2023-04-01T09:58:11.515772Z","shell.execute_reply":"2023-04-01T09:58:11.747217Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_datagen = ImageDataGenerator(rescale=1./255)\ntest_datagenerator=test_datagen.flow_from_dataframe(dataframe=dataset_test,\n                                                   x_col=\"image_path\",\n                                                   y_col=\"target\",\n                                                   target_size=(WIDTH, HEIGHT),\n                                                   class_mode=\"binary\",\n                                                   batch_size=150)","metadata":{"execution":{"iopub.status.busy":"2023-04-01T09:58:11.750416Z","iopub.execute_input":"2023-04-01T09:58:11.750824Z","iopub.status.idle":"2023-04-01T09:58:11.818722Z","shell.execute_reply.started":"2023-04-01T09:58:11.75078Z","shell.execute_reply":"2023-04-01T09:58:11.81736Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# CNN Model\n1. Convolution layer - extract the information from the image using a filter.\n2. Feature map - filter and map into image into size of 3*3\n3. Pooling layer - reduce the dimension of feature map 2*2","metadata":{}},{"cell_type":"code","source":"# Compile the layers\nmodel = Sequential() \nmodel.add(Conv2D(32, kernel_size=(3,3), input_shape=(WIDTH, HEIGHT, 3), activation='relu'))\nmodel.add(Conv2D(64, kernel_size=(3,3), activation = 'relu'))\nmodel.add(MaxPooling2D(pool_size=2))\nmodel.add(Dropout(0.25))\nmodel.add(Flatten())\nmodel.add(Dense(128, activation='relu'))\nmodel.add(Dropout(0.5))\nmodel.add(Dense(1, activation='sigmoid'))","metadata":{"execution":{"iopub.status.busy":"2023-04-01T09:58:11.820044Z","iopub.execute_input":"2023-04-01T09:58:11.820393Z","iopub.status.idle":"2023-04-01T09:58:12.385151Z","shell.execute_reply.started":"2023-04-01T09:58:11.820364Z","shell.execute_reply":"2023-04-01T09:58:12.383574Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Display the model\nfrom tensorflow.keras.utils import plot_model\nfrom IPython.display import Image\nplot_model(model, to_file='convnet.png', show_shapes=True,show_layer_names=True)\nImage(filename='convnet.png') ","metadata":{"execution":{"iopub.status.busy":"2023-04-01T09:58:12.386903Z","iopub.execute_input":"2023-04-01T09:58:12.388265Z","iopub.status.idle":"2023-04-01T09:58:14.007886Z","shell.execute_reply.started":"2023-04-01T09:58:12.388192Z","shell.execute_reply":"2023-04-01T09:58:14.006366Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install visualkeras\nimport visualkeras\nvisualkeras.layered_view(model, legend=True) ","metadata":{"execution":{"iopub.status.busy":"2023-04-01T09:58:14.010002Z","iopub.execute_input":"2023-04-01T09:58:14.011171Z","iopub.status.idle":"2023-04-01T09:58:27.421508Z","shell.execute_reply.started":"2023-04-01T09:58:14.011126Z","shell.execute_reply":"2023-04-01T09:58:27.420049Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.compile(loss=\"binary_crossentropy\", optimizer='adam', metrics=['accuracy'])\nmodel.summary()","metadata":{"execution":{"iopub.status.busy":"2023-04-01T09:58:27.423869Z","iopub.execute_input":"2023-04-01T09:58:27.424283Z","iopub.status.idle":"2023-04-01T09:58:27.448003Z","shell.execute_reply.started":"2023-04-01T09:58:27.424219Z","shell.execute_reply":"2023-04-01T09:58:27.446556Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training","metadata":{}},{"cell_type":"code","source":"History=model.fit(train_datagenerator,\n                       epochs=10,\n                       validation_data=test_datagenerator,\n                       validation_steps=dataset_test.shape[0]/150,\n                       steps_per_epoch=dataset_train.shape[0]/150)","metadata":{"execution":{"iopub.status.busy":"2023-04-01T09:58:27.449587Z","iopub.execute_input":"2023-04-01T09:58:27.449942Z","iopub.status.idle":"2023-04-01T12:30:06.43807Z","shell.execute_reply.started":"2023-04-01T09:58:27.449913Z","shell.execute_reply":"2023-04-01T12:30:06.436376Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Plotting accuracy & loss","metadata":{}},{"cell_type":"code","source":"acc = History.history['accuracy']\nval_acc = History.history['val_accuracy']\nloss = History.history['loss']\nval_loss = History.history['val_loss']\n\nepochs = range(len(acc))\n\nplt.plot(epochs, acc, 'bo', label='Training accuracy')\nplt.plot(epochs, val_acc, 'b', label='Validation accuracy')\nplt.title('Training and validation accuracy')\nplt.legend()\n\nplt.figure()\n\nplt.plot(epochs, loss, 'go', label='Training Loss')\nplt.plot(epochs, val_loss, 'g', label='Validation Loss')\nplt.title('Training and validation loss')\nplt.legend()\n\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-04-01T12:30:06.440561Z","iopub.execute_input":"2023-04-01T12:30:06.441101Z","iopub.status.idle":"2023-04-01T12:30:06.90665Z","shell.execute_reply.started":"2023-04-01T12:30:06.44105Z","shell.execute_reply":"2023-04-01T12:30:06.904876Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Evaluating the model","metadata":{}},{"cell_type":"code","source":" test_loss, test_acc = model.evaluate(test_datagenerator, steps=len(test_datagenerator), verbose=1)\n print('Loss: %.3f' % (test_loss * 100.0))\n print('Accuracy: %.3f' % (test_acc * 100.0)) ","metadata":{"execution":{"iopub.status.busy":"2023-04-01T12:30:06.90893Z","iopub.execute_input":"2023-04-01T12:30:06.909369Z","iopub.status.idle":"2023-04-01T12:30:52.849148Z","shell.execute_reply.started":"2023-04-01T12:30:06.909329Z","shell.execute_reply":"2023-04-01T12:30:52.848308Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Confusion Matrix","metadata":{}},{"cell_type":"code","source":"from sklearn.metrics import confusion_matrix\nimport itertools\n\npredictions = model.predict(x=test_datagenerator, steps= len(test_datagenerator), verbose=0)\n\ntest_datagenerator.classes\n\ncm = confusion_matrix(y_true=test_datagenerator.classes, y_pred=np.argmax(predictions, axis=-1))","metadata":{"execution":{"iopub.status.busy":"2023-04-01T12:30:52.850115Z","iopub.execute_input":"2023-04-01T12:30:52.850484Z","iopub.status.idle":"2023-04-01T12:31:38.516442Z","shell.execute_reply.started":"2023-04-01T12:30:52.850446Z","shell.execute_reply":"2023-04-01T12:31:38.512954Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def plot_confusion_matrix(cm, classes,normalize=False,title='Confusion matrix',cmap=plt.cm.Blues):\n    \n    plt.imshow(cm, interpolation='nearest', cmap=cmap)\n    plt.title(title)\n    plt.colorbar()\n    tick_marks = np.arange(len(classes))\n    plt.xticks(tick_marks, classes, rotation=45)\n    plt.yticks(tick_marks, classes)\n    if normalize:\n        cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]\n        print(\"Normalized confusion matrix\")\n    else:\n        print('Confusion matrix, without normalization')\n        print(cm)\n    \n    thresh = cm.max() / 2.\n    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):\n        plt.text(j, i, cm[i, j],\n        horizontalalignment=\"center\",\n        color=\"white\" if cm[i, j] > thresh else \"black\")\n        plt.tight_layout()\n        plt.ylabel('True label')\n        plt.xlabel('Predicted label')","metadata":{"execution":{"iopub.status.busy":"2023-04-01T12:31:38.523557Z","iopub.execute_input":"2023-04-01T12:31:38.524211Z","iopub.status.idle":"2023-04-01T12:31:38.53593Z","shell.execute_reply.started":"2023-04-01T12:31:38.524166Z","shell.execute_reply":"2023-04-01T12:31:38.534413Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### cm_plot_labels = ['0_cat', '1_dog']\n\nplot_confusion_matrix(cm=cm, classes=cm_plot_labels, title='Confusion Matrix')","metadata":{}}]}