{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## CNN Architectures : VGG, Resnet, InceptionNet, XceptionNet \n\n### UseCases : Image Feature Extraction + Transfer Learning\n\n\n<br>\n\nA Gold mine dataset for comuter vision is the ImageNet dataset. It consists of about 14 M hand-labelled annotated images which contains over 22,000 day-to-day categories. Every year ImageNet competition is hosted in which the smaller version of this dataset (with 1000 categories) is used with an aim to accurately classify the images. Many winning solutions of the ImageNet Challenge have used state of the art convolutional neural network architectures to beat the best possible accuracy thresholds. In this kernel, I have discussed these popular architectures such as VGG16, 19, ResNet, AlexNet etc. In the end, I have explained how to generate image features using pretrained models and use them in machine learning models. \n\n## Contents \n\n<br>\n\nFrom the high level perspective, I have discussed three main components \n\n<ul>\n    <li>1. CNN Architectures   </li>\n<ul>\n    <li>1. 1 VGG16</li>\n    <li>1.2 VGG19 </li>\n    <li>1.3 InceptionNet</li>\n    <li>1.4 Resnet </li>\n    <li>1.5 XceptionNet</li>\n</ul>\n    <li>2. Image Feature Extraction  </li>\n    <li>3. Transfer Learning  </li>\n</ul>\n\n\n## 1. CNN Architectures\n## 1.1 &nbsp;&nbsp; VGG16 \n\nVGG16 was publised in 2014 and is one of the simplest (among the other cnn architectures used in Imagenet competition). It's Key Characteristics are:   \n\n1. This network contains total 16 layers in which weights and bias parameters are learnt.    \n2. A total of 13 convolutional layers are stacked one after the other and 3 dense layers for classification.     \n3. The number of filters in the convolution layers follow an increasing pattern (similar to decoder architecture of autoencoder).     \n4. The informative features are obtained by max pooling layers applied at different steps in the architecture.    \n5. The dense layers comprises of 4096, 4096, and 1000 nodes each.   \n6. The cons of this architecture are that it is slow to train and produces the model with very large size.   \n\nThe VGG16 architecture is given below: \n\n![](https://tech.showmax.com/2017/10/convnet-architectures/image_0-8fa3b810.png)\n\n## Implementation : VGG16\nLet's see how we can create this architecture using python's keras library. The following code block shows the implementation of VGG16 in keras. \n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true}},{"cell_type":"code","source":"from keras.layers import Input, Conv2D, MaxPooling2D\nfrom keras.layers import Dense, Flatten\nfrom keras.models import Model\n\n_input = Input((224,224,1)) \n\nconv1  = Conv2D(filters=64, kernel_size=(3,3), padding=\"same\", activation=\"relu\")(_input)\nconv2  = Conv2D(filters=64, kernel_size=(3,3), padding=\"same\", activation=\"relu\")(conv1)\npool1  = MaxPooling2D((2, 2))(conv2)\n\nconv3  = Conv2D(filters=128, kernel_size=(3,3), padding=\"same\", activation=\"relu\")(pool1)\nconv4  = Conv2D(filters=128, kernel_size=(3,3), padding=\"same\", activation=\"relu\")(conv3)\npool2  = MaxPooling2D((2, 2))(conv4)\n\nconv5  = Conv2D(filters=256, kernel_size=(3,3), padding=\"same\", activation=\"relu\")(pool2)\nconv6  = Conv2D(filters=256, kernel_size=(3,3), padding=\"same\", activation=\"relu\")(conv5)\nconv7  = Conv2D(filters=256, kernel_size=(3,3), padding=\"same\", activation=\"relu\")(conv6)\npool3  = MaxPooling2D((2, 2))(conv7)\n\nconv8  = Conv2D(filters=512, kernel_size=(3,3), padding=\"same\", activation=\"relu\")(pool3)\nconv9  = Conv2D(filters=512, kernel_size=(3,3), padding=\"same\", activation=\"relu\")(conv8)\nconv10 = Conv2D(filters=512, kernel_size=(3,3), padding=\"same\", activation=\"relu\")(conv9)\npool4  = MaxPooling2D((2, 2))(conv10)\n\nconv11 = Conv2D(filters=512, kernel_size=(3,3), padding=\"same\", activation=\"relu\")(pool4)\nconv12 = Conv2D(filters=512, kernel_size=(3,3), padding=\"same\", activation=\"relu\")(conv11)\nconv13 = Conv2D(filters=512, kernel_size=(3,3), padding=\"same\", activation=\"relu\")(conv12)\npool5  = MaxPooling2D((2, 2))(conv13)\n\nflat   = Flatten()(pool5)\ndense1 = Dense(4096, activation=\"relu\")(flat)\ndense2 = Dense(4096, activation=\"relu\")(dense1)\noutput = Dense(1000, activation=\"softmax\")(dense2)\n\nvgg16_model  = Model(inputs=_input, outputs=output)","metadata":{"_uuid":"f8e3ec93ecb551f460cc3dad2a48df7b8399a9c9","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## PreTrained Model : VGG16\n\n\n\nKeras library also provides the pre-trained model in which one can load the saved model weights, and use them for different purposes : transfer learning, image feature extraction, and object detection. We can load the model architecture given in the library, and then add all the weights to the respective layers. \n\nBefore using the pretrained models, lets write a few functions which will be used to make some predictions. First, load some images and preprocess them. ","metadata":{"_uuid":"42faa0d2e466c898a2dafaeb92295a95ac10583e"}},{"cell_type":"code","source":"from keras.applications.vgg16 import decode_predictions\nfrom keras.applications.vgg16 import preprocess_input\nfrom keras.preprocessing import image\nimport matplotlib.pyplot as plt \nfrom PIL import Image \nimport seaborn as sns\nimport pandas as pd \nimport numpy as np \nimport os \n\nimg1 = \"../input/dogs-vs-cats-redux-kernels-edition/train/cat.11679.jpg\"\nimg2 = \"../input/dogs-vs-cats-redux-kernels-edition/train/dog.2811.jpg\"\nimg3 = \"../input/flowers-recognition/flowers/flowers/sunflower/7791014076_07a897cb85_n.jpg\"\nimg4 = \"../input/fruits/fruits-360_dataset/fruits-360/Training/Banana/254_100.jpg\"\nimgs = [img1, img2, img3, img4]\n\ndef _load_image(img_path):\n    img = image.load_img(img_path, target_size=(224, 224))\n    img = image.img_to_array(img)\n    img = np.expand_dims(img, axis=0)\n    img = preprocess_input(img)\n    return img \n\ndef _get_predictions(_model):\n    f, ax = plt.subplots(1, 4)\n    f.set_size_inches(80, 40)\n    for i in range(4):\n        ax[i].imshow(Image.open(imgs[i]).resize((200, 200), Image.ANTIALIAS))\n    plt.show()\n    \n    f, axes = plt.subplots(1, 4)\n    f.set_size_inches(80, 20)\n    for i,img_path in enumerate(imgs):\n        img = _load_image(img_path)\n        preds  = decode_predictions(_model.predict(img), top=3)[0]\n        b = sns.barplot(y=[c[1] for c in preds], x=[c[2] for c in preds], color=\"gray\", ax=axes[i])\n        b.tick_params(labelsize=55)\n        f.tight_layout()","metadata":{"_uuid":"99d88f29ec0337fbcf9c778a0bceaa92dca4ee04","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now, we can perform following steps : \n1. import VGG16 architecture from keras.applications  \n2. Add the saved weights to the architecture \n3. Use model to perform predictions ","metadata":{"_uuid":"0f3387ea97c3ad83a7a5d8ebc650e2a42f493066"}},{"cell_type":"code","source":"from keras.applications.vgg16 import VGG16\nvgg16_weights = '../input/vgg16/vgg16_weights_tf_dim_ordering_tf_kernels.h5'\nvgg16_model = VGG16(weights=vgg16_weights)\n_get_predictions(vgg16_model)","metadata":{"_uuid":"b003c6f9d5fc09f142c6cff8714e165d3a542fbe","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 1.2 &nbsp;&nbsp; VGG19 \n\nVGG19 is a similar model architecure as VGG16 with three additional convolutional layers, it consists of a total of 16 Convolution layers and 3 dense layers.  Following is the architecture of VGG19 model. In VGG networks, the use of 3 x 3 convolutions with stride 1 gives an effective receptive filed equivalent to 7 * 7. This means there are fewer parameters to train. \n\n![](https://cdn-images-1.medium.com/max/1600/1*cufAO77aeSWdShs3ba5ndg.jpeg)\n\n","metadata":{"_uuid":"54c5c8da0c9d25d923ad94ceef7afd140dcfa97f"}},{"cell_type":"code","source":"from keras.applications.vgg19 import VGG19\nvgg19_weights = '../input/vgg19/vgg19_weights_tf_dim_ordering_tf_kernels.h5'\nvgg19_model = VGG19(weights=vgg19_weights)\n_get_predictions(vgg19_model)","metadata":{"_uuid":"73e61439cdc48836234deda711db6839ad975013","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## &nbsp;&nbsp; 1.3 InceptionNets\n\nAlso known as GoogleNet consists of total 22 layers and was the winning model of 2014 image net challenge. \n\n- Inception modules are the fundamental block of InceptionNets. The key idea of inception module is to design good local network topology (network within a network)  \n- These modules or blocks acts as the multi-level feature extractor in which convolutions of different sizes are obtained to create a diversified feature map\n- The inception modules also consists of 1 x 1 convolution blocks whose role is to perform dimentionaltiy reduction.  \n- By performing the 1x1 convolution, the inception block preserves the spatial dimentions but reduces the depth. So the overall network's dimentions are not increased exponentially.  \n- Apart from the regular output layer, this network also consists of two auxillary classification outputs which are used to inject gradients at lower layers.  \n\n<br><br>\n\nThe inception module is shown in the following figure:  \n\n![](https://hackathonprojects.files.wordpress.com/2016/09/inception_implement.png?w=649&h=337)\n\nThe complete architecture is shown below: \n\n![](https://cdn-images-1.medium.com/max/2000/1*uXfC5fcbDsL0TJG4T8PsVw.png)\n\n<br>\n\n### Pre-Trained Model : InceptionV3","metadata":{"_uuid":"864680ffb526762ccad64f0d6f95c886de12b2c3"}},{"cell_type":"code","source":"from keras.applications.inception_v3 import InceptionV3\ninception_weights = '../input/inceptionv3/inception_v3_weights_tf_dim_ordering_tf_kernels.h5'\ninception_model = InceptionV3(weights=inception_weights)\n_get_predictions(inception_model)","metadata":{"_uuid":"55f1b29e8180ddf39cf86886d2086566de21d0f4","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 1.4 Resnets\n\nOriginal Paper : https://arxiv.org/pdf/1512.03385.pdf\n\nAll the previous models used deep neural networks in which they stacked many convolution layers one after the other. It was learnt that deeper networks are performing better. However, it turned out that this is not really true. Following are the problems with deeper networks: \n\n- Network becomes difficult to optimize  \n- Vanishing / Exploding Gradeints  \n- Degradation Problem ( accuracy first saturates and then degrades )  \n\n### Skip Connections   \n\nSo to address these problems, authors of the resnet architecture came up with the idea of skip connections with the hypothesis that the deeper layers should be able to learn something as equal as shallower layers. A possible solution is copying the activations from shallower layers and setting additional layers to identity mapping.   These connections are enabled by skip connections which are shown in the following figure. \n\n![](https://cdn-images-1.medium.com/max/987/1*pUyst_ciesOz_LUg0HocYg.png)\n\nSo the role of these connections is to perform identity function over the activation of shallower layer, which in-turn produces the same activation. This output is then added with the activation of the next layer. To enable these connections or essentially enable this addition operation, one need to ensure the same dimentions of convolutions through out the network, that's why resnets have same 3 by 3 convolutions throughout. \n\n### Key Advantage \n\nBy using residual blocks in the network, one can construct networks of any depth with the hypothesis that new layers are actually helping to learn new underlying patterns in the input data. The authors of the paper were able to create the deep neural network architecture with 152 layers. The variants of Resnets such as resnet34, resnet50, resnet101 have produced the solutions with very high accuracy in Imagenet competitions. \n\n### Why it works ? \n\nLets discuss why residual networks are successful and enables the addition of more and more layers without the key problems ie. without hurting the network performance. \n\nConsider a plain neural network (A) without residual network as shown. So in the network (A) the input X is passed to this Neural Network (NN) to give the activation A1. \n\n  <br>\n\n![](https://i.imgur.com/9j8bKaY.png)\n\nNow, consider a more deeper network (B) in which a residual block (with 2 extra layers and a skip connection) is added in the previous network. So now, the activation A1 is being passed to Residual Block which in turns gives new activation A3. \n\nif there was no skip connection, then A3 was: \n\n>  A3 = relu ( W2 . A2 + b2)              ..... (without skip connection)\n\nwhere W2 and b2 are weights and bias associated with layer L2. But, with skip connection another term A1 will be passed to L2. So the equation of A3 will be modified as: \n\n> A3 = relu ( W2 . A2 + b2 + A1) \n\nIf we use L2 regularization or the weight decay methods, they will force W2 and b2 to become close to zero. In the worst case, if these become zero, then \n\n> A3 = relu (A1)   \n\nbecause relu will output 0 for negative, A1 for positive and we know that A1 is previous activation from relu which is positive. \n\n> A3 = A1 \n\nThis means that Identitiy function is easy for residual blocks to learn. By addition of residual blocks, model complexity was not increased. As this is only copying the previous activation to the next layers. However this is only the worst case situation, but the it may turn out that these additional layers learns something useful. In that case, the network performance will improve. \n\nHence, adding the residual blocks / skip connections does not hurt the network performance but infact increases the chances that new layers will learn something useful. \n\nLet's look at the usage using pre-trained resnet 50 model. ","metadata":{"_uuid":"bfb5488e2aaf9f46d92aed89021fc4a3a27bbf19"}},{"cell_type":"code","source":"from keras.applications.resnet50 import ResNet50\nresnet_weights = '../input/resnet50/resnet50_weights_tf_dim_ordering_tf_kernels.h5'\nresnet_model = ResNet50(weights=resnet_weights)\n_get_predictions(resnet_model)","metadata":{"_uuid":"7e8441e52f70fe3103312dc9e1dd0cd103e90e66","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 1.5 Xception Nets\n\nXception is an extension of the Inception architecture which replaces the standard Inception modules with depthwise separable convolutions.","metadata":{"_uuid":"53c26efe9ea54cfc0852ebe389fb05592307d864"}},{"cell_type":"code","source":"from keras.applications.xception import Xception\nxception_weights = '../input/xception/xception_weights_tf_dim_ordering_tf_kernels.h5'\nxception_model = Xception(weights=xception_weights)","metadata":{"_uuid":"584f18cd3ce168413f72076362db2663e6442baf","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Comparison of different architectures: \n\nThe following image describes the relative comparison of these architectures in terms of performance as size. \n<br><br>\n\n![](http://www.houseofbots.com/images/news/573/cover.png)\n\n<br><br>\n\n## 2. Image Feature Extraction using PreTrained Models \n\nLets look at how one can use pre-trained models for feature extraction, The extracted features can be used for Machine Learning purposes. \n\nFirst step is to load the weights of the pre-trained model in the model architecture. Notice, that an additional argument is passed include_top = False, which states that we do not want to add the last layer of this architecture. ","metadata":{"_uuid":"dadcad36efb34766ecb244797f6339e2ee14067f"}},{"cell_type":"code","source":"resnet50 = ResNet50(weights='imagenet', include_top=False)","metadata":{"_uuid":"8fd638c0b5f124f0dc5805ca31a8517d6df1f6d4","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"As the next step, we will pass an image to this model and identify the features. ","metadata":{"_uuid":"cf05582ea7eecc9e839f996d218ad2c98ce3c768"}},{"cell_type":"code","source":"def _get_features(img_path):\n    img = image.load_img(img_path, target_size=(224, 224))\n    img_data = image.img_to_array(img)\n    img_data = np.expand_dims(img_data, axis=0)\n    img_data = preprocess_input(img_data)\n    resnet_features = resnet50.predict(img_data)\n    return resnet_features\n\nimg_path = \"../input/dogs-vs-cats-redux-kernels-edition/train/dog.2811.jpg\"\nresnet_features = _get_features(img_path)","metadata":{"_uuid":"6eaca5c0a04641da7a0e592f5b41d3bd19139989","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"now the extracted features are stored in the variable resnet_features. One can flatten them or sequee them in order to use them in ML models.  Flatten will produce a long vector of feature elements. Squeeze will produce a 3D matrix of the features","metadata":{"_uuid":"ebd7b1dd8ad79987ca62bc6cc712c3538a9ee21e"}},{"cell_type":"code","source":"features_representation_1 = resnet_features.flatten()\nfeatures_representation_2 = resnet_features.squeeze()\n\nprint (\"Shape 1: \", features_representation_1.shape)\nprint (\"Shape 2: \", features_representation_2.shape)","metadata":{"_uuid":"eb48827bfad5ff9bfddbe9df991d32e3449f9451","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 3. Transfer Learning Example \n\n<br>\n\nLets look at the implemetation of transfer learning using pre-trained model features. First, we 'll create a dataset containing two classes of images : bananas and strawberrys. Also add a test dataset contianing images from both classes.\n\n### 3.1 Dataset Preparation","metadata":{"_uuid":"145461de10bdd86deeaa1bfb27af0a4e5f8eaeff"}},{"cell_type":"code","source":"basepath = \"../input/fruits/fruits-360_dataset/fruits-360/Training/\"\nclass1 = os.listdir(basepath + \"Banana/\")\nclass2 = os.listdir(basepath + \"Strawberry/\")\n\ndata = {'banana': class1[:10], \n        'strawberry': class2[:10], \n        'test': [class1[11], class2[11]]}","metadata":{"_uuid":"d075c7be0c290ff513df855fe1707ea145ab9330","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Transfer learning can be implemented in two steps: \n\nStep 1 : Image Feature Exraction  \nStep 2 : Training a Classifier  \n\n### Step 1 : Feature Extraction using pre-trained models (resnet50)\n\nIterate in the images, call the same function used in point 2 for image feature extraction, we will use the flatten representation of these features ","metadata":{"_uuid":"5c3f81bf1bdf0266e09e180ef4fecd8c4e61cc8e"}},{"cell_type":"code","source":"features = {\"banana\" : [], \"strawberry\" : [], \"test\" : []}\ntestimgs = []\nfor label, val in data.items():\n    for k, each in enumerate(val):        \n        if label == \"test\" and k == 0:\n            img_path = basepath + \"/Banana/\" + each\n            testimgs.append(img_path)\n        elif label == \"test\" and k == 1:\n            img_path = basepath + \"/Strawberry/\" + each\n            testimgs.append(img_path)\n        else: \n            img_path = basepath + label.title() + \"/\" + each\n        feats = _get_features(img_path)\n        features[label].append(feats.flatten())        ","metadata":{"_uuid":"715ae76b5aca0b8fce478fe1a3c2c9639c7bd789","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Next, Convert the features from dictionary format to pandas dataframe. A long dataframe will be created. I will be applying variance filter later on this dataframe to reduce the dimentionality. Other ideas to avoid this step : perform PCA / SVD to obtain the dense features. ","metadata":{"_uuid":"0fecb0e6eb0759b69ccf58cb255cc2f6751216b3"}},{"cell_type":"code","source":"dataset = pd.DataFrame()\nfor label, feats in features.items():\n    temp_df = pd.DataFrame(feats)\n    temp_df['label'] = label\n    dataset = dataset.append(temp_df, ignore_index=True)\ndataset.head()","metadata":{"scrolled":true,"_uuid":"57970a5ba3a19144849babd51102424a531cd207","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Prepare X (predictors) and y (target) from the dataset","metadata":{"_uuid":"254f97b6e4cecd5a8b8e4ee3ddf300a4d878968e"}},{"cell_type":"code","source":"y = dataset[dataset.label != 'test'].label\nX = dataset[dataset.label != 'test'].drop('label', axis=1)","metadata":{"_uuid":"8732b6bd71eb01c4fc7bda6faf7d999d062aab76","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Step 2: Write a classifier to predict two classes\n\nwe will write a simple neural network (multi layer perceptron classifier) using sklearn for training purposes. ","metadata":{"_uuid":"d9af01bdf68adfce750b88fa13ff4cd85db580b4"}},{"cell_type":"code","source":"from sklearn.feature_selection import VarianceThreshold\nfrom sklearn.neural_network import MLPClassifier\nfrom sklearn.pipeline import Pipeline\n\nmodel = MLPClassifier(hidden_layer_sizes=(100, 10))\npipeline = Pipeline([('low_variance_filter', VarianceThreshold()), ('model', model)])\npipeline.fit(X, y)\n\nprint (\"Model Trained on pre-trained features\")","metadata":{"_uuid":"d65f6ef1b025b1913bcfb41b091280cd2cb68f33","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's predict the output on new images and check the outcome.","metadata":{"_uuid":"8408e267c963ab8c3b49564a1ef833c4ec87b17a"}},{"cell_type":"code","source":"preds = pipeline.predict(features['test'])\n\nf, ax = plt.subplots(1, 2)\nfor i in range(2):\n    ax[i].imshow(Image.open(testimgs[i]).resize((200, 200), Image.ANTIALIAS))\n    ax[i].text(10, 180, 'Predicted: %s' % preds[i], color='k', backgroundcolor='red', alpha=0.8)\nplt.show()","metadata":{"_uuid":"feeefbaa75207a3c4bea5dc189b47dd7269224ff","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"So a simple neural network with only 20 rows of training data is able to correctly classify the two images on test set. \n\n### EndNotes \nThanks for viewing this kernel, If you liked it, please upvote. ","metadata":{"_uuid":"fd8247fb898e1666eaf4e76026a6b332ef686970"}}]}