{"id":2306,"date":"2019-02-19T18:44:19","date_gmt":"2019-02-19T18:44:19","guid":{"rendered":"https:\/\/www.danielpradilla.info\/blog\/?p=2306"},"modified":"2019-02-22T08:14:35","modified_gmt":"2019-02-22T08:14:35","slug":"classifying-convolutional-neural-network-keras","status":"publish","type":"post","link":"https:\/\/www.danielpradilla.info\/blog\/classifying-convolutional-neural-network-keras\/","title":{"rendered":"Classifying fruits with a Convolutional Neural Network in Keras"},"content":{"rendered":"<p>I followed a tutorial on Convolutional Neural Networks that left many questions unanswered. Soon I realized that the actual process of architecting a Neural Network and setting the parameters seemed to be much more experimental than I thought. It took a while to find explanations that a rookie like me could understand. Most of the posts I found gloss over the difficult details, like what NN configuration to choose, or which library to choose, <a href=\"https:\/\/stats.stackexchange.com\/questions\/226923\/why-do-we-use-relu-in-neural-networks-and-how-do-we-use-it\">or why would you use ReLU in an image-recognition NN<\/a>&nbsp; and focused on easier and more consistent tasks, like setting up a Python environment.<\/p>\n<p>&nbsp;<\/p>\n<h3>TL;DR<\/h3>\n<p><a href=\"https:\/\/keras.io\/\">Keras<\/a> allows you to build a neural network in about 10 minutes. You spend the remaining 20 hours training, testing, and tweaking.<\/p>\n<p>If you wish to learn how a Convolutional Neural Network is used to classify images, <a href=\"https:\/\/youtu.be\/FmpDIaiMIeA\">this is a pretty good video<\/a>.<\/p>\n<p><a href=\"https:\/\/www.superdatascience.com\/the-ultimate-guide-to-convolutional-neural-networks-cnn\/\">The ultimate guide to convolutional neural networks<\/a> honors its name.<\/p>\n<p>This example illustrates how a CNN recognizes numbers: <a href=\"https:\/\/transcranial.github.io\/keras-js\/#\/mnist-cnn\">Basic Convnet for MNIST<\/a><\/p>\n<p>Get the code at&nbsp;<a href=\"https:\/\/github.com\/danielpradilla\/python-keras-fruits\">https:\/\/github.com\/danielpradilla\/python-keras-fruits<\/a>.<\/p>\n<p><a href=\"#prediction_title\">Jump to the predictions<\/a>.<\/p>\n<p>&nbsp;<\/p>\n<h3>A simple problem<\/h3>\n<p>In order to learn the basics, I started a simple \u2013um, &#8220;simple&#8221;\u2013 project to build a deep neural network to correctly classify a set of 17 kinds of fruits from the <a href=\"https:\/\/www.kaggle.com\/moltean\/fruits\">Kaggle Fruits 360 dataset<\/a>.<\/p>\n<p><a href=\"https:\/\/keras.io\/\">Keras<\/a> provides an easy interface to create and train Neural Networks, hiding most of the tedious details and allowing you to focus on the NN structure. It interfaces with the most popular NN frameworks: Tensorflow, CNTK,&nbsp;and Theano. This answered one of my first questions, &#8220;<a href=\"https:\/\/medium.com\/implodinggradients\/tensorflow-or-keras-which-one-should-i-learn-5dd7fa3f9ca0\">Keras vs Tensorflow, which one should you learn and when to use each one?<\/a>&#8221; \u2013short answer: learn Keras until you need Tensorflow.<\/p>\n<p>Keras allows you to go from idea to working NN in about 10 minutes. Which is impressive. The Keras workflow looks like this:<\/p>\n<ul>\n<li>Get training and testing data<\/li>\n<li>Define the layers<\/li>\n<li>Compile the model<\/li>\n<li>Train model<\/li>\n<li>Evaluate the model<\/li>\n<li>Get predictions<\/li>\n<\/ul>\n<h3>Get training and testing data<\/h3>\n<p>Keras provides a neat file-system-based helper for ingesting the training and testing datasets. You create a &#8220;training&#8221; and &#8220;validation&#8221; folder, and put your images there. In the case of a multi-category classifier like this one, each image class will have their own folder \u2013&#8221;Apple&#8221;, &#8220;Oranges&#8221;, etc. <strong>flow_from_directory()<\/strong> will automatically infer the labels from the directory structure of the folders containing images. Every subfolder inside the training-folder (or validation-folder) will be considered a target class.<\/p>\n<p>Alternatively, you can ingest the data on your own and do a manual split.<\/p>\n<p><strong>flow_from_directory<\/strong> is a property of <strong>ImageDataGenerator<\/strong>, a generator class that offers a memory-efficient iterator object. When you have large datasets it&#8217;s always wise to use a generator that yields batches of training data. More info at &#8220;<a href=\"https:\/\/medium.com\/difference-engine-ai\/keras-a-thing-you-should-know-about-keras-if-you-plan-to-train-a-deep-learning-model-on-a-large-fdd63ce66bd2\">A thing you should know about Keras if you plan to train a deep learning model on a large dataset<\/a>&#8221;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"2307\" data-permalink=\"https:\/\/www.danielpradilla.info\/blog\/classifying-convolutional-neural-network-keras\/avocado-predict-2\/\" data-orig-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/avocado-predict-2.jpg\" data-orig-size=\"250,250\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"avocado-predict-2\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/avocado-predict-2.jpg\" data-large-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/avocado-predict-2.jpg\" class=\"aligncenter size-full wp-image-2307\" src=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/avocado-predict-2.jpg\" alt=\"\" width=\"250\" height=\"250\" srcset=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/avocado-predict-2.jpg 250w, https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/avocado-predict-2-150x150.jpg 150w\" sizes=\"auto, (max-width: 250px) 100vw, 250px\" \/><\/p>\n<p>Remember that, even though we clearly see that this is an avocado, all the neural network may see is the result of some edge-detection filters. So an avocado is this apparently green oval thing placed in the middle of the field. If you train the NN with perfectly-centered avocados and then feed it an off-center avocado, you might get a poor prediction.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"2311\" data-permalink=\"https:\/\/www.danielpradilla.info\/blog\/classifying-convolutional-neural-network-keras\/avocado-predict-notequal\/\" data-orig-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/avocado-predict-notequal.jpg\" data-orig-size=\"600,250\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"avocado-predict-notequal\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/avocado-predict-notequal-300x125.jpg\" data-large-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/avocado-predict-notequal.jpg\" class=\"aligncenter size-full wp-image-2311\" src=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/avocado-predict-notequal.jpg\" alt=\"\" width=\"600\" height=\"250\" srcset=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/avocado-predict-notequal.jpg 600w, https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/avocado-predict-notequal-300x125.jpg 300w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>In order to account for this and facilitate the training process, Keras provides the <strong>ImageGenerator<\/strong> class, which allows you to define configuration parameters for augmenting the data by applying random changes in brightness, rotation, zoom, and skewing. This is a way to artificially expand the data set and increase the robustness of the training data.<\/p>\n<p>One particularly important image augmentation step is to rescale the RGB values in the image to be within the [0,1] range, dividing each color value by 255. This normalizes the differences in pixel ranges across all images, and contributes to creating a network that learns the most essential features of each fruit. More info at <a href=\"https:\/\/machinelearningmastery.com\/image-augmentation-deep-learning-keras\/\">Image Augmentation for Deep Learning with Keras<\/a>.<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\n\ntest_gen = k.preprocessing.image.ImageDataGenerator(\nrotation_range=0.1,\nwidth_shift_range=0.1,\nheight_shift_range=0.1,\nbrightness_range=&#x5B;0.5, 1.5],\nchannel_shift_range=0.05,\nrescale=1.\/255\n)\n\ntest_images_iter = test_gen.flow_from_directory(TEST_PATH,\ntarget_size = TARGET_SIZE,\nclasses = VALID_FRUITS,\nclass_mode = 'categorical',\nseed = SEED)\n<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"2353\" data-permalink=\"https:\/\/www.danielpradilla.info\/blog\/classifying-convolutional-neural-network-keras\/screenshot-2019-02-13-at-09-12-21\/\" data-orig-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.12.21.png\" data-orig-size=\"363,547\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Screenshot 2019-02-13 at 09.12.21\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.12.21-199x300.png\" data-large-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.12.21.png\" class=\"aligncenter size-full wp-image-2353\" src=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.12.21.png\" alt=\"\" width=\"363\" height=\"547\" srcset=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.12.21.png 363w, https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.12.21-199x300.png 199w\" sizes=\"auto, (max-width: 363px) 100vw, 363px\" \/><\/p>\n<h4>More image training data<\/h4>\n<p>There are a couple of datasets that are commonly used for training Neural Networks<br \/>\n<a href=\"http:\/\/cocodataset.org\/#home\">COCO<\/a><br \/>\n<a href=\"https:\/\/www.cs.toronto.edu\/~kriz\/cifar.html\">CFAR<\/a><\/p>\n<h3>Network Architecture<\/h3>\n<p>What is the configuration of the Neural Network? How many convolutional layers, how many filters in each convolutional layer and what&#8217;s the size of these filters? These were some of the questions that arose as I started to read tutorials, papers, and documentation. Turns out that in the majority of the cases, the answer is &#8220;it depends&#8221;. It depends on the data, your previous decisions on image size, your goals in terms of accuracy\/speed, and your personal understanding of what you deem a good solution.<\/p>\n<h4>Understanding Convolutional Neural Networks<\/h4>\n<p>This guide presents a high-level overview of the typical neural network structures that you can build in Keras:&nbsp;<a href=\"https:\/\/keras.io\/getting-started\/sequential-model-guide\/\">https:\/\/keras.io\/getting-started\/sequential-model-guide\/<\/a><\/p>\n<p>I watched a bunch of videos and this one provides the best explanation of what is a CNN and how it works:<\/p>\n<p><span class=\"embed-youtube\" style=\"text-align:center; display: block;\"><iframe loading=\"lazy\" class=\"youtube-player\" width=\"750\" height=\"422\" src=\"https:\/\/www.youtube.com\/embed\/FmpDIaiMIeA?version=3&#038;rel=1&#038;showsearch=0&#038;showinfo=1&#038;iv_load_policy=1&#038;fs=1&#038;hl=en-US&#038;autohide=2&#038;wmode=transparent\" allowfullscreen=\"true\" style=\"border:0;\" sandbox=\"allow-scripts allow-same-origin allow-popups allow-presentation allow-popups-to-escape-sandbox\"><\/iframe><\/span><\/p>\n<p><a href=\"https:\/\/www.superdatascience.com\/the-ultimate-guide-to-convolutional-neural-networks-cnn\/\">The ultimate guide to convolutional neural networks<\/a> honors its name. But the best way to understand how a CNN and each layer works at the most fundamental level is with these two examples:<\/p>\n<p><a href=\"https:\/\/transcranial.github.io\/keras-js\/#\/mnist-cnn\">Basic Convnet for MNIST<\/a><\/p>\n<p><a href=\"http:\/\/scs.ryerson.ca\/~aharley\/vis\/conv\/flat.html\">The number game<\/a><\/p>\n<p><a href=\"https:\/\/transcranial.github.io\/keras-js\/#\/mnist-cnn\"><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"2313\" data-permalink=\"https:\/\/www.danielpradilla.info\/blog\/classifying-convolutional-neural-network-keras\/screenshot-2019-02-16-at-17-18-32\/\" data-orig-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-16-at-17.18.32.png\" data-orig-size=\"910,643\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Screenshot 2019-02-16 at 17.18.32\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-16-at-17.18.32-300x212.png\" data-large-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-16-at-17.18.32.png\" class=\"aligncenter size-full wp-image-2313\" title=\"\" src=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-16-at-17.18.32.png\" alt=\"\" width=\"910\" height=\"643\" srcset=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-16-at-17.18.32.png 910w, https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-16-at-17.18.32-300x212.png 300w, https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-16-at-17.18.32-768x543.png 768w\" sizes=\"auto, (max-width: 910px) 100vw, 910px\" \/><\/a><\/p>\n<p>Layers are the building blocks of Neural Networks, you can think of them as processing units that are stacked (or um layered) and connected. In the case of feed-forward networks, like CNNs, the layers are connected sequentially. Commonly, each layer is comprised of nodes, or &#8220;neurons&#8221;, which perform individual calculations, but I rather think of layers as computation stages, because it&#8217;s not always clear that each layer contains neurons.<\/p>\n<p>The process of creating layers with Keras is pretty straightforward. Just call <strong>keras.layers<\/strong> and push them into a list.<\/p>\n<p>The two questions I found the hardest to answer were How many layers do I have to set up? and What is the number of nodes in each one? These are often overlooked in tutorials because you are just following the decisions of someone else. I bet that in the coming months we will see an abstraction similar to Keras with which you won&#8217;t even have to think of the number of layers.<\/p>\n<p>The easy layers to figure out are the input and output layers. The input layer has one input node and in Keras there is not need to define it. The output layer depends on the type of neural network. If it&#8217;s a regressor or a binary classifier, then it has one output \u2013you are either returning a number or a true\/false value. If it&#8217;s a multi-class classifier with a softmax function at the end, you will need one node per class \u2013in our case, one per type of fruit we are attempting to classify.<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\n\nrtn = k.Sequential() #input\n\n##something??\n\nrtn.add(k.layers.Dense(units = len(trained_classes))) #output\n\n<\/pre>\n<p>Now, for the hidden layers, we need to think about the structure of a Convolutional Neural Network. In general terms, Convolutional Neural Networks have two parts: a set of convolution layers and a set of fully connected layers. The convolution layers produce feature maps (mappings of activations of the different parts of an image), which are then pooled, flattened out and passed on to the fully connected layers.<\/p>\n<p><a href=\"https:\/\/www.superdatascience.com\/blogs\/the-ultimate-guide-to-convolutional-neural-networks-cnn\"><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"2315\" data-permalink=\"https:\/\/www.danielpradilla.info\/blog\/classifying-convolutional-neural-network-keras\/35_blog_image_34\/\" data-orig-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/35_blog_image_34.png\" data-orig-size=\"866,392\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"35_blog_image_34\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/35_blog_image_34-300x136.png\" data-large-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/35_blog_image_34.png\" class=\"wp-image-2315 size-full\" src=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/35_blog_image_34.png\" alt=\"\" width=\"866\" height=\"392\" srcset=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/35_blog_image_34.png 866w, https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/35_blog_image_34-300x136.png 300w, https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/35_blog_image_34-768x348.png 768w\" sizes=\"auto, (max-width: 866px) 100vw, 866px\" \/><\/a><\/p>\n<p>source: <a href=\"https:\/\/www.superdatascience.com\/blogs\/the-ultimate-guide-to-convolutional-neural-networks-cnn\">https:\/\/www.superdatascience.com\/blogs\/the-ultimate-guide-to-convolutional-neural-networks-cnn<\/a><\/p>\n<p>For the convolution layers, we need to strike a balance between performance and quality of the output. In this particular case, I noticed that having a single convolution already produced 97% accuracy, and I added an extra convolution because the convergence of the validation accuracy seemed smoother. Since all the images were of high quality \u2013a rotation of a single fruit with transparent background\u2013 a single convolution would&#8217;ve done the job. I bet that there are cases with lower-quality images in which the extra convolutions will improve the performance of the network.<\/p>\n<p>The best explanation that I found for convolutional layers is to imagine a flashlight sliding over areas of an image and capturing the data (1s and 0s) that is being made visible.<\/p>\n<p><a href=\"https:\/\/medium.com\/syncedreview\/a-guide-to-receptive-field-arithmetic-for-convolutional-neural-networks-42f33d4378e0\"><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"2317\" data-permalink=\"https:\/\/www.danielpradilla.info\/blog\/classifying-convolutional-neural-network-keras\/0_iqndzwynecr5tckc\/\" data-orig-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/0_iqNdZWyNeCr5tCkc.gif\" data-orig-size=\"395,449\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"0_iqNdZWyNeCr5tCkc\" data-image-description=\"\" data-image-caption=\"&lt;p&gt;source: https:\/\/medium.com\/syncedreview\/a-guide-to-receptive-field-arithmetic-for-convolutional-neural-networks-42f33d4378e0&lt;\/p&gt;\n\" data-medium-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/0_iqNdZWyNeCr5tCkc-264x300.gif\" data-large-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/0_iqNdZWyNeCr5tCkc.gif\" class=\"size-full wp-image-2317\" src=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/0_iqNdZWyNeCr5tCkc.gif\" alt=\"\" width=\"395\" height=\"449\"><\/a><br \/>\n<a href=\"https:\/\/medium.com\/syncedreview\/a-guide-to-receptive-field-arithmetic-for-convolutional-neural-networks-42f33d4378e0\">source: https:\/\/medium.com\/syncedreview\/a-guide-to-receptive-field-arithmetic-for-convolutional-neural-networks-42f33d4378e0<\/a><\/p>\n<p>The flashlight is what we call a filter, the area that it is shining over is the receptive field and the way that the data is interpreted depends on weights and parameters. Each filter will have 1s and 0s arranged in a particular way, forming patterns that capture a particular feature, one filter will be pretty good at capturing straight lines, another one will be good at curves or checkered patterns. As the flashlight slides (convolves) the receptive field over the image, the values contained in the filter will be multiplied by the values contained in the image, creating what is called an activation map, a matrix that shows how a particular filter &#8220;activated&#8221; the information contained in the image.<\/p>\n<p>The number of filters in the convolution is largely empirical. Common practice seems to be that, as you go deeper in the network, you learn more features, so you add more filters to each successive convolutional layer. I started with 16 and 32 for the next layer and then observed better results by switching to 32\/64 and then to 64\/128. These are single-fruit images with a blank background. A more complex dataset might require a larger number of filters, or more layers.<\/p>\n<p>The kernel_size must be an odd integer. What I&#8217;ve seen most is 3&#215;3 for small images (less than 128 pixels in size) or 5&#215;5 and 7&#215;7 for larger images.<\/p>\n<p>I left striding at 1, but you may increase it if you need to reduce the output dimensions of the convolution.<\/p>\n<p>I set padding to &#8220;same&#8221; to keep the output size of the convolution equal to the input size.<\/p>\n<p>I used L2 kernel regularization and drop-off because that&#8217;s what I read I should do to reduce overfitting.<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\n\n#first convolution\nrtn.add(k.layers.Conv2D(filters = 64, kernel_size = (3,3),\npadding = 'same',\nstrides=(1, 1),\ninput_shape = (IMG_WIDTH, IMG_HEIGHT, CHANNELS),\nkernel_regularizer=k.regularizers.l2(0.0005),\nname='conv2d_1'))\n\n#second convolution\nrtn.add(k.layers.Conv2D(filters = 128, kernel_size = (3,3),\npadding = 'same',\nname='conv2d_2'))\n\n<\/pre>\n<p>I read many guides using <a href=\"https:\/\/www.cs.toronto.edu\/~hinton\/absps\/JMLRdropout.pdf\">Dropout<\/a> in between convolution layers for reducing overfitting. The Dropout algorithm randomly sets some of the layer&#8217;s values to 0, in order to force the network to learn new paths for representing the same data.<\/p>\n<p>I read that Dropout is most effective as a regularization method in the fully connected portion of the NN. For the convolution, two methods are available, <a href=\"https:\/\/arxiv.org\/pdf\/1411.4280.pdf\">Spatial Dropout<\/a> and <a href=\"https:\/\/arxiv.org\/pdf\/1502.03167.pdf\">Batch Normalization<\/a>. From the Keras documentation, we can learn that Spatial Dropout seems to be the dropout method to use between convolution layers:<\/p>\n<blockquote><p>&#8220;If adjacent pixels within feature maps are strongly correlated (as is normally the case in early convolution layers) then regular dropout will not regularize the activations and will otherwise just result in an effective learning rate decrease&#8221;<\/p><\/blockquote>\n<p>For the activations, I used <a href=\"https:\/\/medium.com\/tinymind\/a-practical-guide-to-relu-b83ca804f1f7\">Rectified Linear Unit<\/a>. ReLU is ideal for enhancing the transitions between pixels (edges, changes in colors). I also used Leaky ReLU (<a href=\"https:\/\/arxiv.org\/pdf\/1502.01852.pdf\">paper<\/a>) to avoid the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Rectifier_(neural_networks)#Potential_problems\">dying ReLU problem<\/a>. In case you need to do some kind of visual recognition. ReLU and Leaky ReLU seem to perform better than the traditional sigmoid function used in neural networks.<\/p>\n<p>At the end of all the convolutions, we insert a pooling layer, which down-samples the feature maps and reduces the dimensionality of the problem. Pooling takes the maximum value of a sliding receptive field over each feature map. It also helps make the detection of certain features invariant to scale and orientation. In other words, it makes the network more robust by focusing on the most important features of the image.<\/p>\n<p><a href=\"source: https:\/\/adventuresinmachinelearning.com\/convolutional-neural-networks-tutorial-tensorflow\/\"><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"2319\" data-permalink=\"https:\/\/www.danielpradilla.info\/blog\/classifying-convolutional-neural-network-keras\/pooling-demonstration-v2\/\" data-orig-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Pooling-demonstration-v2.jpg\" data-orig-size=\"793,301\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Pooling-demonstration-v2\" data-image-description=\"\" data-image-caption=\"&lt;p&gt;source: https:\/\/adventuresinmachinelearning.com\/convolutional-neural-networks-tutorial-tensorflow\/&lt;\/p&gt;\n\" data-medium-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Pooling-demonstration-v2-300x114.jpg\" data-large-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Pooling-demonstration-v2.jpg\" class=\"size-full wp-image-2319\" src=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Pooling-demonstration-v2.jpg\" alt=\"\" width=\"793\" height=\"301\" srcset=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Pooling-demonstration-v2.jpg 793w, https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Pooling-demonstration-v2-300x114.jpg 300w, https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Pooling-demonstration-v2-768x292.jpg 768w\" sizes=\"auto, (max-width: 793px) 100vw, 793px\" \/><\/a><br \/>\n<a href=\"source: https:\/\/adventuresinmachinelearning.com\/convolutional-neural-networks-tutorial-tensorflow\/\">source: https:\/\/adventuresinmachinelearning.com\/convolutional-neural-networks-tutorial-tensorflow\/<\/a><\/p>\n<p>After the pooling layer, we flatten the resulting matrix into a vector and we feed it through a fully-connected network. Each element in this vector will correspond to one or more features detected in the convolution, and the purpose of this fully-connected layers is to make classifications. In other words, after the convolution, we staple a standard neural network classifier. At the tail end, the fully-connected network will produce a probability distribution per each class, and these distributions will depend on the weight attributed to each neuron in the final classification. Though back propagation, Keras will automatically adjust the weight of each of these connections in order to minimize the classification error.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"2321\" data-permalink=\"https:\/\/www.danielpradilla.info\/blog\/classifying-convolutional-neural-network-keras\/35_blog_image_26\/\" data-orig-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/35_blog_image_26.png\" data-orig-size=\"866,394\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"35_blog_image_26\" data-image-description=\"&lt;p&gt;source: https:\/\/www.superdatascience.com\/blogs\/the-ultimate-guide-to-convolutional-neural-networks-cnn&lt;\/p&gt;\n\" data-image-caption=\"\" data-medium-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/35_blog_image_26-300x136.png\" data-large-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/35_blog_image_26.png\" class=\"aligncenter size-full wp-image-2321\" title=\"\" src=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/35_blog_image_26.png\" alt=\"\" width=\"866\" height=\"394\" srcset=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/35_blog_image_26.png 866w, https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/35_blog_image_26-300x136.png 300w, https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/35_blog_image_26-768x349.png 768w\" sizes=\"auto, (max-width: 866px) 100vw, 866px\" \/><\/p>\n<p>The key decision point at this stage will be the number of units that each layer of the fully connected network will have. <a href=\"http:\/\/www.faqs.org\/faqs\/ai-faq\/neural-nets\/part3\/section-10.html\">This section of the comp.ai.neural-nets FAQ discusses the issue at length<\/a>. I take away this part:<\/p>\n<blockquote><p>&#8220;For a noise-free quantitative target variable, twice as many training cases as weights may be more than enough to avoid overfitting. For a very noisy categorical target variable, 30 times as many training cases as weights may not be enough to avoid overfitting. An intelligent choice of the number of hidden units depends on whether you are using early stopping or some other form of regularization. If not, you must simply try many networks with different numbers of hidden units, estimate the generalization error for each one, and choose the network with the minimum estimated generalization error.&#8221;<\/p><\/blockquote>\n<p>This sample is relatively low noise and, after much repetition, I found an ok-performing network with 250 units.<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\n\n# flatten into feature vector\nrtn.add(k.layers.Flatten())\n\n# output features onto a dense layer\n#rtn.add(k.layers.Dense(units = len(trained_classes_labels) * 20, name='dense_1' ) )\nrtn.add(k.layers.Dense(units = 250, name='dense_1' ) )\nrtn.add(k.layers.Activation('relu', name='activation_dense_1'))\n\n# randomly switch off 50% of the nodes per epoch step to avoid overfitting\nrtn.add(k.layers.Dropout(0.5))\n\n# output layer with the number of units equal to the number of categories\nrtn.add(k.layers.Dense(units = len(trained_classes_labels), name='dense_2'))\nrtn.add(k.layers.Activation('softmax', name='activation_final'))\n\n<\/pre>\n<p>&nbsp;<\/p>\n<h4>Visualizing the network architecture<\/h4>\n<p>I was going to put here a really cool visualization of the network, but quickly found out that that&#8217;s a <a href=\"https:\/\/medium.com\/inbrowserai\/simple-diagrams-of-convoluted-neural-networks-39c097d2925b\">whole other area of research and experimentation<\/a>. I&#8217;ll leave it for a future post. This is the output of <strong>model.summary()<\/strong><\/p>\n<pre>Layer (type) Output Shape Param #\n=================================================================\nconv2d_1 (Conv2D) (None, 35, 35, 64) 1792\n_________________________________________________________________\nbatch_normalization_3 (Batch (None, 35, 35, 64) 256\n_________________________________________________________________\nactivation_conv2d_1 (Activat (None, 35, 35, 64) 0\n_________________________________________________________________\nspatial_dropout2d_2 (Spatial (None, 35, 35, 64) 0\n_________________________________________________________________\nconv2d_2 (Conv2D) (None, 35, 35, 128) 73856\n_________________________________________________________________\nbatch_normalization_4 (Batch (None, 35, 35, 128) 512\n_________________________________________________________________\nactivation_conv2d_2 (LeakyRe (None, 35, 35, 128) 0\n_________________________________________________________________\nmax_pooling2d_2 (MaxPooling2 (None, 17, 17, 128) 0\n_________________________________________________________________\nflatten_2 (Flatten) (None, 36992) 0\n_________________________________________________________________\ndense_1 (Dense) (None, 250) 9248250\n_________________________________________________________________\nactivation_dense_1 (Activati (None, 250) 0\n_________________________________________________________________\ndropout_2 (Dropout) (None, 250) 0\n_________________________________________________________________\ndense_2 (Dense) (None, 17) 4267\n_________________________________________________________________\nactivation_final (Activation (None, 17) 0\n=================================================================\nTotal params: 9,328,933\nTrainable params: 9,328,549\nNon-trainable params: 384\n_________________________________________________________________<\/pre>\n<h3>Compilation<\/h3>\n<p>After we define our network architecture, we can compile the model using <strong>model.compile<\/strong><\/p>\n<p>For the loss function, I used <a href=\"https:\/\/gombru.github.io\/2018\/05\/23\/cross_entropy_loss\/\">categorical-cross-entropy<\/a> because this is a multi-class classification problem. Binary-cross-entropy would be the one in a binary classification problem and mean-squared-error for a regression.<\/p>\n<p>Regarding the optimizer, RMSprop is the recommended one in the Keras documentation, although I see many people using Adam. I actually tested with Adam and it seemed to converge quicker, but then the accuracy degraded on each epoch.<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\n\nmy_model.compile(loss = 'categorical_crossentropy',\nmetrics = &#x5B;'accuracy'],\noptimizer = k.optimizers.RMSprop(lr = 1e-4, decay = 1e-6) )\n\n<\/pre>\n<h3>Fitting<\/h3>\n<p>This is the step which actually generates the model. There are two functions which you can use to fit a model in Keras.<\/p>\n<ul>\n<li><strong>fit<\/strong> loads the whole dataset in memory<\/li>\n<li><strong>fit_generator<\/strong> feeds off a generator and <a href=\"https:\/\/medium.com\/difference-engine-ai\/keras-a-thing-you-should-know-about-keras-if-you-plan-to-train-a-deep-learning-model-on-a-large-fdd63ce66bd2\">it&#8217;s intended for large datasets<\/a><\/li>\n<\/ul>\n<p>I will use <strong>fit_generator<\/strong> in this example because i&#8217;m using an iterator (<strong>flow_from_directory<\/strong>) to load the images.<\/p>\n<p>The fit process runs a full cycle of training and testing multiple times. Each of this runs is called an epoch. After each epoch we can measure the accuracy and loss of the model. If we run a <a href=\"https:\/\/mattmazur.com\/2015\/03\/17\/a-step-by-step-backpropagation-example\/\">backward propagation algorithm<\/a>, we can minimize the error of the model using <a href=\"https:\/\/en.wikipedia.org\/wiki\/Gradient_descent\">gradient descent<\/a> on each successive epoch. The number of samples used for the gradient descent calculation is specified with the <strong>batch_size<\/strong> parameter. Keras automatically runs the back propagation algorithm for you and adjusts the weights. After each run, you can instruct Keras to run certain commands, like for example saving a model checkpoint with the current weights.<\/p>\n<blockquote><p>&#8220;For computational simplicity, not all training data is fed to the network at once. Rather, let&#8217;s say we have total 1600 images, we divide them in small batches say of size 16 or 32 called batch-size. Hence, it will take 100 or 50 rounds(iterations) for complete data to be used for training. This is called one epoch, i.e. in one epoch the networks sees all the training images once&#8221;<\/p><\/blockquote>\n<p>source: <a href=\"https:\/\/cv-tricks.com\/tensorflow-tutorial\/training-convolutional-neural-network-for-image-classification\/\">https:\/\/cv-tricks.com\/tensorflow-tutorial\/training-convolutional-neural-network-for-image-classification\/<\/a><\/p>\n<p>The fit_generator returns a history object with 4 measures: training loss, training accuracy, validation loss and validation accuracy. The training metrics are a comparison of the output of the model with the training images and the validation metrics are a comparison with the test or validation images (the ones not used for training).<\/p>\n<p>Since you want to know how good your model is generalizing, you should focus on the validation metrics. Each epoch constitutes a complete training and validation cycle and Keras provides an automated way of saving the best results of all the epochs with the <a href=\"https:\/\/keras.io\/callbacks\/#modelcheckpoint\"><strong>ModelCheckpoint<\/strong><\/a> callback.<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\n\nstart = dt.now()\nhistory = my_model.fit_generator(\n# training data\ntrain_images_iter,\n\n# epochs\nsteps_per_epoch = train_images_iter.n \/\/ BATCH_SIZE, #floor per batch size\nepochs = EPOCHS,\n\n# validation data\nvalidation_data = test_images_iter,\nvalidation_steps = test_images_iter.n \/\/ BATCH_SIZE,\n\n# print progress\nverbose = 1,\ncallbacks = &#x5B;\n#early stopping in case the loss stops decreasing\nk.callbacks.EarlyStopping(monitor='val_loss', patience=3),\n# only save the model if the monitored quantity (val_loss or val_acc) has improved\nk.callbacks.ModelCheckpoint(&quot;fruits_checkpoints.h5&quot;, monitor='val_loss', save_best_only = True),\n# only needed for visualising with TensorBoard\nk.callbacks.TensorBoard(log_dir = &quot;logs\/{:%d_%b_%Y_%H:%M:%S}&quot;.format(dt.now()) )\n]\n)\n<\/pre>\n<h3>Evaluate the model<\/h3>\n<p>I followed <a href=\"https:\/\/keras.io\/visualization\/\">the Keras docs on model visualization<\/a> to generate the accuracy and loss plots.<\/p>\n<p>You can also use <a href=\"https:\/\/www.tensorflow.org\/guide\/summaries_and_tensorboard\">TensorBoard<\/a>, which offers an easy way to compare multiple runs with different parameters.<\/p>\n<p>Plotting is very useful as it allows you to see whereas the model is undefitting or overfitting. Underfitting, or under-learning is pretty easy to spot: the training lines don&#8217;t converge as expected (to 0 loss or 100% accuracy). Overfitting is a little bit trickier. For such a clean dataset, any sudden changes in the validation line is a sign of overfitting. If the accuracy stops improving is also a sign of overfitting, this is when the early stopping feature might become handy.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"2323\" data-permalink=\"https:\/\/www.danielpradilla.info\/blog\/classifying-convolutional-neural-network-keras\/screenshot-2019-02-18-at-07-57-33\/\" data-orig-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-18-at-07.57.33.png\" data-orig-size=\"391,256\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Screenshot 2019-02-18 at 07.57.33\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-18-at-07.57.33-300x196.png\" data-large-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-18-at-07.57.33.png\" class=\"aligncenter size-full wp-image-2323\" src=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-18-at-07.57.33.png\" alt=\"\" width=\"391\" height=\"256\" srcset=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-18-at-07.57.33.png 391w, https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-18-at-07.57.33-300x196.png 300w\" sizes=\"auto, (max-width: 391px) 100vw, 391px\" \/> <img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"2325\" data-permalink=\"https:\/\/www.danielpradilla.info\/blog\/classifying-convolutional-neural-network-keras\/screenshot-2019-02-18-at-07-57-27\/\" data-orig-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-18-at-07.57.27.png\" data-orig-size=\"394,254\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Screenshot 2019-02-18 at 07.57.27\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-18-at-07.57.27-300x193.png\" data-large-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-18-at-07.57.27.png\" class=\"aligncenter size-full wp-image-2325\" src=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-18-at-07.57.27.png\" alt=\"\" width=\"394\" height=\"254\" srcset=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-18-at-07.57.27.png 394w, https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-18-at-07.57.27-300x193.png 300w\" sizes=\"auto, (max-width: 394px) 100vw, 394px\" \/><\/p>\n<p>In this case, I tried to smooth the lines for loss and accuracy by introducing and tweaking dropout and batch normalization.<\/p>\n<h3 id=\"prediction_title\">Predictions<\/h3>\n<p>Once we have our fitted model, we can feed an image to the predict method and get a list of probabilities of the image belong to each class. We can also use the predict_classes method to get the label of the most probable class.<\/p>\n<p>In order to feed the image to the predict method, you will need to load it and convert it to an array \u2013remembering to divide it by 255 to normalize the channels.<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\n\nloaded_image = k.preprocessing.image.load_img(path=PREDICTION_PATH+'\/'+filename, target_size=(IMG_WIDTH,IMG_HEIGHT,CHANNELS))\n#convert to array and resample dividing by 255\nimg_array = k.preprocessing.image.img_to_array(loaded_image) \/ 255.\n\n#add sample dimension. the predictor is expecting (1, CHANNELS, IMG_WIDTH, IMG_HEIGHT)\nimg_np_array = np.expand_dims(img_array, axis = 0)\n#img_class = my_model.predict_classes(img_np_array)\n\npredictions = my_model.predict(img_np_array)\n\n<\/pre>\n<p>I downloaded a bunch of images from the internet, taking care of finding some with the same orientation as the training and testing images. I wasn&#8217;t expecting this neural network to do magic. I put all the images in the same folder and ran the prediction code in a loop.<\/p>\n<p>Can we recognize an avocado? Yes!<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"2327\" data-permalink=\"https:\/\/www.danielpradilla.info\/blog\/classifying-convolutional-neural-network-keras\/screenshot-2019-02-13-at-09-03-41\/\" data-orig-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.03.41.png\" data-orig-size=\"270,270\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Screenshot 2019-02-13 at 09.03.41\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.03.41.png\" data-large-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.03.41.png\" class=\"aligncenter size-full wp-image-2327\" src=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.03.41.png\" alt=\"\" width=\"270\" height=\"270\" srcset=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.03.41.png 270w, https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.03.41-150x150.png 150w\" sizes=\"auto, (max-width: 270px) 100vw, 270px\" \/><\/p>\n<p>How about a pomegranate \u2013this one is tricky, because its round shape is shared with many fruits<img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"2341\" data-permalink=\"https:\/\/www.danielpradilla.info\/blog\/classifying-convolutional-neural-network-keras\/screenshot-2019-02-13-at-09-04-28\/\" data-orig-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.28.png\" data-orig-size=\"276,293\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Screenshot 2019-02-13 at 09.04.28\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.28.png\" data-large-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.28.png\" class=\"aligncenter size-large wp-image-2341\" src=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.28.png\" alt=\"\" width=\"276\" height=\"293\"><\/p>\n<p>A pear?<img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"2335\" data-permalink=\"https:\/\/www.danielpradilla.info\/blog\/classifying-convolutional-neural-network-keras\/screenshot-2019-02-13-at-09-04-10\/\" data-orig-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.10.png\" data-orig-size=\"273,289\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Screenshot 2019-02-13 at 09.04.10\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.10.png\" data-large-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.10.png\" class=\"aligncenter size-large wp-image-2335\" src=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.10.png\" alt=\"\" width=\"273\" height=\"289\"><\/p>\n<p>A kiwi?<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"2329\" data-permalink=\"https:\/\/www.danielpradilla.info\/blog\/classifying-convolutional-neural-network-keras\/screenshot-2019-02-13-at-09-03-48\/\" data-orig-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.03.48.png\" data-orig-size=\"274,294\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Screenshot 2019-02-13 at 09.03.48\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.03.48.png\" data-large-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.03.48.png\" class=\"aligncenter size-large wp-image-2329\" src=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.03.48.png\" alt=\"\" width=\"274\" height=\"294\"><\/p>\n<p>A sliced kiwi? Guess where the error comes from<img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"2331\" data-permalink=\"https:\/\/www.danielpradilla.info\/blog\/classifying-convolutional-neural-network-keras\/screenshot-2019-02-13-at-09-03-53\/\" data-orig-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.03.53.png\" data-orig-size=\"275,290\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Screenshot 2019-02-13 at 09.03.53\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.03.53.png\" data-large-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.03.53.png\" class=\"aligncenter size-large wp-image-2331\" src=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.03.53.png\" alt=\"\" width=\"275\" height=\"290\"><\/p>\n<p>A kiwi, the animal<img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"2333\" data-permalink=\"https:\/\/www.danielpradilla.info\/blog\/classifying-convolutional-neural-network-keras\/screenshot-2019-02-13-at-09-04-05\/\" data-orig-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.05.png\" data-orig-size=\"270,292\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Screenshot 2019-02-13 at 09.04.05\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.05.png\" data-large-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.05.png\" class=\"aligncenter size-large wp-image-2333\" src=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.05.png\" alt=\"\" width=\"270\" height=\"292\"><\/p>\n<p>A pineapple?<img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"2337\" data-permalink=\"https:\/\/www.danielpradilla.info\/blog\/classifying-convolutional-neural-network-keras\/screenshot-2019-02-13-at-09-04-17\/\" data-orig-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.17.png\" data-orig-size=\"271,272\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Screenshot 2019-02-13 at 09.04.17\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.17.png\" data-large-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.17.png\" class=\"aligncenter size-large wp-image-2337\" src=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.17.png\" alt=\"\" width=\"271\" height=\"272\" srcset=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.17.png 271w, https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.17-150x150.png 150w\" sizes=\"auto, (max-width: 271px) 100vw, 271px\" \/><\/p>\n<p>The network thinks that this other pineapple is a strawberry. This is because the training and testing images only have pineapples without leaves.<img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"2339\" data-permalink=\"https:\/\/www.danielpradilla.info\/blog\/classifying-convolutional-neural-network-keras\/screenshot-2019-02-13-at-09-04-22\/\" data-orig-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.22.png\" data-orig-size=\"269,291\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Screenshot 2019-02-13 at 09.04.22\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.22.png\" data-large-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.22.png\" class=\"aligncenter size-large wp-image-2339\" src=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.22.png\" alt=\"\" width=\"269\" height=\"291\"><\/p>\n<p>Speaking of strawberries:&nbsp;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"2351\" data-permalink=\"https:\/\/www.danielpradilla.info\/blog\/classifying-convolutional-neural-network-keras\/screenshot-2019-02-19-at-08-18-43\/\" data-orig-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-19-at-08.18.43.png\" data-orig-size=\"237,241\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Screenshot 2019-02-19 at 08.18.43\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-19-at-08.18.43.png\" data-large-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-19-at-08.18.43.png\" class=\"aligncenter size-full wp-image-2351\" src=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-19-at-08.18.43.png\" alt=\"\" width=\"237\" height=\"241\"><\/p>\n<p>How about a&nbsp;<em>drawing<\/em> of a strawberry?<img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"2345\" data-permalink=\"https:\/\/www.danielpradilla.info\/blog\/classifying-convolutional-neural-network-keras\/screenshot-2019-02-13-at-09-04-50\/\" data-orig-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.50.png\" data-orig-size=\"274,289\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Screenshot 2019-02-13 at 09.04.50\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.50.png\" data-large-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.50.png\" class=\"aligncenter size-large wp-image-2345\" src=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.50.png\" alt=\"\" width=\"274\" height=\"289\"><\/p>\n<p>Is Strawberry Shortcake a strawberry? You bet!<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"2343\" data-permalink=\"https:\/\/www.danielpradilla.info\/blog\/classifying-convolutional-neural-network-keras\/screenshot-2019-02-13-at-09-04-45\/\" data-orig-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.45.png\" data-orig-size=\"273,291\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Screenshot 2019-02-13 at 09.04.45\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.45.png\" data-large-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.45.png\" class=\"aligncenter size-large wp-image-2343\" src=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.04.45.png\" alt=\"\" width=\"273\" height=\"291\"><\/p>\n<p>Ok that last one is maybe a fluke. Or is it? The features of a strawberry are definitely there.<\/p>\n<h4>Gratuitous &#8220;What does the network see?&#8221; images<\/h4>\n<p>These were made with the <a href=\"https:\/\/raghakot.github.io\/keras-vis\/\">Keras Visualization Toolkit<\/a><\/p>\n<p>Saliency<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"2355\" data-permalink=\"https:\/\/www.danielpradilla.info\/blog\/classifying-convolutional-neural-network-keras\/screenshot-2019-02-13-at-09-03-13\/\" data-orig-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.03.13.png\" data-orig-size=\"372,516\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Screenshot 2019-02-13 at 09.03.13\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.03.13-216x300.png\" data-large-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.03.13.png\" class=\"aligncenter size-full wp-image-2355\" src=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.03.13.png\" alt=\"\" width=\"372\" height=\"516\" srcset=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.03.13.png 372w, https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-13-at-09.03.13-216x300.png 216w\" sizes=\"auto, (max-width: 372px) 100vw, 372px\" \/><\/p>\n<p>Activation maps<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"2357\" data-permalink=\"https:\/\/www.danielpradilla.info\/blog\/classifying-convolutional-neural-network-keras\/screenshot-2019-02-19-at-08-36-41\/\" data-orig-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-19-at-08.36.41.png\" data-orig-size=\"443,389\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Screenshot 2019-02-19 at 08.36.41\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-19-at-08.36.41-300x263.png\" data-large-file=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-19-at-08.36.41.png\" class=\"aligncenter size-full wp-image-2357\" src=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-19-at-08.36.41.png\" alt=\"\" width=\"443\" height=\"389\" srcset=\"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-19-at-08.36.41.png 443w, https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/Screenshot-2019-02-19-at-08.36.41-300x263.png 300w\" sizes=\"auto, (max-width: 443px) 100vw, 443px\" \/><\/p>\n<h3>Some additional things I learned<\/h3>\n<p>Reading the tutorials one would think that the architecture of the network needs to be though out in advance. I discovered that there might be a lot of tweaking and back-and-forth involved. This also implies <strong>a lot<\/strong> of waiting for the training steps. These were \u2013approximately\u2013 all the iterations of my network<\/p>\n<pre>Conv(16) -&gt; Pooling -&gt; Flatten -&gt; Dense (100) -&gt; Dense (17)\n\nConv(32) -&gt; Pooling -&gt; Flatten -&gt; Dense (100) -&gt; Dense (17)\n\nConv(32) -&gt; SpatialDropout-&gt; Conv(64) -&gt; Pooling -&gt; Flatten -&gt; Dense (100) -&gt; Dropout(0.5) -&gt; Dense (17)\n\nConv(32) -&gt; SpatialDropout-&gt; Conv(64) -&gt; BatchNormalization-&gt; Pooling -&gt; Flatten -&gt; Dense (100) -&gt; Dropout(0.5) -&gt; Dense (17)\n\nConv(32) -&gt; BatchNormalization -&gt; SpatialDropout-&gt; Conv(64) -&gt; BatchNormalization-&gt; Pooling -&gt; Flatten -&gt; Dense (300) -&gt; Dropout(0.5) -&gt; Dense (17)\n\nConv(64) -&gt; BatchNormalization -&gt; SpatialDropout-&gt; Conv(128) -&gt; BatchNormalization-&gt; Pooling -&gt; Flatten -&gt; Dense (250) -&gt; Dropout(0.5) -&gt; Dense (17)\n<\/pre>\n<p>I also found out that tensorflow-gpu <a href=\"https:\/\/www.tensorflow.org\/install\/gpu\">currently only runs on CUDA-enabled NVIDIA GPUs<\/a>, so no luck with my current Macbook \u2013yes, maybe you can hack something with OpenCL, but if you are going down that route, just switch the underlying Keras library from Tensorflow to Theano.&nbsp;<\/p>\n<p>One of my mistakes was that I was applying too much dropout, effectively making my model underfit. I was applying too many corrective measures to a problem I didn&#8217;t have, yet. I should&#8217;ve started the other way around: overfitting the model, then take measures against overfitting.<\/p>\n<p>I&#8217;ve seen a lot of <a href=\"https:\/\/jhui.github.io\/2017\/03\/12\/TensorBoard-visualize-your-learning\/\">beautiful TensorBoard visualizations<\/a>, but if you build your models with Keras and you don&#8217;t explicitly create scopes, expect a very messy network graph.<\/p>\n<p>Speaking about being messy, it is helpful to have some ideas on how to structure code and &#8220;proper&#8221; software engineering. This applies in almost any situation in Data Science, but specially in cases like this in which you perform a lot of recurring experiments.<\/p>\n<p>I was confused when I trained my first model and saw that the testing accuracy was higher than the training accuracy \u2013and the training loss much higher than the testing loss. Intuition points to the contrary. How is that possible?<\/p>\n<p>From the Kears FAQ:<\/p>\n<blockquote><p>&#8220;A Keras model has two modes: training and testing. Regularization mechanisms, such as Dropout and L1\/L2 weight regularization, are turned off at testing time.<br \/>\nBesides, the training loss is the average of the losses over each batch of training data. Because your model is changing over time, the loss over the first batches of an epoch is generally higher than over the last batches. On the other hand, the testing loss for an epoch is computed using the model as it is at the end of the epoch, resulting in a lower loss.&#8221;<\/p><\/blockquote>\n<p>Meaning: that behavior is normal.<\/p>\n<p>You can get the complete code for this post at <a href=\"https:\/\/github.com\/danielpradilla\/python-keras-fruits\">https:\/\/github.com\/danielpradilla\/python-keras-fruits<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I followed a tutorial on Convolutional Neural Networks that left many questions unanswered. Soon I realized that the actual process of architecting a Neural Network and setting the parameters seemed to be much more experimental than I thought. It took a while to find explanations that a rookie like me could understand. Most of the&hellip; <a class=\"more-link\" href=\"https:\/\/www.danielpradilla.info\/blog\/classifying-convolutional-neural-network-keras\/\">Continue reading <span class=\"screen-reader-text\">Classifying fruits with a Convolutional Neural Network in Keras<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":2360,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[174,331],"tags":[360,361],"class_list":["post-2306","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-bestof","category-software-development-en-en","tag-machine-learning","tag-neural-networks","entry"],"aioseo_notices":[],"jetpack_featured_media_url":"https:\/\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2019\/02\/william-felker-38344-unsplash.jpg","jetpack_shortlink":"https:\/\/wp.me\/p1tlzy-Bc","jetpack_sharing_enabled":true,"jetpack-related-posts":[{"id":2226,"url":"https:\/\/www.danielpradilla.info\/blog\/10-things-i-learned-while-setting-up-five-masternodes\/","url_meta":{"origin":2306,"position":0},"title":"10 things I learned while setting up five masternodes","author":"Daniel Pradilla","date":"08\/05\/2018","format":false,"excerpt":"Over the past few weeks, I've been experimenting with masternodes as alternatives\/replacements to traditional crypto mining rigs. Like with many other crypto-related things, I was surprised to find such a huge community and wealth of options. It's akin to opening a window into another world. What interests me the most\u2026","rel":"","context":"In &quot;Lifestyle&quot;","block_context":{"text":"Lifestyle","link":"https:\/\/www.danielpradilla.info\/blog\/category\/lifestyle\/"},"img":{"alt_text":"Photo by Denys Nevozhai on Unsplash","src":"https:\/\/i0.wp.com\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2018\/05\/denys-nevozhai-154974-unsplash.jpg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2018\/05\/denys-nevozhai-154974-unsplash.jpg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2018\/05\/denys-nevozhai-154974-unsplash.jpg?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2018\/05\/denys-nevozhai-154974-unsplash.jpg?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":1937,"url":"https:\/\/www.danielpradilla.info\/blog\/5-essential-steps-for-promoting-your-business-in-social-networks\/","url_meta":{"origin":2306,"position":1},"title":"5 essential steps for promoting your business in social networks","author":"Daniel Pradilla","date":"02\/03\/2013","format":false,"excerpt":"Social networks have forever changed the way we market ourselves and make money. When 100% of your leads have accounts in more than one social network, you stop wondering if your company should use social networks and start thinking how to profit from using them. So, whether you think that\u2026","rel":"","context":"In &quot;Social Media&quot;","block_context":{"text":"Social Media","link":"https:\/\/www.danielpradilla.info\/blog\/category\/socialmedia\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2011\/05\/the-social-network.png?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":1831,"url":"https:\/\/www.danielpradilla.info\/blog\/linkedin-for-professionals\/","url_meta":{"origin":2306,"position":2},"title":"Linkedin for professionals","author":"Daniel Pradilla","date":"11\/12\/2012","format":false,"excerpt":"Just as Facebook has become our default social lubricant, LinkedIn is the global standard to connect with other professionals. Almost all of my friends who have changed jobs in the past year, have been hired for their current job via LinkedIn. Nevertheless, they all have an embarrassing LinkedIn profile. Yes,\u2026","rel":"","context":"In &quot;Best of&quot;","block_context":{"text":"Best of","link":"https:\/\/www.danielpradilla.info\/blog\/category\/bestof\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2010\/10\/linkedin-logo1.jpeg?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":1909,"url":"https:\/\/www.danielpradilla.info\/blog\/when-was-the-last-time-you-did-a-backup\/","url_meta":{"origin":2306,"position":3},"title":"When was the last time you did a backup?","author":"Daniel Pradilla","date":"21\/01\/2013","format":false,"excerpt":"Thou shalt back up frequently is one of my commandments. According to a recent study, only 28% of people perform a daily backup. And knowing my friends, that seems encouraging! There're only two kinds of hard drives: the ones that have failed and the ones that are going to fail.\u2026","rel":"","context":"In &quot;Lifestyle&quot;","block_context":{"text":"Lifestyle","link":"https:\/\/www.danielpradilla.info\/blog\/category\/lifestyle\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2012\/04\/Hindenburg.jpeg?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":1830,"url":"https:\/\/www.danielpradilla.info\/blog\/3-linkedin-profiles-that-work\/","url_meta":{"origin":2306,"position":4},"title":"3 LinkedIn profiles that work while you sleep","author":"Daniel Pradilla","date":"11\/12\/2012","format":false,"excerpt":"A few weeks ago I wrote about the importance of having a good LinkedIn profile and how you could revamp yours. Days later, I searched among my friends and found three profiles that stand out above the average. So much so, that they motivated me to write a follow-up article\u2026","rel":"","context":"In &quot;Social Media&quot;","block_context":{"text":"Social Media","link":"https:\/\/www.danielpradilla.info\/blog\/category\/socialmedia\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2010\/10\/linkedin-logo-300x199.jpg?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":1558,"url":"https:\/\/www.danielpradilla.info\/blog\/3-perfiles-de-linkedin-que-trabajan-mientras-los-demas-duermen\/","url_meta":{"origin":2306,"position":5},"title":"3 perfiles de LinkedIn que trabajan mientras los dem\u00c3\u00a1s duermen","author":"Daniel Pradilla","date":"30\/11\/2010","format":false,"excerpt":"Hace unas semanas escrib\u00c3\u00ad sobre la importancia de tener un buen perfil de LinkedIn y c\u00c3\u00b3mo pod\u00c3\u00adas remozar el tuyo. D\u00c3\u00adas m\u00c3\u00a1s tarde, busqu\u00c3\u00a9 entre mis amistades y encontr\u00c3\u00a9 tres perfiles que destacan por encima del promedio. Tanto as\u00c3\u00ad, que me motivaron a hacer un art\u00c3\u00adculo de seguimiento y compartir\u2026","rel":"","context":"In &quot;Redes Sociales&quot;","block_context":{"text":"Redes Sociales","link":"https:\/\/www.danielpradilla.info\/blog\/category\/redessociales\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.danielpradilla.info\/blog\/wp-content\/uploads\/2010\/10\/linkedin-logo-300x199.jpg?resize=350%2C200","width":350,"height":200},"classes":[]}],"_links":{"self":[{"href":"https:\/\/www.danielpradilla.info\/blog\/wp-json\/wp\/v2\/posts\/2306","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.danielpradilla.info\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.danielpradilla.info\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.danielpradilla.info\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.danielpradilla.info\/blog\/wp-json\/wp\/v2\/comments?post=2306"}],"version-history":[{"count":0,"href":"https:\/\/www.danielpradilla.info\/blog\/wp-json\/wp\/v2\/posts\/2306\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.danielpradilla.info\/blog\/wp-json\/wp\/v2\/media\/2360"}],"wp:attachment":[{"href":"https:\/\/www.danielpradilla.info\/blog\/wp-json\/wp\/v2\/media?parent=2306"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.danielpradilla.info\/blog\/wp-json\/wp\/v2\/categories?post=2306"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.danielpradilla.info\/blog\/wp-json\/wp\/v2\/tags?post=2306"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}