<?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" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Jiwon Jeong on Medium]]></title>
        <description><![CDATA[Stories by Jiwon Jeong on Medium]]></description>
        <link>https://medium.com/@jiwon.jeong?source=rss-1df293b5ca56------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*Yqh4zWBcAo7nEUw9dwNSmg.jpeg</url>
            <title>Stories by Jiwon Jeong on Medium</title>
            <link>https://medium.com/@jiwon.jeong?source=rss-1df293b5ca56------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 15:38:53 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@jiwon.jeong/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Deep Dive into the Computer Vision World: Part 3]]></title>
            <link>https://medium.com/data-science/deep-dive-into-the-computer-vision-world-part-3-abd7fd2c64ef?source=rss-1df293b5ca56------2</link>
            <guid isPermaLink="false">https://medium.com/p/abd7fd2c64ef</guid>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[computer-vision]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[deep-learning]]></category>
            <category><![CDATA[object-detection]]></category>
            <dc:creator><![CDATA[Jiwon Jeong]]></dc:creator>
            <pubDate>Tue, 27 Aug 2019 12:43:28 GMT</pubDate>
            <atom:updated>2019-09-01T07:57:42.121Z</atom:updated>
            <content:encoded><![CDATA[<p>YOLO, SSD, FPN and RetinaNet, more unified ones</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ziVCF1iI0jBw39NLjXo3Sw.jpeg" /></figure><p>Reading papers can be uncomfortable and scary at first. And one way to overcome this is by reading a well-written article in an easier tone. If a paper you picked is challenging to digest, get some intuition from a demystifying video or a blog (like this one 😉). After you get the core concept of the network, then try again with the original one. It’ll become more comfortable and more fun to read than just jumping right into the hard one.</p><p>This is the third story of “Deep Dive into the Computer Vision world,” and the complete set of the series is as follows:</p><ol><li><a href="https://towardsdatascience.com/deep-dive-into-the-computer-vision-world-f35cd7349e16?source=friends_link&amp;sk=449ea5da20c884dadca23b907efb7e13">Starting from VGG, ResNet, Inception Network and MobileNet</a></li><li><a href="https://towardsdatascience.com/deep-dive-into-the-computer-vision-world-part-2-7a24efdb1a14?source=friends_link&amp;sk=4fec4dfc9499c930f263c6808b2f369d">Regions with CNN, let’s start object detection</a></li><li>YOLO, SSD, FPN and RetinaNet, more unified ones</li><li>From object detection to instance segmentation (TBU)</li></ol><p>In the previous post, we discussed object detection networks- R-CNN, Fast R-CNN, and Faster R-CNN. These are based on two-stage architecture. Generating the candidates for object locations at the first stage and localizing the objects and classifying the detected objects at the second stage.</p><p>Now the next networks to be explored are SSD, YOLO, FPN and RetinaNet. These networks are based on one-stage architecture. The biggest change is that these no longer need regional proposals, hence forming a more unified and fully convolutional network, which results in brings faster performance. Now let’s see how this can be possible one by one.</p><h3>SSD</h3><p>SSD stands for <strong>Single Shot Detector, </strong>and there are three main points in its architecture. First, this network can detect in <em>multiple scales</em>. With the layers coming after the base network, it yields several feature maps with different resolutions, which enables the network to work at multi-scales.</p><p>Second, these predictions are made in a convolutional way. A set of convolutional filters make the predictions for the location of the objects and the class score. This is where the name of “Single Shot” comes from. Instead of having additional classifiers and regressors, now the detections are made in a single shot!</p><p>And lastly, there are a fixed number of default boxes associated with these feature maps. Similar to the anchors of Faster R-CNN, the default boxes are applied at each feature map cell.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*RNNGiFeFK5qldRiX" /><figcaption>The architecture of SSD</figcaption></figure><p>The entire architecture of the network is as shown above. It uses VGG-16 as its base network, and there are additional feature layers which decrease in size progressively. This can be considered to be quite complicated at first glance, so let’s split it into two parts.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/631/1*CRboa2GcQt63kXAEy6txEw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LqztolmxGOErDE4L0AFJzA.png" /></figure><p>In the picture on the left, the feature layer from the base network produces a detection by the convolutional filters. When the dimensional size of the feature layer is N x N with P channels, the filter size here is <strong>3x3xP</strong>. Now, as I said above, there are a set of default bounding boxes at each cell. So if we have K number of the boxes and C number of classes (including the “background” class), we compute the four offset values against the original default box, and the class scores for each box. Therefore the total number of filters becomes <strong>(NxN)K(4+C)</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*pOcgszypno4cZzTU-4jPwg.png" /></figure><p>SSD makes all the predictions for the bounding box location and the class scores at once with the convolutional filters. It doesn’t require a region proposal network anymore. And the same process is done at the extra feature layers in different scales, which improves accuracy significantly. Recall that the feature maps in different resolutions, see the different sizes of figures or objects in an image (<a href="https://towardsdatascience.com/deep-dive-into-the-computer-vision-world-f35cd7349e16?source=friends_link&amp;sk=449ea5da20c884dadca23b907efb7e13">I explained this with Inception Network</a>). Therefore, the prediction across scales becomes critical in detection tasks.</p><p>After we get the outcome from all these layers, we match them with the ground truth box and choose the best one. As most of the boxes are going to be overlapped heavily, we can use <a href="https://www.youtube.com/watch?v=fqTyP5KILFg&amp;t=12s"><strong>Non-Maximum Suppression</strong></a> to choose one. But still, there is quite a challenging problem, <strong>the class imbalance</strong>. When we use the regional proposals, they are reasonable candidates for having an object. But here, the network starts from a set of default boxes at each location with multiple scales and shapes. As a result, most of the boxes are going to be negative in the outcome. This creates an imbalance in classes. For example, the “Background” class becomes an easy example and the real “object” classes become hard examples. The easy negative examples can overwhelm the distribution of the classes and degenerate the model. <em>(Note that this issue is going to be dealt with again in RetinaNet)</em></p><p>To remedy this, SSD takes only some of the negative matches so that the ratio between positive and negative matches could be 3:1. This is called <strong>hard negative mining</strong>. Hard negative mining is a classic problem in object detection, which means a negative class is hard to tell as negative. A model should classify it as negative, but it gives high confidence as the negative cases become way more than the positive cases. We input only the positive matches into the objective function so that it calculates the loss between the ground truth and the predicted bounding box.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*W-KaUenxIZduPcgVBJql3g.png" /><figcaption><a href="https://arxiv.org/abs/1512.02325">The Experiment Results of SSD</a></figcaption></figure><p>One interesting thing is that about 80% of the forward time is spent on the base network, which indicates that a faster base network could even further improve the speed. And data augmentation also plays an important role for higher accuracy. Compared to Faster R-CNN using only an original image and horizontally flipping, variations in input sizes, shapes, and random sampling improve the performance. Also, it is observed that various default box shapes are better for accuracy as well.</p><h3>YOLO</h3><p>YOLO is an acronym of You Only Look Once. Similar to SSD, YOLO doesn’t use regional proposals and works in a “single-shot” way. There are 3 main variations of YOLO. Rather than covering all the transformation process, I’m going to wrap all and explore the final version. If you’d like to know all the detailed process, check out <a href="https://medium.com/@jonathan_hui/real-time-object-detection-with-yolo-yolov2-28b1b93e2088">this excellent article</a> by <a href="https://medium.com/u/bd51f1a63813">Jonathan Hui</a>. (Literally, this is the best YOLO tutorial I’ve ever seen).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BIIbBb48dFxFBgCsCBDvsA.png" /><figcaption>The architecture of YOLO Ver.3. Let’s start from its tail where the predictions are made!</figcaption></figure><p>The whole network architecture is as shown above. The numbers in the circles are the layer numbers. It uses <strong>Darknet-53</strong> as its base network, and there are additional layers for detecting objects. Let’s first put our attention at the tail where the predictions are made. YOLO is also a fully convolutional network, so it predicts the detection by convolution. After passing through several layers, the detection is made by 1x1 convolution from the feature maps in three different scales.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VxEA3CGy4XY7nOmLp_OOkA.png" /><figcaption>Figures from <a href="https://arxiv.org/abs/1506.02640">Ver.1</a> (on the left) and <a href="https://pjreddie.com/media/files/papers/YOLOv3.pdf">Ver.3</a> (on the right)</figcaption></figure><p>When the size of the feature map is N x N with P channels, 1x1xP size of filters is applied. YOLO also has the default bounding boxes (They are called <strong><em>anchors </em></strong>in the original paper). For each box at each cell, the 4 bounding box offset values, the objectness score and the class scores are predicted. The objectness score reflects how confident it is that the box contains an object. So now the number of predictions for each grid becomes <strong>5+C</strong>. So if the number of boxes is <strong>K</strong>, the total number of filters is <strong>(NxN)K(5+C)</strong> for each scale.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ldgX_i-lL4VwbQUG0s5POw.png" /><figcaption>Now let’s see the main branch line. YOLO also operates in multi-scales like SSD, yet in a way a little bit different.</figcaption></figure><p>As I said, the resolution of the feature maps gets smaller as it goes deeper, and this gives an impact on the scales when the feature maps see the objects. So to achieve multiple scales, YOLO brings higher resolution features from a previous layer simply by a passthrough layer. This is the same with the shortcut connection of ResNet.</p><p>So at the 84th layer, we take the feature map from the two previous layers and upsample it by 2. And then we bring a feature map from the 36th layer and concatenate it with the upsampled features. The same process is repeated one more time at the 96th layer for the final scale. By doing so, we can add finer and more meaningful features into the prediction.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/436/1*CLePVJmZWLxIMm3Qt8707A.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*U-KxyXPIlnEhHM3NDn1LoA.png" /><figcaption>Figures from Ver.3 (on the left) and Ver. 2 (on the right)</figcaption></figure><p>The base network was AlexNet at ver.1 and Darknet was first used at ver.2 with 19 convolutional layers. This became 53 layers at ver.3 as you can see above. Another important thing worth mentioning is that the default boxes are extracted from <strong>K-means clustering</strong>, rather than being selected by hand. The network learns to adjust the boxes during training but having better priors from the start helps the network for sure. So YOLO ver.3 extracts the 9 default boxes by clustering. By concatenating the higher resolution features from the previous layer and a passthrough layer, YOLO gets better detection with small objects compared to SSD.</p><h3>FPN</h3><p>FPN stands for <strong>Feature Pyramid Network</strong>. A network with a pyramid structure has been studied and used before this. And SSD is also a pyramid-like structure (try rotating it vertically). But it can’t be said it’s a pyramidal feature hierarchy strictly as shown in figure ©. Because it starts to form a pyramid from a higher level, not from the low level. (Remember VGG Conv5_3 was the first layer for detection)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/732/1*dlJltaGZlyh57xT1Oa2SSw.png" /><figcaption><a href="https://arxiv.org/abs/1612.03144">Figures from FPN</a></figcaption></figure><p>What’s the point having this kind of architecture? The information for the precise location of an object can be detected in the deeper level of the network. On the contrary, the semantics gets stronger in the lower level. (we’re going to talk about this in the next series). When a network has one way of just going more in-depth, there aren’t many semantic data we can use to classify objects at a pixel level. Therefore by implementing a feature pyramid network, we can produce multi-scale feature maps from all levels, and the features from all these levels are semantically strong.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4v71S9xe4p-mV8kEM-SIZg.png" /><figcaption><a href="https://arxiv.org/abs/1612.03144">The architecture of FPN</a></figcaption></figure><p>So the architecture of FPN is as shown above. There are three directions, bottom-up, top-down and lateral directions. The bottom-up pathway is the feedforward computation of the backbone ConvNet and ResNet as used in the original paper. There are 5 stages and the size of the feature map at each stage has a scaling step of 2. They are differentiated by their dimensions, and we call them as <strong>C1, C2, C3, C4 </strong>and <strong>C5</strong>. The higher the layer goes, the smaller the size of the feature maps gets along the bottom-up pathway.</p><p>Now at the level <strong>C5</strong>, we move to the top-down pathway. Upsampling is applied to the output maps at each level. And the corresponding map that has the same spatial size from the bottom-up pathway, is merged via a lateral connection. A 1x1 convolution is applied before the addition to match the depth size. Then, we apply a 3x3 convolution on the merged map to reduce <a href="https://en.m.wikipedia.org/wiki/Aliasing">aliasing effect</a> of upsampling. By doing so, we can combine the low resolution and semantically strong features (from top-down pathway) with high resolution and semantically weak features (from bottom-up pathway). This implies we can have rich semantics at all scales. And as shown in the picture, The same process is iterated and produces <strong>P2, P3 P4</strong> and <strong>P5</strong>.</p><p>Now the last part of the network is an actual classification. Actually, FPN isn’t an object detector in itself. A detector isn’t <em>built-in </em>so we need to plug in a detector from this stage. RPN and Fast R-CNN are used in the original paper, and only FPN with RPN case is depicted in the above picture. In the RPN, there are two sibling layers for an object classifier and bounding box regressor. So in FPN, this part is attached to the tail of each level of <strong>P2, P3, P4 </strong>and <strong>P5</strong>. I won’t cover much about this here, because the reason I picked FPN was to explain RetinaNet.</p><h3>RetinaNet</h3><p>RetinaNet is more about proposing a new loss function to handle class imbalance, rather than publishing a novel new network. Let’s bring the class imbalance back again. One-stage detectors such as SSD and YOLO are apparently faster but still fall behind in accuracy compared to two-stage detectors. And the class imbalance issue is one of the reasons for this drawback.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*shphqtA1fvNIpB3rNXN69A.png" /><figcaption><a href="https://arxiv.org/abs/1708.02002">Focal Loss</a></figcaption></figure><p>So the authors proposed a new loss function called <strong>focal loss </strong>by putting a weight on easy examples. The mathematical expression is as shown above. It’s a cross-entropy loss multiplied with a modulating factor. The modulating factor reduces the impact of easy examples on the loss.</p><p>For example, compare the loss when <strong><em>Pₜ</em></strong>= 0.9 and <strong><em>γ </em></strong>= 2. If we say the cross-entropy loss as CE, then the focal loss becomes -0.01CE. The loss becomes 100 times lower. And if the <strong><em>Pₜ </em></strong>gets bigger, say 0.968, with the constant <strong><em>γ</em></strong>, the focal loss becomes -0.001CE (as (1–0.968)² = (0.032)² ≈ 0.001). So it puts down the easy examples even harder and adjusting the imbalance in return. And when <strong><em>γ </em></strong>increases, as you can see the graph on the right, the loss gets smaller as the weight increases when <strong><em>Pₜ </em></strong>is constant.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*MN3eMmUZniOsLSux" /><figcaption><a href="https://arxiv.org/abs/1708.02002">The architecture of RetinaNet</a></figcaption></figure><p>The architecture of RetinaNet is as shown above. As we now know ResNet, RPN of Faster R-CNN and FPN, there is nothing new here. The network can be divided into two parts, a backbone with (a) and (b), and two subnets for classification and box regression. The backbone network is composed of ResNet and FPN and the pyramid has levels <strong>P3 </strong>to <strong>P7</strong>.</p><p>As with RPN, there are prior anchor boxes as well. The size of anchors changes according to its level. At the level <strong>P3</strong>, the anchor <em>area </em>is 32*32 and at <strong>P7</strong>, it’s 512*512. The anchors have three different ratios and three different sizes, so the number of anchors <strong>A </strong>for each level is 9. Therefore, the dimension of output from a box regression subnet is <strong>(N x N) x 4A. </strong>And when the number of classes in the dataset is <strong>K</strong>, the output from a classification subnet becomes <strong>(N x N) x KA</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/672/1*8WVVQrl29Rpnsa0U2i0giA.png" /><figcaption><a href="https://arxiv.org/abs/1708.02002">Speed vs Accuracy on COCO dataset</a></figcaption></figure><p>The result was quite eye-catching. RetinaNet outperformed all the previous networks in both accuracy and speed. RetinaNet-101–500 indicates the network with ResNet-101 and a 500-pixel image scale. Using larger scales gives higher accuracy than all two-stage approaches, while still being fast enough as well.</p><h3>Take away</h3><p>There are two kinds of object detection networks, one-stage and two-stage. Each has its own pros and cons. <strong>Two-stage networks</strong>, such as Faster R-CNN, are accurate but impractical for real application. <strong>One-stage networks</strong>, such as SSD and YOLO, are fast enough but the accuracy is relatively low. This was the motivation of RetinaNet, developing a one-stage network achieving the two rabbit.</p><p>Object detection should work on <strong>multi-scales </strong>because the scales of objects aren’t constant. We saw what strategies the networks use for scale-invariant detection. SSD had multiple layers, downsizing the resolutions in a way. YOLO had 3 layers in multi-scales with upsampling and passthrough layers. FPN and RetinaNet made a pyramid working both in top-down and lateral directions.</p><p><strong>Class imbalance </strong>is another pain in an object detection task. There have been several methods to remedy this. SSD used <strong>hard negative mining </strong>but there is a major drawback that we have to use only some of the examples collected. Another solution RetinaNet proposed was a <strong>focal loss</strong>. By adding the weight parameter, we can suppress the contribution of the easy examples and also, use all the examples efficiently.</p><h3>References</h3><ul><li>Wei Liu et al. [<a href="https://arxiv.org/abs/1512.02325">SSD: Single Shot MultiBox Detector</a>], 2016</li><li>Joseph Redmon et al. [<a href="https://arxiv.org/abs/1506.02640">You Only Look Once: Unified, Real-Time Object Detection</a>], 2016</li><li>Joseph Redmon et al. [<a href="https://arxiv.org/abs/1612.08242">YOLO9000: Better, Faster, Stronger</a>], 2016</li><li>Tsung-Yi Lin et al. [<a href="https://arxiv.org/abs/1612.03144">Feature Pyramid Networks for Object Detection</a>], 2017</li><li>Joseph Redmon et al. [<a href="https://pjreddie.com/media/files/papers/YOLOv3.pdf">YOLOv3: An Incremental Improvement</a>], 2018</li><li>Tsung-Yi Lin et al. [<a href="https://arxiv.org/abs/1708.02002">Focal Loss for Dense Object Detection</a>], 2018</li></ul><p>Did this story resonate with you? Please share your insight with us. I’m always open to talk, so feel free to leave comments below and share your thoughts. I also share interesting and useful resources on <a href="https://www.linkedin.com/in/jiwon-jeong/"><strong>LinkedIn</strong></a> so feel free to follow and reach out to me. I’ll be back with another interesting story, next time. As always, stay tuned!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=abd7fd2c64ef" width="1" height="1" alt=""><hr><p><a href="https://medium.com/data-science/deep-dive-into-the-computer-vision-world-part-3-abd7fd2c64ef">Deep Dive into the Computer Vision World: Part 3</a> was originally published in <a href="https://medium.com/data-science">TDS Archive</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Deep Dive into the Computer Vision World: Part 2]]></title>
            <link>https://medium.com/data-science/deep-dive-into-the-computer-vision-world-part-2-7a24efdb1a14?source=rss-1df293b5ca56------2</link>
            <guid isPermaLink="false">https://medium.com/p/7a24efdb1a14</guid>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[computer-vision]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[deep-learning]]></category>
            <category><![CDATA[object-detection]]></category>
            <dc:creator><![CDATA[Jiwon Jeong]]></dc:creator>
            <pubDate>Tue, 16 Jul 2019 14:11:02 GMT</pubDate>
            <atom:updated>2019-08-29T13:00:22.221Z</atom:updated>
            <content:encoded><![CDATA[<h4>Regions with CNN, Let’s start object detection!</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*82g9vFAyax8QnkkgP4y2VQ.jpeg" /></figure><p>This is the second story of “Deep Dive into the Computer Vision world,” and the complete set of the series is as follows:</p><ol><li><a href="https://towardsdatascience.com/deep-dive-into-the-computer-vision-world-f35cd7349e16?source=friends_link&amp;sk=449ea5da20c884dadca23b907efb7e13">Starting from VGG, ResNet, Inception Network and MobileNet</a></li><li>Regions with CNN, let’s start object detection</li><li><a href="https://towardsdatascience.com/deep-dive-into-the-computer-vision-world-part-3-abd7fd2c64ef?source=friends_link&amp;sk=876a90f05dcef8f9f0546b42adaec42d">YOLO, SSD and RetinaNet, more unified ones</a></li><li>From object detection to instance segmentation (TBU)</li></ol><p>In the previous post, we discussed five popular networks, VGG, ResNet, Inception Network, Xception and MobileNet. These networks are now a major component in advanced networks, hence it’s necessary to know their architectures.</p><p>Now, we’re going to the next page. From image classification to object detection. So far, what we’ve discussed was one large convolutional neural network. Although they have many layers, they are one network at the end. But R-CNN and its variants that will be our focus, have this network as a part. A CNN is now a part of an entire “system” processing more complex tasks such as object detection and image segmentation. Starting with R-CNN, we’re going to see how these networks had been transformed and the underlying idea for the changes.</p><h3>From image classification to object detection</h3><p>Before we jump right into the R-CNN “family,” let’s briefly check the basic idea of image classification and image detection. What’s the difference between them? What additional work do we need to do for detecting objects in an image?</p><p>First, whatever the number of classes we have, there is going to be an additional class- the background. An object detector is required to answer the question, “is there an object?”, which is not the case for image classification. Second, when there is an object, it’s still not sufficient with “yes, there is!” (Quite ridiculous to imagine..😅) It should also tell us, “where is the object located?” We need the position of the detected objects. This may sound simple but the implementation isn’t. And when we take speed and efficiency challenges into account, things get even more complicated.</p><p>Therefore object detection would be like.. looking for the regions of an object, localizing it and classifying what it is. Having this basic concept in mind, we’re now ready to start the second topic from R-CNN.</p><h3>R-CNN</h3><p>R-CNN answers the question, <em>“to what extent can the CNN classification results on ImageNet generalize to object detection results?” </em>So this network can be said to be the beginning of object detection <em>family tree</em>, which has great importance in the application of a neural network. The basic structure is composed of three steps-extracting region proposals, computing CNN, and classification.</p><p>First, we extract some regions that seem promising to have an object from an input image. R-CNN used selective search for getting those regions of interest (ROI). Selective search is a region proposal algorithm that segments an image based on the intensity of the pixels. If you’d like to get an in-depth understanding of Selective Search, here is <a href="http://www.huppelen.nl/publications/selectiveSearchDraft.pdf"><strong>the original paper</strong></a>. The basic idea is shown below. It first starts with the overly segmented picture and draws a bounding box around each segment. And based on their similarities in color, texture, size and shape compatibility, it keeps grouping adjacent ones and forming larger segments. R-CNN extracts around 2000 region proposals by this method and feeds them to the CNN. And this is the reason it is named R-CNN, <em>Regions with CNN features</em>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/934/1*6UfdQmJE78IdEs6q41zdXg.png" /><figcaption><a href="http://www.huppelen.nl/publications/selectiveSearchDraft.pdf">Selective Search for Object Recognition</a></figcaption></figure><p>After getting the candidates, the second step is entering a large convolutional neural network. Each proposal is resized into a fixed size and is input into the CNN separately. R-CNN used AlexNet (It was 2014, there weren’t ResNet or InceptionNet at that time) and we get 4096-dimensional feature vector from each proposal.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1zG21rqVp-0Sg6U-uSE4bQ.png" /></figure><p>And at the last step, the output extracted from the CNN is fed into a set of <em>class-specific</em> linear SVM models. We optimize one linear SVM per each class, and we get the output image with the bounding boxes whose score is higher than a threshold value. In case of having overlapping boxes, it only takes one by applying non-maximum suppression.</p><p>One interesting part worth mentioning is how it overcame the data scarcity issue. The researchers had the challenge to train such a large network with only a small quantity of labeled data. And the solution was pre-training the CNN on a different data with labels (which is supervised learning), and then do fine-tuning with the original dataset.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/728/1*p_pmv_WO2y_m97BPVgEZfA.png" /><figcaption><a href="http://www.huppelen.nl/publications/selectiveSearchDraft.pdf">R-CNN architecture</a></figcaption></figure><p>You may have noticed some inefficient parts that could be enhanced. The selective search is computation intensive. And processing the CNN for each ROI was repetitive work, which requires a huge amount of computation cost again. The need for the pre-training process and the separate classifiers were unattractive. And the storage for these models is too big. Although R-CNN was a milestone achievement, it had several drawbacks to be improved.</p><h3>Fast R-CNN</h3><p>Fast R-CNN is the next version of the previous work. What changes had been made here? Processing convolutional mapping repetitively was improved. The first change occurred at the repetitive convolution layers.</p><p>Let’s assume it takes <em>N seconds to </em>compute one single convolutional network. As R-CNN input 2000 RoI to the network separately, the total processing time will be 2000*N seconds. Instead of processing the CNN individually, now we do it only once by sharing the convolution with the proposals altogether.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uaNOCXYBcN2uD451BpZK_g.png" /></figure><p>As you can see above, this network takes two data input, an original image and a set of the region proposals as input. The whole image feeds forward through the network to produce a feature map. With this feature map, each region proposal passes a pooling layer and fully connected layers to create a feature vector. Therefore, the convolutional computation is done once, not for each proposal.</p><p>And the multiple classifiers are replaced with two sibling layers. One is a softmax function for classifying the object with the possibility estimates for each class, and the other is a bounding-box regressor returning the coordinates of the detected object. So the resulting feature vector is fed into these two layers and we get the outcome from the two layers.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/685/1*AzkmdVnpfXfhoHKXAUviBA.png" /><figcaption><a href="https://arxiv.org/pdf/1504.08083.pdf">Fast R-CNN architecture</a></figcaption></figure><p>Now, this model made changes to share convolutions and to detach the additional classifiers. By merging “multiple bodies” into one and taking off the “heavy tail,” we could achieve less computation and storage. This architecture allows us to train all the weights together, including even that of the softmax classifier and the multiple regressors. And this means the propagation will flow back and forth updating all the weights. Awesome progress! However, we still have a chance to get better performance.</p><h3>Faster R-CNN</h3><p>Still, Fast R-CNN was hard to be used in real time detection. And the major reason for such time delay stemmed from selective search. It had computation bottleneck and it needed to be replaced with a more efficient way. <em>But how? How could we get region proposals without Selective Search? What about maximizing the usage of the ConvNet?</em> Researchers found that the feature maps in Fast R-CNN also can be used for generating region proposals. Therefore by being free from Selective Search, much efficient network could be developed.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rP2RwzHNROX8vwVBbMEt9Q.png" /></figure><p>Faster R-CNN is composed of two modules, Region Proposal Network (RPN) and Fast R-CNN. We first input an image to a “mini-network” and it will output the feature maps. By sliding a small window over the feature maps, we extract region proposals. And each proposal is fed into two sibling layers, a softmax classifier and a bounding-box regressor.</p><p>These two layers may seem similar to the last layers of Fast R-CNN, but they are derived for a different purpose. For each proposal, the classifier estimates the probability of the presence of an object in an image. And the regressor returns the coordinates of the bounding box. So they are for generating candidates, not predicting the actual objects.</p><p>We only take the proposals whose scores are higher than a threshold and input them with the feature map together to Fast R-CNN. The following steps are the same. They are input into a convolutional network and RoI pooling layers. And then the last layers will be a classifier and a regressor that finally predicts the real objects in images.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/607/1*XyQa5bD1oT7YdDrOkU6p8Q.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/796/1*lVE5BICv4IvRERUptLj3DA.png" /><figcaption><a href="https://arxiv.org/pdf/1506.01497.pdf">Faster R-CNN architecture (left) and Region Proposal Network (right)</a></figcaption></figure><p>One important property of this network is <em>translation invariant</em>, which is achieved by anchors and the way it computes proposals relative to the anchors<em>.</em> <a href="https://stats.stackexchange.com/questions/208936/what-is-translation-invariance-in-computer-vision-and-convolutional-neural-netwo"><em>What is translation invariant?</em></a> Simply put, it means we can detect an object regardless of its rotation, relocation, size change and what not. An object in an image can be at the center or the upper left. The same object can be at wide or long scale depending on the perspective. To prevent a model from failing to locate an object because of translation, we make anchor boxes of multiple scales and aspect ratios as in the picture shown above. These boxes are put at the center of the sliding window. So if there’s <strong>K</strong> number of the boxes at a certain position, we get <strong>2K </strong>scores and <strong>4K </strong>coordinates of <strong>K </strong>boxes.</p><p>In conclusion, at the Region Proposal Network, we slide a window with multiple anchor boxes over the feature map and evaluate each box by the classifier and the regressor. The proposals below the threshold are rejected, hence, feeding only the promising one to the next steps.</p><p>More than that, this model optimized in 4 steps of alternative training by fine-tuning the layers unique to RPN and Fast R-CNN separately while fixing the shared layers. This allows the model to share weights forming a unified network and brought higher performance both in efficiency and accuracy. To compare the performance, The time for proposals of Faster R-CNN was 10 milliseconds per image (5 frames per second for the whole process), while 2 seconds for Selective Search using CPU.</p><h3>Take away</h3><p>From R-CNN to Faster R-CNN, the networks got transformed removing dependence on other components. R-CNN could be enhanced by dropping linear SVMs and sharing convolution computations. And Fast R-CNN also changed to share the convolution to remove Selective Search. By integrating the whole process into one network, we could achieve higher accuracy as well as faster speed.</p><h3>References</h3><ul><li>J.R.R. Uijlings et al, <a href="http://www.huppelen.nl/publications/selectiveSearchDraft.pdf"><em>Selective Search for Object Recognition</em></a>, 2012</li><li>Ross Girshick et al, <a href="https://arxiv.org/pdf/1311.2524.pdf"><em>Rich feature hierarchies for accurate object detection and semantic segmentation</em></a>, 2014</li><li>Kaiming He et al, <a href="http://Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition"><em>Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition</em></a>, 2015</li><li>Ross Girshick, <a href="https://arxiv.org/pdf/1504.08083.pdf"><em>Fast R-CNN</em></a>, 2015</li><li>Shaoqing Ren et al, <a href="https://arxiv.org/pdf/1506.01497.pdf"><em>Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks</em></a>, 2015</li><li>Kaiming He et al. <a href="https://arxiv.org/abs/1703.06870"><em>Mask R-CNN</em></a>, 2018</li></ul><p>Did this story resonate with you? Please share your insight with us. I’m always open to talk, so feel free to leave comments below and share your thoughts. I also share interesting and useful resources on <a href="https://www.linkedin.com/in/jiwon-jeong/"><strong>LinkedIn</strong></a> so feel free to follow and reach out to me. I’ll be back with another interesting story, next time. As always, stay tuned!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7a24efdb1a14" width="1" height="1" alt=""><hr><p><a href="https://medium.com/data-science/deep-dive-into-the-computer-vision-world-part-2-7a24efdb1a14">Deep Dive into the Computer Vision World: Part 2</a> was originally published in <a href="https://medium.com/data-science">TDS Archive</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Deep Dive into the Computer Vision World: Part 1]]></title>
            <link>https://medium.com/data-science/deep-dive-into-the-computer-vision-world-f35cd7349e16?source=rss-1df293b5ca56------2</link>
            <guid isPermaLink="false">https://medium.com/p/f35cd7349e16</guid>
            <category><![CDATA[computer-vision]]></category>
            <category><![CDATA[image-recognition]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[neural-networks]]></category>
            <dc:creator><![CDATA[Jiwon Jeong]]></dc:creator>
            <pubDate>Tue, 02 Jul 2019 12:54:20 GMT</pubDate>
            <atom:updated>2019-08-29T12:59:44.351Z</atom:updated>
            <content:encoded><![CDATA[<h4>Starting from VGG, ResNet, GoogLeNet, and MobileNet</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GhwhRLNyKvjdqSxE5SAzbA.png" /></figure><p>After finishing the basics of neural networks, the next step would be learning some of the “famous rock star models” in this field. ResNet, Inception Net, Faster R-CNN, YOLO and so on. Studying these models can be divided into three parts: The applications, the implementations, and intuition behind these architectures. There are already plenty of resources on how to use pre-trained models and how to build them. But grasping the real intuition behind the model is overlooked sometimes. What’s the researchers’ intention for building a model with such structures? What motivated them to take such an approach? And what can we infer from the outcome?</p><p>This article is the next series to “<a href="https://towardsdatascience.com/the-most-intuitive-and-easiest-guide-for-artificial-neural-network-6a3f2bc0eecb?source=friends_link&amp;sk=a9021d009dcd1651c16b59c6e48d0533"><strong>The Most Intuitive and Easiest Guide</strong></a>” tutorials and the complete set of the series is as follows:</p><ol><li>Starting from VGG, ResNet, Inception Network and MobileNet</li><li><a href="https://towardsdatascience.com/deep-dive-into-the-computer-vision-world-part-2-7a24efdb1a14?source=friends_link&amp;sk=4fec4dfc9499c930f263c6808b2f369d">Regions with CNN, let’s start object detection!</a></li><li><a href="https://towardsdatascience.com/deep-dive-into-the-computer-vision-world-part-3-abd7fd2c64ef?source=friends_link&amp;sk=876a90f05dcef8f9f0546b42adaec42d">YOLO, SSD and RetinaNet, more unified ones</a></li><li>From object detection to instance segmentation (TBU)</li></ol><p>This article assumes that you’re already familiar with <a href="https://towardsdatascience.com/the-most-intuitive-and-easiest-guide-for-convolutional-neural-network-3607be47480?source=friends_link&amp;sk=17c6523b385d28525c9e68e2da28b9e8">the basic concept of convolutional neural network</a>. On top of that, we’re going to dive deeper and see various convolutional transformation and modification with VGG, ResNet, Inception Network and MobileNet. Walking through them one by one, we’ll answer the questions mentioned above so that we truly understand the underlying meaning of these architectures.</p><h3>VGG</h3><p>ImageNet is a large dataset used for the research in the computer vision field. Since 2010, a competition has been held annually under the name of <a href="http://www.image-net.org/challenges/LSVRC/">ImageNet Large Scale Visual Recognition Challenge</a> (ILSVRC). The VGGNet was the winner of the ILSVRC competition in 2014. Although it has a simple architecture compared to the other fancy networks, it is still one of the most popular networks.</p><p>One interesting part of this network is that it used only 3x3 filters even in the first layers. If we compare it to the previous ones, <a href="https://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf"><strong>AlexNet</strong></a><strong>, </strong>the winner in 2012,<strong> </strong>used <strong>11x11 </strong>and <strong>5x5</strong> filters at the first two layers. And <a href="https://arxiv.org/pdf/1311.2901.pdf"><strong>ZFnet</strong></a><strong>, </strong>the winner in 2013, used <strong>7x7 </strong>filters. Instead of using a relatively large size of a filter in the first convolution layer, the size of the filters VGG network used is 3x3 only.</p><p>So what’s the intention for trying this? The answer is to reduce the number of parameters. Let’s do some calculation here. How many parameters does a single <strong>7x7 </strong>convolution layer with the channel size C require? It’s <strong>49C²</strong>. Now, what’s the number with 3 layers of <strong>3x3 </strong>convolution layers with the same channel size? (9C²)x3 = <strong>27C²</strong>. See how many parameters decreases. Reducing the number of weights can handle overfitting by reducing the complexity of the network. Moreover, as we can have an activation layer for each convolution layer, this encourages the model’s learning as well.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*m10KcUlwlcT7bhZUahVdvw.png" /><figcaption><a href="https://arxiv.org/pdf/1512.03385.pdf">The architecture of VGG-19</a></figcaption></figure><p>Along with the VGG networks or AlexNet, most of the Convolutional networks followed the basic structure: a series of convolutional layers, a pooling layer and an activation layer with some of the normalization. But going deeper and deeper, people came across some serious problems and started to redesign this approach.</p><h3>ResNet</h3><p>Intuitively we’d like to expect “deeper is better.” However, researchers found was it wasn’t the case. As a network gets extremely deeper and deeper, the performance gets worse. One of the reasons is a vanishing/ exploding gradient problem. Although we already have several ways to address this issue such as gradient clipping, there is a degradation problem that isn’t due to overfitting. The birth of ResNet started from here. <strong><em>Why is that?</em></strong></p><p>Let’s assume that we have a good network with desirable accuracy. Now we’re going to copy this network and add just 1 layer but having no additional function. This is called an <strong>identity mapping</strong>, which means getting the output exactly the same with the input. Compared to other complex layers with many parameters, this will be a piece of cake to our “smart” model. So there should be no harm to the performance of the network, right?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*SltFjgDwRVXoGHQQN-BWcg.png" /></figure><p>Then what will be the outcome if we attach more and more identity layers? As identity layers give no impact to the output, there should be no degradation in the performance. It seems a reasonable guess, but the outcome of this experiment was different from our expectations. The performance of this model was worse than the shallower model without identity layers.</p><p>This outcome indicates “deeper is better” is not applicable to neural networks even with the identity mapping.<strong> </strong>Just attaching identity layers directly to a network makes it hard to train. <em>Okay, then what about putting identity layers as an additional layer?</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KsNEtDepdgy4HM_vn4Z75w.png" /></figure><p>Now, this is where ResNet comes into play. Let’s say <strong>H(x) </strong>as the outcome of the network. And if we put an additional path, the original function becomes <strong>F(x) + x </strong>where <strong>F(x) </strong>is the function of <strong>the main branch</strong> as shown above. The identity layer is also called <strong>a shortcut connection </strong>or <strong>a skip connection</strong>. This brings the concept of “residual learning” to the network. Now the main branch is trained to approximate <strong>H(x) — x</strong> and make <strong>F(x) </strong>go closer to zero. This means giving the layers a certain direction for learning, which makes the training easier. Moreover, the network can skip some of the layers through the identity layer. This allows us to have deeper layers but still with high performance.</p><p>You may ask how it’s possible to add <strong>F(x)</strong> and x when the two layers have different sizes. We can handle this problem by the zero padding or a linear projection at the shortcut connections. So there are two kinds of shortcut connections in ResNet, one without weights and the other with weights.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*iPlZWKddl1AI9u0doMZN_Q.png" /></figure><p>The picture above shows the convolutional block of ResNet 50-layers. It has a main branch with 3 convolution layers and a shortcut connection with a <strong>1x1 </strong>convolution. Here we may need to ask,<strong> <em>why 1x1–3x3–1x1 convolution?</em></strong></p><p>To answer this question, it is necessary to understand how the dimension of data changes according to filter size. Please have a look at the picture on the right. If we say the shape of the input is (32, 32, 256), the dimension decreases when it passes the <strong>3x3 </strong>filter compared to the previous step. And when it passes the second 1x1 filter, it increases again. This is making a bottleneck. By forcing the data to fit into a smaller dimension, we can get a more efficient feature representation.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*XzHdLSCXGf6jyTTiCMkcOw.png" /><figcaption><a href="https://arxiv.org/pdf/1512.03385.pdf">The comparison of a plain network and ResNet</a></figcaption></figure><p>Optimizing the deep neural network easily with much fewer filters, much less computation, and higher accuracy. This simple yet novel reformulation brought such a beautiful achievement is how ResNet blew people’s minds in 2015. With this fundamental intuition on ResNet, I’d like you to explore the network from <a href="https://arxiv.org/abs/1512.03385"><strong>the original paper</strong></a>. And <a href="https://arxiv.org/pdf/1603.05027.pdf"><strong>here</strong></a> is the second part of the research on experimenting the variants of the residual block. Or you can find an easy explanation on ResNet <a href="https://towardsdatascience.com/an-overview-of-resnet-and-its-variants-5281e2f56035"><strong>here</strong></a>.</p><h3>GoogLeNet</h3><p>Going deeper with high performance is good. We need deep neural networks to implement complex and challenging tasks. But there is another factor to consider when we evaluate the networks. Think about when we have to embed a model in a mobile application. It’ll be not desirable to have a “too heavy” model for sure. But more than that, there is a computational cost problem that is quite critical in real practice. So now the question becomes, <strong><em>how can we go deeper with higher efficiency?</em></strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/400/0*aifMXoQqlR-EGQUC.jpg" /><figcaption><a href="https://knowyourmeme.com/memes/we-need-to-go-deeper">Know your meme</a></figcaption></figure><p>One place where the researchers had found inefficient computation occurred was a fully connected architecture. In a convolutional approach, a fully connected architecture indicates that a layer <strong><em>l</em></strong> feeds from a layer <strong><em>l-1</em></strong><em> </em>and is connected to the next layer <strong><em>l+1</em></strong> as shown below on the left. The researchers found any uniform increase in the number of the filters doubles the computation. So they suggest moving from densely connected architectures to sparsely connected architectures.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2LLDMLUHAyzRZNCh3CtLFw.png" /></figure><p>Instead of implementing one <strong><em>type</em> </strong>of convolutions, the sparse architecture does multiple convolutions with multiple filter sizes as shown above. And the dimension of output from these layers can be matched up with ‘SAME’ padding.</p><p>There is an important meaning in having several filters with different sizes here. During the training, the scale of features a network detects are small and local at first, and it gets bigger as it goes deeper. For example, let’s say the model detects a cat. The model sees a small scale of features such as the fur pattern of the cat, and then it moves to a bigger scale such as the shape of its ear, and then the number of legs.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/0*DdoXtROUqQNFZROb" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/0*gijmQgWP1YRP4AyP" /></figure><p>So even with the pictures where the cats are at different scales like above, the model still processes the same scale of feature mapping. This is inefficient and it hurts the performance. Therefore having multiple filters indicates now it knows how to “zoom in and out” according to the input image. It enables the model to detect the image in different scales at a time. And by doing so, we can also handle off the work of choosing parameters as well as reducing the inefficient computation. This is the first kick of Inception network.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZW2AbyxrHYim1FWl9udzjw.png" /></figure><p>So the Inception Module looked like the one on the left of the picture above. But there was a problem. <strong><em>But what about the computation cost? </em></strong>Sparely connected architectures still require the same or more amount of computation anyways! The solution of the researchers to handle this problem was implementing <strong>1x1 </strong>convolutions as shown above on the right. The purpose of the <strong>1x1 </strong>convolutions here is dimension reduction just as what we discussed with ResNet. See the computation times are reduced given the (N, N, C) shape of the input image. If the size of the image is 32 x 32 with the 129 channels, we can reduce the number of parameters 9 times less!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8DvMmswp1n2vf5y4mx0Ubw.png" /><figcaption><a href="https://arxiv.org/abs/1409.4842">The architecture of Inception Network</a></figcaption></figure><p>The final architecture of GoogleNet is shown above. The modules are concatenated, forming 22 layers in total. This is quite a deep network and it also couldn’t avoid the notorious vanishing gradient problem. So the way the researchers took to handle this issue was making side branches.</p><p>What do we mean by “gradient vanish?” It indicates that the value of the gradient gets smaller and smaller as it goes deeper, which means the gradient has little information to learn. So we can say that the gradient from the early stages carries more information. In other words, we can have additional information by adding helper classifiers in the intermediate layers. Moreover, as we discussed above, the features detected are different along with the layers. Therefore the “intermediate products” are also beneficial for classifying the labels. We add the loss from these branches to the total loss of the network with weighted.</p><h3>MobileNet</h3><p>Inspired by the networks we discussed above, “going deeper and more complicated” was the high trend in CNN research. Although those networks brought higher accuracy, they were not efficient or ‘light’ enough for a real-world application. To be carried out in a computationally limited platform with a timely manner, they need to be smaller and faster.</p><p>One solution the researchers proposed was forming a factorized convolutions. Factorizing convolutions indicates factorizing a standard convolution into a depthwise convolution and a pointwise convolution. This is called depthwise separable convolution.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*idbfJJLR_w8ibvb2LTOtIA.png" /></figure><p>In depthwise separable convolution, we first split each channel of the input and the filter, and convolve the input with the corresponding filter, or in a <em>depthwise </em>manner. And then concatenate the output together. This is <strong><em>depthwise convolution</em></strong>. After that, we do <strong><em>pointwise convolution</em></strong>, which is the same as <em>1x1 convolution</em>. Now see the final outcome. Isn’t it the same with that of a standard 3x3 convolution? If so, what’s the point for doing this?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/749/1*nt9pYOgWOt5RMqm7Hr6MDQ.png" /></figure><p>The answer is computation. Let’s compare the computational cost of the two. When the shape of input is (<strong>N</strong>,<strong> N</strong>,<strong> C1</strong>) and the output channel is <strong>C2 </strong>with the filter size <strong>F</strong>, the total computation of the two should be as shown on the left. So as you can see, the computational cost is reduced with depthwise separable convolution. According to <a href="https://arxiv.org/pdf/1704.04861.pdf">the original paper</a>, MobileNet requires 8 to 9 times less computation with only a small reduction in accuracy compared to the same architecture but with standard convolutions.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kJegKTV54PIbgK-Zgs4XaQ.png" /><figcaption><a href="https://arxiv.org/pdf/1704.04861.pdf">The applications of MobileNet</a></figcaption></figure><p>Depthwise separable convolution is also used to Xception, and its paper was released earlier than that of MobileNet. But I found MobileNet paper gives a more straightforward explanation on the concept of depthwise separable convolution.</p><h3>Take away</h3><p>Now do the architectures become simpler and tangible to you? Like I said, understanding the thesis behind the structure is essential. So I’d like to go over some kicks of the networks again.</p><p>Firstly, the filter size at the convolution layers has more meanings than just feature mapping. It can change the number of parameters like VGG. We can also create bottlenecks to extract meaningful representations of the data. <strong>1x1 </strong>convolution can serve more than a linear transformation. It can serve as a useful trick for giving more non-linearity, changing the dimension, and reducing the computation without impacting the input data significantly.</p><p>To get higher performance, ResNet utilized the residuals by shortcut connection. This layer promoted the optimization better by conveying weights across the layers efficiently. On the other hand, the strategy of Inception Network was using multiple filters by building sparse architectures. It was for dealing with the change in scales during training. Lastly, MobileNet chose to factorize a convolution into two steps. With depthwise separable convolutions, MobileNet could reduce computation and the model size dramatically.</p><h3>References</h3><ul><li>Karen Simonyan and Andrew Zisserman, <a href="https://arxiv.org/abs/1409.1556"><em>Very deep convolutional network for large-scale image recognition</em></a>, 2015</li><li>Kaiming He, et al. <a href="https://arxiv.org/abs/1512.03385"><em>Deep residual learning for image recognition</em></a>, 2015</li><li>Christian Szegedy, et al. <a href="https://arxiv.org/abs/1409.4842"><em>Going deeper with convolutions</em></a>, 2014</li><li>Christian Szegedy et al., <a href="https://arxiv.org/pdf/1602.07261.pdf">Inception-v4, Inception-ResNet and the Impact of Residual Connections on Learning</a>, 2016</li><li>Franc¸ois Chollet, <a href="https://arxiv.org/pdf/1610.02357.pdf">Xception: Deep Learning with Depthwise Separable Convolutions</a>, 2017</li><li>Andrew G. Howard et al., <a href="https://arxiv.org/pdf/1704.04861.pdf">MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications</a>, 2017</li></ul><p>Did this story resonate with you? Please share your insight with us. I’m always open to talk, so feel free to leave comments below and share your thoughts. I also share interesting and useful resources on <a href="https://www.linkedin.com/in/jiwon-jeong/"><strong>LinkedIn</strong></a> so feel free to follow and reach out to me. I’ll be back with another interesting story, next time. As always, stay tuned!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f35cd7349e16" width="1" height="1" alt=""><hr><p><a href="https://medium.com/data-science/deep-dive-into-the-computer-vision-world-f35cd7349e16">Deep Dive into the Computer Vision World: Part 1</a> was originally published in <a href="https://medium.com/data-science">TDS Archive</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Hackathon! Get Ready with Me ]]></title>
            <link>https://medium.com/data-science/hackathon-get-ready-with-me-3192c7690cb7?source=rss-1df293b5ca56------2</link>
            <guid isPermaLink="false">https://medium.com/p/3192c7690cb7</guid>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[hackathons]]></category>
            <dc:creator><![CDATA[Jiwon Jeong]]></dc:creator>
            <pubDate>Sun, 16 Jun 2019 15:38:51 GMT</pubDate>
            <atom:updated>2019-06-22T02:43:05.248Z</atom:updated>
            <content:encoded><![CDATA[<h4>Especially for an aspiring data scientist</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/0*EpgJxzkpD8GfcA8g" /></figure><p>A hackathon is a competition where various people, including programmers, developers and graphic designers, collaborate to design software projects intensively in a short period of times. The goal of a hackathon is to create a functional product at the end of the event.</p><p>Last month, I had an opportunity to participate in the <a href="https://seoul.hackjunction.com/">JunctionX Seoul</a> Hackathon held by Junction Ltd. Our team made an iOS application that brings an optimized shopping service experience for Korean beauty product powered by image search engines. This application can help travelers who want to explore Korean beauty products but not knowing what to try due to an obvious language barrier. Users can find useful data about a product just by scanning it and the information is translated into their local languages. There is also a searching history page which could be utilized for further customized service. You can find more about it <a href="https://devpost.com/software/skana#_=_"><strong>here</strong></a> as well.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_vz-uN_cMUDmLroUHZQOkQ.png" /><figcaption>The app, <a href="https://drive.google.com/open?id=1NZ7DESxk2QWhWprcv5n5ZWlmr8x5J4DI">Skana</a></figcaption></figure><p>Rather than talking about what I made, however, I’d like to share what I learned in the process. So today, I’m going to talk about the 4 reasons why you should apply for a place in a Hackathon (especially if you’re an aspiring data scientist), how you should prepare and some of my tips. I hope this article can motivate you to go out of your comfort zone and get some profound inspiration just as I did.</p><h3>1. Why Hackathons</h3><p>If you haven’t tried a hackathon or a programming competition before, and say you ask me if it’s worth trying, I’ll nod my head without hesitation. Here are the 4 reasons why you should go to a hackathon.</p><h4>Beyond “modeling”</h4><p>We, data science learners, tend to work alone or study alone. We tend to invest most of our time in data preprocessing and wangling. And we pour most of our energy into studying machine learning or deep learning algorithms. Our project usually starts with importing data (or sometimes with building data by ourselves) and ends with the evaluation of the predictions.</p><p>Almost every real-world project in the world sadly doesn’t work that way. We don’t work alone. We work with other team members. From database to deployment and product management, all the processes should be done through collaboration. Having toolkits beyond the “modeling” territory can be a great plus for aspirants. (Many articles are pointing out this and you can find one <a href="https://towardsdatascience.com/the-third-wave-data-scientist-1421df7433c9"><strong>here</strong></a> as well) Now the important question, where can we learn and experience this?</p><p><a href="https://www.analyticsvidhya.com/blog/2019/05/exclusive-interview-sonny-laskar-kaggle-master-analytics-vidhya-hackathon-expert/?utm_source=feedburner&amp;utm_medium=email&amp;utm_campaign=Feed%3A+AnalyticsVidhya+%28Analytics+Vidhya%29"><em>“Unlike competitions, nobody gives you two .csv files called train and test and a nicely written evaluation metric. Almost 80% of the efforts go into defining the problem and getting and processing data. Remaining 20% effort goes into pure modeling and deployment.”</em></a></p><p>A hackathon can be a great chance to collaborate with others and make a real-world project. Although there are some limitations, still you can learn how to communicate with others (who do not have much of the knowledge in machine learning as you do) and understand the overall workflow for releasing an actual product. And if you get a chance of being on the team with someone who knows a lot better than yourself in machine learning, I believe it’ll be such a great time to push your limits as well.</p><h4>Business thinking is more crucial than you think</h4><p>The first step of the project will be deciding what to build. And I can tell half of the winning chance is determined here. You should get off on the right foot. Implementation is also a critical point but the business value plays an essential role. If it’s neglected, your work can be nothing but just showing off how technically competent you are.</p><p><a href="https://towardsdatascience.com/the-third-wave-data-scientist-1421df7433c9"><em>“Likewise, data scientists are hired to create business value, not just to build models. Ask yourself: How will the outcome of my work influence company decisions? What do I have to do to maximize this effect? With this entrepreneurial spirit, the third wave data scientist does not only produce actionable insights, but also seeks that they bring about real change.”</em></a></p><p>You’d have seen many people pointing out the importance of business mindset already. Finding a hidden need of users and observing a problem in the market. Suggesting a solution with your technical knowledge. Developing a service that can become profitable. At a hackathon, you are asked to think and discuss deeply with this viewpoint.</p><p>I heard of a guy who had won programming competitions more than 15 times. He retired in his ‘prize hunting’ career after he won one of Google’s competitions. Do you know what his secret was? It was just business sense and a little bit of wit. By making a clever and creative product, he made people shout “Brilliant!” which definitely leaves an impression on panels. So if you’re looking for an additional tip for winning, having business values with some wit can be a great bonus.</p><h4>Great chance to handle extreme stress</h4><p>The goal of a hackathon is creating a functional product during a short period (commonly 2 or 3 days). You are asked to present a prototype or demo of your product during these days. It’s all about limited time, limited resources and limited energy problems. Sometimes, you could have situations where things go wrong and you’d have to instantly find solutions. Sometimes you have to prioritize your work, which means what to come first and when to stop. You could feel intense pressure to do all your tasks and not to be a burden on the team. And as time passes, becoming completely famished and exhausted is literally inevitable! (More like energy drain or depletion until finally “falling” asleep).</p><p>If you’re someone like me, who tend to freeze at an extremely stressful moment, you’d be overwhelmed even by the air at the competition hall. It was my first time in a Hackathon so I experienced my brain being paralyzed several times. I had to get some fresh air and relax to ease off the tension. But this also became a great chance for me to learn how to dealing with stress. Because it’s unavoidable meeting those unexpected problems in the workplace and we have to manage those demanding moments.</p><h4>The real-time of ”Hello, world”</h4><p>Meeting new people is also a great pleasure, especially if they are like-minded people. At a hackathon, you can meet various people from different regions and with different specialties but with the same interest. They can be web developers, app developers or designers but they are people who’re willing to invest their weekend in developing something cool.</p><p>I met people who came from Russia to Korea just for this event. I also met a Ukrainian lady studying Computer Science in Korea. There were people from Denmark, French, China, and Japan. Meeting new people itself is a great joy. But more than that, I could hear what kind of pain points they have and what they are really passionate about. My horizon could become broadened in programming by chatting with fellow participants. And this is a reason why I think you should try a Hackathon with generous topics rather than heavily focused on the data science fields.</p><p>Also, it’d be good to care about what others do as well. Although you’ll be too busy to take extra care of others, this will help you understand the whole process as I mentioned above. We’re here to learn new things, anyway!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*pVtY7MsREgWH9VrVGlDJIA.png" /><figcaption>Photos by <a href="https://seoul.hackjunction.com/">JunctionXSeoul</a> and Me giving a presentation (on the right bottom)</figcaption></figure><h3>2. How to prepare</h3><p>So now does a Hackathon sound worth the try? If so, then your next question could be how you should prepare. Actually, nothing. Just go for it. Whatever skillsets you have, don’t hesitate to apply for. Hackathons are not a stage only for professionals. But if you’d like to prepare something before the day, then I’d like to list some of the skills I find useful and applicable.</p><h4>Backend and Deployment</h4><p>This is not a project that starts with importing the data and ends with some evaluation plots. You need to collect the data and build a database according to your team project. After you finish with data analysis or modeling part, you have to pass your model to frontend. Or you might need to deploy your work on a web by yourself. Therefore having database and Flask in your toolkits can be a huge asset for this event.</p><p>If you’re totally new to database, you can start your learning from understanding <a href="https://medium.com/xplenty-blog/the-sql-vs-nosql-difference-mysql-vs-mongodb-32c9980e67b2"><em>the difference between the SQL and NoSQL</em></a>. Here is a compact <a href="https://www.youtube.com/watch?v=xaWlS9HtWYw&amp;list=PL-osiE80TeTsKOdPrKeSOp4rN3mza8VHN"><em>SQL course for beginners</em></a> but there are tons of tutorials on databases besides this. So go and pick according to your taste.</p><p><a href="http://flask.pocoo.org/">Flask</a> is a microframework written in Python which is easy to learn and very useful for the model deployment. Here is an excellent tutorial on <a href="https://towardsdatascience.com/deploying-keras-deep-learning-models-with-flask-5da4181436a2"><em>how to deploy a Keras deep learning models with Flask</em></a><em> </em>by <a href="https://medium.com/u/a80e1f69e782">Ben Weber</a>. You can also find <a href="https://www.youtube.com/playlist?list=PL-osiE80TeTs4UjLw5MM6OjgkjFeUxCYH"><em>a series of video courses for Flask</em></a> here.</p><h4>API and Cloud Service</h4><p>Conditions could be different for each hackathon, but the one I took part in highly depended on API and Cloud Services. There were several sub tracks and the participants were asked to use particular API to participate in a given track. But besides this condition, you’d need to collect data by yourself anyway. Where will you get the data? And what about the computing power for training? You’d have probably left your “super-computer” with GPUs at home.</p><p>Therefore it’d be better to have an experience of getting data from API and handling data in a JSON format. If you’re new to API, <a href="https://towardsdatascience.com/what-is-api-and-how-to-use-youtube-api-65525744f520?source=friends_link&amp;sk=eedcecef15f918954df8ff6fbec0334a"><em>this article</em></a><em> </em>can explain to you what API is all about.</p><p>Ability to use a cloud service like AWS or Google Cloud GPU is also necessary. Here is <a href="https://aws.amazon.com/ec2/getting-started/"><em>an official guide for AWS EC2</em></a><em> </em>but here is <a href="https://www.notion.so/Junction-X-Seoul-9320fbe9fee94e0aac0b705bbbd7e17f"><em>a friendly video </em></a>by School of AI. You can also find<strong> </strong><a href="https://www.datacamp.com/community/tutorials/aws-ec2-beginner-tutorial"><em>a more detailed tutorial for beginners</em></a><strong> </strong>by <a href="https://medium.com/u/c07aac64b6e1">Michael Galarnyk</a>.</p><h4>Fully charged energy of yourself</h4><p>Get enough sleep. Take control of your condition. This is not a joke. During the competition, I slept for 4 hours in total. Sleeping less can be a good strategy but you need to be in good condition when you enter the hall. Also, a neck pillow and a blanket for a catnap can be as much useful item as “Flask” or “AWS.” 😂😂</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ewdryd-AhLdOTlH3w4sBcQ.png" /><figcaption>Photos by <a href="https://seoul.hackjunction.com/">JunctionXSeoul</a></figcaption></figure><h3>3. My small last tips</h3><p>Lastly, here are some of my small tips.</p><h4>How to form your team</h4><p>If you’re a solo applicant, you have to build your team from the bottom. There can be a social network platform like Slack where you can get the information from other applicants and make a team before the event. You might get a chance to join a premade team with one opening.</p><p>If you have an idea on what you want to develop, divide it into parts and look for crew members who can take on each part. You need to find someone who has the skill different from us (like data modeling). The common list of members you’d need to have is a backend engineer, a web/app developer, and a UI/UX designer.</p><h4>Be honest with what you can do</h4><p>You might think your ability is not enough for doing this. So you might want to hide your level or pretend knowing more than you do. But you really don’t have to. Hackathons are open to everyone. And no matter how much you know about programming, there is a portion that can be filled only by you! Is the person next to you know much more than you do? Awesome! You get more chances to learn. Asking for help is a better way to “help” your team than struggling alone and wasting precious time.</p><h4>Enjoy, Enjoy and Enjoy!</h4><p>It’s more than winning. Although you should do your best during the time, don’t lose a smile on your face. Enjoy the time for learning, meeting new people and working with them. Enjoy the time for being creative and developing a new thing that doesn’t exist in the world. Enjoy the time for being stressed and going beyond your limits. It will be extremely demanding. At the end of the event, however, you’ll go back to your home with new thoughts and lessons on your mind even if you don’t win a prize. And you’d find yourself looking forward to the next Hackathon like me. 😉</p><p>Did this story resonate with you? Please share your insight with us. I’m always open to talk, so feel free to leave comments below and share your thoughts. I also share interesting and useful resources on <a href="https://www.linkedin.com/in/jiwon-jeong/"><strong>LinkedIn</strong></a> so feel free to follow and reach out to me. I’ll be back with another interesting story, next time. As always, stay tuned!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3192c7690cb7" width="1" height="1" alt=""><hr><p><a href="https://medium.com/data-science/hackathon-get-ready-with-me-3192c7690cb7">Hackathon! Get Ready with Me 🤓😎</a> was originally published in <a href="https://medium.com/data-science">TDS Archive</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Computer Vision for Beginners: Part 4]]></title>
            <link>https://medium.com/data-science/computer-vision-for-beginners-part-4-64a8d9856208?source=rss-1df293b5ca56------2</link>
            <guid isPermaLink="false">https://medium.com/p/64a8d9856208</guid>
            <category><![CDATA[computer-vision]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[image-processing]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[python]]></category>
            <dc:creator><![CDATA[Jiwon Jeong]]></dc:creator>
            <pubDate>Tue, 23 Apr 2019 09:53:43 GMT</pubDate>
            <atom:updated>2019-04-23T13:24:02.296Z</atom:updated>
            <content:encoded><![CDATA[<h4>Contour Detection and Having A Little Bit of Fun</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/726/1*_-Gxkq3Uj804Nc6Wu1DBdA.png" /></figure><p>There are lots of ways out there one can adapt to make learning progress efficiently. As for me, combining studies with a little bit of fun is the best strategy. In this series of tutorials, many images have been used to demonstrate image processing concepts. It might interest you to note that people enjoyed the parts of the series where I applied the concept to the images in a funny way. And I recommend you also merge fun with studying.</p><p>This is the last part of OpenCV tutorial for beginners and the complete set of the series is like follows:</p><ol><li><a href="https://towardsdatascience.com/computer-vision-for-beginners-part-1-7cca775f58ef">Understanding color models and drawing figures on images</a></li><li><a href="https://towardsdatascience.com/computer-vision-for-beginners-part-2-29b3f9151874">The basics of image processing with filtering and gradients</a></li><li><a href="https://towardsdatascience.com/computer-vision-for-beginners-part-3-79de62dbeef7">From feature detection to face detection</a></li><li><strong><em>Contour detection and having a little bit of fun</em></strong></li></ol><p>From the first series, we saw how to draw a figure on an image. We also talked about how to apply blurring and thresholding with various options. And in part 3, we discussed several detection algorithms including edge detection. There is another detection technique called contour detection which is essential in object detection. And after that, we’re going to round up this series with a little bit of fun playing with masking.</p><p>This article assumes you’ve been following the previous steps or you already know those concepts. But if it’s not the case, please check the previous parts of this series. The complete code for this tutorial is available on <a href="https://github.com/jjone36/vision_4_beginners/blob/master/part4_contour_mapping.ipynb"><strong>Github</strong></a><strong> </strong>as always.</p><h3>Contour detection</h3><p>You may be already familiar with the word ‘contour.’ I’ve used this term several times in previous posts. <a href="https://en.wikipedia.org/wiki/Contour_line"><strong>A contour line</strong></a> indicates a curved line representing the boundary of the same values or the same intensities. A contour map is the most straightforward example we can think of.</p><p>But then you may ask this. What’s the difference between edges and contours? The two terms are often used interchangeably so it could be a bit confusing. To put it simply, the concept of edges lies in a local range while the concept of contours is at the overall boundary of a figure. Edges are points whose values change significantly compared to their neighboring points. Contours, on the other hand, are closed curves which are obtained from edges and depicting a boundary of figures. You can find a further explanation <a href="https://stackoverflow.com/questions/17103735/difference-between-edge-detection-and-image-contours"><strong>here</strong></a>.</p><pre># Load the image<br>img = cv2.imread(&#39;images/pine_apple.jpg&#39;)<br>img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)<br>plt.imshow(img)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/427/1*F2dznG-AJRkklpQcRQm25g.png" /></figure><p>So what we’re going to do is detecting the contour of this pineapple. Before applying the detection algorithm, we need to convert the image into grayscale and apply thresholding as follows. All of these steps are what we’ve discussed in the previous series.</p><pre># Blurring for removing the noise <br>img_blur = cv2.bilateralFilter(img, d = 7, <br>                               sigmaSpace = 75, sigmaColor =75)</pre><pre># Convert to grayscale <br>img_gray = cv2.cvtColor(img_blur, cv2.COLOR_RGB2GRAY)</pre><pre># Apply the thresholding<br>a = img_gray.max()  <br>_, thresh = cv2.threshold(img_gray, a/2+60, a,cv2.THRESH_BINARY_INV)<br>plt.imshow(thresh, cmap = &#39;gray&#39;)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/376/1*9p5ZlGFJz0_HFKHthyEvRw.png" /></figure><p>Contour detection can be implemented by the functioncv2.findContours() in OpenCV and there are two important parameters here. mode is the way of finding contours, and method is the approximation method for the detection. I ask you to find other information from <a href="https://docs.opencv.org/2.4/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html#findcontours"><strong>the documentation</strong></a>.</p><pre># Find the contour of the figure <br>image, contours, hierarchy = cv2.findContours(<strong><br>                                   </strong>image = thresh, <br>                                   <strong>mode = cv2.RETR_TREE, <br>                                   method = cv2.CHAIN_APPROX_SIMPLE</strong>)</pre><p>The mode cv2.RETR_TREE finds all the promising contour lines and reconstructs a full hierarchy of nested contours. The method cv2.CHAIN_APPROX_SIMPLE returns only the endpoints that are necessary for drawing the contour line. And as you can see above, this function gives the image, the detected contours and their hierarchy as its output.</p><p>The returned contour is a list of points consisting of the contour lines. To draw the outer line of the figure, we’ll sort the contours by their area. With the selected contour line, cv2.drawContours() will depict the bounding line along with the points as shown below.</p><pre># Sort the contours <br>contours = sorted(contours, key = cv2.contourArea, reverse = True)</pre><pre># Draw the contour <br>img_copy = img.copy()<br>final = <strong>cv2.drawContours(</strong>img_copy, contours, contourIdx = -1, <br>                         color = (255, 0, 0), thickness = 2<strong>)</strong><br>plt.imshow(img_copy)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/433/1*ulXyqlK5LAVhgHe3i4HsKA.png" /></figure><p>It’s simple, right? Although the shadow is also added at the bottom, the outcome is pretty satisfactory.</p><h3>More about contours</h3><p>There are more things we can do with the contour. We can find the centroid of an image or calculate the area of a boundary field with the help of the notion called image moment. What does a moment mean here? The word <em>‘moment’</em> is a short period of time in common usage. But in physics terminology, a <a href="https://en.wikipedia.org/wiki/Moment_(physics)"><strong>moment</strong></a> is the product of the distance and another physical quantity meaning how a physical quantity is distributed or located. So in computer vision, <a href="https://en.wikipedia.org/wiki/Image_moment"><strong>Image moment</strong></a> is how image pixel intensities are distributed according to their location. It’s a weighted average of image pixel intensities and we can get the centroid or spatial information from the image moment.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OMU_8F-NDbeLbIy8sFj9UA.png" /></figure><p>There are three types of moments- spatial moments, central moments, and central normalized moments. We can get the image moment with the function cv2.moments() in OpenCV and it returns 24 different moments. If you print the output M as shown below, it’ll return the 24 moments in a dictionary format.</p><pre># The first order of the contours<br>c_0 = contours[0]</pre><pre># image moment<br>M = cv2.moments(c_0)<br>print(M.keys())</pre><p>I’d like to focus on the implementation of the image moments here. Additional reading resources can be found at the end of this article if you may have an interest.</p><p>To get the area of the contours, we can implement the function cv2.contourArea() . Why don’t we try several contours here? If you input the first, second and third contours, you’ll get the decreasing values as shown below. This shows that the contour detection algorithm forms the hierarchy of the detected boundaries.</p><pre># The area of contours <br>print(&quot;1st Contour Area : &quot;, cv2.contourArea(contours[0])) # 37544.5<br>print(&quot;2nd Contour Area : &quot;, cv2.contourArea(contours[1])) # 75.0<br>print(&quot;3rd Contour Area : &quot;, cv2.contourArea(contours[2])) # 54.0</pre><p>The arc length of the contour can be obtained by the function cv2.arcLength() . The parameter closed indicates whether the curve should be closed or not.</p><pre># The arc length of contours <br>print(cv2.arcLength(contours[0], closed = True))      # 2473.3190<br>print(cv2.arcLength(contours[0], closed = False))  <strong>  </strong> # 2472.3190</pre><p>Now let’s try plotting the centroid and the extreme points of our pineapple. We can get the centroid point with the formula as follows.</p><pre># The centroid point<br>cx = int(M[&#39;m10&#39;] / M[&#39;m00&#39;])<br>cy = int(M[&#39;m01&#39;] / M[&#39;m00&#39;])</pre><p>The extrema are the endpoints on the left and right, at the top and bottom. And we can arrange x and y coordinates separately as follows.</p><pre># The extreme points<br>l_m = tuple(<strong>c_0[c_0[:, :, 0]</strong>.argmin()][0])<br>r_m = tuple(<strong>c_0[c_0[:, :, 0]</strong>.argmax()][0])<br>t_m = tuple(<strong>c_0[c_0[:, :, 1]</strong>.argmin()][0])<br>b_m = tuple(<strong>c_0[c_0[:, :, 1]</strong>.argmax()][0])</pre><pre>pst = [l_m, r_m, t_m, b_m]<br>xcor = [p[0] for p in pst]<br>ycor = [p[1] for p in pst]</pre><p>Now let’s plot all these points on the image.</p><pre># Plot the points<br>plt.figure(figsize = (10, 16))<br>plt.subplot(1, 2, 1)<br>plt.imshow(image, cmap = &#39;gray&#39;)<br>plt.scatter([cx], [cy], c = &#39;b&#39;, s = 50)</pre><pre>plt.subplot(1, 2, 2)<br>plt.imshow(image, cmap = &#39;gray&#39;)<br>plt.scatter(xcor, ycor, c = &#39;b&#39;, s = 50)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/957/1*zh_AX-nV5T-1DqlM2j2mnw.png" /></figure><h3>Other shapes of contours</h3><p>Besides the compact contour, we can also draw a convex contour or rectangular contour line of a figure. Let’s try a straight rectangular shape first. With the outer contour line, we’ll draw the rectangle around the object. The function cv2.boundingRect() returns the 4 points of the bounding box as shown below.</p><pre># The first order of the contours<br>c_0 = contours[0]</pre><pre># Get the 4 points of the bounding rectangle<br>x, y, w, h = cv2.boundingRect(c_0)</pre><pre># Draw a straight rectangle with the points<br>img_copy = img.copy()<br>img_box = cv2.rectangle(img_copy, (x, y), (x+w, y+h), color = (255, 0, 0), thickness = 2)</pre><p>Note that this straight rectangle isn’t the minimum among other possible boundings. We can extract the rectangle with the minimum area with the function cv2.minAreaRect() which finds a rotated rectangle enclosing the input 2D point set. After that, we get the 4 corners of this rectangle and put them at the contour parameter as shown below.</p><pre># Get the 4 points of the bounding rectangle with the minimum area<br>rect = cv2.minAreaRect(c_0)<br>box = cv2.boxPoints(rect)<br>box = box.astype(&#39;int&#39;)</pre><pre># Draw a contour with the points <br>img_copy = img.copy()<br>img_box_2 = cv2.drawContours(img_copy, <strong>contours = [box]</strong>, <br>                             contourIdx = -1, <br>                             color = (255, 0, 0), thickness = 2)</pre><p>Now let’s check the result and compare the two different contour boxes.</p><pre>plt.figure(figsize = (10, 16))<br>plt.subplot(1, 2, 1); plt.imshow(img_box)<br>plt.subplot(1, 2, 2); plt.imshow(img_box_2)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/903/1*B2veTSIBJBQCw9DmRFrDqg.png" /></figure><p>We can also draw a convex shape contour with the function cv2.convexHull(). This takes a set of points and returns the convex hull from the given set. And by inputting this returned points as contours in cv2.drawContours() , we can get the convex contour as follows.</p><pre># Detect the convex contour<br>hull = cv2.convexHull(c_0)</pre><pre>img_copy = img.copy()<br>img_hull = cv2.drawContours(img_copy, <strong>contours = [hull]</strong>, <br>                            contourIdx = 0, <br>                            color = (255, 0, 0), thickness = 2)<br>plt.imshow(img_hull)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/462/1*wtJWfEVQUKyJ-7YullGDbw.png" /></figure><p>There are so many things to cover in contour detections. I encourage you to check <a href="https://docs.opencv.org/3.4.6/d3/d05/tutorial_py_table_of_contents_contours.html"><strong>the documentation</strong></a> and explore more options by yourself.</p><h3>A little bit of fun with masking</h3><p>For the last exercise of this series, I’d like to have some fun time with doing image masking. We can add two different images with arithmetic operations such as image addition or bitwise operation. So our final task is attaching our Mr.pineapple on a man’s left shoulder.</p><pre># Import the large image<br>backpacker = cv2.imread(&#39;images/backpacker.jpg&#39;)<br>backpacker = cv2.cvtColor(backpacker, cv2.COLOR_BGR2RGB)<br>plt.imshow(backpacker)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/560/1*GvVB4WgAgRc5WO5NqYZtrw.png" /></figure><p>We’ll cut the region of interest (the shoulder part) from the large image and the small image as shown below. Note that the size of the two images (the height and the width of the image) should be the same here.</p><pre># Crop the small image and the roi<br>roi = backpacker[750:1150, 300:500]<br>img_2 = img[40:440, 80:280]</pre><pre>plt.figure(figsize = (6, 6))<br>plt.subplot(1, 3, 1); plt.imshow(roi)<br>plt.subplot(1, 3, 3); plt.imshow(img_2)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/745/1*WhEkPyj_3_e8HgMV05dSqQ.png" /></figure><p>The next step is making the mask for each image. We’re going to implement bitwise operation with the masks which should be a binary image. By thresholding the small image img_2 , we create the mask. And then with cv2.bitwise_not(), we make another mask which is exactly the opposite of the first one.</p><pre># Creating the mask for the roi and small image<br>img_gray = cv2.cvtColor(img_2, cv2.COLOR_RGB2GRAY)<br>_, mask = cv2.threshold(img_gray, 254/2+100, 255, cv2.THRESH_BINARY)<br>mask_inv = cv2.bitwise_not(mask)</pre><pre>plt.figure(figsize = (6, 6))<br>plt.subplot(1, 3, 1); plt.imshow(mask, cmap = &#39;gray&#39;)<br>plt.subplot(1, 3, 3); plt.imshow(mask_inv, cmap = &#39;gray&#39;)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/702/1*JVSrcLV0sK_WJJN8n7ErTA.png" /></figure><p>If we implement the function cv2.bitwise_and() and the masks, they will pass only the white area of the image. Therefore if we apply the first mask to the roi image, we can set the background image from it. In the same way, if we apply the second one to the img_2 image, we can make the foreground image with the fruit.</p><pre># Masking <br>img_bg = cv2.bitwise_and(roi, roi, mask = mask)<br>img_fg = cv2.bitwise_and(img_2, img_2, mask = mask_inv)<br>dst = cv2.add(img_fg, img_bg)</pre><pre>plt.figure(figsize = (10, 6))<br>plt.subplot(1, 3, 1); plt.imshow(img_bg)<br>plt.subplot(1, 3, 2); plt.imshow(img_fg)<br>plt.subplot(1, 3, 3); plt.imshow(dst)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/849/1*HLMJBoiVt_MkQlrTdpBw4w.png" /></figure><p>The outcome is a bit shambles considering the shadow at the bottom but let’s keep it. If you’d like to learn how the bitwise operation works, a detailed explanation can be found <a href="https://stackoverflow.com/questions/44333605/what-does-bitwise-and-operator-exactly-do-in-opencv"><strong>here</strong></a>. Now we’re ready to attach this combined image to the original one. So we can simply do this by putting the dst image at the roi position as shown below.</p><pre># Final output<br>backpacker[750:1150, 300:500] = dst<br>display(backpacker)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/503/1*eBQDrL6BaspKXXfV1mxBgQ.png" /></figure><p>😄👋 Have a nice time with the backpacker man!</p><h3>Summary</h3><p>From part 1 to part 4, we’ve learned quite a lot about the basics of image processing and computer vision. Did you enjoy all these tutorials so far? I hope you now get confident with the skills while following this series.</p><p>If you’re a complete starter in image processing, it is normal being unfamiliar with the terms and the usage of the OpenCV functions at first. The best way to practice is by implementing all of these by yourself. Pick any kind of images you like and just have fun with them. Changing color modes, rotating and resizing the image, masking and pasting it on a different image. Or you could also apply the detection technique of edges, corners and contours. While having fun by combining several preprocessing and trying different parameters, you can get used to OpenCV library faster without realizing.</p><h3>Resources</h3><ul><li><a href="https://en.wikipedia.org/wiki/Image_moment">Image moments on Wikipedia</a></li><li><a href="https://www.youtube.com/watch?v=AAbUfZD_09s&amp;t=71s">Image moments course on Udacity</a></li><li><a href="https://www.learnopencv.com/shape-matching-using-hu-moments-c-python/">Shape Matching using Hu Moments (C++/Python)</a></li><li><a href="https://www.learnopencv.com/find-center-of-blob-centroid-using-opencv-cpp-python/">Find the Center of a Blob (Centroid) using OpenCV (C++/Python)</a></li></ul><p>Are there errors you would love to correct? Please share your insight with us. I’m always open to talk, so feel free to leave comments below and share your thoughts. I also share interesting and useful resources on <a href="https://www.linkedin.com/in/jiwon-jeong/"><strong>LinkedIn</strong></a> so feel free to follow and reach out to me. I’ll be back with another interesting story, next time. As always, please stay tuned! 😎</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=64a8d9856208" width="1" height="1" alt=""><hr><p><a href="https://medium.com/data-science/computer-vision-for-beginners-part-4-64a8d9856208">Computer Vision for Beginners: Part 4</a> was originally published in <a href="https://medium.com/data-science">TDS Archive</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Computer Vision for Beginners: Part 3]]></title>
            <link>https://medium.com/data-science/computer-vision-for-beginners-part-3-79de62dbeef7?source=rss-1df293b5ca56------2</link>
            <guid isPermaLink="false">https://medium.com/p/79de62dbeef7</guid>
            <category><![CDATA[opencv]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[image-processing]]></category>
            <category><![CDATA[computer-vision]]></category>
            <category><![CDATA[machine-learning]]></category>
            <dc:creator><![CDATA[Jiwon Jeong]]></dc:creator>
            <pubDate>Fri, 05 Apr 2019 10:33:50 GMT</pubDate>
            <atom:updated>2019-04-23T13:16:40.276Z</atom:updated>
            <content:encoded><![CDATA[<h4>From Feature Detection to Face Detection</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/0*WcLE09IeZoRZcBJ7" /></figure><p>Detection task is one of the major tasks for computer vision, and there are so many ways we can utilize this technique. Identifying an error that can be critical when it occurs but can’t be recognizable by human’s eyes. Detecting an unsafe and risky motion or moment to save a life. Perceiving spatial information for a self-driving car. The examples of Object detection usage are countless and making those tasks automatic will bring us safety as well as efficiency.</p><p>This is the third part of OpenCV tutorial for beginners and the complete set of the series is as follows:</p><ol><li><a href="https://towardsdatascience.com/computer-vision-for-beginners-part-1-7cca775f58ef">Understanding color models and drawing figures on images</a></li><li><a href="https://towardsdatascience.com/computer-vision-for-beginners-part-2-29b3f9151874">The basics of image processing with filtering</a></li><li><strong><em>From feature detection to face detection</em></strong></li><li><a href="https://towardsdatascience.com/computer-vision-for-beginners-part-4-64a8d9856208">Contour detection and having a little bit of fun</a></li></ol><p>We’ve discussed several color modes and how to draw figures on images such as a rectangle and a circle. And then in part 2, we talked about image processing concepts such as blurring, gradient, erosion, dilation and so on. Today we are going to apply those concepts to detect features of images and face detection at the end.</p><p>This article assumes you’ve been following the previous steps or you already know those concepts. But if it’s not the case, please check the previous parts of this series. The complete code for this tutorial is available on <a href="https://github.com/jjone36/vision_4_beginners/blob/master/part3_object_detection.ipynb"><strong>Github</strong></a>. Now let’s get it started!</p><h3>Edge Detection</h3><p><a href="https://en.wikipedia.org/wiki/Edge_detection"><strong>Edge detection</strong></a> means identifying points in an image where the brightness changes sharply or discontinuously. We can draw line segments with those points, which are called <strong><em>edges</em></strong>. Actually, we already learned one of the edge detection techniques last time. Do you remember? Gradient filtering with Sobel and Laplacian operation. By calculating the derivatives of pixel values in a given direction, gradient filtering can depict the edges of images.</p><p><a href="https://en.wikipedia.org/wiki/Canny_edge_detector"><strong>Canny detection</strong></a> is another type of edge detection techniques. It’s one of the most popular algorithms for detecting edges, which is performed in four steps: <strong><em>Noise reduction, Finding gradient and its direction, Non-maximum suppression </em></strong>and <strong><em>hysteresis thresholding</em></strong>.</p><p>The algorithm starts with Gaussian blurring and I guess we already know the reason for removing noises in images. And then it finds the intensity gradient of the image with a Sobel kernel. With the derived gradient and direction, every pixel is checked if a certain point is a local maximum in its surrounding points. If it’s not, this point is suppressed to zero (total absence, black). This is called <strong><em>non-maximum suppression</em></strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/869/1*geOqlrXjkodWq0WPjYRklg.png" /></figure><p>If the point is considered to be a local maximum, it goes to the next stage. The final stage is the last decision stage whether the edges at the previous steps are really edges or not. This is called <strong><em>Hysteresis Thresholding </em></strong>and we need two threshold values here.</p><p>Given the two different threshold values, we get three ranges of values. So if the intensity gradient of a point is higher than the upper threshold, it will be considered as ‘sure-edge.’ If the gradient of a point is lower than the lower threshold, the point will be discarded. And in case of the gradient being in the middle of the two thresholds, we see its connectivity to other ‘sure-edge’ points. If there’s no connection, it will be discarded as well.</p><pre>img = cv2.imread(&#39;images/giraffe.jpg&#39;)<br>img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)</pre><pre># Canny detection without blurring<br>edges = <strong>cv2.Canny(</strong>image=img, threshold1=127, threshold2=127<strong>)</strong></pre><pre>plt.figure(figsize = (20, 20))<br>plt.subplot(1, 2, 1); plt.imshow(img)<br>plt.axis(&#39;off&#39;)<br>plt.subplot(1, 2, 2); plt.imshow(edges)<br>plt.axis(&#39;off&#39;)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1006/1*Rf9VDRWDGQX2zPtOnrM6MQ.png" /></figure><p>I just used the median value for the two thresholds without blurring and the result isn’t quite desirable. Now let’s try different threshold values this time.</p><pre># Set the lower and upper threshold<br>med_val = np.median(img)</pre><pre>lower = int(max(0, .7*med_val))<br>upper = int(min(255, 1.3*med_val))</pre><p>To see how blurring can change the outcome, I’m going to apply two different sizes of kernels, (5x5) and (9x9). And I’m going to try changing the upper threshold values by adding 100. So we have 4 types of processed images as follows:</p><pre># Blurring with ksize = 5 <br>img_k5 = cv2.blur(img, ksize = (5, 5))</pre><pre># Canny detection with different thresholds<br>edges_k5 = cv2.Canny(img_k5, threshold1 = lower, threshold2 = upper)<br>edges_k5_2 = cv2.Canny(img_k5, lower, upper+100)</pre><pre># Blurring with ksize = 9 <br>img_k9 = cv2.blur(img, ksize = (9, 9))</pre><pre># Canny detection with different thresholds<br>edges_k9 = cv2.Canny(img_k9, lower, upper)<br>edges_k9_2 = cv2.Canny(img_k9, lower, upper+100)</pre><pre># Plot the images<br>images = [edges_k5, edges_k5_2, edges_k9, edges_k9_2]<br>plt.figure(figsize = (20, 15))<br>for i in range(4):<br>    plt.subplot(2, 2, i+1)<br>    plt.imshow(images[i])<br>    plt.axis(&#39;off&#39;)<br>plt.show()</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/808/1*8iz2t7A8zN7nzyqJEO1Gvw.png" /></figure><p>As you can see above, blurring helps to remove noises and we got the better result with (9x9) size of the kernel. Also, we got a better result with the higher upper threshold value.</p><h3>Corner Detection</h3><p>Corner detection is another detection algorithm which is widely used in object detection, motion detection, video tracking and so on. What is a corner in image processing? How can we define a corner with pixels? We see a corner as a junction where edges intersect. Then how can we find them? Finding all edges first and then locating the points where they cross each other? Actually we have another way of making things more efficient, which are <strong><em>Harris corner detection </em></strong>and <strong><em>Shi &amp; Tomasi corner detection</em></strong>.</p><p>These algorithms work as follows. We detect points where there’s a considerable change in their intensity values in all directions. And then we construct a matrix to extract eigenvalues from it. These eigenvalues are for the sake of scoring points to decide if it’s a corner or not. The mathematical expressions are shown below.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*M7v6-iD-P26Mc_4fOwBd8w.png" /></figure><p>Now let’s see how we can implement these with codes. We first need to convert the image into grayscale. <a href="https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_feature2d/py_features_harris/py_features_harris.html"><strong>Harris corner detection</strong></a> can be performed with a function cv2.cornerHarris() in OpenCV.</p><pre>img = cv2.imread(&#39;images/desk.jpg&#39;)<br>img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)<br>img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)</pre><pre># Apply Harris corner detection<br>dst = <strong>cv2.cornerHarris(</strong>img_gray, blockSize = 2, ksize = 3, k = .04<strong>)</strong></pre><p>The parameter blockSize is the size of the window to consider as neighborhood and k is Harris detector free parameter which is shown in the equation above. The result is the score R and we’ll use this to detect corners.</p><pre># Spot the detected corners<br>img_2 = img.copy()<br>img_2[dst&gt;0.01*dst.max()]=[255,0,0]</pre><pre># Plot the image<br>plt.figure(figsize = (20, 20))<br>plt.subplot(1, 2, 1); plt.imshow(img)<br>plt.axis(&#39;off&#39;)<br>plt.subplot(1, 2, 2); plt.imshow(img_2)<br>plt.axis(&#39;off&#39;)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GMyjPviRSkRzUdqjuDV0fA.png" /></figure><p>Let’s try <strong>Shi-Tomasi corner detection</strong> this time. We can use this with a function cv2.goodFeaturesToTrack(). We set the maximum number of corners sorted by the most likelihood (maxCorners). We also assign minimum distance (minDistance) and minimum quality level (qualityLevel) that is required to be considered as corners. After we get the detected corners, we’ll mark those points with circles as follows.</p><pre># Apply Shi-Tomasi corner detection<br>corners = <strong>cv2.goodFeaturesToTrack(</strong>img_gray, maxCorners = 50, <br>                                  qualityLevel = 0.01, <br>                                  minDistance = 10<strong>)</strong><br>corners = np.int0(corners)</pre><pre># Spot the detected corners<br>img_2 = img.copy()<br>for i in corners:<br>    x,y = i.ravel()<br>    cv2.circle(img_2, center = (x, y), <br>               radius = 5, color = 255, thickness = -1)</pre><pre># Plot the image<br>plt.figure(figsize = (20, 20))<br>plt.subplot(1, 2, 1); plt.imshow(img)<br>plt.axis(&#39;off&#39;)<br>plt.subplot(1, 2, 2); plt.imshow(img_2)<br>plt.axis(&#39;off&#39;)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zIbJm4M8YOvsYEpE2SSlgg.png" /></figure><h3>Face Detection</h3><p>The last feature we’re going to see is a face. <a href="https://en.wikipedia.org/wiki/Face_detection"><strong>Face detection</strong></a><strong> </strong>is a technology identifying the presence and the position of human faces in digital images. I want you to differentiate face detection from <a href="https://en.wikipedia.org/wiki/Facial_recognition_system"><strong>face recognition</strong></a><strong> </strong>which indicates detecting the identification of a person by his or her face. So face detection can’t tell us to whom the detected face belongs.</p><p>Face detection is basically a classification task so it’s trained to classify whether there is a target object or not. And <strong><em>Haar Feature-based Cascade Classifier </em></strong>is one of the face detection models available in OpenCV. This is a pre-trained model, which means it already completed training with thousands of images. The 4 key points for understanding this algorithm are <strong><em>Haar features extraction, integral image, Adaboost </em></strong>and <strong><em>cascade classifiers</em></strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ELoJu38cHKMb8e3_IVz_eA.png" /></figure><p><a href="https://en.wikipedia.org/wiki/Haar-like_feature"><strong><em>Haar-like features</em></strong></a><strong><em> </em></strong>are image filters or image kernels used in object detection and the examples are shown above. They owe their name to their intuitive similarity with Haar wavelets which is originally proposed by <a href="https://en.wikipedia.org/wiki/Alfr%C3%A9d_Haar">Alfréd Haar</a>. During detection, we pass the window on an image and do the convolutional operation with the filters to see if there’s the feature we’re looking for is in the image. Here is <a href="https://vimeo.com/12774628"><strong>the video</strong></a><strong> </strong>which visualizes how the detection works.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*84KZtk275LK9PYv81lG9yQ.png" /></figure><p>So how we decide whether there is a wanted feature or not in a given area? Let’s take a look at the picture above. We have a kernel whose upper half is dark and lower half is light. Then we get the mean of the pixel values for each region and subtract the gap between the two. If the result is higher than a threshold, say 0.5, then we conclude there’s the feature we’re detecting. We repeat this process for each kernel while sliding the window over the image.</p><p>Although this isn’t a complex calculation, the total amount of computation becomes huge when we consider it in a whole image. If you’ve seen the video mentioned above, you’d get the intuition into the amount of calculation involved. And this is where an <a href="https://en.wikipedia.org/wiki/Summed-area_table"><strong><em>integral image</em></strong></a> comes into play. The integral image is a way of image representation which is derived to make the feature evaluation faster and more effective.</p><p>As you can see below, there are the pixels of an image on the left and an integral image on the right. Starting from the left upper point, it computes the accumulated sum of pixels under a given rectangular area. On the integral image, the sum of the pixels in the box with the dashed line is written at the lower right point of the box on the right.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yu4aq8LBUJkdI2-75i6m3g.png" /></figure><p>With this pre-calculated table, we can simply get the summed value for a certain area by the values of sub-rectangles (the red, orange, blue and purple box).</p><p>So we solved the computational cost with the integral image. But we’re not done yet. Think about when the detecting window is at the blank background where there’s no object or face. It’ll still be a waste of time if it performs the same process in such a part. And there’s another kick making this detector faster. Implementing <strong><em>a </em></strong><a href="https://en.wikipedia.org/wiki/Cascading_classifiers"><strong><em>cascade classifier</em></strong></a><strong><em> </em></strong>with Adaboost!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MOD1SWG_WGoVo3VyAPlX8Q.png" /></figure><p>A cascade classifier constructs stepwise stages and gives an order among Haar-like features. Basic forms of the features are implemented at the early stages and the more complex ones are applied only for those promising regions. And at each stage, the Adaboost model will be trained by ensembling weak learners. If a subpart, or a sub-window, is classified as ‘not a face-like region’ at the prior stage, it’s rejected to the next step. By doing so, we can only consider the survived ones and achieve much higher speed.</p><h3>Where’s our hero?</h3><p>To make this tutorial more interesting, I’d like to implement this Cascade classifier to detect the face of our marvel heroes. (Machine learning should be fun, don’t you think? 😃😃) Let’s bring Captin Marvel as our first guest.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/527/1*TT9i-deWvAczE2-DuudBBg.png" /></figure><p>We’re going to use only the part of this image. So let’s get the region of interest around her face first and then convert the image into grayscale. The reason for using only one channel is because we’re only interested in the changes in the light intensity of the features.</p><pre>cap_mavl = cv2.imread(&#39;images/captin_marvel.jpg&#39;)</pre><pre># Find the region of interest<br>roi = cap_mavl[50:350, 200:550]<br>roi = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)<br>plt.imshow(roi, cmap = &#39;gray&#39;)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/416/1*fdnKOp9jxIKx_wV7242ShQ.png" /></figure><p>Haar Cascade classifier files basically come along with OpenCV. You can probably find them in the OpenCV folder on your computer. Or you can simply download the file <a href="https://github.com/jjone36/vision_4_beginners/tree/master/haarcascades"><strong>here</strong></a>.</p><pre># Load Cascade filter <br>face_cascade = cv2.CascadeClassifier(&#39;haarcascades/<strong>haarcascade_frontalface_default.xml</strong>&#39;)</pre><p>Next, we’re going to create a function detecting a face and drawing a rectangle around it. To detect the face, we can use the method .detectMulitiScale() of the classifier face_cascade that we loaded above. It returns the four points of the identified region so we’ll draw a rectangle at that position. scaleFactor is a parameter for how much the image size is reduced at each image scale and minNeighbors for how many neighbors each candidate rectangle should be trained. Now let’s apply this function to the image and see the result.</p><pre># Create the face detecting function <br><strong>def </strong>detect_face(img)<strong>:</strong><br>    <br>    img_2 = img.copy()<br>    face_rects = <strong>face_cascade.detectMultiScale(</strong>img_copy, <br>                                               scaleFactor = 1.1,<br>                                               minNeighbors = 3<strong>)<br>    <br>    for </strong>(x, y, w, h) <strong>in </strong>face_rects:<br>        cv2.rectangle(img_2, (x, y), (x+w, y+h), (255, 255, 255), 3)<br>        <br>    <strong>return</strong> img_2</pre><pre># Detect the face<br>roi_detected = detect_face(roi)<br>plt.imshow(roi_detected, cmap = &#39;gray&#39;)<br>plt.axis(&#39;off&#39;)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/425/1*g_lzYa62hA2fkmlW6FEMAA.png" /></figure><p>Great! I think this is quite satisfactory. Why don’t we call other heroes altogether this time? We can implement this classifier to an image with multiple faces as well.</p><pre># Load the image file and convert the color mode<br>avengers = cv2.imread(&#39;images/avengers.jpg&#39;)<br>avengers = cv2.cvtColor(avengers, cv2.COLOR_BGR2GRAY)</pre><pre># Detect the face and plot the result<br>detected_avengers = detect_face(avengers)<br>display(detected_avengers, cmap = &#39;gray&#39;)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ftXCUABE2HUr7mY2MgdmQA.png" /></figure><p>And yes, it sometimes makes a failure catching a ‘non-face’ object or missing a ‘real-face.’ It’s funny that it detected Spider-Man successfully while mistook the hands of Captin America and Black Widow as eyes. We usually get better results with a face looking in front and showing its forehead and eyes clearly.</p><h3>Want to try yours?</h3><p>Would you like to try yours with a Webcam? We can apply the same process. Save the following code scrip as a file and import it on your terminal. If you’re to use Jupyter notebook, put the codes in one cell and execute. You can shut down the window by pressing ESC.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/aca12448850ea68491fea71b8bfd6972/href">https://medium.com/media/aca12448850ea68491fea71b8bfd6972/href</a></iframe><p>Did it successfully detect your face? Hope it doesn’t select your nostril for eyes. 😅</p><h3>What’s next?</h3><p>Did you enjoy the story? Besides the front face classifier, there are various models for eyes, upper body, lower body and so on. You can also try the model detecting a Russian car number plate. So I’d recommend you to play with it as well. Would you like to test your understanding on image processing? Try this <a href="https://www.analyticsvidhya.com/blog/2017/10/image-skilltest/"><strong><em>25 Questions of image processing</em></strong></a>. And here are such great articles providing detailed explanations for the questions you should check.</p><ul><li><a href="https://towardsdatascience.com/my-take-on-25-questions-to-test-a-data-scientist-on-image-processing-with-interactive-code-part-1-a6196f535008">My take on 25 Questions to test a Data Scientist on Image Processing with Interactive Code- Part 1</a></li><li><a href="https://towardsdatascience.com/my-take-on-25-questions-to-test-a-data-scientist-on-image-processing-with-interactive-code-part-2-77eacfd96cf9">My take on 25 Questions to test a Data Scientist on Image Processing with Interactive Code- Part 2</a></li></ul><p>Are there errors you would love to correct? Please share your insight with us. I’m always open to talk, so feel free to leave comments below and share your thoughts. I also share interesting and useful resources on <a href="https://www.linkedin.com/in/jiwon-jeong/"><strong>LinkedIn</strong></a> so feel free to follow and reach out to me. I’ll be back with another interesting Computer Vision story. Please stay tuned!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=79de62dbeef7" width="1" height="1" alt=""><hr><p><a href="https://medium.com/data-science/computer-vision-for-beginners-part-3-79de62dbeef7">Computer Vision for Beginners: Part 3</a> was originally published in <a href="https://medium.com/data-science">TDS Archive</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Computer Vision for Beginners: Part 2]]></title>
            <link>https://medium.com/data-science/computer-vision-for-beginners-part-2-29b3f9151874?source=rss-1df293b5ca56------2</link>
            <guid isPermaLink="false">https://medium.com/p/29b3f9151874</guid>
            <category><![CDATA[opencv]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[computer-vision]]></category>
            <category><![CDATA[image-processing]]></category>
            <dc:creator><![CDATA[Jiwon Jeong]]></dc:creator>
            <pubDate>Tue, 26 Mar 2019 02:36:50 GMT</pubDate>
            <atom:updated>2019-04-23T13:16:22.005Z</atom:updated>
            <content:encoded><![CDATA[<h4>The Basics of Image Processing with Filtering in Python</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/0*PgfXAlo7co_D5f_D" /></figure><p>Preparation refers to the process of getting ready for a task before actually starting it. It could be a preparing step of writing a story, job interview or data modeling. And having a thorough preparation can’t be overemphasized of its importance. If we get off without enough preprocessing, it’s hard to expect to get a desirable result no matter how good data we have.</p><p>This is the second part of OpenCV tutorial for beginners and the complete set of the series is as follows:</p><ol><li><a href="https://towardsdatascience.com/computer-vision-for-beginners-part-1-7cca775f58ef">Understanding color models and drawing figures on images</a></li><li><strong><em>The basics of image processing with filtering</em></strong></li><li><a href="https://towardsdatascience.com/computer-vision-for-beginners-part-3-79de62dbeef7">From feature detection to face detection</a></li><li><a href="https://towardsdatascience.com/computer-vision-for-beginners-part-4-64a8d9856208">Contour detection and having a little bit of fun</a></li></ol><p>Today we’re going to talk about how to manipulate images. These are going to be a preprocessing stage. When it comes to detecting edges and contours, noise gives a great impact on the accuracy of detection. Therefore removing noises and controlling the intensity of the pixel values can help the model to focus on the general details and get higher accuracy. Blurring, thresholding, and morphological transformation are the techniques we use for this purpose.</p><p>This post assumes you are already familiar with the concept of convolution. But if it’s not the case, I’d like to recommend you check <a href="https://towardsdatascience.com/the-most-intuitive-and-easiest-guide-for-convolutional-neural-network-3607be47480"><strong>this post</strong></a><strong> </strong>first. The complete code for this tutorial is also available on <a href="https://github.com/jjone36/vision_4_beginners/blob/master/part2_image_processing.ipynb"><strong>Github</strong></a>. Now let’s walk through how to apply image filtering with OpenCV one by one.</p><h3>Blurring</h3><p>The goal of blurring is to perform noise reduction. But we have to pay extra care here. If we apply edge detection algorithms to the images with high resolution, we’ll get too many detected outcomes that we aren’t interested in.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Py75v-74yoCNA8PgLU6wEw.png" /></figure><p>On the contrary, if we blur the images too much, we‘ll lose the data. Therefore we need to find an adequate amount of blurring we’re going to apply without losing desirable edges.</p><p>There are several techniques used to achieve blurring effects but we’re going to talk about the four major ones used in OpenCV: <strong><em>Averaging blurring, Gaussian blurring, median blurring </em></strong><em>and</em><strong><em> bilateral filtering</em></strong>. All four techniques have a common basic principle, which is applying convolutional operations to the image with a filter (kernel). The values of the applying filters are different between the four blurring methods.</p><p><strong>Average blurring</strong> is taking the average of all the pixel values under the given kernel area and replace the value at the center. For example, suppose we have a kernel with the size of 5X5. We calculate the average of the convoluted outcome and put that result to the center of the given area.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*T7o1lv_Xnkjct-426TAZeQ.png" /></figure><p>Then what will it be like if we increase the size of the kernel? As the size of filters gets bigger, the pixel values will be normalized more. Therefore we can expect the image to get blurred the more. Let’s check out the result with the code as follows. (For comparison, I’ll keep attaching the original image to the result)</p><pre># Import the image and convert to RGB <br>img = cv2.imread(&#39;text.jpg&#39;)<br>img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)</pre><pre># Plot the image with different kernel sizes<br>kernels = [5, 11, 17]</pre><pre>fig, axs = plt.subplots(nrows = 1, ncols = 3, figsize = (20, 20))<strong><br>for </strong>ind, s <strong>in </strong>enumerate(kernels):<br>    img_blurred = <strong>cv2.blur(img, ksize = (s, s))</strong><br>    ax = axs[ind]<br>    ax.imshow(img_blurred)<br>    ax.axis(&#39;off&#39;)<br>plt.show()</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*snCwfwgq5-0JMky-h4DmbQ.png" /></figure><p><strong>Medium blurring</strong> is the same with average blurring except that it uses the median value instead of the average. Therefore when we have to handle sudden noises in the image such as ‘<a href="https://en.wikipedia.org/wiki/Salt-and-pepper_noise">salt and pepper noise</a>,’ it’ll be better to use medium blurring than average blurring.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/675/1*bhA819pLZlZc6w8sBC8AKQ.png" /><figcaption><a href="http://eric-yuan.me/bilateral-filtering/">The shape of a Gaussian filter (on the left) and a Bilateral filter (on the right)</a></figcaption></figure><p><strong>Gaussian blurring </strong>is nothing but using the kernel whose values have a Gaussian distribution. The values are generated by a Gaussian function so it requires a sigma value for its parameter. As you can see the image above, the values of the kernel go higher near the center and go smaller near the corner. It’s good to apply this method to the <a href="https://en.wikipedia.org/wiki/Gaussian_noise">noises that have a normal distribution</a> such as <a href="https://en.wikipedia.org/wiki/White_noise">white noise</a>.</p><p><strong>Bilateral Filtering </strong>is an advanced version of Gaussian blurring. Blurring produces not only dissolving noises but also smoothing edges. And bilateral filter can keep edges sharp while removing noises. It uses Gaussian-distributed values but takes both distance and the pixel value differences into account. Therefore it requires sigmaSpace and sigmaColor for the parameters.</p><pre># Blur the image <br>img_0 = <strong>cv2.blur</strong>(img, ksize = (7, 7))<br>img_1 = <strong>cv2.GaussianBlur</strong>(img, ksize = (7, 7), sigmaX = 0)   <br>img_2 = <strong>cv2.medianBlur</strong>(img, 7)<br>img_3 = <strong>cv2.bilateralFilter</strong>(img, 7, sigmaSpace = 75, sigmaColor =75)</pre><pre># Plot the images<br>images = [img_0, img_1, img_2, img_3]<br>fig, axs = plt.subplots(nrows = 1, ncols = 4, figsize = (20, 20))</pre><pre><strong>for</strong> ind, p <strong>in</strong> enumerate(images):<br>    ax = axs[ind]<br>    ax.imshow(p)<br>    ax.axis(&#39;off&#39;)<br>plt.show()</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*N22d0C7ZF6WocZngNCW9gA.png" /></figure><h3>Thresholding</h3><p>Thresholding transforms images into binary images. We need to set the threshold value and max values and then we convert the pixel values accordingly. There are five different types of thresholding: <strong><em>Binary, the inverse of Binary, Threshold to zero, the inverse of Threshold to Zero, </em></strong><em>and </em><strong><em>Threshold truncation</em></strong>.</p><pre>img = cv2.imread(&#39;gradation.png&#39;)</pre><pre># Thresholding <br>_, thresh_0 = cv2.threshold(img, 127, 255, <strong>cv2.THRESH_BINARY</strong>)<br>_, thresh_1 = cv2.threshold(img, 127, 255, <strong>cv2.THRESH_BINARY_INV</strong>)<br>_, thresh_2 = cv2.threshold(img, 127, 255, <strong>cv2.THRESH_TOZERO</strong>)<br>_, thresh_3 = cv2.threshold(img, 127, 255, <strong>cv2.THRESH_TOZERO_INV</strong>)<br>_, thresh_4 = cv2.threshold(img, 127, 255, <strong>cv2.THRESH_TRUNC</strong>)</pre><pre># Plot the images<br>images = [img, thresh_0, thresh_1, thresh_2, thresh_3, thresh_4]</pre><pre>fig, axs = plt.subplots(nrows = 2, ncols = 3, figsize = (13, 13))<br><strong>for </strong>ind, p <strong>in </strong>enumerate(images):<br>    ax = axs[ind//3, ind%3]<br>    ax.imshow(p)<br>plt.show()</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/730/1*swjBYQOnuNfv1rHM3p39PQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gmOL367EAnlsSdtNFqjB-A.png" /></figure><p>You can see how each type of thresholding can be expressed in a mathematical way and <strong>I(x, y) </strong>is the intensity at the point, or the pixel value at (x, y). But I prefer to understand the concept visually. Take a look at the picture on the right. The images are way better for you to comprehend the difference between the types.</p><p>But don’t you think it’s too harsh to take just one value of threshold and apply it to all parts of an image? What if we have a picture with various amount of lighting in different areas? In this case, applying one value to the whole image would be a bad choice. A better approach would be using different thresholds for each part of the image. There is another technique called <strong><em>Adaptive thresholding</em></strong>, which serves this issue. By calculating the threshold within the neighborhood area of the image, we can achieve a better result from images with varying illumination.</p><pre># Convert the image to grayscale<br>img = cv2.imread(&#39;text.jpg&#39;)<br>img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)</pre><pre># Adaptive Thresholding<br>_, thresh_binary = cv2.threshold(img, thresh = 127, maxval = 255, type = cv2.THRESH_BINARY)<br>adap_mean_2 = cv2.adaptiveThreshold(img, 255, <br>                                    <strong>cv2.ADAPTIVE_THRESH_MEAN_C</strong>, <br>                                    <strong>cv2.THRESH_BINARY</strong>, 7, 2)<br>adap_mean_2_inv = cv2.adaptiveThreshold(img, 255, <br>                                        <strong>cv2.ADAPTIVE_THRESH_MEAN_C</strong>, <br>                                        <strong>cv2.THRESH_BINARY_INV</strong>, 7, 2)<br>adap_mean_8 = cv2.adaptiveThreshold(img, 255, <br>                                    <strong>cv2.ADAPTIVE_THRESH_MEAN_C</strong>, <br>                                    cv2.THRESH_BINARY, 7, <strong>8</strong>)<br>adap_gaussian_8 = cv2.adaptiveThreshold(img, 255, <br>                                    <strong>cv2.ADAPTIVE_THRESH_GAUSSIAN_C</strong>, <br>                                    cv2.THRESH_BINARY, 7, <strong>8</strong>)</pre><p>We need to convert the color mode to grayscale to apply adaptive thresholding. The parameters of adaptive thresholding are maxValue (which I set 255 above), adaptiveMethod , thresholdType , blockSize and C . And the adaptive method here has two kinds: ADAPTIVE_THRESH_MEAN_C , ADAPTIVE_THRESH_GAUSSIAN_C . Let’s just see how images are produced differently.</p><pre># Plot the images<br>images = [img, thresh_binary, adap_mean_2, adap_mean_2_inv, <br>          adap_mean_8, adap_gaussian_8]<br>fig, axs = plt.subplots(nrows = 2, ncols = 3, figsize = (15, 15))</pre><pre><strong>for</strong> ind, p <strong>in </strong>enumerate(images):<br>    ax = axs[ind%2, ind//2]<br>    ax.imshow(p, cmap = &#39;gray&#39;)<br>    ax.axis(&#39;off&#39;)<br>plt.show()</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Y-x9AjrP7F0mlRo3xr822w.png" /></figure><p>We have the original image and the one with binary thresholding on the left line. Compare this with the second and third image on the upper line, which is produced by ADAPTIVE_THRESH_MEAN_C . It shows a more detailed result than that of binary thresholding. We can also see that it gets more explicit when the C value is bigger. C indicates how much we’ll subtract from the mean or weighted mean. With the two images on the right line, we can also compare the effect of ADAPTIVE_THRESH_MEAN_C and ADAPTIVE_THRESH_GAUSSIAN_C with the same C value.</p><h3>Gradient</h3><p>I believe we are already familiar with the concept of gradients. In mathematics, the <a href="https://en.wikipedia.org/wiki/Gradient">gradient</a> geometrically represents the slope of the graph of a function with multi-variables. As it is a vector-valued function, it takes a direction and a magnitude as its components. Here we can also bring the same concept to the pixel values of images as well. The <a href="https://en.wikipedia.org/wiki/Image_gradient">image gradient</a> represents directional changes in the intensity or color mode and we can use this concept for locating edges.</p><pre># Apply gradient filtering<br>sobel_x = <strong>cv2.Sobel</strong>(img, cv2.CV_64F, <strong>dx = 1, dy = 0</strong>, ksize = 5)<br>sobel_y = <strong>cv2.Sobel</strong>(img, cv2.CV_64F, <strong>dx = 0, dy = 1</strong>, ksize = 5)<br>blended = <strong>cv2.addWeighted</strong>(src1=sobel_x, alpha=0.5, src2=sobel_y,<br>                          beta=0.5, gamma=0)<br>laplacian = <strong>cv2.Laplacian</strong>(img, cv2.CV_64F)</pre><p><a href="https://en.wikipedia.org/wiki/Sobel_operator"><strong>Sobel operation</strong></a> uses both Gaussian smoothing and differentiation. We apply it by cv2.Sobel() and two different directions are available: vertical (sobel_x) and horizontal (sobel_y). dx and dy indicates the derivatives. When dx = 1 , the operator calculates the derivatives of the pixel values along the horizontal direction to make a filter.</p><p>We can also apply in both directions by summing the two filters of sobel_x and sobel_y . With the function cv2.addWeighted() , we can calculate the weighted sum of the filters. As you can see above in the code cell, I gave the same amount of weight to the two filters.</p><p><strong>Laplacian operation</strong> uses the second derivatives of x and y. The mathematical expression is shown below.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/618/1*CCoshNQzstEBBFejAQIMMw.png" /></figure><p>A picture is worth a thousand words. Let’s see how the images are like.</p><pre># Plot the images<br>images = [sobel_x, sobel_y, blended, laplacian]<br>plt.figure(figsize = (20, 20))<br>for i in range(4):<br>    plt.subplot(1, 4, i+1)<br>    plt.imshow(images[i], cmap = &#39;gray&#39;)<br>    plt.axis(&#39;off&#39;)<br>plt.show()</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NhA-rfgStylcIjyr5vrFKw.png" /></figure><p>It’s apparent that the first and second image have a directional pattern. With the first image, we can clearly see the edges in the vertical direction. With the second image, we can see the horizontal edges. And both the third and fourth images, the edges on both directions are shown.</p><h3>Morphological transformations</h3><p>It’s also possible to manipulate the figures of images by filtering, which is called as <strong><em>morphological transformation</em></strong>. Let’s talk about erosion and dilation first.</p><p><strong>Erosion</strong> is the technique for shrinking figures and it’s usually processed in a grayscale. The shape of filters can be a rectangle, an ellipse, and a cross shape. By applying a filter we remove any 0 values under the given area.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5hIEcVZNeNia9vcOmpYSNA.png" /></figure><p>Let’s see how these can be implemented in codes.</p><pre>img = cv2.imread(&#39;simpson.jpg&#39;)</pre><pre># Create erosion kernels <br>kernel_0 = np.ones((9, 9), np.uint8)<br>kernel_1 = cv2.getStructuringElement(<strong>cv2.MORPH_ELLIPSE</strong>, (9, 9))<br>kernel_2 = cv2.getStructuringElement(<strong>cv2.MORPH_CROSS</strong>, (9, 9))</pre><pre>kernels = [kernel_0, kernel_1, kernel_2]</pre><pre># Plot the images<br>plt.figure(figsize = (20, 20))<br>for i in range(3):<br>    img_copy = img.copy()<br>    img_copy = cv2.erode(img_copy, kernels[i], iterations = 3)<br>    plt.subplot(1, 3, i+1)<br>    plt.imshow(img_copy)<br>    plt.axis(&#39;off&#39;)<br>plt.show()</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QUxh8p-YQRgW1ClDEGJ96A.png" /></figure><p>Check out how the Simpson’s family shrank with different types of kernels applied. (Sorry to Simpson for losing his hands!) We can see the image with the ellipse filter is eroded in a “round” way while the one with the basic filter with a squared shape is eroded in a “linear” way. The last one with the cross filter shows it shrank in a “diagonal” way.</p><p><strong>Dilation</strong> is the opposite to erosion. It is making objects expand and the operation will be also opposite to that of erosion. Let’s check out the result with the code as follows.</p><pre># Apply dilation<br>kernel = np.ones((9, 9), np.uint8)<br>img_dilate = cv2.dilate(img, kernel, iterations = 3)</pre><pre>plt.figure(figsize = (20, 10))<br>plt.subplot(1, 2, 1); plt.imshow(img, cmap=&quot;gray&quot;)<br>plt.subplot(1, 2, 2); plt.imshow(img_dilate, cmap=&quot;gray&quot;)<br>plt.show()</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3ZGkk3Je_w1djUiVSYjMuQ.png" /></figure><p><strong>Opening </strong>and <strong>closing </strong>operation is the mixed version of erosion and dilation. Opening performs erosion first and then dilation is performed on the result from the erosion while closing performs dilation first and the erosion.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cIQO85byCuxy5-olcI_YZw.png" /></figure><p>As you can see the picture above, closing is useful to detect the overall contour of a figure and opening is suitable to detect subpatterns. We can implement these operators with the function cv2.morphologyEx() shown below. The parameter op indicates which type of operator we’re going to use.</p><pre># Apply the operations<br>kernel = np.ones((9, 9), np.uint8)</pre><pre>img_open = cv2.morphologyEx(img, op= <strong>cv2.MORPH_OPEN</strong>, kernel)<br>img_close = cv2.morphologyEx(img, op= <strong>cv2.MORPH_CLOSE</strong>, kernel)<br>img_grad = cv2.morphologyEx(img, op= <strong>cv2.MORPH_GRADIENT</strong>, kernel)<br>img_tophat = cv2.morphologyEx(img, op= <strong>cv2.MORPH_TOPHAT</strong>, kernel)<br>img_blackhat = cv2.morphologyEx(img, op= <strong>cv2.MORPH_BLACKHAT</strong>, kernel)</pre><pre># Plot the images<br>images = [img, img_open, img_close, img_grad, <br>          img_tophat, img_blackhat]</pre><pre>fig, axs = plt.subplots(nrows = 2, ncols = 3, figsize = (15, 15))<br><strong>for</strong> ind, p <strong>in </strong>enumerate(images):<br>    ax = axs[ind//3, ind%3]<br>    ax.imshow(p, cmap = &#39;gray&#39;)<br>    ax.axis(&#39;off&#39;)<br>plt.show()</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xDSFRKt0SRC3feyTKrH8Bg.png" /></figure><p>Note that the Simpson’s hand is depicted differently in the images with the opening filter and closing filter. Gradient filter (MORPH_CGRADIENT) is the subtracted area from dilation to erosion. Top hat filter (MORPH_TOPHAT) is the subtracted area from opening to the original image while black hot filter (MORPH_BLACKHAT) is that from closing. I’d recommend you to visit <a href="https://homepages.inf.ed.ac.uk/rbf/HIPR2/morops.htm"><strong>here</strong></a><strong> </strong>to get further explanations on morphological operators.</p><h3>What’s next?</h3><p>Did you enjoy the various image manipulation techniques? Besides the things we’ve discussed, there are other things available in OpenCV. So don’t hesitate to take a visit and check out <a href="https://docs.opencv.org/2.4/modules/imgproc/doc/miscellaneous_transformations.html?highlight=adaptivethreshold#https://datascienceschool.net/view-notebook/c4121d311aa34e6faa84f62ef06e43b0/"><strong>OpenCV documentation</strong></a>. Next time will be about the detection techniques such as contour detection and face detections.</p><p>Are there errors you would love to correct? Please share your insight with us. I’m always open to talk, so feel free to leave comments below and share your thoughts. I also share interesting and useful resources on<a href="https://www.linkedin.com/in/jiwon-jeong/"> <strong>LinkedIn</strong></a> so feel free to follow or reach out to me. I’ll be back with the next series of the story. Stay tuned!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=29b3f9151874" width="1" height="1" alt=""><hr><p><a href="https://medium.com/data-science/computer-vision-for-beginners-part-2-29b3f9151874">Computer Vision for Beginners: Part 2</a> was originally published in <a href="https://medium.com/data-science">TDS Archive</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Computer Vision for Beginners: Part 1]]></title>
            <link>https://medium.com/data-science/computer-vision-for-beginners-part-1-7cca775f58ef?source=rss-1df293b5ca56------2</link>
            <guid isPermaLink="false">https://medium.com/p/7cca775f58ef</guid>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[opencv]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[image-processing]]></category>
            <category><![CDATA[computer-vision]]></category>
            <dc:creator><![CDATA[Jiwon Jeong]]></dc:creator>
            <pubDate>Wed, 13 Mar 2019 14:22:25 GMT</pubDate>
            <atom:updated>2019-08-08T00:46:45.224Z</atom:updated>
            <content:encoded><![CDATA[<h4>Introduction to OpenCV and Image Processing in Python</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/1*CwSvaseYli4Jst1ibNXTgQ.jpeg" /></figure><p>Computer Vision is one of the hottest topics in artificial intelligence. It is making tremendous advances in self-driving cars, robotics as well as in various photo correction apps. Steady progress in object detection is being made every day. GANs is also a thing researchers are putting their eyes on these days. Vision is showing us the future of technology and we can’t even imagine what will be the end of its possibilities.</p><p>So do you want to take your first step in Computer Vision and participate in this latest movement? Welcome you are at the right place. From this article, we’re going to have a series of tutorials on the basics of image processing and object detection. This is the first part of OpenCV tutorial for beginners and the complete set of the series is as follows:</p><ol><li><strong><em>Understanding color models and drawing figures on images</em></strong></li><li><a href="https://towardsdatascience.com/computer-vision-for-beginners-part-2-29b3f9151874">The basics of image processing with filtering</a></li><li><a href="https://towardsdatascience.com/computer-vision-for-beginners-part-3-79de62dbeef7">From feature detection to face detection</a></li><li><a href="https://towardsdatascience.com/computer-vision-for-beginners-part-4-64a8d9856208">Contour detection and having a little bit of fun</a></li></ol><p>The first story of this series will be about installing OpenCV, explaining color models and drawing figures on images. The complete code for this tutorial is also available on <a href="https://nbviewer.jupyter.org/github/jjone36/vision_4_beginners/blob/master/01.image_processing/01_introduction.ipynb"><strong>Github</strong></a>. Now let’s get it started.</p><h3>Introduction to OpenCV</h3><p><a href="https://en.wikipedia.org/wiki/Digital_image_processing"><strong>Image processing</strong></a> is performing some operations on images to get an intended manipulation. Think about what we do when we start a new data analysis. We do some data preprocessing and feature engineering. It’s the same with image processing. We do image processing to manipulate the pictures for extracting some useful information from them. We can reduce noises, control the brightness and color contrast. To learn detailed image processing fundamentals, visit <a href="https://www.youtube.com/watch?v=QMLbTEQJCaI"><strong>this video</strong></a><strong>.</strong></p><p>OpenCV stands for <a href="https://opencv.org/"><strong><em>Open Source Computer Vision</em></strong></a> library and it’s invented by Intel in 1999. It’s first written in C/C++ so you may see tutorials more in C languages than Python. But now it’s also getting commonly used in Python for computer vision as well. First things first, let’s set up a proper environment for using OpenCV. The installation can be processed as follows but you can also find the detailed description <a href="https://pypi.org/project/opencv-python/"><strong>here</strong></a><strong>.</strong></p><pre>pip install opencv-python==3.4.2<br>pip install opencv-contrib-python==3.3.1</pre><p>After you finish the installation, try importing the package to see if it works well. If you get the return without any errors, then you’re now ready to go!</p><pre>import cv2<br>cv2.__version__</pre><p>The first step we’re going to do with OpenCV is importing an image and it can be done as follows.</p><pre>import numpy as np<br>import matplotlib.pyplot as plt<br>%matplotlib inline</pre><pre># Import the image<br>img = cv2.imread(&#39;burano.jpg&#39;)<br>plt.imshow(img)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/571/1*Oe3JzhYDb3I6wovNZ-488w.png" /></figure><p>Have you ever been to <a href="https://en.wikipedia.org/wiki/Burano">Burano</a>? It’s one of the most beautiful islands in Italy. If you haven’t been there, you should definitely check this place for your next holidays. But if you already know this island, you’d probably notice there’s something different in this picture. It’s a little bit different from the pictures we usually see from Burano. It should be more delightful than this!</p><p>This is because the default setting of the color mode in OpenCV comes in the order of BGR, which is different from that of Matplotlib. Therefore to see the image in RGB mode, we need to convert it from BGR to RGB as follows.</p><pre># Convert the image into RGB<br>img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)<br>plt.imshow(img_rgb)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/568/1*dIUzwVFYunVzBtRdOb7BSA.png" /></figure><p>Now, this is Burano! Such a lovely island in Italy!</p><h3>More than just RGB</h3><p>Let’s talk about color modes a little bit more. <a href="https://www.designersinsights.com/designer-resources/understanding-color-models/"><strong><em>A color model</em></strong></a><strong><em> </em></strong>is a system for creating a full range of colors using the primary colors. There are two different color models here: <strong><em>additive color models </em></strong>and <strong><em>subtractive color models</em></strong>. Additive models use light to represent colors in computer screens while subtractive models use inks to print those digital images on papers. The primary colors are red, green and blue (RGB) for the first one and cyan, magenta, yellow and black (CMYK) for the latter one. All the other colors we see on images are made by combining or mixing these primary colors. So the pictures can be depicted a little bit differently when they are represented in RGB and CMYK.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CSmlQDizc03csCaSgMgMIw.png" /><figcaption>(<a href="https://www.kinkos.co.kr/bbs/board.php?bo_table=k_magazine&amp;wr_id=9&amp;page=2">Source</a>)</figcaption></figure><p>You would be pretty accustomed to these two kinds of models. In the world of color models, however, there are more than two kinds of models. Among them, <strong><em>grayscale, HSV </em></strong>and <strong><em>HLS </em></strong>are the ones you’re going to see quite often in computer vision.</p><p>A grayscale is simple. It represents images and morphologies by the intensity of black and white, which means it has only one channel. To see images in grayscale, we need to convert the color mode into gray just as what we did with the BGR image earlier.</p><pre># Convert the image into gray scale<br>img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)<br>plt.imshow(img_gray, cmap = &#39;gray&#39;)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*E1cRhyj4ByJ_qrTev3hTlA.png" /></figure><p>Actually, RGB images are made up by stacking three channels: R, G, and B. So if we take each channel and depict them one by one, we can comprehend how the color channels are structured.</p><pre># Plot the three channels of the image<br>fig, axs = plt.subplots(nrows = 1, ncols = 3, figsize = (20, 20))</pre><pre>for i in range(0, 3):<br>    ax = axs[i]<br>    ax.imshow(img_rgb[:, :, i], cmap = &#39;gray&#39;)<br>plt.show()</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zeVGeXCBFE4FLSbv_bUaLw.png" /></figure><p>Take a look at the images above. The three images show you how each channel is composed of. In the R channel picture, the part with the high saturation of red colors looks white. Why is that? This is because the values in the red color parts will be near 255. And in grayscale mode, the higher the value is, the whiter the color becomes. You can also check this with G or B channels and compare how certain parts differ one from another.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/992/1*t6gJMcMAu8EUXcgbhhGy5Q.png" /></figure><p>HSV and HLS take a bit different aspect. As you can see above, they have a three-dimensional representation, and it’s more similar to the way of human perception. <strong><em>HSV </em></strong>stands for hue, saturation and value. <strong><em>HSL </em></strong>stands for hue, saturation and lightness. The center axis for HSV is the value of colors while that for HSL is the amount of light. Along the angles from the center axis, there is hue, the actual colors. And the distance from the center axis belongs to saturation. Transforming the color mode can be done as follows.</p><pre># Transform the image into HSV and HLS models<br>img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)<br>img_hls = cv2.cvtColor(img, cv2.COLOR_BGR2HLS)</pre><pre># Plot the converted images<br>fig, (ax1, ax2) = plt.subplots(nrows = 1, ncols = 2, figsize = (20, 20))<br>ax1.imshow(img_hsv)<br>ax2.imshow(img_hls)<br>plt.show()</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*f3pRIVbutpa9KBwuqsl43w.png" /></figure><p>But why do we have to transform the colors? What are these for? One example that can give the answer is lane detection. Please take a look at the picture below. See how the lanes are detected in different color modes. During the computer vision task, we do multiple color mode transformation along with masking. If you’d like to find more about how image processing is applied in the lane detection task, feel free to check out <a href="https://towardsdatascience.com/finding-lane-lines-simple-pipeline-for-lane-detection-d02b62e7572b"><strong>this post</strong></a> by <a href="https://medium.com/u/26a6ee15a5c5">nachiket tanksale</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Gs_f7aWJQktSkIWLy2m70g.png" /><figcaption><a href="https://towardsdatascience.com/finding-lane-lines-simple-pipeline-for-lane-detection-d02b62e7572b">RGB vs Grayscale (darkened) vs HSV vs HSL</a></figcaption></figure><p>Now I believe you get the idea. Image processing is ‘data preprocessing.’ It’s reducing noises and extracting useful patterns to make classification and detection tasks easier. Therefore all these techniques including the ones we’ll discuss later, are for helping the model to detect the patterns easier.</p><h3>Drawing on images</h3><p>Let’s bring some figures on the image. Now, we’re going to Paris. Have you ever heard of <a href="https://en.wikipedia.org/wiki/Wall_of_Love">the wall of love</a>? It’s a wall which is filled with the words “I love you” in all kinds of international languages. What we’re going to do is finding the words in our language and marking them with a rectangle. As I’m from South Korea, I’ll look up for ‘I love you’ in Korean. First, I’ll make a copy of the original image and then draw a rectangle with cv2.rectangle() We need to give the coordinates values for the upper left point and the lower right point.</p><pre># Copy the image<br>img_copy = img.copy()</pre><pre># Draw a rectangle <br>cv2.rectangle(img_copy, pt1 = (800, 470), pt2 = (980, 530), <br>              color = (255, 0, 0), thickness = 5)<br>plt.imshow(img_copy)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/568/1*6elxrI90FEiDhWplc74mYg.png" /></figure><p>Great! I think I caught the right position. Let’s try again. I can see one more Korean word from the image so I’ll make a circle this time. With cv2.circle() , we need to specify the point of its center and the length of its radius.</p><pre># Draw a circle <br>cv2.circle(img_copy, center = (950, 50), radius = 50, <br>           color = (0, 0, 255), thickness = 5)<br>plt.imshow(img_copy)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/571/1*fMZjDpFbVwTncrQAvOazdg.png" /></figure><p>We can also put text data on the image. Why don’t we write the name of this wall this time? With cv2.putText() , we can designate the position and the font style and size of the text.</p><pre># Add text <br>cv2.putText(img_copy, text = &quot;the Wall of Love&quot;, <br>            org = (250, 250),<br>            fontFace = cv2.FONT_HERSHEY_DUPLEX, <br>            fontScale = 2, <br>            color = (0, 255, 0), <br>            thickness = 2, <br>            lineType = cv2.LINE_AA)<br>plt.imshow(img_copy)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/568/1*ZnWzOppbxJoHp6Gb2R3OZA.png" /></figure><p>This is really a “lovely” wall, isn’t it? Try this yourself and find “I love you” in your language! 😍</p><h3>More than images</h3><p>Now we’ve been to Italy and France. Where would you like to go next? Why don’t we put a map and mark the places? We’re going to create a window and draw figures not by designating the points but by clicking directly on the window. Let’s try a circle first. We first create a function which will draw a circle with the data for the position and clicking of the mouse.</p><pre># Step 1. Define callback function<br><strong>def </strong>draw_circle(event, x, y, flags, param):</pre><pre><strong>    if </strong>event == cv2.EVENT_LBUTTONDOWN:<br>            cv2.circle(img, center = (x, y), radius = 5, <br>                       color = (87, 184, 237), thickness = -1)</pre><pre><strong>    elif </strong>event == cv2.EVENT_RBUTTONDOWN:        <br>            cv2.circle(img, center = (x, y), radius = 10,  <br>                       color = (87, 184, 237), thickness = 1)</pre><p>With cv2.EVENT_LBUTTONDOWN or cv2.EVENT_RBUTTONDOWN , we can bring the data for the position when we press the buttons of the mouse. The position of the mouse will be (x, y) and we’ll draw a circle whose center is at that point.</p><pre># Step 2. Call the window<br>img = cv2.imread(&#39;map.png&#39;)</pre><pre>cv2.namedWindow(winname = &#39;my_drawing&#39;)<br>cv2.setMouseCallback(&#39;my_drawing&#39;, draw_circle)</pre><p>We’ll set a map as the background of the window and name the window as <em>my_drawing</em>. The name of the window can be anything, but it should be the same because this acts like the id of the window. Using the cv2.setMouseCallback() , we make a connection between the window and the function draw_circle we made at step 1.</p><pre># Step 3. Execution<br><strong>while</strong> True:<br>    cv2.imshow(&#39;my_drawing&#39;,img)<br>    <strong>if </strong>cv2.waitKey(10) &amp; 0xFF == 27:<br>       <strong> break</strong></pre><pre>cv2.destroyAllWindows()</pre><p>Now we execute the window using while loop. Don’t forget to set the break unless you are making an infinite loop. The condition of the if clause is setting the window to be shut down when we press ESC on the keyboard. Save this as a file and import it on your terminal. If you’re to use jupyter lab, put the codes in one cell and execute. Now, tell me! Where do you want to go?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/904/1*Q3GfBHxWFS_ziXb1CGQkMg.gif" /></figure><p>Let’s try a rectangle. As a rectangle requires two points for <strong>pt1</strong> and <strong>pt2</strong> in cv2.rectangle() , we need an additional step to set the first click point as <strong>pt1</strong> and the last point as <strong>pt2</strong>. And we’re going to detect the movement of the mouse with cv2.EVENT_MOUSEMOVE and cv2.EVENT_LBUTTONUP .</p><p>We first define drawing = False as a default. When the left button is pressed, drawing becomes true and we give that first position as <strong>pt1</strong>. If drawing is on, it’ll take the current point as <strong>pt2 </strong>and keep drawing rectangles while we move the mouse. It’s like overlapping the figures. When the left button is up, drawing becomes false and it takes the last position of the mouse as its final point of <strong>pt2</strong>.</p><pre># Initialization<br>drawing = False<br>ix = -1<br>iy = -1</pre><pre># create a drawing function<br><strong>def</strong> draw_rectangle(event, x, y, flags, params):<br>    <br>    <strong>global </strong>ix, iy, drawing</pre><pre>    <strong>if</strong> event == cv2.EVENT_LBUTTONDOWN:<br>        drawing = True<br>        ix, iy = x, y<br>        <br>    <strong>elif</strong> event == cv2.EVENT_MOUSEMOVE:<br>        if drawing == True:<br>            cv2.rectangle(img, pt1=(ix, iy), pt2=(x, y), <br>                          color = (87, 184, 237), thickness = -1)<br>            <br>    <strong>elif</strong> event == cv2.EVENT_LBUTTONUP:<br>        drawing = False<br>        cv2.rectangle(img, pt1=(ix, iy), pt2=(x, y), <br>                     color = (87, 184, 237), thickness = -1)</pre><p>Replace draw_circle function to draw_rectangle in step 1. Please don’t forget to make a change inside the callback function, cv2.setMouseCallback() as well. So the whole code script will be as follows. Save this script file and run it on the terminal or the jupyter notebook.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b8887acc313d71d553e64377a5e90ad1/href">https://medium.com/media/b8887acc313d71d553e64377a5e90ad1/href</a></iframe><h3>What’s next?</h3><p>Did you enjoy the first time with OpenCV? You can also try other functions such as drawing a line or a polygon. Feel free to check the documentation for it, which can be found <a href="https://docs.opencv.org/2.4/modules/core/doc/drawing_functions.html"><strong>here</strong></a>. Next time, we’re going to talk about more advanced technologies such as attaching two different images, image contour and object detection.</p><p>⭐️ And you might also find these series interesting as well ⭐️</p><ul><li><strong>The basic concept of convolutional neural networks:</strong></li></ul><p><a href="https://towardsdatascience.com/the-most-intuitive-and-easiest-guide-for-convolutional-neural-network-3607be47480">The Most Intuitive and Easiest Guide for CNN</a></p><ul><li><strong>Advanced networks of convolutional neural networks:</strong></li></ul><p><a href="https://towardsdatascience.com/deep-dive-into-the-computer-vision-world-f35cd7349e16">Deep Dive into the Computer Vision World: Part 1</a></p><p>Are there errors you would love to correct? Please share your insight with us. I’m always open to talk, so feel free to leave comments below and share your thoughts. I also share interesting and useful resources on<a href="https://www.linkedin.com/in/jiwon-jeong/"> LinkedIn</a> so feel free to follow or reach out to me. I’ll be back again with another interesting story next time!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7cca775f58ef" width="1" height="1" alt=""><hr><p><a href="https://medium.com/data-science/computer-vision-for-beginners-part-1-7cca775f58ef">Computer Vision for Beginners: Part 1</a> was originally published in <a href="https://medium.com/data-science">TDS Archive</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Another Stage Of Visualization: Be Reactive with Dash]]></title>
            <link>https://medium.com/data-science/a-gentle-invitation-to-interactive-visualization-with-dash-a200427ccce9?source=rss-1df293b5ca56------2</link>
            <guid isPermaLink="false">https://medium.com/p/a200427ccce9</guid>
            <category><![CDATA[plotly]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[dash]]></category>
            <category><![CDATA[visualization]]></category>
            <category><![CDATA[data-science]]></category>
            <dc:creator><![CDATA[Jiwon Jeong]]></dc:creator>
            <pubDate>Mon, 04 Mar 2019 02:59:50 GMT</pubDate>
            <atom:updated>2019-03-04T14:40:59.901Z</atom:updated>
            <content:encoded><![CDATA[<h4>A gentle invitation to Dash by Plotly</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/0*cpHURXwg9JdSaRtm" /></figure><p>Dash is an open source python library which enables us to create web applications with Plotly. It makes it easy to build an interactive visualization with simple reactive decorators like a dropdown, a slider bar, and markdown text data. We can even update the plots according to the input data with callbacks, and all these functions are available without Javascript or HTML directly. Plotly is a very powerful tool that enables us to make an informative and effective plot in a handy way and Dash can be considered as a stage for showcasing awesome visualization.</p><p>Today I’m going to show you how to create a dashboard with Plotly. And we’ll make a time series line plot and add a dropdown and a slider bar interacting with the plot. There is nice documentation on how to use this library, but I find it could be hard to read the example codes and utilize them at first. So this could be a kind bridge for you to understand how to use Dash with Plotly.</p><h3>Installation</h3><p>To build a dashboard, we need to install some packages as follows.</p><pre>pip install plotly==2.5.1<br>pip install dash==0.21.0<br>pip install dash-core-components==0.22.1<br>pip install dash-html-components==0.10.0<br>pip install dash-renderer==0.12.1</pre><p>As I said above, dash-core-components allows us to build not only graphs but also a dropdown and a text box so that we can update the components accordingly. dash-html-components enables us to use HTML &amp; CSS with Python. It helps us to place HTML components such as Div, H1, and H2 on our dashboard.</p><p>If this is your first time using Dash and HTML syntax, it can be a bit complex and hard to read. Therefore I’d recommend you to take the following script as a skeleton guide to Dash. From now on, what we’re going to do is filling this out step by step.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/914989d8deff3e25ac841f0bfbc585d1/href">https://medium.com/media/914989d8deff3e25ac841f0bfbc585d1/href</a></iframe><p>First things first, we import the libraries. You can add other libraries as you need. And then we initialize Dash by calling the Dash class. This is like putting a blank whiteboard on our table and what we are doing is building up additional applications on that board.</p><pre><strong># Step 1. Launch the application</strong><br>app = dash.Dash()</pre><pre><strong># Step 2. Import the dataset</strong><br>df = pd.read_csv(&#39;finance-charts-apple.csv&#39;)</pre><p>Now let’s bring some ingredients from the CSV file with <em>pandas</em>. The dataset we’re going to use is the Apple stock price data which is available <a href="https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv"><strong>here</strong></a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kpNv2T8zP995aUxNeUQSQQ.png" /></figure><h3>Simple scatter plot on a dashboard</h3><p>I won’t cover the details on Plotly itself, but you can find a nice tutorial for Plotly from <a href="https://www.youtube.com/watch?v=yBU3uPzyQOg&amp;index=2&amp;list=PLE50-dh6JzC4onX-qkv9H3HtPbBVA8M94"><strong>this video</strong></a><strong> </strong>if it’s necessary. You can also learn how to make a 3D plot or Choropleth Map in the series of the video. Here we’ll make a line plot which shows fluctuations in the stock price.</p><pre><strong># Step 3. Create a plotly figure</strong><br>trace_1 = go.Scatter(x = st.Date, y = st[&#39;AAPL.High&#39;],<br>                    name = &#39;AAPL HIGH&#39;,<br>                    line = dict(width = 2,<br>                                color = &#39;rgb(229, 151, 50)&#39;))</pre><pre>layout = go.Layout(title = &#39;Time Series Plot&#39;,<br>                   hovermode = &#39;closest&#39;)</pre><pre>fig = go.Figure(data = [trace_1], layout = layout)</pre><p>Now, it’s time for dash-html-components to come into play. We first put a division and then bring the graph inside it. id is giving a name to this component so that we can call it by its name. You’ll understand what this is for later. And then we make a server to run in step 6. If we set the debug mode equals true, we can easily change and update the app while the server is running.</p><pre><strong># Step 4. Create a Dash layout</strong><br>app.layout = html.Div([<br>               <strong> dcc.Graph(id = &#39;plot&#39;, figure = fig)</strong><br>                      ])</pre><pre><strong># Step 6. Add the server clause</strong><br>if __name__ == &#39;__main__&#39;:<br>    app.run_server(debug = True)</pre><p>Let’s save this script with the name of <strong><em>app.py</em></strong> and import it on the terminal (or anaconda prompt). Note that the working directory should be the same with where you saved the file.</p><pre>C:\Users\jjone\Dash&gt; <strong>python app.py</strong></pre><p>If there is no typo or syntax error, you’ll see the localhost address. You can copy and paste it or you can just type <strong>localhost:8050 </strong>on a new web tab.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/948/1*L55LReQFwnz6FOLEbTWRfA.gif" /></figure><h3>Adding a header and a paragraph</h3><p>We can also have additional components such as putting text data just like HTML. You can find what kind of elements are available <a href="https://dash.plot.ly/dash-html-components"><strong>here</strong></a>. Let’s put a simple heading and a paragraph on our page.</p><pre><strong># Step 4. Create a Dash layout</strong><br>app.layout = html.Div([<br>    <strong>     </strong>   <strong>    # adding a header and a paragraph<br>                html.Div([<br>                    html.H1(&quot;This is my first dashboard&quot;),<br>                    html.P(&quot;Learning Dash is so interesting!!&quot;)<br>                         ], <br>                  </strong>  style = {&#39;padding&#39; : &#39;50px&#39; , <br>                             &#39;backgroundColor&#39; : &#39;#3aaab2&#39;}),</pre><pre># adding a plot        <br>                dcc.Graph(id = &#39;plot&#39;, figure = fig)<br>                   <strong>   </strong>])</pre><p>On top of the graph, I’m going to put one more division and add a header and a paragraph inside it. We have two major components, html.Div and dcc.Graph. Inside html.Div, there are two additional components, header ( html.H1 ) and a paragraph ( html.P ). We can change the margin of the components or the background color with the style attribute. It should be designated in a dictionary format supporting CSS properties.</p><p>Please pay extra care where the parenthesis starts and ends. It’s important to understand the range of the paragraph for each component. As there are so many parenthesizes and square brackets, it can be confusing at first. And It’s so easy to make a syntax mistake as well.</p><p>Put this code at step 4 in our template and see the result. We can simply check the outcome by pressing F5 if the server isn’t shut down.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/945/1*hPwmFhWj_-_17mdkNFeerQ.png" /></figure><p>Now I hope you get the idea of what the dash components and the HTML components are. How to put multiple components and how they go along with together.</p><h3>Dropdown</h3><p>Let’s try making a dropdown on our dashboard this time. We’re going to make another plot which changes its y-axis according to the given option. By the documentation of <a href="https://dash.plot.ly/dash-core-components"><strong>dash components</strong></a>, we can putDropdown as follows.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/858/1*XfWBooI0yAJs-PVYT7b0QA.png" /></figure><p>As you can see, the options should be in the format of a dictionary. In our case, the options will be the columns only with the continuous variables, from the 2nd column to the 10th. With the list comprehension, we can make the option dictionary in just one line.</p><pre>features = st.columns[1:-1]<br>opts = [{&#39;label&#39; : i, &#39;value&#39; : i} for i in features]</pre><p>Now instead of the HTML components, we’re going to put the dropdown component at the bottom of the plot. Take a look at the bold font first because others are just for decorating the major two applications.value is the default value of the dropdown.</p><pre><strong># Step 4. Create a Dash layout</strong><br>app.layout = html.Div([<br>                # adding a plot<br>                dcc.Graph(id = &#39;plot&#39;, figure = fig),</pre><pre><strong>                # dropdown</strong><br>        <strong>        html.P([<br>                    html.Label(&quot;Choose a feature&quot;),<br>                    dcc.Dropdown(id = &#39;opt&#39;, <br>                                 options = opts,<br>                                 value = opts[0])<br>                        ], </strong>style = {&#39;width&#39;: &#39;400px&#39;,<br>                                    &#39;fontSize&#39; : &#39;20px&#39;,<br>                                    &#39;padding-left&#39; : &#39;100px&#39;,<br>                                    &#39;display&#39;: &#39;inline-block&#39;}<strong>)</strong><br>                      ])</pre><p>And just as what we did before, we can replace this code at step 4 in our template and check the outcome.</p><h3>Connecting to graph with callback</h3><p>To update the graph according to the choice of the dropdown, we need to make a connection between input data (the dropdown) and output data (the graph). This will be done by adding a callback function in step 5.</p><pre><strong># Step 5. Add callback functions</strong><br><a href="http://twitter.com/app"><strong>@app</strong></a><strong>.callback(</strong>Output(&#39;plot&#39;, &#39;figure&#39;),<br>             [Input(&#39;opt&#39;, &#39;value&#39;)]<strong>)</strong></pre><p>Do you remember what id we gave to each component? <strong><em>plot </em></strong>and <strong><em>opt. </em></strong>Therefore we can call them with their name as shown above. We take the input data from the options by the name of <strong><em>opt </em></strong>and give the output to the line plot by the name of <strong><em>plot</em></strong>.</p><pre><strong>def</strong> update_figure(<strong>X</strong>):<br>    trace_2 = go.Scatter(x = st.Date, <strong>y = st[X],</strong><br>                       <strong> name = X,</strong><br>                        line = dict(width = 2,<br>                                    color = &#39;rgb(106, 181, 135)&#39;))<br>    fig = go.Figure(data = <strong>[trace_1, trace_2]</strong>, layout = layout)<br>    <strong>return</strong> fig</pre><p>So we take the input data and then return the output as we want by creating an update function. What is our input data here? It’s the name of the feature variable selected from the dropdown, and we are to make it as the y-axis of our second line plot. As there is nothing to be changed with the trace_1, we can simply add trace_2 to the data list. Now if we put this code at step 5 and rerun <strong>app.py</strong>, the result will be is shown below.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/936/1*fC_NBqX04_dPCvc1E2aY-A.gif" /></figure><p>I’d like to briefly talk about a callback before we move on to the next section. Because it’s one of the most frequently used functions while programming any kind of applications. In a nutshell, <a href="https://en.wikipedia.org/wiki/Callback_(computer_programming)"><strong><em>a callback</em></strong></a><strong><em> </em></strong>is a function that is executed later when it gets the real execution command and it’s usually for the purpose of updating. Callback functions don’t work right after they get the command. These people are kind of <strong><em>lazy</em></strong>. They will do their work after the other command lines are executed and we <strong><em>call </em></strong>them <strong><em>back </em></strong>again with the real execution command.</p><p>See how our update_figure() works. This function does his work not right after we import this app but when we give the actual input signal to it by “calling back.” This is what a callback function is. It’s totally fine you use this without understanding but I hope you get the rough idea here. You can also find more details from <a href="https://codeburst.io/javascript-what-the-heck-is-a-callback-aba4da2deced"><strong>this article</strong></a>.</p><h3>RangeSlider</h3><p>Lastly, let’s try a range slider. We’ll add a year range slider to the plot, and it’s very similar to dropdowns. Let’s check the documentation of dash components first.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*pXzCcck7W2eenYAt6FfmSA.png" /></figure><p>I think you can easily get the idea by this time. The key point here is how to make a mark dictionary. As the date period starts from (Feb 17 2015<em>)</em> to (Feb 17 2017)<em>, </em>I’d like to add 7 marks between the period as follows.</p><pre>st[&#39;Date&#39;] = pd.to_datetime(st.Date)<br>dates = [&#39;2015-02-17&#39;, &#39;2015-05-17&#39;, &#39;2015-08-17&#39;, &#39;2015-11-17&#39;,<br>         &#39;2016-02-17&#39;, &#39;2016-05-17&#39;, &#39;2016-08-17&#39;, &#39;2016-11-17&#39;,  <br>         &#39;2017-02-17&#39;]</pre><pre>date_mark = {i : dates[i] for i in range(0, 9)}</pre><p>Now we can simply put the range slider at the dropdown place. I’ll name it as <strong><em>slider </em></strong>this time. min and max are the minimum and the maximum of the slider and value is the default setting of the slider. Again, all the other parts are designing the HTML components with CSS style.</p><pre><strong># Step 4. Create a Dash layout</strong><br>app.layout = html.Div([<br>                # adding a plot<br>                dcc.Graph(id = &#39;plot&#39;, figure = fig),</pre><pre><strong>                # range slider<br>                html.P([<br>                    html.Label(&quot;Time Period&quot;),<br>                    dcc.RangeSlider(id = &#39;slider&#39;,<br>                                    marks = date_mark,<br>                                    min = 0,<br>                                    max = 8,<br>                                    value = [3, 4]) <br>                        ],</strong> style = {&#39;width&#39; : &#39;80%&#39;,<br>                                    &#39;fontSize&#39; : &#39;20px&#39;,<br>                                    &#39;padding-left&#39; : &#39;100px&#39;,<br>                                    &#39;display&#39;: &#39;inline-block&#39;}<strong>)</strong><br>                      ])</pre><p>After making a slider, what will be the next step? Connecting it to the plot! Again we’ll take the input data from the <strong><em>slider</em></strong> and return the output to the <strong><em>plot</em></strong>. But this time, there is a small trick here. Did you notice that the default value of the slider is a list with two values? Please check the code box above again. <strong>value = [3, 4]</strong> Different from the dropdown component, the range slider takes two values in a list. Therefore when we get the input data from the slider, there will be two values of the starting and ending value (<strong>X[0]</strong> , <strong>X[1]</strong>)</p><pre><strong># Step 5. Add callback functions</strong><br><a href="http://twitter.com/app">@app</a>.callback(Output(&#39;plot&#39;, &#39;figure&#39;),<br>             [Input(&#39;slider&#39;, &#39;value&#39;)])<br><strong>def </strong>update_figure<strong>(X):</strong><br>    <strong>st2 = st[(st.Date &gt; dates[X[0]]) &amp; (st.Date &lt; dates[X[1]])]</strong><br>    trace_1 = go.Scatter(x = st2.Date, y = st2[&#39;AAPL.High&#39;],<br>                        name = &#39;AAPL HIGH&#39;,<br>                        line = dict(width = 2,<br>                                    color = &#39;rgb(229, 151, 50)&#39;))<br>    fig = go.Figure(data = [trace_1], layout = layout)<br>   <strong> return</strong> fig</pre><p>Now we’ll filter the data and update the plot with the given period. If you reload the web page again, you’ll see the same result with me.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/940/1*9z1GrsxIIBtb8HCpvPMXUQ.gif" /></figure><h3>Bring it all together!</h3><p>I think we covered quite a lot of things so far. From putting a graph to adding a callback. Now why don’t we bring it all together? I’d recommend you to review the script below step by step. You can combine all the components of the graph, the dropdown and the slider at step 4 just like replacing and changing modules.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b070a922fdcf5a5792ea5c9eb25915b0/href">https://medium.com/media/b070a922fdcf5a5792ea5c9eb25915b0/href</a></iframe><p>Are you ready to see how our first dashboard looks like? 😃😃🙌🙌 BOOM!!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sdhmWqbWpO7cArqG2kqeSQ.gif" /></figure><p>Great work! Did you enjoy building a dashboard? I hope now you become comfortable reading and understanding other Dash code examples. Dash provides<strong> </strong><a href="https://dash.plot.ly/"><strong>such a nice guide</strong></a><strong> </strong>so feel free to explore the sites on your own. You can also check other stuff available in Dash by Plotly.</p><p><a href="https://medium.com/@plotlygraphs/introducing-dash-5ecf7191b503">🌟 Introducing Dash 🌟</a></p><h3>Deployment</h3><p>So far our application is locally hosted on your machine. To make it available to other people, we need to deploy it on the web. There are two choices you can take, App authorization and deploying it to Heroku. The first one requires us to install the <a href="https://github.com/plotly/dash-auth">dash-auth</a> package an there are two different types of authentication, HTTP Basic Auth or Plotly OAuth. You can follow <a href="https://dash.plot.ly/authentication"><strong>this tutorial</strong></a>.</p><p>Another way is by using the hosting platforms such as Heroku, AWS, Google Cloud Platform and so on. Heroku is the easiest way for deploying and you can follow <a href="https://dash.plot.ly/deployment"><strong>this tutorial</strong></a>. The other platforms are just the same with Heroku.</p><p>Beside dropdown, we can also add other components such as text area or uploading data. I recommend you to explore what kind of dash components are available. You can also add as many HTML components as you want and design your board in a much fancier way with CSS properties. If you need to learn some basics of HTML &amp; CSS, you can find a great tutorial from <a href="https://www.youtube.com/playlist?list=PLB5jA40tNf3vh75MBwUb4oqiXFOZ5pYyX"><strong>this video</strong></a>.</p><p>Thank you for reading and I hope you found this post interesting. If there’s anything need to be corrected, please share your insight with us. I’m always open to talk so feel free to leave comments below and share your thoughts. I also share interesting and useful resources on <a href="https://www.linkedin.com/in/jiwon-jeong/">LinkedIn</a> so feel free to follow and reach me out. I’ll be back again with another interesting story next time!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a200427ccce9" width="1" height="1" alt=""><hr><p><a href="https://medium.com/data-science/a-gentle-invitation-to-interactive-visualization-with-dash-a200427ccce9">Another Stage Of Visualization: Be Reactive with Dash</a> was originally published in <a href="https://medium.com/data-science">TDS Archive</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Most Intuitive and Easiest Guide for Recurrent Neural Network]]></title>
            <link>https://medium.com/data-science/the-most-intuitive-and-easiest-guide-for-recurrent-neural-network-873c29da73c7?source=rss-1df293b5ca56------2</link>
            <guid isPermaLink="false">https://medium.com/p/873c29da73c7</guid>
            <category><![CDATA[deep-learning]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[neural-networks]]></category>
            <category><![CDATA[recurrent-neural-network]]></category>
            <category><![CDATA[beginner]]></category>
            <dc:creator><![CDATA[Jiwon Jeong]]></dc:creator>
            <pubDate>Wed, 06 Feb 2019 13:50:41 GMT</pubDate>
            <atom:updated>2019-02-06T14:40:30.984Z</atom:updated>
            <content:encoded><![CDATA[<h4>Demystifying RNN for complete starters</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Ewzai84t9dRGo-3p.jpg" /></figure><p>Everything has its past. And sometimes the past defines us. What route we’ve walked through and what choices we made along the way. They are curved as our history and tells us what kind of person we were and where we’re headed. This is also applicable to data. They can have their past, and that history can be used for predicting what’s coming next, the future. As we have a fortune teller, likewise data, which is called the sequence models.</p><p>This is the third part of ‘The Most Intuitive and Easiest Guide’ series to neural networks. The complete set of this post is as follows:</p><ol><li><a href="https://towardsdatascience.com/the-most-intuitive-and-easiest-guide-for-artificial-neural-network-6a3f2bc0eecb">The Easiest Guide for ANN</a></li><li><a href="https://towardsdatascience.com/the-most-intuitive-and-easiest-guide-for-convolutional-neural-network-3607be47480">The Easiest Guide for CNN</a></li><li>The Easiest Guide for RNN (this post)</li></ol><p>This post assumes that you have pre-knowledge on the basics of neural networks. If you are completely new to this, feel free to check out the first series. Today’s keywords are sequential, vanishing gradients, memory and gates. It can be hard to understand the structures and the mathematical expressions of recurrent neural networks. So this post will focus on the basic intuition with least amount of maths, just like what we did with ANN in the previous post.</p><h3>What’s new here?</h3><p>If you’ve ever heard of sequence models, then you’d probably have seen this kind of diagrams shown below. And It’s so natural that you don’t get the concept portrayed in the diagram at one sight. This is because the sequence models are a bit different from the other neural networks discussed previously.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/921/1*hnKQvsYO2kR-NHhrJAwM6Q.png" /><figcaption><a href="http://colah.github.io/posts/2015-08-Understanding-LSTMs/">Colah’s blog</a></figcaption></figure><p>So before jumping deep into this neural network, why don’t we talk about where the sequence models are used in the real world. This is because it can help us to understand the concept by getting a hint from those examples. So where RNN get the spotlight by data scientists?</p><p>Speech recognition can be one typical example we can find in the closest distance. Alexa, Siri, and Google home. Those devices response to our call and can do some simple tasks such as setting the alarm or sending a message to someone else. We can also find it with google translation or chatbots as well.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/0*0xV3NaURAIB_mehc.jpg" /></figure><p>The sequence model is also popular in the field of genetics. If you have a little background knowledge on DNA, it can be much easier to understand the meaning of sequence here. In human molecules, <a href="https://en.wikipedia.org/wiki/DNA">DNA</a>(deoxyribonucleic acid) is composed of a chain of 4 different types of nucleotides: <strong>A</strong>, <strong>C</strong>, <strong>G</strong>, and <strong>T</strong>. Genetic information exists in the successive combination of the nucleotides. Therefore these orders are essential for all kinds of living things and the sequence model has high usage in predicting the uncovered part of genes.</p><p>What about time series analysis? This neural network is also able to predict what value will come next in stock market price or temperature data. For example, we gather 30 days of temperature data and use this series of data to predict what will be the temperature of 31st.</p><p>Did you notice the common characteristic of these examples? Speech, DNA chains, and time series. The answer is “sequential.” The data is not static or discontinuous, but forgoing and successive. But what do we mean by <em>sequential</em> or <em>consecutive </em>when it comes to data?</p><p>Let’s recall what we’ve been doing with other models like ANN. We had sample data and passed them through layers from left to right. This means that we will input the data at a time and then they will travel toward the output layer. It was feedforward propagation and had only one direction of the flow.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-S0iGXVcbWdOElfVyTX4_Q.png" /></figure><p>In the case of RNN, however, the data aren’t inputted at the same time. As you can see the picture on the right, we will input <strong>X1 </strong>first and then input <strong>X2 </strong>to the result of <strong>X1 </strong>computation. So in the same way,<strong> X3 </strong>is computed with the result from <strong>X2</strong> computation stage.</p><p>Therefore when it comes to data, ‘<strong><em>sequential</em></strong>’ means we have an order in time between the data. When it’s ANN, there isn’t any concept of order in X1, X2 and X3. We just input them at once. In the case of RNN, however, they are inputted at different times. Therefore if we change the order, it becomes significantly different. A sentence will lose its meaning. And when it comes to DNA, this change might create… a mutant.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fgiphy.com%2Fembed%2F3og0II63CbKmOiVjS8%2Ftwitter%2Fiframe&amp;url=https%3A%2F%2Fmedia.giphy.com%2Fmedia%2F3og0II63CbKmOiVjS8%2Fgiphy.gif&amp;image=https%3A%2F%2Fi.giphy.com%2Fmedia%2F3og0II63CbKmOiVjS8%2Fgiphy.gif&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=giphy" width="435" height="180" frameborder="0" scrolling="no"><a href="https://medium.com/media/c0348462f367ccf2059e400c04cc45dc/href">https://medium.com/media/c0348462f367ccf2059e400c04cc45dc/href</a></iframe><p>So the RNN has its appeal in that we can connect the data with the previous data. This means that the model starts to care about the past and what is coming next. As the recurrent units hold the past values, we can refer to this as memory. We are now able to consider a real meaning of ‘context’ in data.</p><h3>The structure of Recurrent Neural Network</h3><p>Now with this basic intuition, let’s go deeper into the structure of RNN. This is a simple RNN with one shallow layer. Our model is now going to take two values: the <strong>X </strong>input value at time <strong>t </strong>and the output value <strong>A </strong>from the previous cell (at time <strong>t-1</strong>).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/935/1*vJ8Vk-gJ5tBSDHGkqVf3kA.png" /></figure><p>Please take a look at the equation <strong>①</strong>.<strong> </strong>There are weights and bias as we did with simple ANN. It’s just adding one more input value <strong>A0</strong>. And there are two different outputs from the cell. The output of <strong>A1 </strong>which will go to the next unit(<strong>②</strong>) and the final output <strong>Y1 </strong>of the unit cell(<strong>③</strong>). Don’t get stressed by all these subscripts. They are just indicating to what value the weight belongs. Now let’s move to the next units.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*af8EGQluDwbr48IsqdhmFA.png" /></figure><p>Simple and cool, right? Now we can predict the future. If it’s about the stock market, we can predict the stock price of a company. We then guess If there is a big dataset say, the history data from 10 years ago, we’ll get better accuracy. So the longer the data, the better the outcome, Right? But the truth is, this model ain’t as ideal as we’d expect.</p><h3>The real thing starts from here</h3><p>The idea of remembering the past is fantastic. But there is one critical problem in back-propagation. Back-propagation is a step for going backward to update the weights of each layer. To update the weights, we get the gradient of the cost function and keep multiplying the gradients at the given layers using chain rule. The actual backward propagation in RNN is a bit complex than this diagram but let’s skip them for simplicity. (For example, The real backward propagation takes not only the final output <strong>Yt</strong> but also all the other output <strong>Y </strong>used by the cost function.)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1011/1*WUbucQbDazy3hoEyS-hxDw.png" /></figure><p>Imagine when the gradients are bigger than 1. The updated values become so big to use it for optimizing. This is called <strong><em>exploding gradients</em></strong>. But this isn’t a severe problem because we can fix the range that gradients can’t go over. The real problem occurs when the gradients are smaller than 1. If we keep multiplying the values lower than 1, the result becomes smaller and smaller. After some steps, there will be no significant difference in outcome, and it can’t make any update in weights. It is called <strong><em>vanishing gradients.</em></strong> It means the back-propagation effect can’t go far enough to reach the early stage of layers.</p><p>This is why people say RNN has a bad memory. If the length of the input values gets longer, we can’t expect actual optimization. This is a really critical problem because the power of neural networks comes from updating weights. Would there be other ways to fix this problem?</p><h3>Let’s just forget about it!</h3><p>To be honest, it’s very hard to get good memories for people with bad memories. We all know that. We can’t remember too many things. The same goes for our model. Instead of dragging all the past, maybe it’ll be better to remember selectively. This is like choosing only the important information and forgetting the rest. Having all those past values causes the vanishing gradients. Therefore we’ll give an additional step to simple RNN, which is called <strong><em>Gated Recurrent Units</em></strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OWs6_jFetL3udddG4QhN6A.png" /></figure><p>The diagram on the left shows the computation inside the unit cell of RNN. There’s nothing new here. It’s just showing that it takes two input values and returns two output values after the computation. On the right side, you can see one small change inside the box. The green box. What is this? This is a <strong><em>gate controller</em></strong>. This box determines whether we should remember or not.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4aPz6MSNRSbCHo9dJDQetA.png" /></figure><p>We’ll compute the tanh activation (<strong>①</strong>) as what we did with RNN, but this won’t be used right ahead. It’s like a candidate. Here we’ll also get the new value (<strong>②</strong>) for the gate. Since it takes the sigmoid function, the output value always goes between 0 to 1. So by multiplying it with the <strong>① </strong>value, we’re going to decide whether we’ll use it or not. When it’s <strong>0</strong>, “<strong><em>No Use</em></strong>” or “<strong><em>No Update.</em></strong>” (Use the previous value in this case) When it’s <strong>1</strong>, “<strong><em>Use</em></strong>” or “<strong><em>Update</em>.</strong>” This is like opening and closing a gate.</p><h3>The gate and the gate</h3><p>Now we finally arrived at the LSTM, <strong><em>Long Short Term Memory networks</em></strong>. Actually, LSTM was proposed earlier than GRUs. But I brought GRUs first because the structure of LSTM is more complex compared to that of GRUs with two more gates. Additionally, there will be a new concept here which is called ‘<strong><em>cell state</em></strong>.’ But you’re already used to this concept. It’s putting the value containing the memories aside from the hidden state value <strong>A</strong>. Let’s have a look at what LSTM has in details. The blue box and the green box (<strong><em>the input gate</em></strong>) in the middle is the same with GRUs. There are two additional green boxes here: <strong><em>the forget gate</em> </strong>and <strong><em>the output gate</em></strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/983/1*Bm6XVEy9gfh_GHHFeTN0jw.png" /></figure><p>The computation will also be the same, so we’re going to get the output from the tanh function (<strong>①</strong>) and the input gate value from the sigmoid function (<strong>②</strong>). But the multiplying step is a bit different this time. Instead of taking only the result of the input gate, we also consider the forget gate value coming from the left.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*eAR6lhV1Ua0kX3bptrFoLQ.png" /></figure><p>Let’s see the equation in line 3 on the right. We have the forget gate, and this will be used for updating the cell state value like what you can see in line 4. Lastly there is one gate left, the output gate. And as you can see in line 6, we get the <strong>A </strong>value at time <strong>t </strong>by multiplying the output gate value and the tanh activation value of <strong>C</strong> at time <strong>t</strong>.</p><p>With these three additional gates, LSTM can have a stronger memory ability. Those are all about controlling which parts or what amount it should remember. It’s so robust and effective that it’s really popular among the sequence models. Then could we say is it better than GRUs for all cases? As we say there’s no silver bullet all the time, GRUs has its own strength as well. Its structure is simpler than LSTM, so it’s sometimes proper to use GRUs to make a big model.</p><h3>Conclusion</h3><p>What we’ve discussed so far was the sequence model with one shallow layer. Then what it would be like when we add more layers? If we say adding more layers goes horizontal in ANN or CNN, it goes vertically in RNN. But the sequence model is already a big model with one or two layers, it may end up overfitting if we add a few more layers on top of that. Therefore applying normalization techniques such as dropout or batch normalization is required.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Q5kFtP8W6GQgBzPjK-b8aw.png" /></figure><p>This post was mainly about understanding the structure of each model from simple RNN to LSTM. The mathematical expressions we walked through can be complicated at first sight. After you get familiar with the overall structures of the models, however, it becomes easy to understand. Because maths are just a numeric expression to represent the concept in a clear and effective manner. If you can see the meaning behind all those numbers, then maths will become fun and interesting too.</p><p>I also brought other additional resources as always. When you’re ready to take a further step with the sequence models, I highly recommend you to check out these articles as well.</p><ul><li><a href="http://colah.github.io/posts/2015-08-Understanding-LSTMs/"><strong>Understanding LSTM Networks</strong></a><strong> </strong>by Christopher Olah: Such a great article on understanding the structure of RNN and LSTM. And Colah’s blog is really popular. You can see many other materials which have this blog as a reference.</li><li><a href="https://www.analyticsvidhya.com/blog/2019/01/sequence-models-deeplearning/"><strong>Must-Read Tutorial to Learn Sequence Modeling</strong></a><strong> </strong>by <a href="https://medium.com/u/50dc5f8ee633">Pulkit Sharma</a>: I already posted his work in the previous blog, and this is the series for the sequence models. Highly recommend you to check out other series as well if you haven’t.</li><li><a href="https://towardsdatascience.com/recurrent-neural-networks-by-example-in-python-ffd204f99470"><strong>Recurrent Neural Networks by Example in Python</strong></a> by <a href="https://medium.com/u/e2f299e30cb9">Will Koehrsen</a>: A gentle guide from the top writer of Medium. This will be a great start for building your first RNN in Python.</li></ul><p>Thank you for reading and I hope you found this post interesting. If there’s anything need to be corrected, please share your insight with us. I’m always open to talk so feel free to leave comments below and share your thoughts. I’ll come back with another exciting project next time. Until then, happy machine learning!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=873c29da73c7" width="1" height="1" alt=""><hr><p><a href="https://medium.com/data-science/the-most-intuitive-and-easiest-guide-for-recurrent-neural-network-873c29da73c7">The Most Intuitive and Easiest Guide for Recurrent Neural Network</a> was originally published in <a href="https://medium.com/data-science">TDS Archive</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>