{"cells":[{"metadata":{"_uuid":"8d7026f3768bbfddb62f8d513ab742ac5fc2bd1a"},"cell_type":"markdown","source":"# Introduction\n\nThis notebook aims to present a very simple technique to perform image classification. Comparing to most of trending techniques, this one do not need a lot of materials, and computational power. So it can be run on almost any laptop. You will see that a ***70% accuracy*** can be reached very easily on the ***dog breed identification*** challenge.\n\nThis notebook is organized as follow : \n1. First some data exploration\n2. Features extraction\n3. Final predictions"},{"metadata":{"_uuid":"b4fe1e1fe381fb15a4b8ebfa5f2a59a3cb988784"},"cell_type":"markdown","source":"# Data exploration"},{"metadata":{"_uuid":"8318cb9b03eedff1d8a3617996db4008838919fa"},"cell_type":"markdown","source":"To experiment with this technique, I chose the **Dog Breed Identification challenge**. But a lot of Image Classification task could be adress in the same way.\n\nFirst let's see what this dataset is all about."},{"metadata":{"trusted":true,"_uuid":"d9a1bce4e3e8c299c7dbd889b843bfa367ead239"},"cell_type":"code","source":"import numpy as np \nimport pandas as pd\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nimport os","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"a40bc115b2c3106a9890f44f13058d847efc6806"},"cell_type":"markdown","source":"## Loading data"},{"metadata":{"trusted":true,"_uuid":"8ea89b06a63e2842f68aa3c24fbc4b8817efd1f3"},"cell_type":"code","source":"path_to_data = '../input/dog-breed-identification'\ndf = pd.read_csv(os.path.join(path_to_data,'labels.csv'))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"74ba5ef12e1354b0b3b2146907537d9e6416ed6e"},"cell_type":"markdown","source":"## Breed distribution"},{"metadata":{"_uuid":"fc1e03c8479f0e321811ea0daab3cd600d0f8252"},"cell_type":"markdown","source":"Let's see the distribution of the different dog breed. \n\nFor this I used pandas which comes with a lot of fancy functions for data exploration."},{"metadata":{"trusted":true,"_uuid":"fb846f6e9921de8c98a6951fa01fc66834c41996"},"cell_type":"code","source":"# Build a dataframe with the number of instances in each class\nbreed_distrib = df['breed'].value_counts()\nbreed_distrib.columns = ['breed', 'number']\n\n# Horizontal bar plot\nplt.figure(figsize=(30,100))\nsns.set(style=\"whitegrid\")\nsns.set(font_scale=5)\nax = sns.barplot(breed_distrib,breed_distrib.index)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"37e530d2250ef8c23198e7025ba5abc9f264c77b"},"cell_type":"markdown","source":"**Good news**, the distribution is relatively balanced. The less represented breed contrains 65 images and the more represented contains 110 images.\n\n**Bad news**, there isn't a lot of images per class. In this context we need to be careful to overfitting. "},{"metadata":{"_uuid":"b6fd1d0af34965850faa7c4e24dd780c40051acb"},"cell_type":"markdown","source":"## Image exploration"},{"metadata":{"_uuid":"529bca5aef074d29f9a995c4a21428f98971ff68"},"cell_type":"markdown","source":"Now let's see what these images look like. And because I don't know myself every dog breed,  let's plot one image for each one ;)"},{"metadata":{"trusted":true,"_uuid":"cd855d3d5cdae0e1e9909f5b5e44b48e1680fed0"},"cell_type":"code","source":"sns.set(font_scale=2)\nn_breeds = len(breed_distrib.index)\nprint('Number of breeds : ', n_breeds)\nfor i in range(n_breeds):\n    br = breed_distrib.index[i]\n    path = df.loc[df['breed'] == br].iloc[0].id + '.jpg'\n    path = os.path.join(path_to_data,'train',path)\n    img = plt.imread(path)\n    plt.axis('off')\n    plt.imshow(img)\n    plt.title(br)\n    plt.show() ","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8db92942e2d59b12a23b38c463ec1a48c6e0f56d"},"cell_type":"markdown","source":"Most of the images are centered on the dog but some of them are surrounded by their master or other objects which harden the task."},{"metadata":{"_uuid":"05d3f44a980ef16da07cb5872e358379a9b94559"},"cell_type":"markdown","source":"# Features Extraction\n\nMost Computer Vision task are solved by using a very large and complicated Neural Network which require millons and millons of parameters to train. This can't be achieved by simple laptop, it requires one or more GPU and a lot of memory. Since I didn't have access to such materials I decided to use a pre-trained model to extract high level features for this task.\n\n## Load pre-trained model\n\nA lot of pre-trained model are available on most machine learning frameworks including PyTorch and Keras. \n\nHere I decided to use Keras and the VGG16 model trained on ImageNet dataset.\n\n![](https://s3.ap-south-1.amazonaws.com/techleer/309.jpg)\n"},{"metadata":{"trusted":true,"_uuid":"ec6fb65b9009a8786b3948b29eeb7b953ddaee3f"},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"097d477b059af427be7ad9918d74bf5b09f66877"},"cell_type":"markdown","source":"VGG16 model is a very deep architecture which achieved great performance on ImageNet. \n\nBut we don't want to perform fine-tuning because it required a lot of computational power. Instead we will cut the model at the first fully connected layer. \n\nWith this new simplified model, we will pass the data throught the network. The ouput is then higher level and abstract features which should be useful to perform classification task."},{"metadata":{"trusted":true,"_uuid":"74d7515776cbba7e0c280b88b24513d07412ac33"},"cell_type":"code","source":"from keras.models import Model\nfrom keras.applications.vgg16 import VGG16\nfrom keras.preprocessing import image\nfrom keras.applications.resnet50 import preprocess_input, decode_predictions","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"d22204ad206ecc6d6e97cac8a44b5f8190d36f2e"},"cell_type":"code","source":"vgg16_weights = '../input/vgg16/vgg16_weights_tf_dim_ordering_tf_kernels.h5'\nbase_model = VGG16(weights=vgg16_weights)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"dc2932eee2804b98e846bada9c75e3be45c8a26d"},"cell_type":"code","source":"base_model.summary()","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","collapsed":true,"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":false},"cell_type":"markdown","source":"Since we do not want to perform fine-tuning because it is very computationally expensive, we will cut vgg16 at its first fully connected layer called 'fc1'."},{"metadata":{"trusted":true,"_uuid":"1a1f2325449544f3f1e862ee4010d20856229409"},"cell_type":"code","source":"model = Model(inputs=base_model.input, outputs=base_model.get_layer('fc1').output)\nmodel.compile(optimizer='rmsprop', loss='categorical_crossentropy')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"5b82ab4f8fc30f29d8c8f63e1a63f705912d2296"},"cell_type":"markdown","source":"## Generator\n\nTo avoid loading all images in memory at once, I used a generator. It enables to load only a batch of images."},{"metadata":{"trusted":true,"_uuid":"de1978374a40487ed03d79b7eb9c0788670de502"},"cell_type":"code","source":"from sklearn.preprocessing import OneHotEncoder, LabelEncoder\n\n\ndf['breed'] = LabelEncoder().fit_transform(df['breed'])\ny = df['breed'] \nonehot = OneHotEncoder()\ny = onehot.fit_transform(np.expand_dims(y, axis=1)).toarray()\n\n#Generator\ndef generator(df):\n    path_train = '../input/dog-breed-identification/train'\n    while 1:\n        for i in range(int(df.shape[0])):\n            img_path = os.path.join(path_train, df.iloc[i]['id']+ '.jpg')\n    \n            img = image.load_img(img_path, target_size=(224, 224))\n            x = image.img_to_array(img)\n            x = np.expand_dims(x, axis=0)\n            x = preprocess_input(x)\n            y = df.iloc[i]['breed']\n            y = onehot.transform(y).toarray()\n            #print(img.shape,np.array([y]).shape)\n            yield (x,y)\n                    \ngen = generator(df)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"f6f5b531701d61c6662f5cd498bbedcd9e52d8bf"},"cell_type":"markdown","source":"## Extract features"},{"metadata":{"trusted":true,"_uuid":"f8368ca37df9a095a8ab294eee0bfa69d45b5ecc"},"cell_type":"code","source":"X_pred = model.predict_generator(gen,steps=10221, verbose=1)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"f6b3e48eb06542c4204227206d19e076a6af7e5f"},"cell_type":"markdown","source":"# Final Predictions\n\nWith these new features, we can now run a simple classifier."},{"metadata":{"trusted":true,"_uuid":"a3473c2a8ba65fb3bdfb878f20add514b072a547"},"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\nX_train, X_test, y_train, y_test = train_test_split(X_pred, df.iloc[:10221]['breed'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"6f8c5304f7cf940145707e75d4434d5dce6d4c73"},"cell_type":"code","source":"from sklearn.ensemble import RandomForestClassifier\n\nclf = RandomForestClassifier(n_estimators=500)\nclf.fit(X_train, y_train)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"ac59764846ac13440a210748354b7706753b3c3e"},"cell_type":"code","source":"y_pred = clf.predict(X_test)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"cc39b01a81d80c862443d6e2b4625e2f1942c9dc"},"cell_type":"markdown","source":"## Evaluation"},{"metadata":{"trusted":true,"_uuid":"f8267fb4db49741e8296eb9bcccdc85ee927fe62"},"cell_type":"code","source":"from sklearn.metrics import accuracy_score\n\nacc = accuracy_score(y_test, y_pred)\n\nprint(\"Incredible accuracy of : \",acc)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"2f27f1647362e5d097b84b516d4fb30f5f79d656"},"cell_type":"markdown","source":"With this simple technique we reached around ***70 % of accuracy***.  It is a good score since there are ***120 different breeds***. "}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}