{"cells":[{"metadata":{"_uuid":"60877491afca909ecb35386b730c86aff179ec8b","_cell_guid":"3b1631d6-e20f-4b0b-b2b6-ce9efad6fe3f"},"cell_type":"markdown","source":"# Building an Image Classifier\nIn the last chapter we had a close look at the inner workings of ConvNets. In this chapters we will build a fully fledged image classifier. We will see up close how a convolutional architecture works and shine some light on the additional tools we need to make it work.\n\n## Cats vs. Dogs\nIt has become a bit of a meme in deep learning to start out with a cat classifier. Not only is it a good excuse to download thousands of cat images and spend hours looking at cats, there is also a bit of history that cats share with deep learning. In 2013, the datascience competition website Kaggle hosted a for fun [cats versus dogs classification](https://www.kaggle.com/c/dogs-vs-cats-redux-kernels-edition) challenge. This was long before neural networks where recognized as the go to method to handle computer vision tasks. Modern, easily accessible tools like TensorFlow, Keras or GPU enabled cloud computing where a thing. Competitors tried all kinds of tools, from random forrests to support vector machines, but all top ranking entries used deep learning. In fact, the solutions developed by some competitors blew the current state of the art out of the water. It became clear to the community that for large ammounts of unstructured data, such as images, deep learning was the way to go. Today it is relatively easy to build a cats versus dogs classifier that just a few years ago would have gotten you a Phd. Let's get started!","outputs":[],"execution_count":null},{"metadata":{"_uuid":"19c422b93bb23dc0d12a6e91772bbcc88a5e540c","collapsed":true,"_cell_guid":"1e5e8fa0-a8af-409a-b325-eee85f014382","trusted":true},"cell_type":"code","source":"# Import matplotlib for plotting\nimport matplotlib.pyplot as plt\n# Import matplotlibs image tool\nimport matplotlib.image as mpimg\n# Flip the switch to get easier matplotlib rendering\n%matplotlib inline\n# for file listing\nimport os\n# for file moving\nfrom shutil import copyfile","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"5cd24d99da3f8213887809c1d3d1be70aec9d582","_cell_guid":"b11a4eb4-b554-4c15-ae6d-db5238e6e1c7"},"cell_type":"markdown","source":"## Surveying the data\nThe data contains about 25000 images, 12500 of cats and 12500 of dogs. We can get an overview of what the train path looks like:","outputs":[],"execution_count":null},{"metadata":{"_uuid":"d41c9540d01d43a994f194ddc7371e269d322996","_cell_guid":"2593fda3-2e32-4852-a8c2-ce4a210bff1b","trusted":true},"cell_type":"code","source":"len(os.listdir('../input/train'))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"6aba76ffdf59fa8efb33ac6f50b0c8bf82840c33","_cell_guid":"2a00ed64-6b2b-4e02-be50-e4a531c05982"},"cell_type":"markdown","source":"To later work well with Keras, we need to bring the files into the following folder structure:\n- train\n    - cat\n        - cat.0.jpg\n        - cat.1.jpg\n        - cat.2.jpg\n        - ...\n    - dog\n        - dog.0.jpg\n        - dog.1.jpg\n        - dog.2.jpg\n        -...\n \nThe following operations does exactly that. It moves the files over into a new directory. The methods used are not part of this course but interesting to read up on if you like. You can find the documentation to os.mkdir as well as os.path.join [here](https://docs.python.org/3/library/os.html) and the documentation to copyfile [here](https://docs.python.org/3/library/shutil.html)","outputs":[],"execution_count":null},{"metadata":{"_uuid":"862539eac4b94cf870768b4c9605c5cb79bfb3f0","_kg_hide-output":true,"_cell_guid":"611fd2e7-afc7-4fae-a905-c8d06a168373","scrolled":true,"trusted":true},"cell_type":"code","source":"# Create destination directories\nos.mkdir('train')\nos.mkdir('train/cat')\nos.mkdir('train/dog')\nos.mkdir('validation')\nos.mkdir('validation/cat')\nos.mkdir('validation/dog')\n# define paths\nsource_path = '../input/train'\n\ncat_train_path = 'train/cat'\ndog_train_path = 'train/dog'\n\ncat_validation_path = 'validation/cat'\ndog_validation_path = 'validation/dog'\n# Loop over image numbering\nfor i in range(12500):\n    cat = 'cat.' + str(i) + '.jpg'\n    dog = 'dog.' + str(i) + '.jpg'\n    # Get source paths\n    cat_source = os.path.join(source_path,cat)\n    dog_source = os.path.join(source_path,dog)\n    # Get destination paths\n    if i < 12000:\n        cat_dest = os.path.join(cat_train_path,cat)\n        dog_dest = os.path.join(dog_train_path,dog)\n    else: \n        cat_dest = os.path.join(cat_validation_path,cat)\n        dog_dest = os.path.join(dog_validation_path,dog)\n    # Move file\n    copyfile(cat_source,cat_dest)\n    copyfile(dog_source,dog_dest)\n    print('Copied',(i+1)*2,'out of 25,000 files',end='\\r')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"b4d7f7a09a368a647e3a69c133a22fda2834cec3","_cell_guid":"63f075c9-0f5b-49ff-95ae-20564ec73bc3"},"cell_type":"markdown","source":"Now that the images are in good order, lets look at a sample image","outputs":[],"execution_count":null},{"metadata":{"_uuid":"fed480a85f8e9ea30b0ac3f1e875af593b57d6c0","_cell_guid":"4f25c293-b22b-4ba9-a173-aa1770feadb8","trusted":true},"cell_type":"code","source":"img=mpimg.imread('train/cat/cat.1.jpg')\nimgplot = plt.imshow(img)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"ca4dd4b04f86b80bba24290c8d09ba84a8368d1d","_cell_guid":"6bf8caa1-50cb-488c-b7c9-2ff37e47f634"},"cell_type":"markdown","source":"Change the numbers in the image name to look at a few other cats and get an overview of what we are working with. You will notice that not all cat images are perfect portraits. These images where scraped from the Internet, so some are of multiple cats or of cats in wired poses. Let's look at some dog images:","outputs":[],"execution_count":null},{"metadata":{"_uuid":"1500b8b6e98d2b03d28d49f507b0bc33443cfb43","_cell_guid":"16e2b1f8-f5c3-4b69-998f-217e2f19a8f1","trusted":true},"cell_type":"code","source":"img=mpimg.imread('train/dog/dog.4.jpg')\nimgplot = plt.imshow(img)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"95d8631a82c4d87ad2ea10be3bed499f41a0865e","_cell_guid":"64d5a2f4-2f52-49ba-aa31-e276ff6bfd6a"},"cell_type":"markdown","source":"Again, try out a few different images to get a feel for what is going on. You will also notice that not all images are in the same format or have the same resolution.","outputs":[],"execution_count":null},{"metadata":{"_uuid":"9f1b35ddec3387394ea5e0a6204d4c5209af1ffd","_cell_guid":"141dff53-8cf8-4075-ad03-631752967f92"},"cell_type":"markdown","source":"## Setting up the neural net\nTo classify cats and dogs, we will use a convolutional neural network. It uses the ``Conv2D`` Keras layer we already discussed in the last chapter. Additionally it uses two new layers, ``MaxPool``and ``Flatten``.\n\n## Maxpooling\nMax pool pools a bunch of pixels together by just retaining the maximum value. This downsizes the image. A typical pooling window is 2 by to pixels, so it groups 4 pixels into one. For a pool window with activations like this:\n\n||1|2|\n|-|-|-|\n|1|0.1|0.3|\n|2|0.7|0.5|\n\nMaxpool would return 0.7. Maxpool treats the different layers independently, so we will retain the number of layers and downsize the width and the height only. Maxpool as not trainable parameters itself.\n\n## Flatten\nThe flatten layer turns the three dimensional convolutional blocks into one dimensional vectors that can be fed into a densely connected layer. This is done to transition from convolutional layers to dense ones.","outputs":[],"execution_count":null},{"metadata":{"_uuid":"87e8d4ef604a3d655ec2986a6dd6db10491f41d4","_cell_guid":"cfe9f675-ed6f-4586-89cc-e9cfa1469d7c","trusted":true},"cell_type":"code","source":"from keras.preprocessing.image import ImageDataGenerator\nfrom keras.models import Sequential\nfrom keras.layers import Activation, Dropout, Flatten, Dense, Conv2D, MaxPooling2D","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"1af26c441c2a20f592ae9999560221d9ddc5ce01","_cell_guid":"dd261153-e4e9-4920-a8df-d83c1ce4f51d"},"cell_type":"markdown","source":"We need to know the shape of our images. Images can be loaded 'channels first', meaning that in the representation of the shape the image the number of channels comes first. Otherwise, the images can be loaded 'channels last'. This is a setting in Keras. Usually it is best to just take what Keras chose by itself rather than tinkering with it so we will just request this setting:","outputs":[],"execution_count":null},{"metadata":{"_uuid":"9459a65457610ca634b804179f0138259d9d27ef","collapsed":true,"_cell_guid":"821e0393-bec9-412d-b6ad-4dcb9ba0ab6a","trusted":true},"cell_type":"code","source":"# The keras backend module has information stored about how Keras wants to interact with its backend (TensorFlow)\nfrom keras import backend as K\n\n# We will resize all images to 150 by 150 pixels\nimg_width, img_height = 150, 150\n\nif K.image_data_format() == 'channels_first':\n    input_shape = (3, img_width, img_height)\nelse:\n    input_shape = (img_width, img_height, 3)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8db3d1255ff128b878515e26678284ed1cdc5908","_cell_guid":"243e648e-48f8-43e7-8816-be639f6e2645"},"cell_type":"markdown","source":"Now we get to the model itself:","outputs":[],"execution_count":null},{"metadata":{"_uuid":"4f54d1e2be388aff66b6192f4bc3379c43b24caf","collapsed":true,"_cell_guid":"8e24ace5-f5b8-4f41-b117-c7eea72aadb1","trusted":true},"cell_type":"code","source":"model = Sequential()\n# First conv layer:\n# Filters: 32\n# Kernel: 3x3 (9px)\nmodel.add(Conv2D(32, (3, 3), input_shape=input_shape))\nmodel.add(Activation('relu'))\n# Max pool\n# Size: 2x2 (combine 4px to 1)\nmodel.add(MaxPooling2D(pool_size=(2, 2)))\n\n# Second conv layer\n# Filters: 32\n# Kernel: 3x3 (9px)\nmodel.add(Conv2D(32, (3, 3)))\nmodel.add(Activation('relu'))\n# Max pool\n# Size: 2x2 (combine 4px to 1)\nmodel.add(MaxPooling2D(pool_size=(2, 2)))\n\n# Third conv layer\n# Filters: 64\n# Kernel: 3x3 (9px)\nmodel.add(Conv2D(64, (3, 3)))\nmodel.add(Activation('relu'))\n# Max pool\n# Size: 2x2 (combine 4px to 1)\nmodel.add(MaxPooling2D(pool_size=(2, 2)))\n\n# Flatten turns 3D map into vector\nmodel.add(Flatten())\n# Dense layer\nmodel.add(Dense(64))\nmodel.add(Activation('relu'))\nmodel.add(Dropout(0.5))\n# Output layer\nmodel.add(Dense(1))\nmodel.add(Activation('sigmoid'))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"95dad0a1f9f083382cf841304830417acd1bb21a","_cell_guid":"d4889826-4390-42da-ad63-379000c4fa9a"},"cell_type":"markdown","source":"Note how we have implicitly rephrased our problem into a binary classification? We now discriminate cat, not cat. A not cat must then be a dog.","outputs":[],"execution_count":null},{"metadata":{"_uuid":"c743f20386068606cf09c03f4b362dacd3e07fae","collapsed":true,"_cell_guid":"7da8b1f6-6bb7-4050-9e67-2d710bc389fd","trusted":true},"cell_type":"code","source":"model.compile(loss='binary_crossentropy',\n              optimizer='adam',\n              metrics=['accuracy'])","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"48533f250b76c7b913c21f85f7287da566913188","_cell_guid":"dbb0f415-9820-4a28-aee3-1fcaef53a3ff"},"cell_type":"markdown","source":"As you can see from the code above we have three convolutional layers. Each convolutional layer is followed by a maxpooling layer. You can see how maxpooling reduces the size of the image while not touching the number of layers. You can also see that the first densely connected layer has way more parameters than convolutional layers. This showx how effective ConvNets are, archieving great results while keeping the number of parameters low.","outputs":[],"execution_count":null},{"metadata":{"_uuid":"3fab5e5bd35fa5739ff45a7cc546a9f17ea3c4f3","_cell_guid":"1f64da7e-77b5-4acf-8516-1381b05f47fb","trusted":true},"cell_type":"code","source":"model.summary()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"c2675bc453c15fc2ed25ae8ef20875ecf3f5be6a","_cell_guid":"6ebf94d7-b643-4cdd-bf44-43b59f84d626"},"cell_type":"markdown","source":"## Setting up the data pipeline\nIn previous chapters, we have simply loaded all data into a numpy matrix and trained on that matrix. For images, this is not practical. A numpy matrix holding thousands of images would use a massive amount of memory. It makes much more sense to build a pipeline that supplies new images to the model as needed. The images get loaded from the hard drive, processed and then turned into numpy matrices that can get fed into the model. Keras has some built in tools that make this process easy. The data pipeline will look like this:\n\n![Data flow](https://storage.googleapis.com/aibootcamp/Week%203/assets/dataflow.png)\n\n### The data generator\nThe Keras [image data generator](https://keras.io/preprocessing/image/) takes images from files and turns them into tensor image data, which are very similar to numpy matrices holding the image. The image data generator can handle many different forms of pre processing. Here, we just want to look at some methods of image augumentation.\n\nImage augumentation is the process of making random transformations to an image, like rotating it slightly or flipping it. Cropping the image or slightly stretching it is also very popular. This gives us multiple images out of one image, effectively increasing the amount of training data we have availeble by an order of magnitude. It leads to a more robust network that can recognize cats even if they look at the camera from a tilted angle.","outputs":[],"execution_count":null},{"metadata":{"_uuid":"3a7ccdbbe4153361ef9d277a37ddf4091e3b6715","collapsed":true,"_cell_guid":"d4efdf0b-95ef-4fd3-b0bc-385d4cdb5ba5","trusted":true},"cell_type":"code","source":"from keras.preprocessing.image import ImageDataGenerator\n\ndatagen = ImageDataGenerator(\n        rotation_range=40, # Rotate up to 40 degrees\n        width_shift_range=0.2, # Shift width by max 20%\n        height_shift_range=0.2, # Shift height by max 20%\n        rescale=1./255, # Rescale pixel activations from 0 to 255 (as it is in files) to 0 to 1\n        shear_range=0.2, # Cut away max 20% of the image\n        zoom_range=0.2, # Zoom in 20% max\n        horizontal_flip=True, # Flip image randomly\n        fill_mode='nearest') # Fill missing pixels with the nearest value","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"2465540b2574edb9563f15902b81ff0901bae81b","_cell_guid":"c4c28042-0151-4022-9392-d8ecb7018ae2"},"cell_type":"markdown","source":"Our data generator generates images from the cat image like this:\n![Transform](https://storage.googleapis.com/aibootcamp/Week%203/assets/datagen_transform.png)","outputs":[],"execution_count":null},{"metadata":{"_uuid":"ebe7cb41b1ff50100e6c90838b3543fc63d35ade","_cell_guid":"31dd9553-1d53-41ec-b167-fa1ae863f6ac"},"cell_type":"markdown","source":"When testing the model we also want to use a data generator to get images into the model but we do not want to transform them. Thus we use a separate image generator for testing that only rescales the images:","outputs":[],"execution_count":null},{"metadata":{"_uuid":"aba04ea639e84da31faf0c7701549699b2792f15","collapsed":true,"_cell_guid":"dbd4c8a1-f216-4ca2-80ba-a4c00c5b5eaa","trusted":true},"cell_type":"code","source":"# only rescaling\nvalidation_datagen = ImageDataGenerator(rescale=1./255)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"954b84246924f886eda2525a714cf9fb13a1c266","_cell_guid":"ad6d7f8d-64d2-41f3-b764-4e6afc14205e"},"cell_type":"markdown","source":"### Flow from directory\nFor classification to work with keras, the images have to be sorted into a certain folder structure. All images of one class have to be in the same folder. The class folders have to be in one folder which we then pass to the data flow function. The folder structure for our cats and dogs classification looks like this:\n- /data\n    - cat\n        - cat.0.jpg\n        - cat.1.jpg\n        - cat.2.jpg\n        - ...\n    - dog\n        - dog.0.jpg\n        - dog.1.jpg\n        - dog.2.jpg\n        - ...\n\nWe set up our flow from directory for train and test set like this:","outputs":[],"execution_count":null},{"metadata":{"_uuid":"e9b295a091742432737a05f75c2b94a4df200178","_cell_guid":"afec3ec7-d6ea-4bf5-8d14-efd033edcec7","trusted":true},"cell_type":"code","source":"# Set up batch size\nbatch_size = 16\ntrain_generator = datagen.flow_from_directory(\n        'train',  # this is the target directory\n        target_size=(150, 150),  # all images will be resized to 150x150\n        batch_size=batch_size, # How many images do we need at a time\n        class_mode='binary')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"96145e4ce1116620d2387f342b56e4dd1c65f838","_cell_guid":"43a57b91-8166-49f2-b015-2cab57254f13","trusted":true},"cell_type":"code","source":"validation_generator = validation_datagen.flow_from_directory(\n        'validation',  # this is the target directory\n        target_size=(150, 150),  # all images will be resized to 150x150\n        batch_size=batch_size, # How many images do we need at a time\n        class_mode='binary')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"04f1d9f42c9b48988f5932c50871f3e2014d05db","_cell_guid":"b607dfd8-329a-4d42-94f7-b9eb23a342f6"},"cell_type":"markdown","source":"## Training the model\nWe will train the model we will validate it at the same time. After each epoch, we will do a validation run to make sure that our model generalizes well and does not overfit the training set. To train our model on data that is coming from generators, we use Keras [fit_generator](https://keras.io/models/sequential/) function. It works a little bit different than the fit function we have used before. The main difference is that we do not have to specify a batch size to the fit function since we have already specified to the flow from directory function above. Instead, we have to specify how many steps we want to take per epoch. Each step will do one forward and backward pass on one batch. \nA common setting for the steps per epoch is the integer rounded divison of the number of images and the batch size.\n\nTraining this model can take a while:","outputs":[],"execution_count":null},{"metadata":{"_uuid":"49e35979bc50e82e7b01177725567a7fc216a74d","_cell_guid":"59ef3fd3-089a-4bc8-afc1-8baa37afe7a1","trusted":false,"collapsed":true},"cell_type":"code","source":"history = model.fit_generator(\n        train_generator, # Get training data from training generator\n        steps_per_epoch=12000 // batch_size, # // is the integer rounded division\n        epochs=3, # train for 50 epochs\n        validation_data=validation_generator, # Validate with validation generator\n        validation_steps=1000 // batch_size)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"bcb9712d8878daeac0c088f70063506162f92b56","collapsed":true,"_cell_guid":"10219ff4-5849-4748-86b1-11375f3a19f2","trusted":false},"cell_type":"code","source":"plt.plot(history.history['acc'])","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"22440f1078f60c99b2e36ac579057e01f627c054","_cell_guid":"e69feef7-ec36-47b9-896b-4c7f9db563d2"},"cell_type":"markdown","source":"Due to Kaggle Kernel limitations we can run this only for a few epochs. After training more, the model should archieve between 79% and 81%. At the time of the original Cats vs. Dogs competition in 2013, that would have been state of the art and you might have won the competition! You might have noted we have not used all 25000 images from the dataset but only 2000. We did this to save on disk space, download time and computational cost in this tutorial. With more images and training we probably could have done better. In the next chapter we will look at ways to archieve state of the art results.\n\n## Using our cat classifier\nRemember that our classifier is a binary classifier? This means it will output a single value between 0 and 1. We can see what a number means from our data generator, which also generates the labels:","outputs":[],"execution_count":null},{"metadata":{"_uuid":"ebda6043fce658725fd7a94239005e6c4bbf8667","_cell_guid":"2c05fd63-84c6-44b8-bb14-f46d9b564964","trusted":true},"cell_type":"code","source":"train_generator.class_indices","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"389f0286ffa322ef0b833edad36eb0ff974be0c9","_cell_guid":"f30409e0-6cda-4e1f-b6fe-0c0431410f47"},"cell_type":"markdown","source":"Zero means cat, one means dog. This means that the model should output values above 0.5 if a dog image is fed into it. We can feed a single image into the model like this:","outputs":[],"execution_count":null},{"metadata":{"_uuid":"ad7ad7aeb551ef7064f0677bf0fa54b68b88b5b1","_cell_guid":"751c76d8-3847-4288-980e-ab932b29b08e","trusted":true},"cell_type":"code","source":"# Load the whole image module\nfrom keras.preprocessing import image\n# Load numpy, how did we live without it so far??\nimport numpy as np\n# Specify the file path\nimg_path = 'validation/dog/dog.12001.jpg'\n# Load and resize the image\nimg = image.load_img(img_path, target_size=(150, 150))\n# Turn the image into a numpy array\nx = image.img_to_array(img)\n# Resize the matrix to meet the expected shape\nx = np.expand_dims(x, axis=0)\n# Rescale the image\nx = x / 255\n\n# Obtain prediction\nfeatures = model.predict(x)\n# Output prediction\nprint('Dog probability: ' + str(features))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"6bf4f69ea01b249b9d9c4d048a8fc56f75a15a7b","_cell_guid":"2fedbff7-2075-4975-ad00-a26ca38b0836"},"cell_type":"markdown","source":"This our model gives this image a high probability of being a dog. Let's render it:","outputs":[],"execution_count":null},{"metadata":{"_uuid":"c0023ec57680c00185b1f4e9f8fed2c4687b378c","_cell_guid":"1ce378cb-b991-4b87-9152-1180410bfe2f","trusted":true},"cell_type":"code","source":"img=mpimg.imread(img_path)\nimgplot = plt.imshow(img)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"7200082b5d91708d2c3527b8fa9e117c444fb6ad","_cell_guid":"0beffa22-a970-4999-8d8b-a6e3b3684a81"},"cell_type":"markdown","source":"And what a good boy it is! Let's loop over dog images to view some wrong predictions:","outputs":[],"execution_count":null},{"metadata":{"_uuid":"8a495ab5a6203e26bb99e85fa6dedeaada89a04d","_cell_guid":"554dbd59-f33b-420a-a361-ba8ace41f780","trusted":true},"cell_type":"code","source":"wrong_classifications = []\nfor i in range(12000,12500):\n    img_path = 'validation/dog/dog.' +str(i)+ '.jpg'\n    # Load and resize the image\n    img = image.load_img(img_path, target_size=(150, 150))\n    # Turn the image into a numpy array\n    x = image.img_to_array(img)\n    # Resize the matrix to meet the expected shape\n    x = np.expand_dims(x, axis=0)\n    # Rescale the image\n    x = x / 255\n\n    # Obtain prediction\n    features = model.predict(x)\n    if features < 0.5:\n        wrong_classifications.append(img_path)\n        print('Actual: Dog, Predicted: Cat')\n        img=mpimg.imread(img_path)\n        imgplot = plt.imshow(img)\n        plt.show()\n    if len(wrong_classifications) > 5:\n        break","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"fa277e94df0844f56163d55579f37d088cc91b18","_cell_guid":"f5969f3d-1be2-40a0-b1dd-8d8a10207ffb"},"cell_type":"markdown","source":"The images tend to be dogs in 'cat like' poses. Yet, many of these dogs are clearly recognizable as such. There still is a lot room for improvement. Let's look at some wrongly classified cats:","outputs":[],"execution_count":null},{"metadata":{"_uuid":"751ff3ae801b4832d95634f064794374bd7a1b77","_cell_guid":"7a98545d-e1cf-4014-9948-de312a5b88b2","trusted":true},"cell_type":"code","source":"wrong_classifications = []\nfor i in range(12000,12500):\n    img_path = 'validation/cat/cat.' +str(i)+ '.jpg'\n    # Load and resize the image\n    img = image.load_img(img_path, target_size=(150, 150))\n    # Turn the image into a numpy array\n    x = image.img_to_array(img)\n    # Resize the matrix to meet the expected shape\n    x = np.expand_dims(x, axis=0)\n    # Rescale the image\n    x = x / 255\n\n    # Obtain prediction\n    features = model.predict(x)\n    if features > 0.5:\n        wrong_classifications.append(img_path)\n        print('Actual: Cat, Predicted: Dog')\n        img=mpimg.imread(img_path)\n        imgplot = plt.imshow(img)\n        plt.show()\n    if len(wrong_classifications) > 5:\n        break","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"464632af1090edc0a96f03d7dcdd06a6b97332d8","_cell_guid":"afb74655-703d-4e55-9934-90571992a4fd"},"cell_type":"markdown","source":"Again, a human has no problem telling these cats as cats. In the next chapter we will build a model that will do better. Never the less, the model we have built today is a decent baseline model to benchmark more advanced tools against. We should save the model:","outputs":[],"execution_count":null},{"metadata":{"_uuid":"21d93a9a73d3c134259fc302761f8217f82218cf","collapsed":true,"_cell_guid":"da352129-e59f-4a0d-989c-a3d4a8f17e87","trusted":false},"cell_type":"code","source":"model.save_weights('first_try.h5')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"49b11b7dc54e69411cb36280c446799d64ef3798","_cell_guid":"ef3e30ec-1709-49a4-9e24-867a86a1bd81"},"cell_type":"markdown","source":"## Summary\nIn this chapter you have seen how to build a convolutional neural network in Keras and train it from scratch. You have learned about a few new techniques:\n- Maxpool reduces the image size\n- Flatten turns convolutional cubes into flat vectors\n- Image data generators prepare images for training\n- Flow from directory reads images from disks","outputs":[],"execution_count":null},{"metadata":{"collapsed":true,"_uuid":"84076af335e561f6adbc3befada1408a7b8d7107","_cell_guid":"e237f1e1-a479-4606-9539-2f1c09ac1241","trusted":false},"cell_type":"code","source":"# Cleanup for kaggle\n# Kaggle only allows a certain amount of output files so we have to remove \n# our resorted training data at the end of the kernel\n!rm -r train\n!rm -r validation","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"anaconda-cloud":{},"language_info":{"name":"python","version":"3.6.4","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}