<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ image classification - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ image classification - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Mon, 27 Jul 2026 20:16:09 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/image-classification/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Perform Classification with Automated Machine Learning (AutoML) ]]>
                </title>
                <description>
                    <![CDATA[ In this article I will show you how to use Automated Machine Learning (AutoML) to build a classifier for tabular data. And don't worry – I will explain all strange definitions :) There won't be any math in this article (although I like math, it is co... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/classification-with-python-automl/</link>
                <guid isPermaLink="false">66d46089246e57ac83a2c7b7</guid>
                
                    <category>
                        <![CDATA[ automation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ image classification ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Machine Learning ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Piotr Plonski ]]>
                </dc:creator>
                <pubDate>Mon, 11 May 2020 19:15:12 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/05/Untitled-Design--1-.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this article I will show you how to use Automated Machine Learning (AutoML) to build a classifier for tabular data. And don't worry – I will explain all strange definitions :)</p>
<p>There won't be any math in this article (although I like math, it is concise). I will try to show things in such way that you can better understand Machine Learning (and AutoML).</p>
<h2 id="heading-first-things-first-what-is-machine-learning">First things first: what is Machine Learning?</h2>
<p><strong>Machine Learning (ML)</strong> is a very broad topic. We can use its definition to explain what it is: teaching a machine to do a task. This is very similar to programming!</p>
<p>The key difference is that in programming, you need to provide an exact recipe (code) that tells the machine how it should perform. In <strong>Machine Learning</strong> you also provide the code, but that code will tell the machine how to learn based on previous examples (historical data).</p>
<p>This code is then used to create a <strong>Machine Learning model</strong>. All future actions done by the machine will be computed by the model.</p>
<p>This is a very loose definition, but you should get a basic understanding about ML from it. I've prepared some schematic pictures showing how programming vs Machine Learning works. I hope they will help you to visualize the difference.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/programming.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>In programming, humans need to provide exact steps (code) to tell a machine how it should process input data.</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/ml.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>In Machine Learning, humans need to provide code and historical data for creating Machine Learning Models. After ML Model training, it can be used for computing outputs on unseen data.</em></p>
<p>In the above pictures you can see that programming is often much simpler than Machine Learning (smaller number of total steps, and no need for historical data).</p>
<p>And it often feels like programming is much easier than ML. But there are situations where providing the exact program is impossible.</p>
<p>For example: image classification tasks - say you would like to know what is in the image based on its content. It is impossible to write down all conditions to recognize what is in a picture (pictures can have different size, scales, and so on ...). It is easy to see with the human eye, but writing an exact program is impossible.</p>
<p>But with ML you can create a model that will be able to recognize images. So let's look at some more definitions.</p>
<h3 id="heading-classification">Classification</h3>
<p>Classification is the process of assigning a label (class) to a sample (one instance of data). The ML model that is doing a classification is called a <strong>classifier</strong>.</p>
<h3 id="heading-tabular-data">Tabular data</h3>
<p>Tabular data is simply data in table format, similar to a spreadsheet. Other data formats can be images, video, text, documents, or audio. Data in tabular format has rows which represent samples (observations) and columns which represent features.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/tabular-data_01.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Example of tabular data (Titanic dataset).</em></p>
<p>In this article we will analyze only tabular data. The typical task in ML is to predict one of the columns. Such a column is called the <strong>target</strong> column.</p>
<h2 id="heading-the-iris-data-set">The Iris data set</h2>
<p>I will show you how to build a Machine Leaning model with AutoML on very simple data set called <strong>Iris</strong>. The data can be downloaded from many places (it is the same data!):</p>
<ul>
<li><p>UCI data repository: <a target="_blank" href="https://archive.ics.uci.edu/ml/datasets/Iris">https://archive.ics.uci.edu/ml/datasets/Iris</a></p>
</li>
<li><p>my collection of good data sets for start with ML: <a target="_blank" href="https://github.com/pplonski/datasets-for-start/blob/master/iris/data.csv">https://github.com/pplonski/datasets-for-start/blob/master/iris/data.csv</a></p>
</li>
<li><p>Kaggle: <a target="_blank" href="https://www.kaggle.com/uciml/iris">https://www.kaggle.com/uciml/iris</a></p>
</li>
</ul>
<p>The <strong>Iris</strong> data set contains 150 rows, where each row describes a flower. Each row has 4 features (columns) which describe properties of the flower. These features are:</p>
<ul>
<li><p>sepal length (cm)</p>
</li>
<li><p>sepal width (cm)</p>
</li>
<li><p>petal length (cm)</p>
</li>
<li><p>petal width (cm)</p>
</li>
</ul>
<p>A label (class) is assigned to each flower which tells us what type of iris it is. In this data set there are 3 classes:</p>
<ul>
<li><p>setosa</p>
</li>
<li><p>versicolor</p>
</li>
<li><p>virginica</p>
</li>
</ul>
<p>Let's take the first row of the data. We have:</p>
<ul>
<li><p>sepal length = 5.1 cm</p>
</li>
<li><p>sepal width = 3.5 cm</p>
</li>
<li><p>petal length = 1.4 cm</p>
</li>
<li><p>petal width = 0.2 cm</p>
</li>
<li><p>class = setosa</p>
</li>
</ul>
<p>The first row tells us that someone took the iris type 'setosa', measured its sepal and petal properties, and saved it to the dataset.</p>
<p>Where is the Machine Learning here? Let's assume that we have a set of iris flowers but we don't know what types (classes) they are. We know how to measure the sepal and petal length and width but we can't say what type or class of iris is it.</p>
<p>We can use Machine Learning to <strong>classify</strong> the flower based on our measures. The ML model will take as input the 4 numbers (our measures) and will output the class of the flower.</p>
<h2 id="heading-lets-code">Let's code!</h2>
<p>I will use python in this tutorial. So I assume that you have python installed and know how to install packages.</p>
<p>We will need a few packages, and all of them will be installed with the AutoML package <a target="_blank" href="https://github.com/mljar/mljar-supervised">mljar-supervised</a>. To install it run:</p>
<pre><code class="lang-python">pip install mljar-supervised
</code></pre>
<p>All the code presented in this article is available on [github](https://www.freecodecamp.org/news/p/49d67cd9-1642-43c6-902d-edcfd56ab013/(https://github.com/mljar/mljar-examples/blob/master/Iris_classification/Iris_classification.ipynb). At the beginning, let's import the packages we need:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> pandas <span class="hljs-keyword">as</span> pd
<span class="hljs-keyword">import</span> numpy <span class="hljs-keyword">as</span> np
<span class="hljs-keyword">from</span> sklearn <span class="hljs-keyword">import</span> datasets
<span class="hljs-keyword">from</span> sklearn.model_selection <span class="hljs-keyword">import</span> train_test_split
<span class="hljs-keyword">from</span> supervised.automl <span class="hljs-keyword">import</span> AutoML
</code></pre>
<p>Then load the data:</p>
<pre><code class="lang-python">data = datasets.load_iris()
X = pd.DataFrame(data[<span class="hljs-string">"data"</span>], columns=data[<span class="hljs-string">"feature_names"</span>])
y = pd.Series(data[<span class="hljs-string">"target"</span>], name=<span class="hljs-string">"target"</span>).map({i:v <span class="hljs-keyword">for</span> i, v <span class="hljs-keyword">in</span> enumerate(data[<span class="hljs-string">"target_names"</span>])})
</code></pre>
<p>This is how our data looks like:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/image-75.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>The</em> <code>X</code> variable ( <code>print(X)</code> )</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/image-76.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>The</em> <code>y</code>variable ( <code>print(y)</code> )</p>
<p>We will split our data into two separate sets:</p>
<ul>
<li><p><strong>train</strong> - samples which will be used to train the Machine Learning model</p>
</li>
<li><p><strong>test</strong> - samples which we will use to check how our Machine Learning model is working on unseen (in the training process) data</p>
</li>
</ul>
<pre><code class="lang-python">X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=<span class="hljs-number">0.1</span>)
</code></pre>
<p>We will use 90% of our data for training (90%*150=135 samples) and 10% (15 samples) for testing.</p>
<p>Now that we have our data ready we can train the Machine Learning model. Maybe you've heard that there are many ML algorithms. All of them can be used for model training, such as the following models:</p>
<ul>
<li><p>Decision Tree,</p>
</li>
<li><p>Logistic Regression,</p>
</li>
<li><p>Random Forest,</p>
</li>
<li><p>Neural Networks,</p>
</li>
<li><p>Xgboost,</p>
</li>
</ul>
<p>just to name the few.</p>
<h3 id="heading-which-model-we-should-use-which-one-is-the-best">Which model we should use? Which one is the best?</h3>
<p>There is no single answer to the above questions. It all depends on the data itself. The common approach is to check as many as you can and select the best performing model. Very often the simplest algorithms are very good to start.</p>
<p>But this is not the end of our problems. Each of the algorithms usually has parameters which control the way the model is trained. They are so-called <strong>hyper-parameters</strong>. They should be carefully set for the algorithm. To select their values we also need to check a few of them.</p>
<p>For selecting algorithm and hyper-parameters we can use a validation which can be performed in many different ways. I won't go into the details of validation. I will just show you the tool which can handle all of the above problems. It is <strong>Automated Machine Learning (AutoML)</strong>.</p>
<p>AutoML can check many different ML algorithms and tune hyper-parameters for them. It will search for the best ML model for available data.</p>
<p>In real-life, AutoML is used to do even more, like feature engineering (preparing features for analysis and constructing new ones) or deploying models as REST APIs.</p>
<p>I'm using <code>AutoML</code> from the <code>mljar-supervised</code> package (of which I'm the author). It has a very simple interface. Let's train the model:</p>
<pre><code class="lang-python">automl = AutoML(algorithms=[<span class="hljs-string">"Decision Tree"</span>, <span class="hljs-string">"Linear"</span>, <span class="hljs-string">"Random Forest"</span>],
                total_time_limit=<span class="hljs-number">5</span>*<span class="hljs-number">60</span>)
automl.fit(X_train, y_train)
</code></pre>
<p>The above two lines will check 3 different algorithms for us: Decision Tree, Logistic Regression and Random Forest. Then it'll select the best one. There is a time limit set to 5 minutes (5*60 seconds) for total training time.</p>
<p>As a result of running <code>AutoML</code> you will get output like this:</p>
<pre><code class="lang-python">Create directory AutoML_1
AutoML task to be solved: multiclass_classification
AutoML will use algorithms: [<span class="hljs-string">'Decision Tree'</span>, <span class="hljs-string">'Linear'</span>, <span class="hljs-string">'Random Forest'</span>]
AutoML will optimize <span class="hljs-keyword">for</span> metric: logloss
AutoML will <span class="hljs-keyword">try</span> to check about <span class="hljs-number">33</span> models
Decision Tree final logloss <span class="hljs-number">0.5453226492448378</span> time <span class="hljs-number">30.04</span> seconds
Decision Tree final logloss <span class="hljs-number">0.6419811899692177</span> time <span class="hljs-number">21.25</span> seconds
Decision Tree final logloss <span class="hljs-number">0.4569697687554296</span> time <span class="hljs-number">16.73</span> seconds
Linear final logloss <span class="hljs-number">0.16507067466592637</span> time <span class="hljs-number">15.68</span> seconds
Random Forest final logloss <span class="hljs-number">0.11891177026579884</span> time <span class="hljs-number">28.72</span> seconds
Random Forest final logloss <span class="hljs-number">0.24256194594421207</span> time <span class="hljs-number">28.73</span> seconds
Random Forest final logloss <span class="hljs-number">0.2761028104749779</span> time <span class="hljs-number">27.61</span> seconds
Random Forest final logloss <span class="hljs-number">0.2536702528991272</span> time <span class="hljs-number">29.0</span> seconds
Random Forest final logloss <span class="hljs-number">0.1752405529204018</span> time <span class="hljs-number">27.86</span> seconds
Random Forest final logloss <span class="hljs-number">0.17394416017742964</span> time <span class="hljs-number">27.69</span> seconds
Ensemble final logloss <span class="hljs-number">0.11781603875353275</span> time <span class="hljs-number">0.36</span> seconds
</code></pre>
<p>The results of running this AutoML experiment are available on <a target="_blank" href="https://github.com/mljar/mljar-examples/tree/master/Iris_classification/AutoML_1#automl-leaderboard">github</a>. When you look into the directory created by <code>AutoML</code> you will see the <code>README.md</code> file. It contains the report from the training:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/image-77.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>What is more, you can check each trained model by clicking on its link:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/image-78.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>To compute predictions, just run the following lines:</p>
<pre><code class="lang-python">y_predicted = automl.predict(X_test)

print(pd.DataFrame({<span class="hljs-string">"Predicted"</span>: y_predicted[<span class="hljs-string">"label"</span>], <span class="hljs-string">"Target"</span>: np.array(y_test)}))
</code></pre>
<p>You will get the following:</p>
<pre><code class="lang-python">     Predicted      Target
<span class="hljs-number">0</span>       setosa      setosa
<span class="hljs-number">1</span>    virginica  versicolor
<span class="hljs-number">2</span>   versicolor  versicolor
<span class="hljs-number">3</span>    virginica   virginica
<span class="hljs-number">4</span>   versicolor  versicolor
<span class="hljs-number">5</span>       setosa      setosa
<span class="hljs-number">6</span>       setosa      setosa
<span class="hljs-number">7</span>   versicolor  versicolor
<span class="hljs-number">8</span>       setosa      setosa
<span class="hljs-number">9</span>   versicolor  versicolor
<span class="hljs-number">10</span>   virginica   virginica
<span class="hljs-number">11</span>  versicolor  versicolor
<span class="hljs-number">12</span>   virginica   virginica
<span class="hljs-number">13</span>   virginica   virginica
<span class="hljs-number">14</span>  versicolor  versicolor
</code></pre>
<p>From the above you can see that there was 1 mistake in the predictions (the row with index 1). The ML model predicted class <code>virginica</code> but it should be <code>versicolor</code>. The accuracy of the ML model is:</p>
<pre><code class="lang-python">Accuracy = <span class="hljs-number">14</span> (correct answers) / <span class="hljs-number">15</span> (total samples) = <span class="hljs-number">93.33</span>%
</code></pre>
<h2 id="heading-summary">Summary</h2>
<p>In this article I showed you the differences between programming and Machine Learning. I hope you understand it a bit better.</p>
<p>Machine Learning is a very broad topic and for sure can't be presented in one article. Learning and applying ML can give you a lot of satisfaction, though, so I encourage everyone to explore further.</p>
<p>Automated Machine Learning improves the process of model training by automating algorithm and hyper-parameters search. I hope that AutoML will make ML more accessible to many developers out there.</p>
<p>If you have any questions or would like to read more articles like this please let me know.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to classify butterflies with deep learning in Keras ]]>
                </title>
                <description>
                    <![CDATA[ By Bert Carremans A while ago I read an interesting blog post on the website of the Dutch organization Vlinderstichting. Every year they organize a count of butterflies. Volunteers help in determining the different butterfly species in their garden. ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/classify-butterfly-images-deep-learning-keras/</link>
                <guid isPermaLink="false">66d45dd5a326133d124409d7</guid>
                
                    <category>
                        <![CDATA[ Data Science ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Deep Learning ]]>
                    </category>
                
                    <category>
                        <![CDATA[ image classification ]]>
                    </category>
                
                    <category>
                        <![CDATA[ keras ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 08 Aug 2019 18:59:32 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/08/1_K4agkAxY1R6zPzK8s_CqbQ-1.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Bert Carremans</p>
<p>A while ago I read an interesting blog post on the website of the Dutch organization <a target="_blank" href="https://www.vlinderstichting.nl/actueel/nieuws/nieuwsbericht/?bericht=1492">Vlinderstichting</a>. Every year they organize a count of butterflies. Volunteers help in determining the different butterfly species in their garden. The Vlinderstichting gathers and analyses the results.</p>
<p>As the determination of the butterfly species is done by the volunteers, inevitably this process is prone to errors. As a result, the Vlinderstichting has to manually check  the submissions, which is time-consuming.</p>
<p>Specifically, there are three butterflies for which the Vlinderstichting receives many wrong determinations. These are</p>
<ul>
<li><a target="_blank" href="https://en.wikipedia.org/wiki/Meadow_brown">Meadow brown</a> or Maniola jurtina</li>
<li><a target="_blank" href="https://en.wikipedia.org/wiki/Gatekeeper_(butterfly)">Gatekeeper</a> or Pyronia tithonus</li>
<li><a target="_blank" href="https://en.wikipedia.org/wiki/Small_heath_(butterfly)">Small heath</a> or Coenonympha pamphilus</li>
</ul>
<p>In this article, I will describe the steps to fit a deep learning model that helps to make the distinction between the first two butterflies.</p>
<h1 id="heading-downloading-images-with-the-flickr-api">Downloading images with the Flickr API</h1>
<p>To train a convolutional neural network I need to find images of butterflies with the correct label. Surely I could take pictures myself of the butterflies that I want to classify. They sometimes fly around in my garden…</p>
<p>Just kidding, that would take ages. For this, I need an automated way to get the images. To do that I use the Flickr API via Python.</p>
<h2 id="heading-setting-up-the-flickr-api">Setting up the Flickr API</h2>
<p>Firstly, I install the <a target="_blank" href="https://pypi.python.org/pypi/flickrapi/2.3">flickrapi package</a> with pip. Then I create the necessary <a target="_blank" href="https://www.flickr.com/services/api/misc.api_keys.html">API keys on the Flickr website</a> to connect to the Flickr API.</p>
<p>Besides the flickrapi package, I import the os and urllib packages for downloading the images and setting up the directories.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flickrapi <span class="hljs-keyword">import</span> FlickrAPI
<span class="hljs-keyword">import</span> urllib
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> config
</code></pre>
<p>In the config module, I define the public and secret keys for the Flickr API. So this is simply a Python script (config.py) with the code below:</p>
<pre><code class="lang-python">API_KEY = <span class="hljs-string">'XXXXXXXXXXXXXXXXX'</span>  // replace <span class="hljs-keyword">with</span> your key
API_SECRET = <span class="hljs-string">'XXXXXXXXXXXXXXXXX'</span>  // replace <span class="hljs-keyword">with</span> your secret
IMG_FOLDER = <span class="hljs-string">'XXXXXXXXXXXXXXXXX'</span>  // replace <span class="hljs-keyword">with</span> your folder to store the images
</code></pre>
<p>I keep these keys in a separate file for security reasons. As a result, you can save the code in a public repository like GitHub or BitBucket and putting the config.py in .gitignore. Consequently, you can share your code with others while not having to worry about someone having access to your credentials.</p>
<p>To extract images of different butterfly species, I wrote a function download_flickr_photos. I will explain this function step by step. In addition, I’ve made the full code available on <a target="_blank" href="https://github.com/bertcarremans/Vlindervinder/tree/master/flickr">GitHub</a>.</p>
<h2 id="heading-input-parameters">Input parameters</h2>
<p>First of all, I check if the input parameters are of the correct type or values. If not, I raise an error. The explanation of the parameters can be found in the docstring of the function.</p>
<pre><code class="lang-python"><span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> (isinstance(keywords, str) <span class="hljs-keyword">or</span> isinstance(keywords, list)):
    <span class="hljs-keyword">raise</span> AttributeError(<span class="hljs-string">'keywords must be a string or a list of strings'</span>)
<span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> (size <span class="hljs-keyword">in</span> [<span class="hljs-string">'thumbnail'</span>, <span class="hljs-string">'square'</span>, <span class="hljs-string">'medium'</span>, <span class="hljs-string">'original'</span>]):
    <span class="hljs-keyword">raise</span> AttributeError(<span class="hljs-string">'size must be "thumbnail", "square", "medium" or "original"'</span>)
<span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> (max_nb_img == <span class="hljs-number">-1</span> <span class="hljs-keyword">or</span> (max_nb_img &gt; <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> isinstance(max_nb_img, int))):
    <span class="hljs-keyword">raise</span> AttributeError(<span class="hljs-string">'max_nb_img must be an integer greater than zero or equal to -1'</span>)
</code></pre>
<p>Secondly, I define some of the parameters that will be used in the walk method later on. I create a list for the keywords and determine from which URL the images need to be downloaded.</p>
<pre><code class="lang-python"><span class="hljs-keyword">if</span> isinstance(keywords, str):
    keywords_list = []
    keywords_list.append(keywords)
<span class="hljs-keyword">else</span>:
    keywords_list = keywords
<span class="hljs-keyword">if</span> size == <span class="hljs-string">'thumbnail'</span>:
    size_url = <span class="hljs-string">'url_t'</span>
<span class="hljs-keyword">elif</span> size == <span class="hljs-string">'square'</span>:
    size_url = <span class="hljs-string">'url_q'</span>
<span class="hljs-keyword">elif</span> size == <span class="hljs-string">'medium'</span>:
    size_url = <span class="hljs-string">'url_c'</span>
<span class="hljs-keyword">elif</span> size == <span class="hljs-string">'original'</span>:
    size_url = <span class="hljs-string">'url_o'</span>
</code></pre>
<h2 id="heading-connecting-to-the-flickr-api">Connecting to the Flickr API</h2>
<p>Next, I connect to the Flickr API. In the FlickrAPI call I use the API keys defined in the config module.</p>
<pre><code class="lang-python">flickr = FlickrAPI(config.API_KEY, config.API_SECRET)
</code></pre>
<h2 id="heading-creating-subfolders-per-butterfly-species">Creating subfolders per butterfly species</h2>
<p>I save the images of each butterfly species in a separate subfolder. The name of each subfolder is the butterfly species’ name, given by the keyword. If the subfolder does not exist yet, I create it.</p>
<pre><code class="lang-python">results_folder = config.IMG_FOLDER + keyword.replace(<span class="hljs-string">" "</span>, <span class="hljs-string">"_"</span>) + <span class="hljs-string">"/"</span>
<span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> os.path.exists(results_folder):
    os.makedirs(results_folder)
</code></pre>
<h2 id="heading-walking-around-in-the-flickr-library">Walking around in the Flickr library</h2>
<pre><code class="lang-python">photos = flickr.walk(
    text=keyword,
    extras=<span class="hljs-string">'url_m'</span>,
    license=<span class="hljs-string">'1,2,4,5'</span>,
    per_page=<span class="hljs-number">50</span>)
</code></pre>
<p>I use the walk method of the Flickr API to search for images for the specified keyword. This walk method has the same parameters as the <a target="_blank" href="http://www.flickr.com/services/api/flickr.photos.search.html">search method</a> in the Flickr API.</p>
<p>In the text parameter<strong><em>,</em></strong> I use the keyword to search for images related to this keyword. Secondly, in the extras parameter<strong><em>,</em></strong> I specify url_m for a small, medium size of the images. More explanation on the image sizes and their respective URL is given in this <a target="_blank" href="http://librdf.org/flickcurl/api/flickcurl-searching-search-extras.html">Flickcurl C library</a>.</p>
<p>Thirdly, in the license parameter<strong><em>,</em></strong> I select images with a non-commercial license. More on the license codes and their meaning can be found on the Flickr <a target="_blank" href="https://www.flickr.com/services/api/flickr.photos.licenses.getInfo.html">API platform</a>. Finally, the per_page parameter specifies how many images I allow per page.</p>
<p>As a result, I have a generator called photos to download the images.</p>
<h2 id="heading-downloading-flickr-images">Downloading Flickr images</h2>
<p>With the photos generator, I can download all the images found for the search query. First I get the specific URL at which I will download the image. Then I increment the count variable and use this counter to create the image filenames.</p>
<p>With the urlretrieve method, I download the image and save it in the folder for the butterfly species. If an error occurs I print out the error message.</p>
<pre><code class="lang-python"><span class="hljs-keyword">for</span> photo <span class="hljs-keyword">in</span> photos:
    <span class="hljs-keyword">try</span>:
        url=photo.get(<span class="hljs-string">'url_m'</span>)
        print(url)
        count += <span class="hljs-number">1</span>
        urllib.request.urlretrieve(url,  results_folder + str(count) +<span class="hljs-string">".jpg"</span>)
    <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
        print(e, <span class="hljs-string">'Download failure'</span>)
</code></pre>
<p>To download multiple butterfly species, I create a list and call the function download_flickr_photos in a for loop. For simplicity, I only download two butterfly species of the three mentioned above.</p>
<pre><code class="lang-python">butterflies = [<span class="hljs-string">'meadow brown butterfly'</span>, <span class="hljs-string">'gatekeeper butterfly'</span>]
<span class="hljs-keyword">for</span> butterfly <span class="hljs-keyword">in</span> butterflies:
    download_flickr_photos(butterfly)
</code></pre>
<h1 id="heading-data-augmentation-of-images">Data augmentation of images</h1>
<p>Training a convnet on a small number of images will result in overfitting. Consequently, the model will make errors in classifying new, unseen images. Data augmentation can help to avoid this. Luckily Keras has some nice tools to transform images easily.</p>
<p>I’d like to compare it with how my son classifies cars on the road. At the moment he’s only 2 years old and hasn’t seen as many cars as an adult. So you could say his training set of images is rather small. Therefore he’s more likely to misclassify cars. For instance, he sometimes takes an ambulance mistakenly for a police van.</p>
<p>As he will grow older, he will see more ambulances and police vans, with the corresponding label that I will give him. So his training set will become larger and thus he will classify them more correctly.</p>
<p>For that reason, we need to provide the convnet with more butterfly images than we have at the moment. An easy solution for that is <em>data augmentation</em>. In short, this means applying a set of transformations to the Flickr images.</p>
<p>Keras provides a <a target="_blank" href="https://keras.io/preprocessing/image/">wide range of image transformations</a>. But first, we’ll have to convert the images so that Keras can work with them.</p>
<h2 id="heading-converting-an-image-to-numbers">Converting an image to numbers</h2>
<p>We start by importing the Keras module. We will demonstrate the image transformations with one example image. For that purpose, we use the load_img method.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> keras.preprocessing.image <span class="hljs-keyword">import</span> ImageDataGenerator, array_to_img, img_to_array, load_img
i = load_img(<span class="hljs-string">'data/train/maniola_jurtina/1.jpg'</span> )
x = img_to_array(i)
x = x.reshape((<span class="hljs-number">1</span>,) + x.shape)
</code></pre>
<p>The load_img method creates a Python Image Library file. We’ll need to convert this to a Numpy array to use it in the ImageDataGenerator method later on. That’s done with the handy img_to_array method. As a result, we have an array of shape 75x75x3. These dimensions reflect the width, height and RGB values.</p>
<p>In fact, each pixel of the image has 3 RGB values. These range between 0 and 255 and represent the intensity of Red, Green and Blue. A lower value stands for higher intensity and a higher value for lower intensity. For instance, one pixel can be represented as a list of these three values [ 78, 136, 60]. Black would represented as [0, 0, 0].</p>
<p>Finally, we need to add an extra dimension to avoid a ValueError when applying the transformations. This is done with the reshape function.</p>
<p>Alright, now we have something to work with. Let’s continue with the transformations.</p>
<h2 id="heading-rotation">Rotation</h2>
<p>By specifying a value between 0 and 180, Keras will randomly choose an angle to rotate the image. It will do this clockwise or counter-clockwise. In our example, the image will be rotated with maximum of 90 degrees.</p>
<p>ImageDataGenerator also has a parameter fill_mode. The default value is ‘nearest’. By rotating the image within the width and height of the original image we end up with “empty” pixels. The fill_mode then uses the nearest pixels to fill this empty space.</p>
<pre><code class="lang-python">imgGen = ImageDataGenerator(rotation_range = <span class="hljs-number">90</span>)
i = <span class="hljs-number">1</span>
<span class="hljs-keyword">for</span> batch <span class="hljs-keyword">in</span> imgGen.flow(x, batch_size=<span class="hljs-number">1</span>, save_to_dir=<span class="hljs-string">'example_transformations'</span>, save_format=<span class="hljs-string">'jpeg'</span>, save_prefix=<span class="hljs-string">'trsf'</span>):
    i += <span class="hljs-number">1</span>
    <span class="hljs-keyword">if</span> i &amp;gt; <span class="hljs-number">3</span>:
        <span class="hljs-keyword">break</span>
</code></pre>
<p>In the flow method, we specify where to save the transformed images. Make sure this directory exists! We also prefix the newly created images for convenience. The flow method would run infinitely, but for this example, we only generate three images. So when our counter reaches this value, we break the for loop. You can see the result below.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-102.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-width-shift">Width shift</h2>
<p>In the width_shift_range parameter, you specify the ratio of the original width by which the image can be shifted to the left or right. Again, the fill_mode will fill up the newly created empty pixels. For the remaining examples, I will only show how to instantiate the ImageDataGenerator with the respective parameter. The code to generate the images is the same as in the rotation example.</p>
<pre><code class="lang-python">imgGen = ImageDataGenerator(width_shift_range = <span class="hljs-number">90</span>)
</code></pre>
<p>In the transformed images we see that the image is shifted to the right. The empty pixels are filled which gives it a bit of a stretched look.</p>
<p>The same can be done for shifting up or down by specifying a value for the height_shift_range parameter.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-103.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-rescale">Rescale</h2>
<p>Rescaling an image will multiply the RGB values of each pixel by a chosen value before any other preprocessing. In our example, we apply min-max scaling to the values. As a result, these values will range between 0 and 1. This makes the values smaller and easier for the model to process.</p>
<pre><code class="lang-python">imgGen = ImageDataGenerator(rescale = <span class="hljs-number">1.</span>/<span class="hljs-number">255</span>)
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-104.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-shear">Shear</h2>
<p>With the shear_range parameter, we can specify how the shearing transformations must be applied. This transformation can produce rather weird images when the value is set too high. So don’t set it too high.</p>
<pre><code class="lang-python">imgGen = ImageDataGenerator(shear_range = <span class="hljs-number">0.2</span>)
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-106.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-zoom">Zoom</h2>
<p>This transformation will zoom inside the picture. Just like the shearing parameter, this value should not be exaggerated to keep the images realistic.</p>
<pre><code class="lang-python">imgGen = ImageDataGenerator(zoom_range = <span class="hljs-number">0.2</span>)
</code></pre>
<h2 id="heading-horizontal-flip">Horizontal flip</h2>
<p>This transformation flips an image horizontally. Life can be simple sometimes…</p>
<pre><code class="lang-python">imgGen = ImageDataGenerator(horizontal_flip = <span class="hljs-literal">True</span>)
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-107.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-all-transformations-combined">All transformations combined</h2>
<p>Now that we have seen the effect of each transformation separately, we apply all the combinations together.</p>
<pre><code class="lang-python">imgGen = ImageDataGenerator(
    rotation_range = <span class="hljs-number">40</span>,
    width_shift_range = <span class="hljs-number">0.2</span>,
    height_shift_range = <span class="hljs-number">0.2</span>,
    rescale = <span class="hljs-number">1.</span>/<span class="hljs-number">255</span>,
    shear_range = <span class="hljs-number">0.2</span>,
    zoom_range = <span class="hljs-number">0.2</span>,
    horizontal_flip = <span class="hljs-literal">True</span>)
i = <span class="hljs-number">1</span>
<span class="hljs-keyword">for</span> batch <span class="hljs-keyword">in</span> imgGen.flow(x, batch_size=<span class="hljs-number">1</span>, save_to_dir=<span class="hljs-string">'example_transformations'</span>, save_format=<span class="hljs-string">'jpeg'</span>, save_prefix=<span class="hljs-string">'all'</span>):
    i += <span class="hljs-number">1</span>
    <span class="hljs-keyword">if</span> i &amp;gt; <span class="hljs-number">3</span>:
        <span class="hljs-keyword">break</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-108.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-setting-up-the-folder-structure">Setting up the folder structure</h2>
<p>We need to store these images in a specific folder structure. As such we can use the method flow_from_directory to augment the images and create the corresponding labels. This folder structure needs to look like this:</p>
<ul>
<li><strong>train</strong></li>
<li>maniola_jurtina</li>
<li>0.jpg</li>
<li>1.jpg</li>
<li>…</li>
<li>pyronia_tithonus</li>
<li>0.jpg</li>
<li>1.jpg</li>
<li>…</li>
<li><strong>validation</strong></li>
<li>maniola_jurtina</li>
<li>0.jpg</li>
<li>1.jpg</li>
<li>…</li>
<li>pyronia_tithonus</li>
<li>0.jpg</li>
<li>1.jpg</li>
<li>…</li>
</ul>
<p>To create this folder structure I created a gist <a target="_blank" href="https://gist.github.com/bertcarremans/679624f369ed9270472e37f8333244f5">img_train_test_split.py</a>. Feel free to use it in your projects.</p>
<h2 id="heading-creating-the-generators">Creating the generators</h2>
<p>Just as before, we specify the configuration parameters for the training generator. The validation images will not be transformed as the training images. We only divide the RGB values to make them smaller.</p>
<p>The flow_from_directory method takes the images from the train or validation folder and generates batches of 32 transformed images. By setting the class_mode to ‘binary’ a one-dimensional label is created based on the image’s folder name.</p>
<pre><code class="lang-python">train_datagen = ImageDataGenerator(
    rotation_range = <span class="hljs-number">40</span>,
    width_shift_range = <span class="hljs-number">0.2</span>,
    height_shift_range = <span class="hljs-number">0.2</span>,
    rescale = <span class="hljs-number">1.</span>/<span class="hljs-number">255</span>,
    shear_range = <span class="hljs-number">0.2</span>,
    zoom_range = <span class="hljs-number">0.2</span>,
    horizontal_flip = <span class="hljs-literal">True</span>)
validation_datagen = ImageDataGenerator(rescale=<span class="hljs-number">1.</span>/<span class="hljs-number">255</span>)
train_generator = train_datagen.flow_from_directory(
    <span class="hljs-string">'data/train'</span>,
    batch_size=<span class="hljs-number">32</span>,
    class_mode=<span class="hljs-string">'binary'</span>)
validation_generator = validation_datagen.flow_from_directory(
    <span class="hljs-string">'data/validation'</span>,
    batch_size=<span class="hljs-number">32</span>,
    class_mode=<span class="hljs-string">'binary'</span>)
</code></pre>
<h2 id="heading-what-about-different-image-sizes">What about different image sizes?</h2>
<p>The Flickr API lets you download images of specific sizes. However, in real-world applications the image sizes are not always constant. If the aspect ratio of the images is the same, we can simply resize the images. Otherwise, we can crop the images. Unfortunately, it is difficult to crop the image while keeping the object we want to classify intact.</p>
<p>Keras can deal with different image sizes. When configuring the model you can specify None for the width and height in input_shape.</p>
<pre><code class="lang-python">input_shape=(<span class="hljs-number">3</span>, <span class="hljs-literal">None</span>, <span class="hljs-literal">None</span>)  <span class="hljs-comment"># Theano</span>
input_shape=(<span class="hljs-literal">None</span>, <span class="hljs-literal">None</span>, <span class="hljs-number">3</span>)  <span class="hljs-comment"># Tensorflow</span>
</code></pre>
<p>I wanted to show that it is possible to work with different image sizes, however, it has some drawbacks.</p>
<ul>
<li>not all layers (e.g. Flatten) will work with None as an input dimension</li>
<li>it can be computationally heavy to run</li>
</ul>
<h1 id="heading-building-the-deep-learning-model">Building the deep learning model</h1>
<p>For the remainder of this article, I will discuss the structure of a convolutional neural network, illustrated with some examples for our butterfly project. At the end of this article, we’ll have our first classification results.</p>
<h2 id="heading-what-layers-does-a-convolutional-neural-network-consist-of">What layers does a convolutional neural network consist of?</h2>
<p>Of course, you can choose how many layers and their type to add to your convolutional neural network (also called CNN or convnet). In this project we will start with the following structure:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-111.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Let’s understand what each layer does and how we create them with Keras.</p>
<h2 id="heading-input-layer">Input layer</h2>
<p>These different versions of the images were modified via several transformations. Then, these images are converted into a numerical representation or a matrix.</p>
<p>The dimensions of this matrix will be width x height x number of (color) channels<em>.</em> For RGB images the number of channels will be three. For grayscale images, this is equal to one. Below you can see a numerical representation of a 7×7 RGB image.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-112.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>As our images are of size 75×75, we need to specify that in the input_shape parameter when adding the first convolutional layer.</p>
<pre><code class="lang-python">cnn = Sequential()
cnn.add(Conv2D(<span class="hljs-number">32</span>,(<span class="hljs-number">3</span>,<span class="hljs-number">3</span>), input_shape = (<span class="hljs-number">3</span> ,<span class="hljs-number">75</span> ,<span class="hljs-number">75</span>)))
</code></pre>
<h2 id="heading-convolutional-layer">Convolutional layer</h2>
<p>In the first layers, the convolutional neural network will look for lower-level features, like horizontal or vertical edges. The further we go in the network it will look for higher-level features, such as a wing of a butterfly, for example. But how does it detect features when it gets only numbers as input? That’s where filters come in.</p>
<h2 id="heading-filters-or-kernels">Filters (or kernels)</h2>
<p>You can think of a filter as a searchlight of a specific size that scans over the image. The filter example below has dimensions of 3x3x3 and contains weights that will detect a vertical edge. For a grayscale image, the dimensions would have been 3x3x1. Usually, a filter has smaller dimensions than the image we want to classify. 3×3, 5×5 or 7×7 are typically used. The third dimension should always be equal to the number of channels.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-113.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>While scanning the image, the RGB values are transformed. It does this transformation by multiplying the RGB values with the filter’s weights. Finally, the multiplied values are then summed over all channels. In our 7x7x3 image example and the 3x3x3 filter, this would result in a 5x5x1 outcome.</p>
<p>The animation below illustrates this convolutional operation. For simplicity, we only look for a vertical edge in the Red channel. Thus, the weights for the Green and Blue channels are all equal to zero. But you should keep in mind that the multiplication results for these channels are added to the result of the Red channel.</p>
<p>As shown below the convolutional layer will produce numerical outcomes. When you have higher numbers, this means that the filter came across the feature it was looking for. In our example, a vertical edge.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/0_ykXVTApvty9Q0lAX-1.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>We can specify that we want more than one filter. These filters could have their own feature to look for in an image. Suppose we use 32 filters of size 3x3x3. The result of all filters is stacked and we end up with a 5x5x32 volume in our example. In the code snippet above we added 32 filters of size 3x3x3.</p>
<h2 id="heading-stride">Stride</h2>
<p>In the example above we saw that the filter moves up one pixel at a time. This is the so-called stride. We could increase the number of pixels the filter moves up. Increasing the stride will reduce the dimensions of the original image much faster. In the example below, you see how the filter moves around with a stride of 2, which would result in a 3x3x1 outcome for a 3x3x3 filter and a 7x7x3 image.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/0_Ds4PLixAjvOMPF9j-1.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-padding">Padding</h2>
<p>By applying a filter, the dimensions of the original image are quickly reduced. Especially the pixels at the edges of the image are only used once in the convolutional operation. This results in a loss of information. If you want to avoid that, you can specify padding. Padding adds “extra pixels” around the image.</p>
<p>Suppose we add padding of one pixel around the 7x7x3 image. This results in a 9x9x3 image. If we apply a 3x3x3 filter and a stride of 1, we end up with a 7x7x1 outcome. So, in that case, we preserve the dimensions of the original image and the outer pixels are used more than once.</p>
<p>You can calculate the resulting outcome of the convolutional operation with specific padding and stride as follows:</p>
<p><strong>1 + [(original dimension + padding x 2 — filter dimension) / stride size]</strong></p>
<p>For example, suppose we have this set-up of our conv layer:</p>
<ul>
<li>7x7x3 image</li>
<li>3x3x3 filter</li>
<li>padding of 1 pixel</li>
<li>stride of 2 pixels</li>
</ul>
<p>That will give 1 + [(7 + 1 x 2–3) / 2] = 4</p>
<h2 id="heading-why-do-we-need-convolutional-layers">Why do we need convolutional layers?</h2>
<p>A benefit of using conv layers is that the number of parameters to estimate is much lower. Much lower compared to having a normal hidden layer. Suppose we continue with our example image of 7x7x3 and a filter of 3x3x3 with no padding and stride of 1. The convolutional layer would have 5x5x1 + 1 bias = 26 weights to estimate. In a neural network with 7x7x3 inputs and 5x5x1 neurons in the hidden layer, we would need to estimate 3.675 weights. Imagine what this number is when you have larger images…</p>
<h2 id="heading-relu-layer">ReLu layer</h2>
<p>Or Rectified Linear unit layer. This layer adds nonlinearity to the network. The convolutional layer is a linear layer as it sums up the multiplications of the filter weights and RGB values.</p>
<p>The outcome of a ReLu function is equal to zero for all values of x &lt;= 0. Otherwise, it is equal to the value of x. The code in Keras to add a ReLu layer is:</p>
<pre><code class="lang-python">cnn.add(Activation(‘relu’))
</code></pre>
<h2 id="heading-pooling">Pooling</h2>
<p>Pooling aggregates the input volume in order to reduce the dimensions further. This speeds up computation time as the number of parameters to be estimated are reduced. Besides that, it helps to avoid overfitting by making the network more robust. Below we illustrate max pooling with a size of 2×2 and stride of 2.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-115.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The code in Keras to add pooling with a size of 2×2 is:</p>
<pre><code class="lang-python">cnn.add(MaxPooling2D(pool_size = (<span class="hljs-number">2</span> ,<span class="hljs-number">2</span>)))
</code></pre>
<h2 id="heading-fully-connected-layer">Fully connected layer</h2>
<p>At the end, the convnet is able to detect higher level features in the input images. This can then serve as an input for a fully connected layer. Before we can do that, we will flatten the output of the last ReLu layer. Flattening means we convert it to a vector. The vector values are then connected to all neurons in the fully connected layer. To do that in Python we use the following Keras functions:</p>
<pre><code class="lang-python">cnn.add(Flatten())        
cnn.add(Dense(<span class="hljs-number">64</span>))
</code></pre>
<h2 id="heading-dropout">Dropout</h2>
<p>Just like pooling, dropout can help to avoid overfitting. It randomly sets a specified fraction of the inputs to zero, during the training of the model. A dropout rate between 20 and 50% is considered to work well.</p>
<pre><code class="lang-python">cnn.add(Dropout(<span class="hljs-number">0.2</span>))
</code></pre>
<h2 id="heading-sigmoid-activation">Sigmoid activation</h2>
<p>Because we want to produce a probability that the image is one of two butterfly species (i.e. binary classification), we can use a sigmoid activation layer.</p>
<pre><code class="lang-python">cnn.add(Activation(<span class="hljs-string">'relu'</span>))
cnn.add(Dense(<span class="hljs-number">1</span>))
cnn.add(Activation( <span class="hljs-string">'sigmoid'</span>))
</code></pre>
<h2 id="heading-applying-the-convolutional-neural-network-on-the-butterfly-images">Applying the convolutional neural network on the butterfly images</h2>
<p>Now we can define the complete convolutional neural network structure as displayed at the beginning of this post. First, we need to import the necessary Keras modules. Then we can start adding the layers that we explained above.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> keras.models <span class="hljs-keyword">import</span> Sequential
<span class="hljs-keyword">from</span> keras.layers <span class="hljs-keyword">import</span> Conv2D, MaxPooling2D
<span class="hljs-keyword">from</span> keras.layers <span class="hljs-keyword">import</span> Activation, Flatten, Dense, Dropout
<span class="hljs-keyword">from</span> keras.preprocessing.image <span class="hljs-keyword">import</span> ImageDataGenerator
<span class="hljs-keyword">import</span> time
IMG_SIZE = <span class="hljs-comment"># Replace with the size of your images</span>
NB_CHANNELS = <span class="hljs-comment"># 3 for RGB images or 1 for grayscale images</span>
BATCH_SIZE = <span class="hljs-comment"># Typical values are 8, 16 or 32</span>
NB_TRAIN_IMG = <span class="hljs-comment"># Replace with the total number training images</span>
NB_VALID_IMG = <span class="hljs-comment"># Replace with the total number validation images</span>
</code></pre>
<p>I made some additional parameters explicit for the conv layers. Here is a short explanation:</p>
<ul>
<li>kernel_size specifies the filter size. So for the first conv layer this is size 2×2</li>
<li>padding = ‘same’ means applying zero padding as such the original image size is preserved.</li>
<li>padding = ‘valid’ means we do not apply any padding.</li>
<li>data_format = ‘channels_last’ is just to specify that the number of color channels is specified last in the input_shape argument.</li>
</ul>
<pre><code class="lang-python">cnn = Sequential()
cnn.add(Conv2D(filters=<span class="hljs-number">32</span>, 
               kernel_size=(<span class="hljs-number">2</span>,<span class="hljs-number">2</span>), 
               strides=(<span class="hljs-number">1</span>,<span class="hljs-number">1</span>),
               padding=<span class="hljs-string">'same'</span>,
               input_shape=(IMG_SIZE,IMG_SIZE,NB_CHANNELS),
               data_format=<span class="hljs-string">'channels_last'</span>))
cnn.add(Activation(<span class="hljs-string">'relu'</span>))
cnn.add(MaxPooling2D(pool_size=(<span class="hljs-number">2</span>,<span class="hljs-number">2</span>),
                     strides=<span class="hljs-number">2</span>))
cnn.add(Conv2D(filters=<span class="hljs-number">64</span>,
               kernel_size=(<span class="hljs-number">2</span>,<span class="hljs-number">2</span>),
               strides=(<span class="hljs-number">1</span>,<span class="hljs-number">1</span>),
               padding=<span class="hljs-string">'valid'</span>))
cnn.add(Activation(<span class="hljs-string">'relu'</span>))
cnn.add(MaxPooling2D(pool_size=(<span class="hljs-number">2</span>,<span class="hljs-number">2</span>),
                     strides=<span class="hljs-number">2</span>))
cnn.add(Flatten())        
cnn.add(Dense(<span class="hljs-number">64</span>))
cnn.add(Activation(<span class="hljs-string">'relu'</span>))
cnn.add(Dropout(<span class="hljs-number">0.25</span>))
cnn.add(Dense(<span class="hljs-number">1</span>))
cnn.add(Activation(<span class="hljs-string">'sigmoid'</span>))
cnn.compile(loss=<span class="hljs-string">'binary_crossentropy'</span>, optimizer=<span class="hljs-string">'rmsprop'</span>, metrics=[<span class="hljs-string">'accuracy'</span>])
</code></pre>
<p>Finally, we compile this network structure and set the loss parameter to binary_crossentropy which is good for binary targets and use accuracy as the evaluation metric.</p>
<p>After having specified the network structure, we create the generators for the training and validation samples. On the training samples, we apply data augmentation as explained above. On the validation samples, we do not apply any augmentation as they are just used to evaluate the model performance.</p>
<pre><code class="lang-python">train_datagen = ImageDataGenerator(
    rotation_range = <span class="hljs-number">40</span>,                  
    width_shift_range = <span class="hljs-number">0.2</span>,                  
    height_shift_range = <span class="hljs-number">0.2</span>,                  
    rescale = <span class="hljs-number">1.</span>/<span class="hljs-number">255</span>,                  
    shear_range = <span class="hljs-number">0.2</span>,                  
    zoom_range = <span class="hljs-number">0.2</span>,                     
    horizontal_flip = <span class="hljs-literal">True</span>)
validation_datagen = ImageDataGenerator(rescale = <span class="hljs-number">1.</span>/<span class="hljs-number">255</span>)
train_generator = train_datagen.flow_from_directory(
    <span class="hljs-string">'../flickr/img/train'</span>,
    target_size=(IMG_SIZE,IMG_SIZE),
    class_mode=<span class="hljs-string">'binary'</span>,
    batch_size = BATCH_SIZE)
validation_generator = validation_datagen.flow_from_directory(
    <span class="hljs-string">'../flickr/img/validation'</span>,
    target_size=(IMG_SIZE,IMG_SIZE),
    class_mode=<span class="hljs-string">'binary'</span>,
    batch_size = BATCH_SIZE)
</code></pre>
<p>With the flow_from_directory method on the generators we can easily go through all the images in the specified directories.</p>
<p>Lastly, we can fit the convolutional neural network on the training data and evaluate with the validation data. The resulting weights of the model can be saved and reused later on.</p>
<pre><code class="lang-python">start = time.time()
cnn.fit_generator(
    train_generator,
    steps_per_epoch=NB_TRAIN_IMG//BATCH_SIZE,
    epochs=<span class="hljs-number">50</span>,
    validation_data=validation_generator,
    validation_steps=NB_VALID_IMG//BATCH_SIZE)
end = time.time()
print(<span class="hljs-string">'Processing time:'</span>,(end - start)/<span class="hljs-number">60</span>)
cnn.save_weights(<span class="hljs-string">'cnn_baseline.h5'</span>)
</code></pre>
<p>The number of epochs is arbitrarily set to 50. An epoch is the cycle of forward propagation, checking the error and then adjusting the weights during backpropagation.</p>
<p>The steps_per_epoch is set to the number of training images divided by the batch size (by the way, the double division symbol will make sure the result is an integer and not a float). Specifying a batch size greater than 1 will speed up the process. Idem for the validation_steps parameter.</p>
<h2 id="heading-results">Results</h2>
<p>After running 50 epochs, we have a training accuracy of 0.8091 and validation accuracy of 0.7359. So the convolutional neural network still suffers from quite some overfitting. We also see that the validation accuracy varies quite a lot. This is because we have a small set of validation samples. It would be better to do k-fold cross-validation for each evaluation round. But that would take quite some time.</p>
<p>To address the overfitting we could:</p>
<ul>
<li>increase the dropout rate</li>
<li>apply dropout at each layer</li>
<li>find more training data</li>
</ul>
<p>We’ll look into the first two options and monitor the result. The results of our first model will serve as a baseline. After applying an extra dropout layer and increasing the dropout rates, the model is a bit less overfitted.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-116.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>I hope you’ve all enjoyed reading this post and learned something new. The full code is available on <a target="_blank" href="https://github.com/bertcarremans/Vlindervinder">Github</a>. Cheers!  </p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to create a simple Image Classifier ]]>
                </title>
                <description>
                    <![CDATA[ By Aditya Image classification is an amazing application of deep learning. We can train a powerful algorithm to model a large image dataset. This model can then be used to classify a similar but unknown set of images.  There is no limit to the applic... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/creating-your-first-image-classifier/</link>
                <guid isPermaLink="false">66d45ddb7df3a1f32ee7f7e7</guid>
                
                    <category>
                        <![CDATA[ Computer Vision ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Deep Learning ]]>
                    </category>
                
                    <category>
                        <![CDATA[ image classification ]]>
                    </category>
                
                    <category>
                        <![CDATA[ neural networks ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 18 Jul 2019 18:06:36 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/07/mnist-fashion3.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Aditya</p>
<p>Image classification is an amazing application of deep learning. We can train a powerful algorithm to model a large image dataset. This model can then be used to classify a similar but unknown set of images. </p>
<p>There is no limit to the applications of image classification. You can use it in your next app or you can use it to solve some real world problem. That's all up to you. But to someone who is fairly new to this realm, it might seem very challenging at first. How should I get my data? How should I build my model? What tools should I use? </p>
<p>In this article we will discuss all of that - from finding a dataset to training your model. I will try to make things as simple as possible by avoiding some technical details (<em>PS: Please note that this doesn't mean those details are not important. I will mention some great resources which you can refer to learn more about those topics</em>).  The purpose of this article is to explain the basic process of building an image classifier and that's what we will focus more on here. </p>
<p>We will build an Image classifier for the <a target="_blank" href="https://research.zalando.com/welcome/mission/research-projects/fashion-mnist/">Fashion-MNIST Dataset</a>. The Fashion-MNIST dataset is a collection of <a target="_blank" href="https://research.zalando.com/">Zalando's</a> article images. It contains 60,000 images for the training set and 10,000 images for the test set data (<em>we will discuss the test and training datasets along with the validation dataset later</em>). These images belong to the labels of 10 different classes. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/07/mnist-fashion.png" alt="Image" width="600" height="400" loading="lazy">
<em><a target="_blank" href="https://research.zalando.com/welcome/mission/research-projects/fashion-mnist/">Source</a></em></p>
<h2 id="heading-importing-libraries">Importing Libraries</h2>
<p>Our goal is to train a deep learning model that can classify a given set of images into one of these 10 classes. Now that we have our dataset, we should move on to the tools we need. There are many libraries and tools out there that you can choose based on your own project requirements. For this one I will stick to the following:</p>
<ol>
<li><a target="_blank" href="https://www.numpy.org/"><strong>Numpy</strong></a> - Python library for numerical computation</li>
<li><a target="_blank" href="https://pandas.pydata.org/"><strong>Pandas</strong></a> - Python library data manipulation</li>
<li><a target="_blank" href="https://matplotlib.org/"><strong>Matplotlib</strong></a> - Python library data visualisation</li>
<li><a target="_blank" href="https://keras.io/"><strong>Keras</strong></a> - Python library based on tensorflow for creating deep learning models</li>
<li><a target="_blank" href="https://jupyter.org/"><strong>Jupyter</strong></a> - I will run all my code on Jupyter Notebooks. You can install it via the link. You can use <a target="_blank" href="https://colab.research.google.com/">Google Colabs</a> also if you need better computational power.</li>
</ol>
<p>Along with these four, we will also use <a target="_blank" href="https://scikit-learn.org/">scikit-learn</a>. The purpose of these libraries will become more clear once we dive into the code. </p>
<p>Okay! We have our tools and libraries ready. Now we should start setting up our code.</p>
<p>Start with importing all the above mentioned libraries. Along with importing libraries I have also imported some specific modules from these libraries. Let me go through them one by one.</p>
<pre><code class="lang-python3">import numpy as np 
import pandas as pd
import matplotlib.pyplot as plt 
import keras 

from sklearn.model_selection import train_test_split 
from keras.utils import to_categorical 

from keras.models import Sequential 
from keras.layers import Conv2D, MaxPooling2D 
from keras.layers import Dense, Dropout 
from keras.layers import Flatten, BatchNormalization
</code></pre>
<p><strong><a target="_blank" href="https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html">train_test_split</a>:</strong> This module splits the training dataset into training and validation data. The reason behind this split is to check if our model is <a target="_blank" href="https://en.wikipedia.org/wiki/Overfitting">overfitting</a> or not. We use a training dataset to train our model and then we will compare the resulting accuracy to validation accuracy. If the difference between both quantities is significantly large, then our model is probably overfitting. We will reiterate through our model building process and making required changes along the way. Once we are satisfied with our training and validation accuracies, we will make final predictions on our test data. </p>
<p><strong>to_categorical:</strong> to_categorical is a keras utility. It is used to convert the categorical labels into <a target="_blank" href="https://machinelearningmastery.com/why-one-hot-encode-data-in-machine-learning/">one-hot encodings</a>. Let's say we have three labels ("apples", "oranges", "bananas"), then one hot encodings for each of these would be [1, 0, 0] -&gt; "apples", [0, 1, 0] -&gt; "oranges",   [0, 0, 1] -&gt; "bananas".</p>
<p>The rest of the Keras modules we have imported are convolutional layers. We will discuss convolutional layers when we start building our model. We will also give a quick glance to what each of these layers do.</p>
<h2 id="heading-data-pre-processing">Data Pre-processing</h2>
<p>For now we will shift our attention to getting our data and analysing it. You should always remember the importance of pre-processing and analysing the data. It not only gives you insights about the data but also helps to locate inconsistencies. </p>
<p>A very slight variation in data can sometimes lead to a devastating result for your model. This makes it important to preprocess your data before using it for training. So with that in mind let's start data preprocessing.</p>
<pre><code class="lang-python3">train_df = pd.read_csv('./fashion-mnist_train.csv')
test_df = pd.read_csv('./fashion-mnist_test.csv')
</code></pre>
<p>First of all let's import our dataset (<em><a target="_blank" href="https://www.kaggle.com/zalando-research/fashionmnist">Here</a> is the link to download this dataset on your system</em>). Once you have imported the dataset, run the following command.</p>
<pre><code class="lang-python3">train_df.head()
</code></pre>
<p>This command will show you how your data looks like. The following screenshot shows the output of this command.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/07/head_output.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>We can see how our image data is stored in the form of pixel values. But we cannot feed data to our model in this format. So, we will have to convert it into numpy arrays. </p>
<pre><code class="lang-python3">train_data = np.array(train_df.iloc[:, 1:])
test_data = np.array(test_df.iloc[:, 1:])
</code></pre>
<p>Now, it's time to get our labels. </p>
<pre><code class="lang-python3">train_labels = to_categorical(train_df.iloc[:, 0])
test_labels = to_categorical(test_df.iloc[:, 0])
</code></pre>
<p>Here, you can see that we have used _to<em>categorical</em> to convert our categorical data into one hot encodings.</p>
<p>We will now reshape the data and cast it into <em>float32</em> type so that we can use it conveniently. </p>
<pre><code>rows, cols = <span class="hljs-number">28</span>, <span class="hljs-number">28</span> 

train_data = train_data.reshape(train_data.shape[<span class="hljs-number">0</span>], rows, cols, <span class="hljs-number">1</span>)
test_data = test_data.reshape(test_data.shape[<span class="hljs-number">0</span>], rows, cols, <span class="hljs-number">1</span>)

train_data = train_data.astype(<span class="hljs-string">'float32'</span>)
test_data = test_data.astype(<span class="hljs-string">'float32'</span>)
</code></pre><p>We are almost done. Let's just finish preprocessing our data by normalizing it. Normalizing image data will map all the pixel values in each image to the values between 0 to 1. This helps us reduce inconsistencies in data. Before normalizing, the image data can have large variations in pixel values which can lead to some unusual behaviour during the training process. </p>
<pre><code>train_data /= <span class="hljs-number">255.0</span>
test_data /= <span class="hljs-number">255.0</span>
</code></pre><h2 id="heading-convolutional-neural-networks">Convolutional Neural Networks</h2>
<p>So, data preprocessing is done. Now we can start building our model. We will build a <a target="_blank" href="http://cs231n.github.io/convolutional-networks/">Convolutional Neural Network</a> for modeling the image data. CNNs are modified versions of regular <a target="_blank" href="https://en.wikipedia.org/wiki/Neural_network">neural networks</a>. These are modified specifically for image data. Feeding images to regular neural networks would require our network to have a large number of input neurons. For example just for a 28x28 image we would require 784 input neurons. This would create a huge mess of training parameters.</p>
<p>CNNs fix this problem by already assuming that the input is going to be an image. The main purpose of convolutional neural networks is to take advantage of the spatial structure of the image and to extract high level features from that and then train on those features. It does so by performing a <a target="_blank" href="https://en.wikipedia.org/wiki/Convolution">convolution</a> operation on the matrix of pixel values.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/07/convSobel.gif" alt="Image" width="600" height="400" loading="lazy">
<em><a target="_blank" href="https://mlnotebook.github.io/post/CNN1/">Source</a></em></p>
<p>The visualization above shows how convolution operation works. And the Conv2D layer we imported earlier does the same thing. The first matrix (<em>from the left</em>) in the demonstration is the input to the convolutional layer. Then another matrix called "filter" or "kernel" is multiplied (matrix multiplication) to each window of the input matrix. The output of this multiplication is the input to the next layer. </p>
<p>Other than convolutional layers, a typical CNN also has two other types of layers: 1) a  p<a target="_blank" href="https://machinelearningmastery.com/pooling-layers-for-convolutional-neural-networks/">ooling layer</a>, and 2) a f<a target="_blank" href="https://stats.stackexchange.com/questions/182102/what-do-the-fully-connected-layers-do-in-cnns">ully connected layer</a>. </p>
<p>Pooling layers are used to generalize the output of the convolutional layers. Along with generalizing, it also reduces the number of parameters in the model by down-sampling the output of the convolutional layer. </p>
<p>As we just learned, convolutional layers represent high level features from image data. Fully connected layers use these high level features to train the parameters and to learn to classify those images. </p>
<p>We will also use the <a target="_blank" href="https://machinelearningmastery.com/dropout-for-regularizing-deep-neural-networks/">Dropout</a>, <a target="_blank" href="https://en.wikipedia.org/wiki/Batch_normalization">Batch-normalization</a> and <a target="_blank" href="https://stackoverflow.com/questions/43237124/role-of-flatten-in-keras">Flatten</a> layers in addition to the layers mentioned above. Flatten layer converts the output of convolutional layers into a one dimensional feature vector. It is important to flatten the outputs because Dense (Fully connected) layers only accept a feature vector as input. Dropout and Batch-normalization layers are for preventing the model from <a target="_blank" href="https://en.wikipedia.org/wiki/Overfitting">overfitting</a>.</p>
<pre><code class="lang-python">train_x, val_x, train_y, val_y = train_test_split(train_data, train_labels, test_size=<span class="hljs-number">0.2</span>)

batch_size = <span class="hljs-number">256</span>
epochs = <span class="hljs-number">5</span>
input_shape = (rows, cols, <span class="hljs-number">1</span>)
</code></pre>
<pre><code>def baseline_model():
    model = Sequential()
    model.add(BatchNormalization(input_shape=input_shape))
    model.add(Conv2D(<span class="hljs-number">32</span>, (<span class="hljs-number">3</span>, <span class="hljs-number">3</span>), padding=<span class="hljs-string">'same'</span>, activation=<span class="hljs-string">'relu'</span>))
    model.add(MaxPooling2D(pool_size=(<span class="hljs-number">2</span>, <span class="hljs-number">2</span>), strides=(<span class="hljs-number">2</span>,<span class="hljs-number">2</span>)))
    model.add(Dropout(<span class="hljs-number">0.25</span>))

    model.add(BatchNormalization())
    model.add(Conv2D(<span class="hljs-number">32</span>, (<span class="hljs-number">3</span>, <span class="hljs-number">3</span>), padding=<span class="hljs-string">'same'</span>, activation=<span class="hljs-string">'relu'</span>))
    model.add(MaxPooling2D(pool_size=(<span class="hljs-number">2</span>, <span class="hljs-number">2</span>)))
    model.add(Dropout(<span class="hljs-number">0.25</span>))

    model.add(Flatten())
    model.add(Dense(<span class="hljs-number">128</span>, activation=<span class="hljs-string">'relu'</span>))
    model.add(Dropout(<span class="hljs-number">0.5</span>))
    model.add(Dense(<span class="hljs-number">10</span>, activation=<span class="hljs-string">'softmax'</span>))
    <span class="hljs-keyword">return</span> model
</code></pre><p>The code that you see above is the code for our CNN model. You can structure these layers in many different ways to get good results. There are many popular CNN architectures which give state of the art results. Here, I have just created my own simple architecture for the purpose of this problem. Feel free to try your own and let me know what results you get :)</p>
<h2 id="heading-training-the-model">Training the model</h2>
<p>Once you have created the model you can import it and then compile it by using the code below.</p>
<pre><code>model = baseline_model()
model.compile(loss=<span class="hljs-string">'categorical_crossentropy'</span>, optimizer=<span class="hljs-string">'sgd'</span>, metrics=[<span class="hljs-string">'accuracy'</span>])
</code></pre><p><strong>model.compile</strong> configures the learning process for our model. We have passed it three arguments. These arguments define the <a target="_blank" href="https://machinelearningmastery.com/loss-and-loss-functions-for-training-deep-learning-neural-networks/">loss function</a> for our model, <a target="_blank" href="https://blog.algorithmia.com/introduction-to-optimizers/">optimizer</a> and <a target="_blank" href="https://keras.io/metrics/">metrics</a>.</p>
<pre><code>history = model.fit(train_x, train_y,
          batch_size=batch_size,
          epochs=epochs,
          verbose=<span class="hljs-number">1</span>,
          validation_data=(val_x, val_y))
</code></pre><p>And finally by running the code above you can train your model. I am training this model for just five epochs but you can increase the number of epochs. After your training process is completed you can make predictions on the test set by using the following code.</p>
<pre><code>predictions= model.predict(test_data)
</code></pre><h2 id="heading-conclusion">Conclusion</h2>
<p>Congrats! You did it, you have taken your first step into the amazing world of computer vision. </p>
<p>You have created a your own image classifier. Even though this is a great achievement, we have just scratched the surface. </p>
<p>There is a lot you can do with CNNs. The applications are limitless. I hope that this article helped you to get an understanding of how the process of training these models works. </p>
<p>Working on other datasets on your own will help you understand this even better. I have also created a GitHub <a target="_blank" href="https://github.com/aditya2000/MNIST-Fashion-">repository</a> for the code I used in this article. So, if this article was useful for you please let me know. </p>
<p>If you have any questions or you want to share your own results or if you just want to say "hi", feel free to hit me up on <a target="_blank" href="https://twitter.com/aditya_dehal">twitter</a>, and I'll try to do my best to help you. And finally <strong>Thanks a lot for reading this article!!</strong> :)</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Day 24: How to build a Deep Learning Image Classifier for Game of Thrones dragons ]]>
                </title>
                <description>
                    <![CDATA[ By Harini Janakiraman Performance of most flavors of the old generations of learning algorithms will plateau. Deep learning, training large neural networks, is scalable and performance keeps getting better as you feed them more data. — Andrew Ng De... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/deep-learning-image-classifier-for-game-of-thrones-dragons-42cd59a1972d/</link>
                <guid isPermaLink="false">66c348d68cfe41ff707fbe2b</guid>
                
                    <category>
                        <![CDATA[ Artificial Intelligence ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Deep Learning ]]>
                    </category>
                
                    <category>
                        <![CDATA[ image classification ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Machine Learning ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 21 Sep 2018 20:27:02 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*TM3_JUHkkaTW36uQZp9FYw.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Harini Janakiraman</p>
<blockquote>
<p>Performance of most flavors of the old generations of learning algorithms will plateau. Deep learning, training large neural networks, is scalable and performance keeps getting better as you feed them more data. — <em>Andrew Ng</em></p>
</blockquote>
<p>Deep learning doesn’t take a huge amount of time or computational resources. Nor does it require highly complex code, and in some cases not even a large amount of training data. Curated best practices are now available as libraries that make it easy to plug in and write your own neural network architectures using a minimal amount of code to achieve more than 90% prediction accuracies.</p>
<p>The two most popular deep learning libraries are: (1) pytorch created by Facebook (we will be using fastai today, which is built on top of pytorch) and (2) the keras-tensorflow framework created by Google.</p>
<h3 id="heading-the-project">The Project</h3>
<p>We will build an image classifier using the Convolutional Neural Network (CNN) model to predict if a given image is that of Drogon or Vicerion (any Game of Thrones fans here in the house? Clap to say yay!).</p>
<p>You can adapt this problem statement to any type of image classification that interests you. Here are some ideas: cat or dog (classic deep learning 101), if a person is wearing glasses or not, bus or car, hot dog vs not-hot dog (Silicon Valley fans also say yay! ;) ).</p>
<h3 id="heading-step-1-installation">Step 1: Installation</h3>
<p>You can use any GPU accelerated cloud computing platform for running your model on. For the purpose of this blog we will be using <a target="_blank" href="https://www.paperspace.com/">Paperspace</a> (most affordable). Complete instructions on how to get this up and running are available <a target="_blank" href="https://github.com/reshamas/fastai_deeplearn_part1/blob/master/tools/paperspace.md"><strong>here</strong></a>.</p>
<p>Once setup, you can launch Jupyter notebook on that machine using the following command:</p>
<pre><code>jupyter notebook
</code></pre><p>This will give you a localhost URL that you can open in your browser and replace “localhost” with your machine’s IP address to launch your notebook.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/gtn9GKmqFmghPbs7fLu6SzJ1hSid9GNpKNvV" alt="Image" width="800" height="351" loading="lazy"></p>
<p>Now you can copy over the iPython notebook and dataset files into the directory structure below from <a target="_blank" href="https://github.com/harinij/100DaysOfCode/tree/master/Day%20023%20-%20Image%20Classifier%20using%20deep%20learning%20CNN%20model"><strong>my github repo</strong></a>.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/3cvYJGymYABQJWr4bP6G29kZDkQSJUUFBGhp" alt="Image" width="800" height="654" loading="lazy"></p>
<p><strong>Note</strong>: Do not forget to shut down the machine from the paperspace console once you are done to avoid getting accidentally charged.</p>
<h3 id="heading-step-2-training"><strong>Step 2: Training</strong></h3>
<p>Follow the instructions in the notebook to initialize the libraries needed for this exercise, and point to the location of the PATH to your data directory. Note that each block of code can be run using “shift+enter.” In case you need additional info on Jupyter notebook commands, you can read more <a target="_blank" href="https://jupyter-notebook.readthedocs.io/en/stable/examples/Notebook/Notebook%20Basics.html">here</a>.</p>
<p>Now, coming to the part of training the image classifier, the following three lines of code form the core of building the deep learning model:</p>
<ol>
<li><strong>data</strong>: represents the validation and training datasets.</li>
<li><strong>learn</strong>: contains the model</li>
<li><strong>learn.fit(learning_rate,epoch)</strong>: Fit the model using two parameters — learning rate and epochs.</li>
</ol>
<p><img src="https://cdn-media-1.freecodecamp.org/images/GSWXa64C56P8YNg4WPrlSvlAjdiTTq0abWP0" alt="Image" width="800" height="109" loading="lazy"></p>
<p>We have set the learning rate to be “0.01” here. Learning rate needs to be a small enough number so that you move through the image in incremental steps of this factor to learn with accuracy. But it shouldn’t be too small, either, as that would result in too many steps/too long to learn. The library has a learning rate finder method “lr_find()” to find the optimal one.</p>
<p>Epoch is set to “3” in the code here and it represents how many times you should run the batch. We can run as many times as we want, but after a point accuracy will start to get worse due to overfitting.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/sas5SvMYXGfSQyeiKB96YURdhkad6r7BrtH2" alt="Image" width="800" height="148" loading="lazy"></p>
<h3 id="heading-step-3-prediction"><strong>Step 3: Prediction</strong></h3>
<p>We will now run prediction on the validation data using the trained model.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/oTZaAYbeABWT0tGnDEtEP7elcN5nb7lNB9Gp" alt="Image" width="800" height="116" loading="lazy"></p>
<p>Pytorch gives a log of prediction, so to get the probability you have to get e to the power of using numpy. Follow the instructions step by step on the notebook in my <a target="_blank" href="https://github.com/harinij/100DaysOfCode/tree/master/Day%20023%20-%20Image%20Classifier%20using%20deep%20learning%20CNN%20model"><strong>github repo</strong></a><strong>.</strong> A probability close to 0 implies its an image of Drogon and a probability close to 1 implies its an image of Viserion.</p>
<h3 id="heading-step-4-visualize"><strong>Step 4: Visualize</strong></h3>
<p>Plotting function can be used to visualize the results of the prediction better. The below images show you correctly classified validation data with 0.2–0.3 indicating it’s Drogon and a probablity of 0.7–0.8 indicating it’s Viserion.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/zf74Dk3FGDS3F3sd5WiGDpH-4vshGm3O-i1Z" alt="Image" width="800" height="239" loading="lazy"></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/3i82iaVHQHUPrEtOMufWCPPAuRu3iqeAhlE8" alt="Image" width="800" height="303" loading="lazy"></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/oQw5JxD8sAhzcnxAiwklzpUy2c4ph8UnbCSZ" alt="Image" width="800" height="305" loading="lazy"></p>
<p>You can also see some of the uncertain predictions if they linger closer to 0.5 probability.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/USlRGvhRFoBP-JmY719kyZaKr-geDY4cldoS" alt="Image" width="800" height="328" loading="lazy"></p>
<p>The image classifier in some scenarios can have uncertain predictions, for example in case of long tailed images, as it grabs a small piece of the square at a time.</p>
<p>In those cases, enhancement techniques can be done to have better results such as data augmentation, optimizing the learning rate, using differential learning rates for different layers, and test-time augmentation. These advanced concepts will be explored in future posts.</p>
<p>This blog was inspired by fastai CNN video. To get an in-depth understanding and continue your quest in Deep Learning, you can take the famous set of courses by Andrew Ng on <a target="_blank" href="https://www.deeplearning.ai/">coursera</a>.</p>
<p><em>If you enjoyed this, please clap <strong>? s</strong>o others can see it as well! Follow me on Twitter @<a target="_blank" href="https://twitter.com/harinilabs">H<strong>ariniLabs</strong></a> or M<a target="_blank" href="https://medium.com/@harinilabs"><strong>edium</strong></a> to get new post updates or to just say Hi :)</em></p>
<p><em>PS: Sign up for my newsletter <a target="_blank" href="http://harinilabs.com/womenintech.html"><strong>here</strong></a> to be the first to get fresh new content and it’s filled with doses of inspiration from the world of #<a target="_blank" href="http://harinilabs.com/womenintech.html"><strong>WomenInTech</strong></a> <strong>—</strong> and yes men can signup too :)</em></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Image Classification in the Browser with JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ By Kevin Scott Machine Learning has a reputation for demanding lots of data and powerful GPU computations. This leads many people to believe that building custom machine learning models for their specific dataset is impractical without a large invest... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/image-classification-in-the-browser-with-javascript-bec7b5a7a8c3/</link>
                <guid isPermaLink="false">66c357bfc7095d76345eaf94</guid>
                
                    <category>
                        <![CDATA[ Artificial Intelligence ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Deep Learning ]]>
                    </category>
                
                    <category>
                        <![CDATA[ image classification ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Machine Learning ]]>
                    </category>
                
                    <category>
                        <![CDATA[ TensorFlow ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 21 Aug 2018 19:50:51 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/0*GYFA_HewF-gOUuHQ.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Kevin Scott</p>
<p>Machine Learning has a reputation for demanding lots of data and powerful GPU computations. This leads many people to believe that building custom machine learning models for their specific dataset is impractical without a large investment of time and resources. In fact, you can leverage Transfer Learning on the web to train an accurate image classifier in less than a minute with just a few labeled images.</p>
<h3 id="heading-whats-image-classification-used-for">What’s Image Classification Used For?</h3>
<p>Teaching a machine to classify images has a wide range of practical applications. You may have seen image classification at work in your photos app, automatically suggesting friends or locations for tagging. Image Classification can be used to <a target="_blank" href="https://www.kaggle.com/c/data-science-bowl-2017">recognize cancer cells</a>, to <a target="_blank" href="https://www.kaggle.com/c/airbus-ship-detection">recognize ships in satelitte imagery</a>, or to <a target="_blank" href="https://www.kaggle.com/c/yelp-restaurant-photo-classification">automatically classify images on Yelp</a>. It can even be used beyond the realm of images, analyzing heat maps of user activity for potential fraud, or Fourier transforms of audio waves.</p>
<p>I recently <a target="_blank" href="https://github.com/thekevinscott/ml-classifier">released an open source tool</a> to quickly train image classification models in your browser. Here’s how it works:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*Qt2zblWA9QDpqAilqovj3Q.gif" alt="Image" width="748" height="789" loading="lazy">
_[https://thekevinscott.github.io/ml-classifier-ui/](https://thekevinscott.github.io/ml-classifier-ui/" rel="noopener" target="<em>blank" title=")</em></p>
<p><a target="_blank" href="http://thekevinscott.github.io/ml-classifier-ui/">Embedded here</a> is a live demo of the tool you can use. <a target="_blank" href="https://github.com/thekevinscott/dataset-tutorial-for-image-classification/data%3CPaste%3E">I’ve put together a dataset for testing here</a> (or feel free to build your own). The dataset has 10 images I downloaded from each of the three most popular searches on <a target="_blank" href="https://pexels.com">pexels.com</a> : Mobile”, “Wood”, and “Notebook”.</p>
<p>Drag the <strong>train</strong> folder into the drop zone, and once the model is trained, upload the <strong>validation</strong> folder to see how well your model can classify novel images.</p>
<h4 id="heading-how-does-this-work">How does this work?</h4>
<p>Transfer Learning is the special sauce that makes it possible to train extremely accurate models in your browser in a fraction of the time. Models are trained on large corpuses of data, and saved as pretrained models. Those pretrained models’ final layers can then be tuned to your specific use case.</p>
<p>This works particularly well in the realm of computer vision, because so many features of images are generalizable. Rob Fergus and Matthew Zeiler <a target="_blank" href="https://arxiv.org/abs/1311.2901">demonstrate in their paper</a> the features learned at the early stages of their model:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/0*5QnKBdFPZXYYP1EZ.png" alt="Image" width="800" height="310" loading="lazy">
<em>Low Level Features</em></p>
<p>The model is beginning to recognize generic features, including lines, circles, and shapes, that are applicable to any set of images. After a few more layers, it’s able to recognize more complex shapes like edges and words:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/0*1oDY_PFqmMdtvteE.png" alt="Image" width="800" height="494" loading="lazy">
<em>Higher Level Features</em></p>
<p>The vast majority of images share general features such as lines and circles. Many share higher level features, things like an “eye” or a “nose”. This allows you to reuse the existing training that’s already been done, and tune just the last few layers on your specific dataset, which is faster and requires less data than training from scratch.</p>
<p>How much less data? <strong>It depends</strong>. How different your data is from your pre-trained model, how complex or variable your data is, and other factors can all play into your accuracy. With the example above, I got to 100% accuracy with 30 images. For something like dogs and cats, just a handful of images is enough to get good results. <a target="_blank" href="https://medium.com/@bingobee01/how-much-data-to-you-need-ba834d074f3a">Adrian G has put together a more rigorous analysis on his blog</a>.</p>
<p>So, it depends on your dataset, but it’s probably less than you think.</p>
<h3 id="heading-show-me-the-code">Show me the Code!</h3>
<p>Next, we’ll look at how to import and tune a pretrained model in JavaScript. We’ll tune <a target="_blank" href="https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet_v1.md">MobileNet</a>, a pretrained model produced by Google.</p>
<blockquote>
<p>MobileNets are a class of convolutional neural network designed by researches at Google. They are coined “mobile-first” in that they’re architected from the ground up to be resource-friendly and run quickly, right on your phone. — <a target="_blank" href="https://hackernoon.com/creating-insanely-fast-image-classifiers-with-mobilenet-in-tensorflow-f030ce0a2991">Matt Harvey</a></p>
</blockquote>
<p>MobileNet is trained on a huge corpus of images called <a target="_blank" href="http://www.image-net.org/">ImageNet</a>, containing over 14 million labeled images belonging to a 1000 different categories. If you download <code>mobilenet_v1_0.25_224</code>, you'll see a structure of files like:</p>
<pre><code>mobilenet_v1_0<span class="hljs-number">.25</span>_224.ckpt.data<span class="hljs-number">-00000</span>-<span class="hljs-keyword">of</span><span class="hljs-number">-00001</span>mobilenet_v1_0<span class="hljs-number">.25</span>_224.ckpt.indexmobilenet_v1_0<span class="hljs-number">.25</span>_224.ckpt.metamobilenet_v1_0<span class="hljs-number">.25</span>_224.tflitemobilenet_v1_0<span class="hljs-number">.25</span>_224_eval.pbtxtmobilenet_v1_0<span class="hljs-number">.25</span>_224_frozen.pbmobilenet_v1_0<span class="hljs-number">.25</span>_224_info.txt
</code></pre><p>Within <code>mobilenet_v1_0.25_224_eval.pbtxt</code>, note the <code>shape</code> attribute:</p>
<pre><code>attr {    <span class="hljs-attr">key</span>: <span class="hljs-string">"shape"</span>    value {      shape {        dim {          <span class="hljs-attr">size</span>: <span class="hljs-number">-1</span>        }        dim {          <span class="hljs-attr">size</span>: <span class="hljs-number">224</span>        }        dim {          <span class="hljs-attr">size</span>: <span class="hljs-number">224</span>        }        dim {          <span class="hljs-attr">size</span>: <span class="hljs-number">3</span>        }      }    }  }
</code></pre><p>This tells us that the first layer of this MobileNet expects to receive a Tensor of Rank 4 with dimensions <code>[any, 224, 224, 3]</code>. (If you're wondering what a Tensor is, <a target="_blank" href="https://thekevinscott.com/tensors-in-javascript/">check out this article first</a>.)</p>
<h4 id="heading-importing-and-setup">Importing and Setup</h4>
<p><a target="_blank" href="https://github.com/thekevinscott/dataset-tutorial-for-image-classification">I’ve set up a repo with the necessary packages</a> to get you going. Clone it and follow the readme instructions to install the packages and run it. In <code>index.js</code>, import Tensorflow.js with:</p>
<pre><code><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> tf <span class="hljs-keyword">from</span> <span class="hljs-string">'@tensorflow/tfjs'</span>;
</code></pre><p>Tensorflow.js provides a function to load a pretrained model asynchronously. We’ll use this to load MobileNet:</p>
<pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">loadMobilenet</span>(<span class="hljs-params"></span>) </span>{  <span class="hljs-keyword">return</span> tf.loadModel(<span class="hljs-string">'https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v1_0.25_224/model.json'</span>);}
</code></pre><h4 id="heading-data-pipelines">Data Pipelines</h4>
<p>At the heart of your machine learning model is data. Building a solid pipeline for processing your data is crucial for success. Often, a <a target="_blank" href="https://thekevinscott.com/dealing-with-mnist-image-data-in-tensorflowjs/">majority of your time will be spent working with your data pipeline</a>.</p>
<blockquote>
<p>It may be surprising to the academic community to know that only a tiny fraction of the code in many machine learning systems is actually doing “machine learning”. When we recognize that a mature system might end up being (at most) 5% machine learning code and (at least) 95% glue code, reimplementation rather than reuse of a clumsy API looks like a much better strategy. — <a target="_blank" href="https://ai.google/research/pubs/pub43146">D. Sculley et all</a></p>
</blockquote>
<p>There’s a few common ways you’ll see image data structured:</p>
<ol>
<li>A list of folders containing images, where the folder name is the label</li>
<li>Images in a single folder, with images named by label (<code>dog-1</code>, <code>dog-2</code>)</li>
<li>Images in a single folder, and a csv or other file with a mapping of label to file</li>
</ol>
<p>There’s no right way to organize your images. Choose whatever format makes sense for you and your team. This dataset is organized by folder.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/0*1zvnB0hbDmMB3BML.gif" alt="Image" width="300" height="220" loading="lazy"></p>
<p>Our data processing pipeline will consist of four parts:</p>
<ol>
<li>Load the image (and turn it into a tensor)</li>
<li>Crop the image</li>
<li>Resize the image</li>
<li>Translate the Tensor into an appropriate input format</li>
</ol>
<h4 id="heading-1-loading-the-image">1. Loading the Image</h4>
<p>Since our machine learning model expects <a target="_blank" href="https://thekevinscott.com/tensors-in-javascript/">Tensors</a>, the first step is to load the image and translate its pixel data into a Tensor. Browsers provide many convenient tools to load images and read pixels, and Tensorflow.js provides a function to convert an <code>Image</code> object into a Tensor. (If you're in Node, you'll have to handle this yourself). This function will take a <code>src</code> URL of the image, load the image, and returns a promise resolving with a 3D Tensor of shape <code>[width, height, color_channels]</code>:</p>
<pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">loadImage</span>(<span class="hljs-params">src</span>) </span>{  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> {    <span class="hljs-keyword">const</span> img = <span class="hljs-keyword">new</span> Image();    img.src = src;    img.onload = <span class="hljs-function">() =&gt;</span> resolve(tf.fromPixels(img));    img.onerror = <span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> reject(err);  });}
</code></pre><h4 id="heading-2-cropping-the-image">2. Cropping the Image</h4>
<p>Many classifiers expect square images. This is not a strict requirement. If you build your own model, you can specify any size resolution you want. However, standard CNN architectures expect that images be of a <strong>fixed size</strong>. Given this necessity, many pretrained models accept squares, in order to support the widest variety of image ratios. (Squares also provide flexibility for handling a variety of <a target="_blank" href="https://medium.com/ymedialabs-innovation/data-augmentation-techniques-in-cnn-using-tensorflow-371ae43d5be9">data augmentation techniques</a>).</p>
<p>We determined above that MobileNet expects 224x224 square images, so we’ll need to first crop our images. We do that by chopping off the edges of the longer side:</p>
<pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">cropImage</span>(<span class="hljs-params">img</span>) </span>{  <span class="hljs-keyword">const</span> width = img.shape[<span class="hljs-number">0</span>];  <span class="hljs-keyword">const</span> height = img.shape[<span class="hljs-number">1</span>];  <span class="hljs-comment">// use the shorter side as the size to which we will crop  const shorterSide = Math.min(img.shape[0], img.shape[1]);  // calculate beginning and ending crop points  const startingHeight = (height - shorterSide) / 2;  const startingWidth = (width - shorterSide) / 2;  const endingHeight = startingHeight + shorterSide;  const endingWidth = startingWidth + shorterSide;  // return image data cropped to those points  return img.slice([startingWidth, startingHeight, 0], [endingWidth, endingHeight, 3]);}</span>
</code></pre><h4 id="heading-3-resizing-the-image">3. Resizing the image</h4>
<p>Now that our image is square, we can resize it to 224x224. This part is easy: Tensorflow.js provides a resize method out of the box:</p>
<pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">resizeImage</span>(<span class="hljs-params">image</span>) </span>{  <span class="hljs-keyword">return</span> tf.image.resizeBilinear(image, [<span class="hljs-number">224</span>, <span class="hljs-number">224</span>]);}
</code></pre><h4 id="heading-4-translate-the-tensor">4. Translate the Tensor</h4>
<p>Recall that our model expects an input object of the shape <code>[any, 224, 224, 3]</code>. This is known as a Tensor of Rank 4. This dimension refers to the number of training examples. If you have 10 training examples, that would be <code>[10, 224, 224, 3]</code>.</p>
<p>We also want our pixel data as a floating point number between -1 and 1, instead of integer data between 0 and 255, a process called normalization. While <a target="_blank" href="https://stackoverflow.com/questions/4674623/why-do-we-have-to-normalize-the-input-for-an-artificial-neural-network">neural networks are generally agnostic to the size</a> of the numbers coming in, using smaller numbers can help the network train faster.</p>
<p>We can build a function that expands our Tensor and translates the integers into floats with:</p>
<pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">batchImage</span>(<span class="hljs-params">image</span>) </span>{  <span class="hljs-comment">// Expand our tensor to have an additional dimension, whose size is 1  const batchedImage = image.expandDims(0);  // Turn pixel data into a float between -1 and 1.  return batchedImage.toFloat().div(tf.scalar(127)).sub(tf.scalar(1));}</span>
</code></pre><h4 id="heading-the-final-pipeline">The Final Pipeline</h4>
<p>Putting all the above functions together into a single function, we get:</p>
<pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">loadAndProcessImage</span>(<span class="hljs-params">image</span>) </span>{  <span class="hljs-keyword">const</span> croppedImage = cropImage(image);  <span class="hljs-keyword">const</span> resizedImage = resizeImage(croppedImage);  <span class="hljs-keyword">const</span> batchedImage = batchImage(resizedImage);  <span class="hljs-keyword">return</span> batchedImage;}
</code></pre><p>We can now use this function to test that our data pipeline is set up correctly. We’ll import an image whose label is known (a drum) and see if the prediction matches the expected label:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/0*JSsuxhIFdn2S4w_x.jpg" alt="Image" width="356" height="500" loading="lazy"></p>
<pre><code><span class="hljs-keyword">import</span> drum <span class="hljs-keyword">from</span> <span class="hljs-string">'./data/pretrained-model-data/drum.jpg'</span>;loadMobilenet().then(<span class="hljs-function"><span class="hljs-params">pretrainedModel</span> =&gt;</span> {  loadImage(drum).then(<span class="hljs-function"><span class="hljs-params">img</span> =&gt;</span> {    <span class="hljs-keyword">const</span> processedImage = loadAndProcessImage(img);    <span class="hljs-keyword">const</span> prediction = pretrainedModel.predict(processedImage);    <span class="hljs-comment">// Because of the way Tensorflow.js works, you must call print on a Tensor instead of console.log.    prediction.print();  });});</span>
</code></pre><p>You should see something like:</p>
<pre><code>[[<span class="hljs-number">0.0000273</span>, <span class="hljs-number">5e-7</span>, <span class="hljs-number">4e-7</span>, ..., <span class="hljs-number">0.0001365</span>, <span class="hljs-number">0.0001604</span>, <span class="hljs-number">0.0003134</span>],]
</code></pre><p>If we inspect the shape of this Tensor, we’ll see that it is <code>[1, 1000]</code>. MobileNet returns a Tensor containing a prediction for every category, and since MobileNet has learned 1000 classes, we receive 1000 predictions, each representing the probability that the given image belongs to a given class.</p>
<p>In order to get an actual prediction, we need to determine the most likely prediction. We flatten the tensor to 1 dimension and get the max value, which corresponds to our most confident prediction:</p>
<pre><code>prediction.as1D().argMax().print();
</code></pre><p>This should produce:</p>
<pre><code><span class="hljs-number">541</span>
</code></pre><p>In the repo you’ll find <a target="_blank" href="https://github.com/thekevinscott/dataset-tutorial-for-image-classification/blob/master/imagenet_labels.json">a copy of the ImageNet class definitions in JSON format</a>. You can import that JSON file to translate the numeric prediction into an actual string:</p>
<pre><code><span class="hljs-keyword">import</span> labels <span class="hljs-keyword">from</span> <span class="hljs-string">'./imagenet_labels.json'</span>;loadMobilenet().then(<span class="hljs-function"><span class="hljs-params">pretrainedModel</span> =&gt;</span> {  ...  const labelPrediction = prediction.as1D().argMax().dataSync()[<span class="hljs-number">0</span>];  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`    Numeric prediction is <span class="hljs-subst">${labelPrediction}</span>    The predicted label is <span class="hljs-subst">${labels[labelPrediction]}</span>    The actual label is drum, membranophone, tympan  `</span>);});
</code></pre><p>You should see that <code>541</code> corresponds to <code>drum, membranophone, tympan</code>, which is the category our image comes from. At this point you have a working pipeline and the ability to leverage MobileNet to predict ImageNet images.</p>
<p>Now let’s look at how to tune MobileNet on your specific dataset.</p>
<h4 id="heading-training-the-model">Training The Model</h4>
<p><img src="https://cdn-media-1.freecodecamp.org/images/0*RchavRu2U0ozZZKl.gif" alt="Image" width="480" height="362" loading="lazy"></p>
<p>We want to build a model that successfully predicts <strong>novel data</strong> — that is, data it hasn’t seen before.</p>
<p>To do this, you first train the model on labeled data — data that has already been identified — and you validate the model’s performance on other labeled data <em>it hasn’t seen before</em>.</p>
<blockquote>
<p>Supervised learning reverses this process, solving for m and b, given a set of x’s and y’s. In supervised learning, you start with many particulars — the data — and infer the general equation. And the learning part means you can update the equation as you see more x’s and y’s, changing the slope of the line to better fit the data. The equation almost never identifies the relationship between each x and y with 100% accuracy, but the generalization is powerful because later on you can use it to do algebra on new data. — <a target="_blank" href="https://hbr.org/2017/10/how-to-spot-a-machine-learning-opportunity-even-if-you-arent-a-data-scientist">Kathryn Hume</a></p>
</blockquote>
<p>When you trained the model above by dragging the <code>training</code> folder in, the model produced a training score. This indicates how many images the classifier was able to learn to successfully predict out of the training set. The second number it produced indicated how many images it could predict that it <em>hadn't seen before</em>. This second score is the one you want to optimize for (well, you want to optimize for both, but the latter number is more applicable to novel data).</p>
<p>We’re going to train on the <strong>colors</strong> dataset. In the repo, you’ll find a folder <code>data/colors</code> that contains:</p>
<pre><code>validation/  blue/    blue<span class="hljs-number">-3.</span>png  red/    red<span class="hljs-number">-3.</span>pngtraining/  blue/    blue<span class="hljs-number">-1.</span>png    blue<span class="hljs-number">-2.</span>png  red/    red<span class="hljs-number">-1.</span>png    red<span class="hljs-number">-2.</span>png
</code></pre><p>Building machine learning models, I’ve found that <em>code-related</em> errors — a missing variable, an inability to compile — are fairly straight forward to fix, whereas <em>training</em> errors — the labels were in an incorrect order, or the images were being cropped incorrectly — are devilish to debug. Testing exhaustively and setting up sanity test cases can help save you a few gray hairs.</p>
<p>The <code>data/colors</code> folder provides a list of solid red and blue colors that are guaranteed to be easy to train with. We'll use these to train our model and ensure that our machine learning code learns correctly, before attempting with a more complicated dataset.</p>
<pre><code><span class="hljs-keyword">import</span> blue1 <span class="hljs-keyword">from</span> <span class="hljs-string">'../data/colors/training/blue/blue-1.png'</span>;<span class="hljs-keyword">import</span> blue2 <span class="hljs-keyword">from</span> <span class="hljs-string">'../data/colors/training/blue/blue-2.png'</span>;<span class="hljs-keyword">import</span> blue3 <span class="hljs-keyword">from</span> <span class="hljs-string">'../data/colors/validation/blue/blue-3.png'</span>;<span class="hljs-keyword">import</span> red1 <span class="hljs-keyword">from</span> <span class="hljs-string">'../data/colors/training/red/red-1.png'</span>;<span class="hljs-keyword">import</span> red2 <span class="hljs-keyword">from</span> <span class="hljs-string">'../data/colors/training/red/red-2.png'</span>;<span class="hljs-keyword">import</span> red3 <span class="hljs-keyword">from</span> <span class="hljs-string">'../data/colors/validation/red/red-3.png'</span>;<span class="hljs-keyword">const</span> training = [  blue1,  blue2,  red1,  red2,];<span class="hljs-comment">// labels should match the positions of their associated imagesconst labels = [  'blue',  'blue',  'red',  'red',];</span>
</code></pre><p>When we previously loaded MobileNet, we used the model without any modifications. When training, we want to use a subset of its layers — specifically, we want to ignore the final layers that produce the one-of-1000 classification. You can inspect the structure of a pretrained model with <code>.summary()</code>:</p>
<pre><code>loadMobilenet().then(<span class="hljs-function"><span class="hljs-params">mobilenet</span> =&gt;</span> {  mobilenet.summary();});
</code></pre><p>In your console should be the model output, and near the end you should see something like:</p>
<pre><code>conv_dw_13_bn (BatchNormaliz [<span class="hljs-literal">null</span>,<span class="hljs-number">7</span>,<span class="hljs-number">7</span>,<span class="hljs-number">256</span>]            <span class="hljs-number">1024</span>      _________________________________________________________________conv_dw_13_relu (Activation) [<span class="hljs-literal">null</span>,<span class="hljs-number">7</span>,<span class="hljs-number">7</span>,<span class="hljs-number">256</span>]            <span class="hljs-number">0</span>         _________________________________________________________________conv_pw_13 (Conv2D)          [<span class="hljs-literal">null</span>,<span class="hljs-number">7</span>,<span class="hljs-number">7</span>,<span class="hljs-number">256</span>]            <span class="hljs-number">65536</span>     _________________________________________________________________conv_pw_13_bn (BatchNormaliz [<span class="hljs-literal">null</span>,<span class="hljs-number">7</span>,<span class="hljs-number">7</span>,<span class="hljs-number">256</span>]            <span class="hljs-number">1024</span>      _________________________________________________________________conv_pw_13_relu (Activation) [<span class="hljs-literal">null</span>,<span class="hljs-number">7</span>,<span class="hljs-number">7</span>,<span class="hljs-number">256</span>]            <span class="hljs-number">0</span>         _________________________________________________________________global_average_pooling2d_1 ( [<span class="hljs-literal">null</span>,<span class="hljs-number">256</span>]                <span class="hljs-number">0</span>         _________________________________________________________________reshape_1 (Reshape)          [<span class="hljs-literal">null</span>,<span class="hljs-number">1</span>,<span class="hljs-number">1</span>,<span class="hljs-number">256</span>]            <span class="hljs-number">0</span>         _________________________________________________________________dropout (Dropout)            [<span class="hljs-literal">null</span>,<span class="hljs-number">1</span>,<span class="hljs-number">1</span>,<span class="hljs-number">256</span>]            <span class="hljs-number">0</span>         _________________________________________________________________conv_preds (Conv2D)          [<span class="hljs-literal">null</span>,<span class="hljs-number">1</span>,<span class="hljs-number">1</span>,<span class="hljs-number">1000</span>]           <span class="hljs-number">257000</span>    _________________________________________________________________act_softmax (Activation)     [<span class="hljs-literal">null</span>,<span class="hljs-number">1</span>,<span class="hljs-number">1</span>,<span class="hljs-number">1000</span>]           <span class="hljs-number">0</span>         _________________________________________________________________reshape_2 (Reshape)          [<span class="hljs-literal">null</span>,<span class="hljs-number">1000</span>]               <span class="hljs-number">0</span>         =================================================================Total params: <span class="hljs-number">475544</span>Trainable params: <span class="hljs-number">470072</span>Non-trainable params: <span class="hljs-number">5472</span>_________________________________________________________________
</code></pre><p>What we’re looking for is the final <code>Activation</code> layer that is not <code>softmax</code> (<code>[softmax](https://en.wikipedia.org/wiki/Softmax_function)</code> <a target="_blank" href="https://en.wikipedia.org/wiki/Softmax_function">is the activation</a> used to boil the predictions down to one of a thousand categories). That layer is <code>conv_pw_13_relu</code>. We return a pretrained model that includes everything up to that activation layer:</p>
<pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">buildPretrainedModel</span>(<span class="hljs-params"></span>) </span>{  <span class="hljs-keyword">return</span> loadMobilenet().then(<span class="hljs-function"><span class="hljs-params">mobilenet</span> =&gt;</span> {    <span class="hljs-keyword">const</span> layer = mobilenet.getLayer(<span class="hljs-string">'conv_pw_13_relu'</span>);    <span class="hljs-keyword">return</span> tf.model({      <span class="hljs-attr">inputs</span>: mobilenet.inputs,      <span class="hljs-attr">outputs</span>: layer.output,    });  });}
</code></pre><p>Let’s write a function to loop through an array of images and return a Promise that resolves when they load.</p>
<pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">loadImages</span>(<span class="hljs-params">images, pretrainedModel</span>) </span>{  <span class="hljs-keyword">let</span> promise = <span class="hljs-built_in">Promise</span>.resolve();  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; images.length; i++) {    <span class="hljs-keyword">const</span> image = images[i];    promise = promise.then(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> {      <span class="hljs-keyword">return</span> loadImage(image).then(<span class="hljs-function"><span class="hljs-params">loadedImage</span> =&gt;</span> {        <span class="hljs-comment">// Note the use of `tf.tidy` and `.dispose()`. These are two memory management        // functions that Tensorflow.js exposes.        // https://js.tensorflow.org/tutorials/core-concepts.html        //        // Handling memory management is crucial for building a performant machine learning        // model in a browser.        return tf.tidy(() =&gt; {          const processedImage = loadAndProcessImage(loadedImage, pretrainedModel);          if (data) {            const newData = data.concat(processedImage);            data.dispose();            return newData;          }          return tf.keep(processedImage);        });      });    });  }  return promise;}</span>
</code></pre><p>We build a sequential promise that iterates over each image and processes it. Alternatively, you can use <code>Promise.all</code> to load images in parallel, but be aware of UI performance if you do that.</p>
<p>Putting those functions together, we get:</p>
<pre><code>buildPretrainedModel().then(<span class="hljs-function"><span class="hljs-params">pretrainedModel</span> =&gt;</span> {  loadImages(training, pretrainedModel).then(<span class="hljs-function"><span class="hljs-params">xs</span> =&gt;</span> {    xs.print();  })});
</code></pre><p>Calling your data “x” and “y” is <a target="_blank" href="https://datascience.stackexchange.com/questions/17598/why-are-variables-of-train-and-test-data-defined-using-the-capital-letter-in-py">a convention in the machine learning world</a>, carrying over from its mathematical origins. You can call your variables whatever you want, but I find it useful to stick to the conventions where I can.</p>
<h4 id="heading-labels">Labels</h4>
<p>Next, you’ll need to convert your labels into numeric form. However, it’s not as simple as assigning a number to each category. To demonstrate, let’s say you’re classifying three categories of fruit:</p>
<pre><code>raspberry - <span class="hljs-number">0</span>blueberry - <span class="hljs-number">1</span>strawberry - <span class="hljs-number">2</span>
</code></pre><p>Denoting numbers like this can imply a relationship where one does not exist, since these numbers are considered <em>ordinal</em> values. They imply some order in the data. Real world consequences of this might be that the network decides that a blueberry is something that is halfway between a raspberry and a strawberry, or that a strawberry is the “best” of the berries.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/0*DTD_3Ji4G-O6Go8t.gif" alt="Image" width="450" height="450" loading="lazy"></p>
<p>To prevent these incorrect assumptions, we use a process called “one hot encoding”, resulting in data that looks like:</p>
<pre><code>raspberry  - [<span class="hljs-number">1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>]blueberry  - [<span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>]strawberry - [<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>]
</code></pre><p>(Two great articles that go into more depth on one hot encoding are <a target="_blank" href="https://hackernoon.com/what-is-one-hot-encoding-why-and-when-do-you-have-to-use-it-e3c6186d008f">here</a> and <a target="_blank" href="https://machinelearningmastery.com/why-one-hot-encode-data-in-machine-learning/">here</a>.) We can leverage Tensorflow.js’s built in <code>oneHot</code> functions to translate our labels:</p>
<pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">oneHot</span>(<span class="hljs-params">labelIndex, classLength</span>) </span>{  <span class="hljs-keyword">return</span> tf.tidy(<span class="hljs-function">() =&gt;</span> tf.oneHot(tf.tensor1d([labelIndex]).toInt(), classLength));};
</code></pre><p>This function takes a particular number (<code>labelIndex</code>, a number that corresponds to a label) and translates it to a one hot encoding, given some number of classes (<code>classLength</code>). We can use the function with the following bit of code, that first builds a mapping of numbers-to-labels off the incoming array of labels, and then builds a Tensor containing those one-hot encoded labels:</p>
<pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getLabelsAsObject</span>(<span class="hljs-params">labels</span>) </span>{  <span class="hljs-keyword">let</span> labelObject = {};  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; labels.length; i++) {    <span class="hljs-keyword">const</span> label = labels[i];    <span class="hljs-keyword">if</span> (labelObject[label] === <span class="hljs-literal">undefined</span>) {      <span class="hljs-comment">// only assign it if we haven't seen it before      labelObject[label] = Object.keys(labelObject).length;    }  }  return labelObject;}function addLabels(labels) {  return tf.tidy(() =&gt; {    const classes = getLabelsAsObject(labels);    const classLength = Object.keys(classes).length;    let ys;    for (let i = 0; i &lt; labels.length; i++) {      const label = labels[i];      const labelIndex = classes[label];      const y = oneHot(labelIndex, classLength);      if (i === 0) {        ys = y;      } else {        ys = ys.concat(y, 0);      }    }    return ys;  });};</span>
</code></pre><p>Now that we have our data, we can build our model. You are welcome to innovate at this stage, but I find that building on others’ conventions tends to produce a good enough model in most cases. We’ll look to the <a target="_blank" href="https://github.com/tensorflow/tfjs-examples/tree/master/webcam-transfer-learning">Webcam Tensorflow.js example</a> for a well structured transfer learning model we’ll reuse largely verbatim.</p>
<p>Things worth highlighting are that the first layer matches the output shape of our pretrained model, and the final <code>softmax</code> layer corresponds to the number of labels, defined as <code>numberOfClasses</code>. 100 units on the second layer is arbitrary, and you can absolutely experiment with changing this number for your particular use case.</p>
<pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getModel</span>(<span class="hljs-params">numberOfClasses</span>) </span>{  <span class="hljs-keyword">const</span> model = tf.sequential({    <span class="hljs-attr">layers</span>: [      tf.layers.flatten({<span class="hljs-attr">inputShape</span>: [<span class="hljs-number">7</span>, <span class="hljs-number">7</span>, <span class="hljs-number">256</span>]}),      tf.layers.dense({        <span class="hljs-attr">units</span>: <span class="hljs-number">100</span>,        <span class="hljs-attr">activation</span>: <span class="hljs-string">'relu'</span>,        <span class="hljs-attr">kernelInitializer</span>: <span class="hljs-string">'varianceScaling'</span>,        <span class="hljs-attr">useBias</span>: <span class="hljs-literal">true</span>      }),      tf.layers.dense({        <span class="hljs-attr">units</span>: numberOfClasses,        <span class="hljs-attr">kernelInitializer</span>: <span class="hljs-string">'varianceScaling'</span>,        <span class="hljs-attr">useBias</span>: <span class="hljs-literal">false</span>,        <span class="hljs-attr">activation</span>: <span class="hljs-string">'softmax'</span>      })    ],  });  model.compile({    <span class="hljs-attr">optimizer</span>: tf.train.adam(<span class="hljs-number">0.0001</span>),    <span class="hljs-attr">loss</span>: <span class="hljs-string">'categoricalCrossentropy'</span>,    <span class="hljs-attr">metrics</span>: [<span class="hljs-string">'accuracy'</span>],  });  <span class="hljs-keyword">return</span> model;}
</code></pre><p>Here are various links if you want to go into a little more depth on the neural networks’ internal parts:</p>
<ul>
<li><code>[tf.sequential](https://js.tensorflow.org/api/0.12.0/#sequential)</code></li>
<li><code>[tf.layers.flatten](https://js.tensorflow.org/api/0.12.0/#layers.flatten)</code></li>
<li><code>[tf.layers.dense](https://js.tensorflow.org/api/0.12.0/#layers.dense)</code></li>
<li><a target="_blank" href="https://www.kaggle.com/dansbecker/rectified-linear-units-relu-in-deep-learning">the activation <code>relu</code></a></li>
<li><code>[adam](https://machinelearningmastery.com/adam-optimization-algorithm-for-deep-learning/)</code> <a target="_blank" href="https://machinelearningmastery.com/adam-optimization-algorithm-for-deep-learning/">optimizer</a></li>
<li><code>[categoricalCrossentropy](https://keras.io/losses/)</code> <a target="_blank" href="https://keras.io/losses/">loss</a></li>
</ul>
<p>The final step is actually train the model, which we do by calling <code>.fit()</code> on the model. We shuffle our training images so the model doesn't learn to rely on the order of the incoming training data, and we train for 20 epochs. (An epoch denotes one cycle through your entire training set.)</p>
<pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">makePrediction</span>(<span class="hljs-params">pretrainedModel, image, expectedLabel</span>) </span>{  loadImage(image).then(<span class="hljs-function"><span class="hljs-params">loadedImage</span> =&gt;</span> {    <span class="hljs-keyword">return</span> loadAndProcessImage(loadedImage, pretrainedModel);  }).then(<span class="hljs-function"><span class="hljs-params">loadedImage</span> =&gt;</span> {    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Expected Label'</span>, expectedLabel);    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Predicted Label'</span>, predict(model, loadedImage));    loadedImage.dispose();  });}buildPretrainedModel().then(<span class="hljs-function"><span class="hljs-params">pretrainedModel</span> =&gt;</span> {  loadImages(training, pretrainedModel).then(<span class="hljs-function"><span class="hljs-params">xs</span> =&gt;</span> {    <span class="hljs-keyword">const</span> ys = addLabels(labels);    <span class="hljs-keyword">const</span> model = getModel(<span class="hljs-number">2</span>);    model.fit(xs, ys, {      <span class="hljs-attr">epochs</span>: <span class="hljs-number">20</span>,      <span class="hljs-attr">shuffle</span>: <span class="hljs-literal">true</span>,    }).then(<span class="hljs-function"><span class="hljs-params">history</span> =&gt;</span> {      <span class="hljs-comment">// make predictions      makePrediction(pretrainedModel, blue3, "0");      makePrediction(pretrainedModel, red3, "1");    });  });});</span>
</code></pre><p>How many epochs should you run for?</p>
<blockquote>
<p>Unfortunately, there is no right answer to this question. The answer is different for different datasets but you can say that the numbers of epochs is related to how diverse your data is — <a target="_blank" href="https://towardsdatascience.com/epoch-vs-iterations-vs-batch-size-4dfb9c7ce9c9">Sagar Sharma</a></p>
</blockquote>
<p>Basically, you can run it until it’s good, or until it’s clear it’s not working, or you run out of time.</p>
<p>You should see 100% accuracy in the training above. Try modifying the code to work on the <a target="_blank" href="https://github.com/thekevinscott/dataset-tutorial-for-image-classification/tree/master/data/pexel-images">Pexels dataset</a>. I found in my testing that my accuracy numbers fall a little bit with this more complex dataset.</p>
<h4 id="heading-final-thoughts">Final thoughts</h4>
<p>In summary, it’s cheap and fast to build on top of a pretrained model and get a classifier that is pretty darn accurate.</p>
<p>When coding machine learning, be careful to test your code at each section of the process and validate with data you know works. It pays to set up a stable and reusable data pipeline early in your process, since so much of your time is spent working with your data.</p>
<p>Finally, if you’re interested in learning more about training CNNs from scratch, a great place to start is <a target="_blank" href="https://fastai.com">Fast.ai</a>’s tutorials for hackers. It’s built in Python but you can translate the ideas in Node.js if you want to stay in Javascript.</p>
<p>Originally published at <a target="_blank" href="https://thekevinscott.com/image-classification-with-javascript">https://thekevinscott.com</a></p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
