Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Events
Videos
Audiobooks
Packt Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

How-To Tutorials

7018 Articles
article-image-brad-miro-talks-tensorflow-2-0-features-and-how-google-is-using-it-internally
Sugandha Lahoti
10 Dec 2019
6 min read
Save for later

Brad Miro talks TensorFlow 2.0 features and how Google is using it internally

Sugandha Lahoti
10 Dec 2019
6 min read
TensorFlow 2.0, released in October, has got developers excited about a myriad of features and its ease of use.  At the EuroPython Conference 2019, Brad Miro, developer programs engineer at Google talked about the updates being made to TensorFlow 2.0. He also gave an overview of how Google is using TensorFlow, moving on to why Python is important for TensorFlow development and how to migrate from TF 1.x to TF 2.0. EuroPython is one of the most popular Python programming language community conferences. Below are some highlights from Brad’s talk at EuroPython. What is TensorFlow? TensorFlow, an open-source deep learning library developed at Google, first released in 2015. It’s a Python framework that includes a number of utilities for helping you write deep neural networks supporting both GPUs and TPUs. A lot of deep learning involves using mathematics, statistics, and algebra and perform low-level optimizations with your system. TensorFlow removes a lot of those abstractions leaving you to focus on actually writing your model. How TensorFlow is used internally at Google Tensorflow is used internally at Google to power all of its machine learning and AI. Google’s data centers are powered using AI and TensorFlow to help optimize the usage of these data centers to reduce bandwidth, to ensure network connections are optimized, and to reduce power consumption. TensorFlow also is useful for performing global localization in Google Maps. It is also used heavily in the Google Pixel range of smartphones to help optimize the software. These technologies are also used in medical research specifically in the field of Computer Vision. For example, Tensorflow is used to distinguish between the retinal image of a healthy eye from the retinal image of an eye that has diabetic retinopathy.   Further Learning If you want to learn to build more computer vision applications with TensorFlow 2.0, check out the book Hands-On Computer Vision with TensorFlow 2 by Benjamin Planche, and Eliot Andres. This book by Packt Publishing is a practical guide to building high-performance systems for object detection, segmentation, video processing, smartphone applications, and more. By the end of the book, you will have both the theoretical understanding and practical skills to solve advanced computer vision problems with TensorFlow 2.0. Furthermore, Google is using AI and TensorFlow to predict whether or not objects in space are planets. To summarize, they use AI to predict whether or not fluctuations in the brightness of an object is due to it being a planet.   Why Python is so important for TensorFlow Python has always been the choice for TensorFlow due to the language being extremely easy to use and having a rich ecosystem for data science including tools such as numpy, scikit-learn, and pandas. When TensorFlow was being built, the idea was that it should have the simplicity of numpy, performance of C but ease of use of Python.  What does TensorFlow 2.0 bring to the table TensorFlow 2.0 is powerful, flexible, scalable and easily deployable.  What’s gone Session.run tf.control_dependencies tf.global_variables_initializer tf.cond, tf.while_loop Tf.contrib What’s new Eager execution enabled by default  tf.function Keras as main high level API Distribution Strategy API SavedModel API  TensorFlow 2.0 had a major API Cleanup. Many API symbols are removed or renamed for better consistency and clarity.  Session.run has been replaced with eager execution which effectively means that your tensorflow code runs like numpy code.  Eager execution enables fast iteration and intuitive debugging without building a graph. It also makes creating and experimenting with models using TensorFlow easier. It can be especially useful when using the tf.keras model subclassing API. TensorFlow 2.0 has tf.function, a python decorator that lets you run regular Python code which is later compiled down to TensorFlow code using AutoGraph. The Distribution Strategy API in TensorFlow 2.0 allows machine learning researchers to distribute training across a wide variety of compute configurations. This release also allows distributed training with Keras’ model.fit and custom training loops. Keras is introduced as the main high-level API. Keras is a popular high-level API used for easy and fast prototyping, building, and training of deep learning models. This will enable developers to easily leverage their various model-building APIs. Using Keras with TensorFlow has two main methods.  Symbolic (Keras sequential) Your model is a graph of layers Any graph you compile will run  TensorFlow helps you debug by catching errors at compile time Imperative method (Keras subclassing) Your model is Python bytecode Complete flexibility and control  Harder to debug/ Harder to maintain  There are pros and cons of using each method; it really just depends on what your specific use cases are. The SavedModel API allows you to save your trained ML model into a language-neutral format. With TensorFlow 2.0, all TensorFlow ecosystem projects including TensorFlow Lite, TensorFlow JS, TensorFlow Serving, and TensorFlow Hub, support SavedModels. On Tensorflow Hub, you can store and download pre-built models. You can use TensorFlow Extended which is a Python library that can be run on your servers to productionalize your models. TensorFlow Lite lets you run your TensorFlow models on edge devices. With TensorFlow.js, you can run machine learning models using javascript in the browser or run them on servers using node. TensorFlow also has Swift for TensorFlow to help developers use Swift to develop machine learning models. “Swift for TensorFlow provides a new programming model that combines the performance of graphs with the flexibility and expressivity of Eager execution, with a strong focus on improved usability at every level of the stack. This is not just a TensorFlow API wrapper written in Swift — we added compiler and language enhancements to Swift to provide a first-class user experience for machine learning developers.”  Other packages that exist in the TensorFlow ecosystem used for niche use cases are TF Probability, TF Agents (reinforcement learning), Tensor2Tensor, TF Ranking, TF Text (natural language processing), TF Federated, TF privacy and more.  How to upgrade from TensorFlow 1.x to TensorFlow 2.0 There are several migration guides available on TensorFlow’s website. You can also use the tf.compat.v1 library for backwards compatibility and the tf_upgrade_v2 script which you can execute on top of any Python script to convert TF 1.x code to 2.0 code. You can also read more about TF 2.0 migration in our book Hands-On Computer Vision with TensorFlow 2 which introduces the automatic migration tool  and compares TensorFlow 1 concepts with their TensorFlow 2 counterparts with a detailed guide on migrating to idiomatic TensorFlow 2 code. You can watch Brad’s full talk on YouTube. This video is licensed under the CC BY-NC-SA 3.0 license.  TensorFlow.js contributor Kai Sasaki on how TensorFlow.js eases web-based machine learning application development Introducing Spleeter, a Tensorflow based python library that extracts voice and sound from any music track. TensorFlow 2.0 released with tighter Keras integration, eager execution enabled by default, and more!
Read more
  • 0
  • 0
  • 23677

article-image-stack-overflow-survey-data-further-confirms-pythons-popularity-as-it-moves-above-java-in-the-most-used-programming-language-list
Richard Gall
10 Apr 2019
5 min read
Save for later

Stack Overflow survey data further confirms Python's popularity as it moves above Java in the most used programming language list

Richard Gall
10 Apr 2019
5 min read
This year's Stack Overflow Developer Survey results provided a useful insight into how the programming language ecosystem is evolving. Perhaps the most remarkable - if unsurprising - insight was the continued and irresistible rise of Python. This year, for the first time, it finished higher in the rankings than Java. We probably don't need another sign that Python is taking over the world, but this is certainly another one to add to the collection.  What we already know about Python's popularity as a programming language Okay, so the Stack overflow survey results weren't that surprising because Python's growth is well-documented. The language has been shooting up the TIOBE rankings, coming third for the first time back in September 2018. The most recent ranking has seen it slip to fourth (C++ is making a resurgence - but that's a story for another time...), but it isn't in decline - it's still growing. In fact, despite moving back into fourth, it's still growing at the fastest rate of any programming language, with 2.36% growth in its rating. For comparison, C++'s rate of growth in the rankings is 1.62%. But it's not just about TIOBE rankings. Even back in September 2017 the Stack Overflow team were well aware of Python's particularly astonishing growth in high-income countries. Read next: 8 programming languages to learn in 2019 Python's growth in the Stack Overflow survey since 2013 It has been pretty easy to trace the growth in the use of Python through the results of every recent Stack Overflow survey. From 2016, it has consistently been on the up: 2013: 21.9% (6th position in the rankings) 2014: 23.4% (again, 6th position in the rankings) 2015: 23.8% (6th) 2016: 24.9% (6th) 2017: 32% (moving up to 5th...) 2018: 38.8% (down to 7th but with a big percentage increase) 2019: 41.7% (4th position) But more interestingly, it would seem that this growth in usage has been driving demand for it. Let's take a look at how things have changed in the 'most wanted' programming language since 2015 - this is the "percentage of developers who are not developing with the language or technology but have expressed interest in developing with it:"  2015: 14.8% (3rd) 2016: 13.3% (4th) 2017: 20.6% (1st) 2018: 25.1% (1st) 2019: 25.7% (1st) Alongside that, it's also worth considering just how well-loved Python is. A big part of this is probably the fact that Python is so effective for the people using it, and helps them solve the problems they want to solve. Those percentages are growing, even though it didn't take top position this year (this is described by Stack Overflow as the "percentage of developers who are developing with the language or technology and have expressed interest in continuing to develop with it"): 2015: 66.6% (10th position) 2016: 62.5% (9th) 2017: 62.7% (6th) 2018: 68% (3rd) 2019: 73.1% (2nd, this time pipped by Rust to the top spot) What's clear here is that Python has a really strong foothold both in the developer mind share (ie. developers believe it's something worth learning) and in terms of literal language use. Obviously, it's highly likely that both things are related - but whatever the reality, it's good to see that process happening in data from the last half a decade. Read next: 5 blog posts that could make you a better Python programmer What's driving the popularity of Python? The obvious question, then, is why Python is growing so quickly. There are plenty of theories out there, and there are certainly plenty of blog posts on the topic. But ultimately, Python's popularity boils down to a few key things.  Python is a flexible language One of the key reasons for Python's growth is its flexibility. It isn't confined to a specific domain. This would go some way of explaining its growth - because it's not limited to a specific job role or task, a huge range of developers are finding uses for it. This has a knock on effect - because the community of users continues to grow, there is much more impetus on developing tools that can support and facilitate the use of Python in diverse domains. Indeed, with the exception of JavaScript, Python is a language that many  developers experience through its huge range of related tools and libraries.   The growth of data science and machine learning While Python isn't limited to a specific domain, the immense rise in interest in machine learning and data analytics has been integral to Python's popularity. With so much data available to organizations and their employees, Python is a language that allows them to actually leverage it. Read next: Why is Python so good for AI and Machine Learning? 5 Python Experts Explain Python's easy to learn The final key driver of Python's growth is the fact that it is relatively easy to learn. It's actually a pretty good place to begin if you're new to programming.  Going back to the first point, it's precisely because it's flexible that people that might not typically write code or see themselves as developers could see Python as a neat solution to a problem they're trying to solve. Because it's not a particularly steep learning curve, it introduces these people to the foundational elements of programming. Something which can only be a good thing, right? The future of Python It's easy to get excited about Python's growth, but what's particularly intriguing about it is what it can indicate about the wider software landscape. That's perhaps a whole new question, but from a burgeoning army of non-developer professionals powered by Python to every engineer wanting to unlock automation, it would appear that the growth of Python both a response and a symptom of significant changes.
Read more
  • 0
  • 0
  • 23652

article-image-train-convolutional-neural-network-in-keras-improve-with-data-augmentation
Amey Varangaonkar
23 Aug 2018
10 min read
Save for later

Train a convolutional neural network in Keras and improve it with data augmentation [Tutorial]

Amey Varangaonkar
23 Aug 2018
10 min read
In this article, we will see how convolutional layers work and how to use them. We will also see how you can build your own convolutional neural network in Keras to build better, more powerful deep neural networks and solve computer vision problems. We will also see how we can improve this network using data augmentation. For a better understanding of the concepts, we will be taking a well-known dataset CIFAR-10. This dataset was created by Alex Krizhevsky, Vinod Nair, and Geoffrey Hinton. The following article has been taken from the book Deep Learning Quick Reference, written by Mike Bernico.  Adding inputs to the network The CIFAR-10 dataset is made up of 60,000 32 x 32 color images that belong to 10 classes, with 6,000 images per class. We'll be using 50,000 images as a training set, 5,000 images as a validation set, and 5,000 images as a test set. The input tensor layer for the convolutional neural network will be (N, 32, 32, 3), which we will pass to the build_network function. The following code is used to build the network: def build_network(num_gpu=1, input_shape=None): inputs = Input(shape=input_shape, name="input") Getting the output The output of this model will be a class prediction, from 0-9. We will use a 10-node softmax.  We will use the following code to define the output: output = Dense(10, activation="softmax", name="softmax")(d2) Cost function and metrics Earlier, we used categorical cross-entropy as the loss function for a multi-class classifier.  This is just another multiclass classifier and we can continue using categorical cross-entropy as our loss function, and accuracy as a metric. We've moved on to using images as input, but luckily our cost function and metrics remain unchanged. Working with convolutional layers We're going to use two convolutional layers, with batch normalization, and max pooling. This is going to require us to make quite a few choices, which of course we could choose to search as hyperparameters later. It's always better to get something working first though. As the popular computer scientist and mathematician Donald Knuth would say, premature optimization is the root of all evil. We will use the following code snippet to define the two convolutional blocks: # convolutional block 1 conv1 = Conv2D(64, kernel_size=(3,3), activation="relu", name="conv_1")(inputs) batch1 = BatchNormalization(name="batch_norm_1")(conv1) pool1 = MaxPooling2D(pool_size=(2, 2), name="pool_1")(batch1) # convolutional block 2 conv2 = Conv2D(32, kernel_size=(3,3), activation="relu", name="conv_2")(pool1) batch2 = BatchNormalization(name="batch_norm_2")(conv2) pool2 = MaxPooling2D(pool_size=(2, 2), name="pool_2")(batch2) So, clearly, we have two convolutional blocks here, that consist of a convolutional layer, a batch normalization layer, and a pooling layer. In the first block, I'm using 64 3 x 3 filters with relu activations. I'm using valid (no) padding and a stride of 1. Batch normalization doesn't require any parameters and it isn't really trainable. The pooling layer is using 2 x 2 pooling windows, valid padding, and a stride of 2 (the dimension of the window). The second block is very much the same; however, I'm halving the number of filters to 32. While there are many knobs we could turn in this architecture, the one I would tune first is the kernel size of the convolutions. Kernel size tends to be an important choice. In fact, some modern neural network architectures such as Google's inception, allow us to use multiple filter sizes in the same convolutional layer. Getting the fully connected layers After two rounds of convolution and pooling, our tensors have gotten relatively small and deep. After pool_2, the output dimension is (n, 6, 6, 32). We have, in these convolutional layers, hopefully extracted relevant image features that this 6 x 6 x 32 tensor represents. To classify images, using these features, we will connect this tensor to a few fully connected layers, before we go to our final output layer. In this example, I'll use a 512-neuron fully connected layer, a 256-neuron fully connected layer, and finally, the 10-neuron output layer. I'll also be using dropout to help prevent overfitting, but only a very little bit! The code for this process is given as follows for your reference: from keras.layers import Flatten, Dense, Dropout # fully connected layers flatten = Flatten()(pool2) fc1 = Dense(512, activation="relu", name="fc1")(flatten) d1 = Dropout(rate=0.2, name="dropout1")(fc1) fc2 = Dense(256, activation="relu", name="fc2")(d1) d2 = Dropout(rate=0.2, name="dropout2")(fc2) I haven't previously mentioned the flatten layer above. The flatten layer does exactly what its name suggests. It flattens the n x 6 x 6 x 32 tensor into an n x 1152 vector. This will serve as an input to the fully connected layers. Working with multi-GPU models in Keras Many cloud computing platforms can provision instances that include multiple GPUs. As our models grow in size and complexity you might want to be able to parallelize the workload across multiple GPUs. This can be a somewhat involved process in native TensorFlow, but in Keras, it's just a function call. Build your model, as normal, as shown in the following code: model = Model(inputs=inputs, outputs=output) Then, we just pass that model to keras.utils.multi_gpu_model, with the help of the following code: model = multi_gpu_model(model, num_gpu) In this example, num_gpu is the number of GPUs we want to use. Training the model Putting the model together, and incorporating our new cool multi-GPU feature, we come up with the following architecture: def build_network(num_gpu=1, input_shape=None): inputs = Input(shape=input_shape, name="input") # convolutional block 1 conv1 = Conv2D(64, kernel_size=(3,3), activation="relu", name="conv_1")(inputs) batch1 = BatchNormalization(name="batch_norm_1")(conv1) pool1 = MaxPooling2D(pool_size=(2, 2), name="pool_1")(batch1) # convolutional block 2 conv2 = Conv2D(32, kernel_size=(3,3), activation="relu", name="conv_2")(pool1) batch2 = BatchNormalization(name="batch_norm_2")(conv2) pool2 = MaxPooling2D(pool_size=(2, 2), name="pool_2")(batch2) # fully connected layers flatten = Flatten()(pool2) fc1 = Dense(512, activation="relu", name="fc1")(flatten) d1 = Dropout(rate=0.2, name="dropout1")(fc1) fc2 = Dense(256, activation="relu", name="fc2")(d1) d2 = Dropout(rate=0.2, name="dropout2")(fc2) # output layer output = Dense(10, activation="softmax", name="softmax")(d2) # finalize and compile model = Model(inputs=inputs, outputs=output) if num_gpu > 1: model = multi_gpu_model(model, num_gpu) model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=["accuracy"]) return model We can use this to build our model: model = build_network(num_gpu=1, input_shape=(IMG_HEIGHT, IMG_WIDTH, CHANNELS)) And then we can fit it, as you'd expect: model.fit(x=data["train_X"], y=data["train_y"], batch_size=32, epochs=200, validation_data=(data["val_X"], data["val_y"]), verbose=1, callbacks=callbacks) As we train this model, you will notice that overfitting is an immediate concern. Even with a relatively modest two convolutional layers, we're already overfitting a bit. You can see the effects of overfitting from the following graphs: It's no surprise, 50,000 observations is not a lot of data, especially for a computer vision problem. In practice, computer vision problems benefit from very large datasets. In fact, Chen Sun showed that additional data tends to help computer vision models linearly with the log of the data volume in https://arxiv.org/abs/1707.02968. Unfortunately, we can't really go find more data in this case. But maybe we can make some. Let's talk about data augmentation next. Using data augmentation Data augmentation is a technique where we apply transformations to an image and use both the original image and the transformed images to train on. Imagine we had a training set with a cat in it: If we were to apply a horizontal flip to this image, we'd get something that looks like this: This is exactly the same image, of course, but we can use both the original and transformation as training examples. This isn't quite as good as two separate cats in our training set; however, it does allow us to teach the computer that a cat is a cat regardless of the direction it's facing. In practice, we can do a lot more than just a horizontal flip. We can vertically flip, when it makes sense, shift, and randomly rotate images as well. This allows us to artificially amplify our dataset and make it seem bigger than it is. Of course, you can only push this so far, but it's a very powerful tool in the fight against overfitting when little data exists. What is the Keras ImageDataGenerator? Not so long ago, the only way to do image augmentation was to code up the transforms and apply them randomly to the training set, saving the transformed images to disk as we went (uphill, both ways, in the snow). Luckily for us, Keras now provides an ImageDataGenerator class that can apply transformations on the fly as we train, without having to hand code the transformations. We can create a data generator object from ImageDataGenerator by instantiating it like this: def create_datagen(train_X): data_generator = ImageDataGenerator( rotation_range=20, width_shift_range=0.02, height_shift_range=0.02, horizontal_flip=True) data_generator.fit(train_X) return data_generator In this example, I'm using both shifts, rotation, and horizontal flips. I'm using only very small shifts. Through experimentation, I found that larger shifts were too much and my network wasn't actually able to learn anything. Your experience will vary as your problem does, but I would expect larger images to be more tolerant of shifting. In this case, we're using 32 pixel images, which are quite small. Training with a generator If you haven't used a generator before, it works like an iterator. Every time you call the ImageDataGenerator .flow() method, it will produce a new training minibatch, with random transformations applied to the images it was fed. The Keras Model class comes with a .fit_generator() method that allows us to fit with a generator rather than a given dataset: model.fit_generator(data_generator.flow(data["train_X"], data["train_y"], batch_size=32), steps_per_epoch=len(data["train_X"]) // 32, epochs=200, validation_data=(data["val_X"], data["val_y"]), verbose=1, callbacks=callbacks) Here, we've replaced the traditional x and y parameters with the generator. Most importantly, notice the steps_per_epoch parameter. You can sample with replacement any number of times from the training set, and you can apply random transformations each time. This means that we can use more mini batches each epoch than we have data. Here, I'm going to only sample as many batches as I have observations, but that isn't required. We can and should push this number higher if we can. Before we wrap things up, let's look at how beneficial image augmentation is in this case: As you can see, just a little bit of image augmentation really helped us out. Not only is our overall accuracy higher, but our network is overfitting much slower. If you have a computer vision problem with just a little bit of data, image augmentation is something you'll want to do. We saw the benefits and ease of training a convolutional neural network from scratch using Keras and then improving that network using data augmentation. If you found the above article to be useful, make sure you check out the book Deep Learning Quick Reference for more information on modeling and training various different types of deep neural networks with ease and efficiency. Top 5 Deep Learning Architectures CapsNet: Are Capsule networks the antidote for CNNs kryptonite? What is a CNN?
Read more
  • 0
  • 0
  • 23624

article-image-implementing-azure-managed-kubernetes-and-azure-container-service-tutorial
Melisha Dsouza
15 Jan 2019
12 min read
Save for later

Implementing Azure-Managed Kubernetes and Azure Container Service [Tutorial]

Melisha Dsouza
15 Jan 2019
12 min read
The next level of virtualization is containers as they provide a better solution than virtual machines within Hyper-V, as containers optimize resources by sharing as much as possible of the existing container platform. Azure Kubernetes Service (AKS) simplifies the deployment and operations of Kubernetes and enables users to dynamically scale their application infrastructure with agility; along with simplifying cluster maintenance with automated upgrades and scaling. Azure Container Service (ACS) simplifies the management of Docker clusters for running containerized applications This tutorial will combine the above-defined concepts and describe how to design and implement containers, and how to choose the proper solution for orchestrating containers. You will get an overview of how Azure can help you to implement services based on containers and get rid of traditional virtualization stuff with redundant OS resources that need to be managed, updated, backed-up, and optimized. To run containers in a cloud environment, no specific installations are required, as you only need the following: A computer with an internet browser An Azure subscription (if not available, a trial could work too). With Azure, you will have the option to order a container directly in Azure as an Azure Container Instance (ACI) or a managed Azure solution using Kubernetes as orchestrator. This tutorial is an excerpt from a book written by Florian Klaffenbach et al. titled  Implementing Azure Solutions - Second Edition. This book will get you up and running with Azure services and teach you how to implement them in your organization. All of the code for this tutorial can be found at GitHub. Azure Container Registry (ACR) If you need to set up a container environment to be used by the developers in your Azure tenant, you will have to think about where to store your container images. In general, the way to do this is to provide a container registry. This registry could reside on a VM itself, but using PaaS services with cloud technologies always provides an easier and more flexible design. This is where Azure Container Service (ACS) comes in, as it is a PaaS solution that provides high flexibility and even features such as replication between geographies. This means you will need to fill in the following details: When you create your container registry, you will need to define the following: The registry name (ending with azurecr.io) The resource group the registry sits in The Azure location The admin user (if you will need to log in to the registry using an account) The SKU: Basic Standard Premium The following table details the features and limits of the basic, standard, and premium service tiers: Resource Basic Standard Premium Storage 10 GiB 100 GiB 500 GiB Max image layer size 20 GiB 20 GiB 50 GiB ReadOps per minute 1,000 3,000 10,000 WriteOps per minute 100 500 2,000 Download bandwidth MBps 30 60 100 Upload bandwidth MBps 10 20 50 Webhooks 2 10 100 Geo-replication N/A N/A Supported  Switching between the different SKUs is supported and can be done using the portal, PowerShell, or CLI. If you still are on a classic ACR, the first step would be to upgrade to a managed registry. Azure Container Instances By running your workloads in ACI, you don't have to set up a management infrastructure for your containers, you just can put your focus on design and building the applications. Creating your first container in Azure Let's create a first simple container in Azure using the portal:  Go to Container Instances under New | Marketplace | Everything, as shown in the following screenshot: After having chosen the Container Instances entry in the resources list, you will have to define some properties like: We will need to define the Azure container name. Of course, this needs to be unique in your environment. Then, we will need to define the source of the image and to which resource group and region it should be deployed within Azure. As already mentioned, containers can reside on Windows and Linux, because this needs to be defined at first. Afterwards, we will need to define the resources per container: Cores Memory Ports Port protocol Restart policy (if the container went offline) After having deployed the corresponding container registry, we can start working with the container instance: When hitting the URL posted in the left part, under FQDN, you should see the following screenshot: After we have finalized the preceding steps, we have an ACI up and running, which means that you are able to provide container images, load them up to Azure, and run them. Azure Marketplace containers In the public Azure Marketplace, you can find existing container images that just can be deployed to your subscription. These are pre-packaged images that give you the option to start with your first container in Azure. As cloud services provide reusability and standardization, this entry point is always good to look at first. Before starting with this, we will need to check if the required resource providers are enabled on the subscription you are working with. Otherwise, we will need to register them by hitting the Register entry and waiting a few minutes for completion, as shown in the following screenshot: Now, we can start deploying marketplace containers such as the container image for WordPress, which is used as a sample, as shown in the following screenshot: At first, we will need to decide on the corresponding image and choose to create a new ACR, or use an existing one. Furthermore, the Azure region, the resource group, and the tag (for example, version) need to be defined in the following dialog: Now that the registry is being created, we will need to update the permission settings, also called enable admin registry. This can be done with the Admin user Enable button as shown in the following screenshot:  Regarding the SKU, this is just another point where we can set the priority and define performance. This may take some minutes to be enabled. Now, we can start deploying container images from the container registry, as you can see in the following screenshot with the WordPress image that is already available in the registry: At first, we will need to choose the corresponding container from the registry; right-click the tag version from the Tags section: Having done that, we will need to hit the Deploy to web app menu entry to deploy the web app to Azure: As the properties that need to be filled are some defaults for Web Apps, it is quite easy to set them: Finally, the first containerized image for a web app has been deployed to Azure. Container orchestration One of the most interesting topics with regard to containers is that they provide technology for scaling. For example, if we need more performance on a website that is running containerized, we would just spin off an additional container to load-balance the traffic. This could even be done if we needed to scale down. The concept of container orchestration Regarding this technology, we need an orchestration tool to provide this feature set. There are some well-known container orchestration tools available on the market, such as the following: Docker swarm DC/OS Kubernetes Kubernetes is the most-used one, and therefore could be deployed as a service in most public cloud services, such as in Azure. It provides the following features: Automated container placement: On the container hosts, to best spread the load between them Self-healing: For failed containers, restarting them in a proper way Horizontal scaling: Automated horizontal scaling (up and down) based on the existing load Service discovery and load balancing: By providing IP-addresses to containers and managing DNS registrations Rollout and rollback: Automated rollout and rollback for containers, which provides another self-healing feature as updated containers that are newly rolled-out are just rolled back if something goes wrong Configuration management: By updating secrets and configurations without the need to fully rebuild the container itself Azure Kubernetes Service (AKS) Installing, maintaining, and administering a Kubernetes cluster manually could mean a huge investment of time for a company. In general, these tasks are one-off costs and therefore it would be best to not waste these resources. In Azure today, there is a feature called AKS, where K emphasizes that it is a managed Kubernetes service. For AKS, there is no charge for Kubernetes masters, you just have to pay for the nodes that are running the containers. Before you start, you will have to fulfill the following prerequisites: An Azure account with an active subscription Azure CLI installed and configured Kubernetes command-line tool, kubectl, installed Make sure that the Azure subscription you use has these required resources—storage, compute, networking, and a container service: For the first step, you need to choose Kubernetes service and choose to create your AKS deployment for your tenant. The following parameters need to be defined: Resource group for the deployment Kubernetes cluster name Azure region Kubernetes version DNS prefix Then,  hit the Authentication tab, as shown in the following screenshot: On the Authentication tab, you will need to define a service principal or choose and existing one, as AKS needs a service principal to run the deployment. In addition, you could enable the RBAC feature, which gives you the chance to define fine-grained permissions based on Azure AD accounts and groups. On the Networking tab, you can choose either to add the Kubernetes cluster into an existing VNET, or create a new one. In addition, the HTTP routing feature can be enabled or disabled: On the Monitoring tab, you have the option to enable container monitoring and link it to an existing Log Analytics workspace, or create a new one: The following is the source from which to set your required tags: Finally, the validation will check for any misconfigurations and create the Azure ARM template for the deployment. Clicking the Create button will start the deployment phase, which could run for several minutes or even longer depending on the chosen feature, and scale: After the deployment has finished, the Kubernetes dashboard is available. You can view the Kubernetes dashboard by clicking on the View Kubernetes dashboard link, as shown in the following screenshot: The dashboard looks something like the one shown in the following screenshot: As you can see in the preceding screenshot, there are four steps to open the dashboard. At first, we will need to install the Azure CLI in its most current version using the statement that is mentioned in the following screenshot: Afterward, the AKS CLI needs to be enabled. It is called kubectl.exe. Finally, after setting all the parameters (and when you have performed steps 3 and 4 from the preceding task list), the following dashboard should open in a new browser window: The preceding dashboard provides a way to monitor and administer your Azure Kubernetes environment, in general, from a GUI. If a new Kubernetes version becomes available, you can easily update it from the Azure portal yourself with one click, as shown in the following screenshot: If you need to scale your AKS hosts, this is quite easy too, as you can do it through the Azure portal. A maximum of 100 hosts with 3 vCPUs and 10.5 GB RAM per host is currently possible: You can now upload your containers to your AKS-enabled Docker and have a huge scalable infrastructure with a minimum of administrative tasks and time for the implementation itself. If you need to monitor AKS, the integration with Azure monitoring is integrated completely. By clicking the Monitor container health link, you will be directed to the following overview: The Nodes tab provides the following information per node: This not only gives a brief overview of the health status but also the number of containers and the load on the node itself. The Controllers view provides detailed information on the AKS controller, its services, status, and uptime: And finally, the Containers tab gives a deep overview of the health state of each container running in the infrastructure (system containers included): By hitting the Search logs section, you can define your own custom Azure monitoring searches and integrate them in your custom portal: To get everything up-and-running, the following to-do list gives a brief overview of all the tasks needed to provide an app within AKS: Prepare the AKS App: Create the container registry: Create the Kubernetes cluster Run the application in AKS: Scale the application in AKS: Update the application in AKS: AKS has the following service quotas and limits: Resource Default limit Max nodes per cluster 100 Max pods per node (basic networking with KubeNet) 110 Max pods per node (advanced networking with Azure CNI) 301 Max clusters per subscription 100 As you have seen, AKS in Azure provides great features with a minimum of administrative tasks. Summary In this tutorial, we learned the basics required to understand, deploy, and manage container services in a public cloud environment. Basically, the concept of containers is a great idea and surely the next step in virtualization that applications need to go to. Setting up the environment manually is quite complex, but by using the PaaS approach, the setup procedure is quite simple (because of automation) and allows you to just start using it. To understand how to build robust cloud solutions on Azure, check out our book Implementing Azure Solutions - Second Edition  Microsoft Connect(); 2018 Azure updates: Azure Pipelines extension for Visual Studio Code, GitHub releases and much more! Introducing Grafana’s ‘Loki’ (alpha), a scalable HA multi-tenant log aggregator for cloud natives; optimized for Grafana, Prometheus and Kubernetes DigitalOcean launches its Kubernetes-as-a-service at KubeCon+CloudNativeCon to ease running containerized app
Read more
  • 0
  • 0
  • 23618

article-image-using-srgans-to-generate-photo-realistic-images-tutorial
Natasha Mathur
18 Apr 2019
16 min read
Save for later

Using SRGANs to Generate Photo-realistic Images [Tutorial]

Natasha Mathur
18 Apr 2019
16 min read
Super-Resolution Generative Adversarial Network, or SRGAN, is a Generative Adversarial Network (GAN) that can generate super-resolution images from low-resolution images, with finer details and higher quality. CNNs were earlier used to produce high-resolution images that train quicker and achieve high-level accuracy. However, in some cases, they are incapable of recovering finer details and often generate blurry images. In this tutorial, we will learn how to implement an SRGAN network in the Keras framework that will be capable of generating high-resolution images.  SRGANs were introduced in the paper titled, Photo-Realistic Single Image Super-Resolution Using a Generative Adversarial Network, by Christian Ledig, Lucas Theis, Ferenc Huszar, Jose Caballero, Andrew Cunningham, and others. This tutorial is an excerpt taken from the book Generative Adversarial Networks Projects written by Kailash Ahirwar. The book explores unsupervised techniques for training neural networks and includes seven end-to-end projects in the GAN domain. Downloading the CelebA dataset For this tutorial, we will use the large-scale CelebFaces Attributes (CelebA) dataset, which is available here. The dataset contains 202, 599 face images of celebrities. The dataset is available for non-commercial research purposes only and can't be used for commercial purposes. If you intend to use the dataset for commercial purposes, seek permissions from the owners of the images. We will use the CelebA dataset to train our SRGAN network. Perform the following steps to download and extract the dataset: Download the dataset from the following link: https://www.dropbox.com/sh/8oqt9vytwxb3s4r/AAB06FXaQRUNtjW9ntaoPGvCa?dl=0 Extract images from the downloaded img_align_celeba.zip by executing the following command: unzip img_align_celeba.zip We have now downloaded and extracted the dataset. We can now start working on the Keras implementation of SRGAN. The Keras implementation of SRGAN SRGAN has three neural networks, a generator, a discriminator, and a pre-trained VGG19 network on the Imagenet dataset. In this section, we will write the implementation for all the networks. Let's start by implementing the generator network. Before starting to write the implementations, create a Python file called main.py and import the essential modules, as follows: import glob import os import numpy as np import tensorflow as tf from keras import Input from keras.applications import VGG19 from keras.callbacks import TensorBoard from keras.layers import BatchNormalization, Activation, LeakyReLU, Add, Dense, PReLU, Flatten from keras.layers.convolutional import Conv2D, UpSampling2D from keras.models import Model from keras.optimizers import Adam from keras_preprocessing.image import img_to_array, load_img from scipy.misc import imsave The generator network Let's start by writing the layers for the generator network in the Keras framework and then create a Keras model, using the functional API of the Keras framework. Perform the following steps to implement the generator network in Keras: Start by defining the hyperparameters required for the generator network: residual_blocks = 16 momentum = 0.8 input_shape = (64, 64, 3) Next, create an input layer to feed input to the network, as follows: input_layer = Input(shape=input_shape) The input layer takes an input image of a shape of (64, 64, 3) and passes it to the next layer in the network. Next, add the pre-residual block (2D convolution layer), as follows: Configuration: Filters: 64 Kernel size: 9 Strides: 1 Padding: same Activation: relu: gen1 = Conv2D(filters=64, kernel_size=9, strides=1, padding='same', activation='relu')(input_layer) Next, write a method with the entire code for the residual block, as shown here: def residual_block(x): """ Residual block """ filters = [64, 64] kernel_size = 3 strides = 1 padding = "same" momentum = 0.8 activation = "relu" res = Conv2D(filters=filters[0], kernel_size=kernel_size, strides=strides, padding=padding)(x) res = Activation(activation=activation)(res) res = BatchNormalization(momentum=momentum)(res) res = Conv2D(filters=filters[1], kernel_size=kernel_size, strides=strides, padding=padding)(res) res = BatchNormalization(momentum=momentum)(res) # Add res and x res = Add()([res, x]) return res Now, add 16 residual blocks using the residual_block function, defined in the last step: res = residual_block(gen1) for i in range(residual_blocks - 1): res = residual_block(res) The output of the pre-residual block goes to the first residual block. The output of the first residual block goes to the second residual block, and so on, up to the 16th residual block. Next, add the post-residual block (a 2D convolution layer followed by a batch normalization layer), as follows: Configuration: Filters: 64 Kernel size: 3 Strides: 1 Padding: same Batchnormalization: Yes (momentum=0.8): gen2 = Conv2D(filters=64, kernel_size=3, strides=1, padding='same')(res) gen2 = BatchNormalization(momentum=momentum)(gen2) Now, add an Add layer to take the sum of the output from the pre-residual block, which is gen1, and the output from the post-residual block, which is gen2. This layer generates another tensor of similar shape. gen3 = Add()([gen2, gen1]) Next, add an upsampling block, as follows: Configuration: Upsampling size: 2 Filers: 256 Kernel size: 3 Strides: 1 Padding: same Activation: PReLU: gen4 = UpSampling2D(size=2)(gen3) gen4 = Conv2D(filters=256, kernel_size=3, strides=1, padding='same')(gen4) gen4 = Activation('relu')(gen4) Next, add another upsampling block, as follows: Configuration: Upsampling size: 2 Filers: 256 Kernel size: 3 Strides: 1 Padding: same Activation: PReLU: gen5 = UpSampling2D(size=2)(gen4) gen5 = Conv2D(filters=256, kernel_size=3, strides=1, padding='same')(gen5) gen5 = Activation('relu')(gen5) Finally, add the output convolution layer: Configuration: Filters: 3 (equal to number of channels) Kernel size: 9 Strides: 1 Padding: same Activation: tanh: gen6 = Conv2D(filters=3, kernel_size=9, strides=1, padding='same')(gen5) output = Activation('tanh')(gen6) Once you have defined all the layers in the network, you can create a Keras model. We have defined a Keras sequential graph using Keras's functional API. Let's create a Keras model by specifying the input and output for the network. Now, create a Keras model and specify the inputs and the outputs for the model, as follows: model = Model(inputs=[input_layer], outputs=[output], name='generator') We have successfully created a Keras model for the generator network. Now wrap the entire code for the generator network inside a Python function, as follows: def build_generator(): """ Create a generator network using the hyperparameter values defined below :return: """ residual_blocks = 16 momentum = 0.8 input_shape = (64, 64, 3) # Input Layer of the generator network input_layer = Input(shape=input_shape) # Add the pre-residual block gen1 = Conv2D(filters=64, kernel_size=9, strides=1, padding='same', activation='relu')(input_layer) # Add 16 residual blocks res = residual_block(gen1) for i in range(residual_blocks - 1): res = residual_block(res) # Add the post-residual block gen2 = Conv2D(filters=64, kernel_size=3, strides=1, padding='same')(res) gen2 = BatchNormalization(momentum=momentum)(gen2) # Take the sum of the output from the pre-residual block(gen1) and the post-residual block(gen2) gen3 = Add()([gen2, gen1]) # Add an upsampling block gen4 = UpSampling2D(size=2)(gen3) gen4 = Conv2D(filters=256, kernel_size=3, strides=1, padding='same')(gen4) gen4 = Activation('relu')(gen4) # Add another upsampling block gen5 = UpSampling2D(size=2)(gen4) gen5 = Conv2D(filters=256, kernel_size=3, strides=1, padding='same')(gen5) gen5 = Activation('relu')(gen5) # Output convolution layer gen6 = Conv2D(filters=3, kernel_size=9, strides=1, padding='same')(gen5) output = Activation('tanh')(gen6) # Keras model model = Model(inputs=[input_layer], outputs=[output], name='generator') return model We have successfully created a Keras model for the generator network. In the next section, we will create a Keras model for the discriminator network. The discriminator network Let's start by writing the layers for the discriminator network in the Keras framework and then create a Keras model, using the functional API of the Keras framework. Perform the following steps to implement the discriminator network in Keras: Start by defining the hyperparameters required for the discriminator network: leakyrelu_alpha = 0.2 momentum = 0.8 input_shape = (256, 256, 3) Next, create an input layer to feed input to the network, as follows: input_layer = Input(shape=input_shape) Next, add a convolution block, as follows: Configuration: Filters: 64 Kernel size: 3 Strides: 1 Padding: same Activation: LeakyReLU with alpha equal to 0.2: dis1 = Conv2D(filters=64, kernel_size=3, strides=1, padding='same')(input_layer) dis1 = LeakyReLU(alpha=leakyrelu_alpha)(dis1) Next, add another seven convolution blocks, as follows: Configuration: Filters: 64, 128, 128, 256, 256, 512, 512 Kernel size: 3, 3, 3, 3, 3, 3, 3 Strides: 2, 1, 2, 1, 2, 1, 2 Padding: same for each convolution layer Activation: LealyReLU with alpha equal to 0.2 for each convolution layer: # Add the 2nd convolution block dis2 = Conv2D(filters=64, kernel_size=3, strides=2, padding='same')(dis1) dis2 = LeakyReLU(alpha=leakyrelu_alpha)(dis2) dis2 = BatchNormalization(momentum=momentum)(dis2) # Add the third convolution block dis3 = Conv2D(filters=128, kernel_size=3, strides=1, padding='same')(dis2) dis3 = LeakyReLU(alpha=leakyrelu_alpha)(dis3) dis3 = BatchNormalization(momentum=momentum)(dis3) # Add the fourth convolution block dis4 = Conv2D(filters=128, kernel_size=3, strides=2, padding='same')(dis3) dis4 = LeakyReLU(alpha=leakyrelu_alpha)(dis4) dis4 = BatchNormalization(momentum=0.8)(dis4) # Add the fifth convolution block dis5 = Conv2D(256, kernel_size=3, strides=1, padding='same')(dis4) dis5 = LeakyReLU(alpha=leakyrelu_alpha)(dis5) dis5 = BatchNormalization(momentum=momentum)(dis5) # Add the sixth convolution block dis6 = Conv2D(filters=256, kernel_size=3, strides=2, padding='same')(dis5) dis6 = LeakyReLU(alpha=leakyrelu_alpha)(dis6) dis6 = BatchNormalization(momentum=momentum)(dis6) # Add the seventh convolution block dis7 = Conv2D(filters=512, kernel_size=3, strides=1, padding='same')(dis6) dis7 = LeakyReLU(alpha=leakyrelu_alpha)(dis7) dis7 = BatchNormalization(momentum=momentum)(dis7) # Add the eight convolution block dis8 = Conv2D(filters=512, kernel_size=3, strides=2, padding='same')(dis7) dis8 = LeakyReLU(alpha=leakyrelu_alpha)(dis8) dis8 = BatchNormalization(momentum=momentum)(dis8) Next, add a dense layer with 1,024 nodes, as follows: Configuration: Nodes: 1024 Activation: LeakyReLU with alpha equal to 0.2: dis9 = Dense(units=1024)(dis8) dis9 = LeakyReLU(alpha=0.2)(dis9) Then, add a dense layer to return the probabilities, as follows: output = Dense(units=1, activation='sigmoid')(dis9) Finally, create a Keras model and specify the inputs and the outputs for the network: model = Model(inputs=[input_layer], outputs=[output], name='discriminator') Wrap the entire code for the discriminator network inside a function, as follows: def build_discriminator(): """ Create a discriminator network using the hyperparameter values defined below :return: """ leakyrelu_alpha = 0.2 momentum = 0.8 input_shape = (256, 256, 3) input_layer = Input(shape=input_shape) # Add the first convolution block dis1 = Conv2D(filters=64, kernel_size=3, strides=1, padding='same')(input_layer) dis1 = LeakyReLU(alpha=leakyrelu_alpha)(dis1) # Add the 2nd convolution block dis2 = Conv2D(filters=64, kernel_size=3, strides=2, padding='same')(dis1) dis2 = LeakyReLU(alpha=leakyrelu_alpha)(dis2) dis2 = BatchNormalization(momentum=momentum)(dis2) # Add the third convolution block dis3 = Conv2D(filters=128, kernel_size=3, strides=1, padding='same')(dis2) dis3 = LeakyReLU(alpha=leakyrelu_alpha)(dis3) dis3 = BatchNormalization(momentum=momentum)(dis3) # Add the fourth convolution block dis4 = Conv2D(filters=128, kernel_size=3, strides=2, padding='same')(dis3) dis4 = LeakyReLU(alpha=leakyrelu_alpha)(dis4) dis4 = BatchNormalization(momentum=0.8)(dis4) # Add the fifth convolution block dis5 = Conv2D(256, kernel_size=3, strides=1, padding='same')(dis4) dis5 = LeakyReLU(alpha=leakyrelu_alpha)(dis5) dis5 = BatchNormalization(momentum=momentum)(dis5) # Add the sixth convolution block dis6 = Conv2D(filters=256, kernel_size=3, strides=2, padding='same')(dis5) dis6 = LeakyReLU(alpha=leakyrelu_alpha)(dis6) dis6 = BatchNormalization(momentum=momentum)(dis6) # Add the seventh convolution block dis7 = Conv2D(filters=512, kernel_size=3, strides=1, padding='same')(dis6) dis7 = LeakyReLU(alpha=leakyrelu_alpha)(dis7) dis7 = BatchNormalization(momentum=momentum)(dis7) # Add the eight convolution block dis8 = Conv2D(filters=512, kernel_size=3, strides=2, padding='same')(dis7) dis8 = LeakyReLU(alpha=leakyrelu_alpha)(dis8) dis8 = BatchNormalization(momentum=momentum)(dis8) # Add a dense layer dis9 = Dense(units=1024)(dis8) dis9 = LeakyReLU(alpha=0.2)(dis9) # Last dense layer - for classification output = Dense(units=1, activation='sigmoid')(dis9) model = Model(inputs=[input_layer], outputs=[output], name='discriminator') return model In this section, we have successfully created a Keras model for the discriminator network. The adversarial network The adversarial network is a combined network that uses the generator, the discriminator, and VGG19. In this section, we will create an adversarial network. Perform the following steps to create an adversarial network: Start by creating an input layer for the network: input_low_resolution = Input(shape=(64, 64, 3)) The adversarial network will receive an image of a shape of (64, 64, 3), which is why we have created an input layer. Next, generate fake high-resolution images using the generator network, as follows: fake_hr_images = generator(input_low_resolution) Next, extract the features of the fake images using the VGG19 network, as follows: fake_features = vgg(fake_hr_images) Next, make the discriminator network non-trainable in the adversarial network: discriminator.trainable = False We are making the discriminator network non-trainable because we don't want to train the discriminator network while we train the generator network. Next, pass the fake images to the discriminator network: output = discriminator(fake_hr_images) Finally, create a Keras model, which will be our adversarial model: model = Model(inputs=[input_low_resolution], outputs=[output, fake_features]) Wrap the entire code for the adversarial model inside a Python function: def build_adversarial_model(generator, discriminator, vgg): input_low_resolution = Input(shape=(64, 64, 3)) fake_hr_images = generator(input_low_resolution) fake_features = vgg(fake_hr_images) discriminator.trainable = False output = discriminator(fake_hr_images) model = Model(inputs=[input_low_resolution], outputs=[output, fake_features]) for layer in model.layers: print(layer.name, layer.trainable) print(model.summary()) return model We have now successfully implemented the networks in Keras. Next, we train the network on the dataset that we downloaded. Training the SRGAN Training the SRGAN network is a two-step process. In the first step, we train the discriminator network. In the second step, we train the adversarial network, which eventually trains the generator network. Let's start training the network. Perform the following steps to train the SRGAN network: Start by defining the hyperparameters required for the training: # Define hyperparameters data_dir = "Paht/to/the/dataset/img_align_celeba/*.*" epochs = 20000 batch_size = 1 # Shape of low-resolution and high-resolution images low_resolution_shape = (64, 64, 3) high_resolution_shape = (256, 256, 3) Next, define the training optimizer. For all networks, we will use Adam optimizer with the learning rate equal to 0.0002 and beta_1 equal to 0.5: # Common optimizer for all networks common_optimizer = Adam(0.0002, 0.5) Building and compiling the networks In this section, we will go through the different steps required to build and compile the networks: Build and compile the VGG19 network: vgg = build_vgg() vgg.trainable = False vgg.compile(loss='mse', optimizer=common_optimizer, metrics= ['accuracy']) To compile VGG19, use mse as the loss, accuracy as the metrics, and common_optimizer as the optimizer. Before compiling the network, disable the training, as we don't want to train the VGG19 network. Next, build and compile the discriminator network, as follows: discriminator = build_discriminator() discriminator.compile(loss='mse', optimizer=common_optimizer, metrics=['accuracy']) To compile the discriminator network, use mse as the loss, accuracy as the metrics, and common_optimizer as the optimizer. Next, build the generator network: generator = build_generator() Next, create an adversarial model. Start by creating two input layers: input_high_resolution = Input(shape=high_resolution_shape) input_low_resolution = Input(shape=low_resolution_shape) Next, use the generator network to symbolically generate high-resolution images from the low-resolution images: generated_high_resolution_images = generator(input_low_resolution) Use VGG19 to extract feature maps for the generated images: features = vgg(generated_high_resolution_images) Make the discriminator network non-trainable, because we don't want to train the discriminator model during the training of the adversarial model: discriminator.trainable = False Next, use the discriminator network to get the probabilities of the generated high-resolution fake images: probs = discriminator(generated_high_resolution_images) Here, probs represent the probability of the generated images belonging to a real dataset. Finally, create and compile the adversarial network: adversarial_model = Model([input_low_resolution, input_high_resolution], [probs, features]) adversarial_model.compile(loss=['binary_crossentropy', 'mse'], loss_weights=[1e-3, 1], optimizer=common_optimizer) To compile the adversarial model, use binary_crossentropy and mse as the loss functions, common_optimizer as the optimizer, and [0.001, 1] as the loss weights. Add Tensorboard to visualize the training losses and to visualize the network graphs: tensorboard = TensorBoard(log_dir="logs/".format(time.time())) tensorboard.set_model(generator) tensorboard.set_model(discriminator) Create a loop that should run for the specified number of epochs: for epoch in range(epochs): print("Epoch:{}".format(epoch)) After this step, all of the code will be inside this for loop. Next, sample a batch of high-resolution and low-resolution images, as follows: high_resolution_images, low_resolution_images = sample_images(data_dir=data_dir, batch_size=batch_size,low_resolution_shape=low_resolution_shape, high_resolution_shape=high_resolution_shape) The code for the sample_images function is as follows. It is quite descriptive and can be understood by going through it. It contains different steps to load and resize the images to generate high-resolution as well as low-resolution images: def sample_images(data_dir, batch_size, high_resolution_shape, low_resolution_shape): # Make a list of all images inside the data directory all_images = glob.glob(data_dir) # Choose a random batch of images images_batch = np.random.choice(all_images, size=batch_size) low_resolution_images = [] high_resolution_images = [] for img in images_batch: # Get an ndarray of the current image img1 = imread(img, mode='RGB') img1 = img1.astype(np.float32) # Resize the image img1_high_resolution = imresize(img1, high_resolution_shape) img1_low_resolution = imresize(img1, low_resolution_shape) # Do a random flip if np.random.random() < 0.5: img1_high_resolution = np.fliplr(img1_high_resolution) img1_low_resolution = np.fliplr(img1_low_resolution) high_resolution_images.append(img1_high_resolution) low_resolution_images.append(img1_low_resolution) return np.array(high_resolution_images), np.array(low_resolution_images) Next, normalize the images to convert the pixel values to a range between [-1, 1], as follows: high_resolution_images = high_resolution_images / 127.5 - 1. low_resolution_images = low_resolution_images / 127.5 - 1. It is very important to convert the pixel values to a range of between -1 to 1. Our generator network has tanh at the end of the network. The tanh activation function squashes values to the same range. While calculating the loss, it is necessary to have all values in the same range. After this step is complete, we train the discriminator network, generator network, and then further visualize the images and evaluate the model. In this tutorial, we learned how to download the CelebA dataset, and implemented the project in Keras before training the SRGAN.  If you want to learn more about how to evaluate the trained SRGAN network, and optimizing the trained model, be sure to check out the book Generative Adversarial Networks Projects. Generative Adversarial Networks: Generate images using Keras GAN [Tutorial] What you need to know about Generative Adversarial Networks Generative Adversarial Networks (GANs): The next milestone In Deep Learning
Read more
  • 0
  • 0
  • 23618

article-image-openstreetmap-gathering-data-using-gps
Packt
23 Sep 2010
19 min read
Save for later

OpenStreetMap: Gathering Data using GPS

Packt
23 Sep 2010
19 min read
  OpenStreetMap Be your own cartographer Collect data for the area you want to map with this OpenStreetMap book and eBook Create your own custom maps to print or use online following our proven tutorials Collaborate with other OpenStreetMap contributors to improve the map data Learn how OpenStreetMap works and why it's different to other sources of geographical information with this professional guide Read more about this book (For more resources on OpenStreetMap, see here.) OpenStreetMap is made possible by two technological advances: Relatively affordable, accurate GPS receivers, and broadband Internet access. Without either of these, the job of building an accurate map from scratch using crowdsourcing would be so difficult that it almost certainly wouldn't work. Much of OpenStreetMap's data is based on traces gathered by volunteer mappers, either while they're going about their daily lives, or on special mapping journeys. This is the best way to collect the source data for a freely redistributable map, as each contributor is able to give their permission for their data to be used in this way. The traces gathered by mappers are used to show where features are, but they're not usually turned directly into a map. Instead, they're used as a backdrop in an editing program, and the map data is drawn by hand on top of the traces. This means you don't have to worry about getting a perfect trace every time you go mapping, or about sticking exactly to paths or roads. Errors are canceled out over time by multiple traces of the same features. OpenStreetMap uses other sources of data than mappers' GPS traces, but they each have their own problems: Out-of-copyright maps are out-of-date, and may be less accurate than modern surveying methods. Aerial imagery needs processing before you can trace it, and it doesn't tell you details such as street names. Eventually, someone has to visit locations in person to verify what exists in a particular place, what it's called, and other details that you can't discern from an aerial photograph If you already own a GPS and are comfortable using it to record traces, you can skip the first section of this article and go straight to Techniques. If you want very detailed information about surveying using GPS, you can read the American Society of Civil Engineers book on the subject, part of which is available on Google Books at http://bit.ly/gpssurveying. Some of the details are out-of-date, but the general principles still hold. If you are already familiar with the general surveying techniques, and are comfortable producing information in GPX format, you can skip most of this article and head straight for the section Adding your traces to OpenStreetMap. What is GPS? GPS stands for Global Positioning System, and in most cases this refers to a system run by the US Department of Defense, properly called NAVSTAR. The generic term for such a system is a Global Navigation Satellite System (GNSS), of which NAVSTAR is currently the only fully operational system. Other equivalent systems are in development by the European Union (Galileo), Russian Federation (GLONASS), and the People's Republic of China (Compass). OpenStreetMap isn't tied to any one GNSS system, and will be able to make use of the others as they become available. The principles of operation of all these systems are essentially the same, so we'll describe how NAVSTAR works at present. NAVSTAR consists of three elements: the space segment, the control segment, and the user segment. The space segment is the constellation of satellites orbiting the Earth. The design of NAVSTAR is for 24 satellites, of which 21 are active and three are on standby. However, there are currently 31 satellites in use, as replacements have been launched without taking old satellites out of commission. Each satellite has a highly accurate atomic clock on board, and all clocks in all satellites are kept synchronized. Each satellite transmits a signal containing the time and its own position in the sky. The control segment is a number of ground stations, including a master control station in Colorado Springs. These stations monitor the signal from the satellites and transmit any necessary corrections back to them. The corrections are necessary because the satellites themselves can stray from their predicted paths. The user segment is your GPS receiver. This receives signals from multiple satellites, and uses the information they contain to calculate your position. Your receiver doesn't transmit any information, and the satellites don't know where you are. The receiver has its own clock, which needs to be synchronized with those in the space segment to perform its calculations. This isn't the case when you first turn it on, and is one of the reasons why it can take time to get a fix. Your GPS receiver calculates your position by receiving messages from a number of satellites, and comparing the time included in each message to its own clock. This allows it to calculate your approximate distance from each satellite, and from that, your position on the Earth. If it uses three satellites, it can calculate your position in two dimensions, giving you your latitude (lat) and longitude (long). With signals from four satellites, it can give you a 3D fix, adding altitude to lat and long. The more satellites your receiver can "see", the more accurate the calculated position will be. Some receivers are able to use signals from up to 12 satellites at once, assuming the view of the satellites isn't blocked by buildings, trees, or people. You're obviously very unlikely to get a GPS fix indoors. Many GPS receivers can calculate the amount of error in your position due to the configuration of satellites you're using. Called the Dilution of Precision (DOP), the number produced gives you an idea of how good a fix you have given the satellites you can get a signal from, and where they are in the sky. The higher the DOP, the less accurate your calculated position is. The precision of a GPS fix improves with the distance between the satellites you're using. If they're close together, such as mostly directly overhead, the DOP will be high. Use signals from satellites spread evenly across the sky, and your position will be more accurate. Which satellites your receiver uses isn't something you can control, but more modern GPS chipsets will automatically try to use the best configuration of satellites available, rather than just those with the strongest signals. DOP only takes into account errors caused by satellite geometry, not other sources of error, so a low DOP isn't a guarantee of absolute accuracy. The system includes the capability to introduce intentional errors into the signal, so that only limited accuracy positioning is available to non-military users. This capability, called Selective Availability (SA) was in use until 1990, when President Clinton ordered it to be disabled. Future NAVSTAR satellites will not have SA capabilities, so the disablement is effectively permanent. The error introduced by SA reduced the horizontal accuracy of a civilian receiver, typically to 10m, but the error could be as high as 100m. Had SA still been in place, it's unlikely that OpenStreetMap would have been as successful. NAVSTAR uses a coordinate system known as WGS84, which defines a spheroid representing the Earth, and a fixed line of longitude or datum from which other longitudes are measured. This datum is very close to, but not exactly the same as the Prime Meridian at Greenwich in South East London. The equator of the spheroid is used as the datum for latitude. Other coordinate systems exist, and you should note that no printed maps use WGS84, but instead use a slightly different system that makes maps of a given area easier to use. Examples of other coordinate systems include the OSGB36 system used by British national grid references. When you create a map from raw geographic data, the latitudes and longitudes are converted to the x and y coordinates of a flat plane using an algorithm called a projection. You've probably heard of the Mercator projection, but there are many others, each of which is suitable for different areas and purposes. What's a GPS trace? A GPS trace or tracklog is simply a record of position over time. It shows where you traveled while you were recording the trace. This information is gathered using a GPS receiver that calculates your position and stores it every so many seconds, depending on how you have configured your receiver. If you record a trace while you're walking along a path, what you get is a trace that shows you where that path is in the world. Plot these points on a graph, and you have the start of a map. Walk along any adjoining paths and plot these on the same graph, and you have something you can use to navigate. If many people generate overlapping traces, eventually you have a fully mapped area. This is the general principle of crowdsourcing geographic data. You can see the result of many combined traces in the following image. This is the junction of the M4 and M25 motorways, to the west of London. The motorways themselves and the slip roads joining them are clearly visible. Traces are used in OpenStreetMap to show where geographical features are, but usually only as a source for drawing over, not directly. They're also regarded as evidence that a mapper has actually visited the area in question, and not just copied the details from another copyrighted map. Most raw GPS traces aren't suitable to be made directly into maps, because they contain too many points for a given feature, will drift relative to a feature's true position, and you'll also take an occasional detour. Although consumer-grade GPS receivers are less accurate than those used by professional surveyors, if enough traces of the same road or path are gathered, the average of these traces will be very close to the feature's true position. OpenStreetMap allows mappers to make corrections to the data over time as more accurate information becomes available. In addition to your movements, most GPS receivers allow you to record specific named points, often called waypoints. These are useful for recording the location of point features, such as post boxes, bus stops, and other amenities. We'll cover ways of using waypoints later in the article. What equipment do I need? To collect traces suitable for use in OpenStreetMap, you'll need some kind of GPS receiver that's capable of recording a log of locations over time, known as a track log, trace, or breadcrumb trail. This could be a hand-held GPS receiver, a bicycle-mounted unit, a combination of a GPS receiver and a smartphone, or in some cases a vehicle satellite navigation system. There are also some dedicated GPS logger units, which don't provide any navigation function, but merely record a track log for later processing. You'll also need some way of getting the recorded traces off your receiver and onto your PC. This could be a USB or serial cable, a removable memory card, or possibly a Bluetooth connection. There are reviews of GPS units by mappers in the OpenStreetMap wiki. There are also GPS receivers designed specifically for surveying, which have very sensitive antennas and link directly into geographic information systems (GIS). These tend to be very expensive and less portable than consumer-grade receivers. However, they're capable of producing positioning information accurate to a few centimeters rather than meters. You also need a computer connected to the Internet. A broadband connection is best, as once you start submitting data to OpenStreetMap, you will probably end up downloading lots of map tiles. It is possible to gather traces and create mapping data while disconnected from the Internet, but you will need to upload your data and see the results at some point. OpenStreetMap data itself is usually represented in Extensible Markup Language (XML) format, and can be compressed into small files. The computer itself can be almost any kind, as long as it has a web browser, and can run one of the editors, which Windows, Mac OS X, and Linux all can. You'll probably need some other kit while mapping to record additional information about the features you're mapping. Along with recording the position of each feature you map, you'll need to note things such as street names, route numbers, types of shops, and any other information you think is relevant. While this information won't be included in the traces you upload on openstreetmap.org, you'll need it later on when you're editing the map. Remember that you can't look up any details you miss on another map without breaking copyright, so it's important to gather all the information you need to describe a feature yourself. A paper notebook and pencil is the most obvious way of recording the extra information. They are inexpensive and simple to use, and have no batteries to run out. However, it's difficult to use on a bike, and impossible if you're driving, so using this approach can slow down mapping. A voice recorder is more expensive, but easier to use while still moving. Record a waypoint on your GPS receiver, and then describe what that waypoint represents in a voice recording. If you have a digital voice recorder, you can download the notes onto your PC to make them easier to use, and JOSM—the Java desktop editing application—has a support for audio mapping built-in. A digital camera is useful for capturing street names and other details, such as the layout of junctions. Some recent cameras have their own built-in GPS, and others can support an external receiver, and will add the latitude, longitude, and possibly altitude, often known as geotags, to your pictures automatically. For those that don't, you can still use the timestamp on the photo to match it to a location in your GPS traces. We'll cover this later in the article. Some mappers have experimented with video recordings while mapping, but the results haven't been encouraging so far. Some of the problems with video mapping are: It's difficult to read street signs on zoomed-out video images, and zooming in on signs is impractical. If you're recording while driving or riding a bike, the camera can only point in one direction at once, while the details you want to record may be in a different direction. It's difficult to index recordings when using consumer video cameras, so you need to play the recording back in real time to extract the information, a slow process. Automatic processing of video recordings taken with multiple cameras would make the process easier, but this is currently beyond what volunteer mappers are able to afford. Smartphones can combine several of these functions, and some include their own GPS receiver. For those that don't, or where the internal GPS isn't very good, you can use an external Bluetooth GPS module. Several applications have been developed that make the process of gathering traces and other information on a smartphone easier. Look on the Smartphones page on the OpenStreetMap wiki at http://wiki.openstreetmap.org/wiki/Smartphones. Making your first trace Before you set off on a long surveying trip, you should familiarize yourself with the methods involved in gathering data for OpenStreetMap. This includes the basic operation of your GPS receiver, and the accompanying note-taking. Configuring your GPS receiver The first thing to make sure is that your GPS is using the W GS84 coordinate system. Many receivers also include a local coordinate system in their settings to make them easier to use with printed maps. So check in your settings which system you're getting your location in. OpenStreetMap only uses WGS84, so if you record your traces in the wrong system, you could end up placing features tens or even hundreds of meters away from their true location. Next, you should set the recording frequency as high as it will go. You need your GPS to record as much detail as possible, so setting it to record your location as often as possible will make your traces better. Some receivers can record a point once per second; if yours doesn't, it's not a problem, but use the highest setting (shortest interval) possible. Some receivers also have a "smart" mode that only records points where you've changed direction significantly, which is fine for navigation, but not for turning into a map. If your GPS has this, you'll need to disable it. One further setting on some GPSs is to only record a point every so many metres, irrespective of how much time has elapsed. Turning this on can be useful if you're on foot and taking it easy, but otherwise keep it turned off. Another setting to check, particularly if you're using a vehicle satellite navigation system, is "snap to streets" or a similar name. When your receiver has this setting on, your position will always be shown as being on a street or a path in its database, even if your true position is some way off. This causes two problems for OpenStreetMap: if you travel down a road that isn't in your receiver's database, its position won't be recorded, and the data you do collect is effectively derived from the database, which not only breaks copyright, but also reproduces any errors in that database. Next, you need to know how to start and stop recording. Some receivers can record constantly while they're turned on, but many will need you to start and stop the process. Smartphone-based recorder software will definitely require starting and stopping. If you're using a smartphone with an external Bluetooth GPS module, you may also need to pair the devices and configure the receiver in your software. Once you're happy with your settings, you can have a trial run. Make a journey you have to make anyway, or take a short trip to the shops and back (or some other reasonably close landmark if you don't live near shops). It's important that you're familiar with your test area, as you'll use your local knowledge to see how accurate your results are. Checking the quality of your traces When you return, get the trace you've recorded off your receiver, and take a look at it on your PC using an OpenStreetMap editor or by uploading the trace. Now, look at the quality of the trace. Some things to look out for are, as follows: Are lines you'd expect to be straight actually straight, or do they have curves or deviations in them? A good trace reflects the shape of the area you surveyed, even if the positioning isn't 100% accurate. I f you went a particular way twice during your trip, how well do the two parts of the trace correspond? Ideally, they should be parallel and within a few meters from each other. When you change direction, does the trace reflect that change straight away, or does your recorded path continue in the same direction and gradually turn to your new heading? If you've recorded any waypoints, how close are they to the trace? They should ideally be directly on top of the trace, but certainly no more than a few meters away. The previous image shows a low-quality GPS trace. If you look at the raw trace on the left, you can see a few straight lines and differences in traces of the same area. The right-hand side shows the trace with the actual map data for the area, showing how they differ. In this image, we see a high-quality GPS trace. This trace was taken by walking along each side of the road where possible. Note that the traces are straight and parallel, reflecting the road layout. The quality of the traces makes correctly turning them into data much easier. If you notice these problems in your test trace, you may need to alter where you keep your GPS while you're mapping. Sometimes, inaccuracy is a result of the make-up of the area you're trying to map, and nothing will change that, short of using a more sensitive GPS. For the situations where that's not the case, the following are some tips on improving accuracy. Making your traces more accurate You can dramatically improve the accuracy of your traces by putting your GPS where it can get a good signal. Remember that it needs to have a good signal all the time, so even if you seem to get a good signal while you're looking at your receiver, it could drop in strength when you put it away. If you're walking, the best position is in the top pocket of a rucksack, or attached to the shoulder strap. Having your GPS in a pocket on your lower body will seriously reduce the accuracy of your traces, as your body will block at least half of the sky. If you're cycling, a handlebar mount for your GPS will give it a good view of the sky, while still making it easy to add waypoints. A rucksack is another option. In a vehicle, it's more difficult to place your GPS where it will be able to see most of the sky. External roof-mounted GPS antennas are available, but they're not cheap and involve drilling a hole in the roof of your car. The best location is as far forward on your dashboard as possible, but be aware some modern car windscreens contain metal, and may block GPS signals. In this case, you may be able to use the rear parcel shelf, or a side window providing you can secure your GPS. Don't start moving until you have a good fix. Although most GPS receivers can get a fix while you're moving, it will take longer and may be less accurate. More recent receivers have a "warm start" feature where they can get a fix much faster by caching positioning data from satellites. You also need to avoid bias in your traces. This can occur when you tend to use one side of a road more than the other, either because of the route you normally take, or because there is only a pavement on one side of the road. The result of this is that the traces you collect will be off-center of the road's true position by a few meters. This won't matter at first, and will be less of a problem in less densely-featured areas, but in high-density residential areas, this could end up distorting the map slightly.
Read more
  • 0
  • 0
  • 23594
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at $19.99/month. Cancel anytime
article-image-bootstrap-4-objects-components-flexbox-and-layout
Packt
21 Aug 2017
14 min read
Save for later

Bootstrap 4 Objects, Components, Flexbox, and Layout

Packt
21 Aug 2017
14 min read
In this article by Ajdin Imsirovic author of the book Bootstrap 4 Cookbook, we have three recipes from the book, in which we will be looking at using CSS to override Bootstrap 4 styling and create customized blockquotes. Next. we will look at how to utilize SCSS to control the number of card columns at different screen sizes. We will wrap it up with the third recipe, in which we will look at classes that Bootstrap 4 uses to implement flex-based layouts. Specifically, we will switch the flex direction of card components, based on the screen size. (For more resources related to this topic, see here.) Customizing the blockquote element with CSS In this recipe, we will examine how to use and modify Bootstrap's blockquote element. The technique we'll employ is using the :before and :after CSS pseudo-classes. We will add HTML entities to the CSS content property, and then style their position, size, and color. Getting ready Navigate to the recipe4 page of the chapter 3 website, and preview the final result that we are trying to achieve (its preview is available in chapter3-complete/app, after running harp server in the said folder). To get this look, we are using all the regular Bootstrap 4 CSS classes, with the addition of .bg-white, added in the preceding recipe. In this recipe, we will add custom styles to .blockquote. How to do it... In the empty chapter3/start/app/recipe4.ejs file, add the following code: <div class="container mt-5"> <h1>Chapter 3, Recipe 4:</h1> <p class="lead">Customize the Blockquote Element with CSS</p> </div> <!-- Customizing the blockquote element --> <div class="container"> <div class="row mt-5 pt-5"> <div class="col-lg-12"> <blockquote class="blockquote"> <p>Blockquotes can go left-to-right. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repellat dolor pariatur, distinctio doloribus aliquid recusandae soluta tempore. Vero a, eum.</p> <footer class="blockquote-footer">Some Guy, <cite>A famous publication</cite> </footer> </blockquote> </div> <div class="col-lg-12"> <blockquote class="blockquote blockquote-reverse bg-white"> <p>Blockquotes can go right-to-left. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quisquam repellendus sequi officia nulla quaerat quo.</p> <footer class="blockquote-footer">Another Guy, <cite>A famous movie quote</cite> </footer> </blockquote> </div> <div class="col-lg-12"> <blockquote class="blockquote card-blockquote"> <p>You can use the <code>.card-blockquote</code> class. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aliquid accusamus veritatis quasi.</p> <footer class="blockquote-footer">Some Guy, <cite>A reliable source</cite> </footer> </blockquote> </div> <div class="col-12"> <blockquote class="blockquote bg-info"> <p>Blockquotes can go left-to-right. Lorem ipsum dolor sit amet. </p> <footer class="blockquote-footer">Some Guy, <cite>A famous publication</cite> </footer> </blockquote> </div> </div> </div> In main-03-04.scss, add the following code: blockquote.blockquote { padding: 2rem 2rem 2rem 4rem; margin: 2rem; quotes: "201C" "201D"; position: relative; } blockquote:before { content: open-quote; font-family: Georgia, serif; font-size: 12rem; opacity: .04; font-weight: bold; position:absolute; top:-6rem; left: 0; } blockquote:after { content: close-quote; font-size: 12rem; opacity: .04; font-family: Georgia, serif; font-weight: bold; position:absolute; bottom:-11.3rem; right: 0; } In main.scss, uncomment @include for main-03-04.scss. Run grunt sass and harp server. How it works... In this recipe, we are using the regular blockquote HTML element and Bootstrap's classes for styling it. To make it look different, we primarily use the following tweaks: Setting the blockquote.blockquote position to relative Setting the :before and :after pseudo-classes, position to absolute In blockquote.blockquote, setting the padding and margin. Also, assigning the values for opening and closing quotes, using CSS (ISO) encoding for the two HTML entities Using Georgia font to style the content property in pseudo-classes Setting the font-size of pseudo-classes to a very high value and giving the font a very high opacity, so as to make it become more background-like With absolute positioning in place, it is easy to place the quotes in the exact location, using negative rem values Controlling the number of card columns on different breakpoints with SCSS This recipe will involve some SCSS mixins, which will alter the behavior of the card-columns component. To be able to showcase the desired effect, we will have to have a few hundred lines of compiled HTML code. This poses an issue; how do we show all that code inside a recipe? Here, Harp partials come to the rescue! Since most of the code in this recipe is repetitive, we will make a separate file. The file will contain the code needed to make a single card. Then, we will have a div with the class of card-columns, and this div will hold 20 cards, which will, in fact, be 20 calls to the single card file in our source code before compilation. This will make it easy for us to showcase how the number of cards in this card-columns div will change, based on screen width. To see the final result, open the chapter4/complete code's app folder, and run the console (that is, bash) on it. Follow it up with the harp server command, and navigate to localhost:9000 in your browser to see the result we will achieve in this recipe.  Upon opening the web page as explained in the preceding paragraph, you should see 20 cards in a varying number of columns, depending on your screen size. Getting ready To get acquainted with how card-columns work, navigate to the card-columns section of the Bootstrap documentation at https://v4-alpha.getbootstrap.com/components/card/#card-columns. How to do it… Open the currently empty file located at chapter4start/app/recipe04-07.ejs, and add the following code: <div class="container-fluid"> <div class="mt-5"> <h1><%- title %></h1> <p><a href="https://v4-alpha.getbootstrap.com/components/card/#card-columns" target="_blank">Link to bootstrap card-columns docs</a></p> </div><!-- /.container-fluid --> <div class="container-fluid mt-5 mb-5"> <div class="card-columns"> <!-- cards 1 to 5 --> <%- partial("partial/_recipe04-07-samplecard.ejs") %> <%- partial("partial/_recipe04-07-samplecard.ejs") %> <%- partial("partial/_recipe04-07-samplecard.ejs") %> <%- partial("partial/_recipe04-07-samplecard.ejs") %> <%- partial("partial/_recipe04-07-samplecard.ejs") %> <!-- cards 6 to 10 --> <%- partial("partial/_recipe04-07-samplecard.ejs") %> <%- partial("partial/_recipe04-07-samplecard.ejs") %> <%- partial("partial/_recipe04-07-samplecard.ejs") %> <%- partial("partial/_recipe04-07-samplecard.ejs") %> <%- partial("partial/_recipe04-07-samplecard.ejs") %> <!-- cards 11 to 15 --> <%- partial("partial/_recipe04-07-samplecard.ejs") %> <%- partial("partial/_recipe04-07-samplecard.ejs") %> <%- partial("partial/_recipe04-07-samplecard.ejs") %> <%- partial("partial/_recipe04-07-samplecard.ejs") %> <%- partial("partial/_recipe04-07-samplecard.ejs") %> <!-- cards 16 to 20 --> <%- partial("partial/_recipe04-07-samplecard.ejs") %> <%- partial("partial/_recipe04-07-samplecard.ejs") %> <%- partial("partial/_recipe04-07-samplecard.ejs") %> <%- partial("partial/_recipe04-07-samplecard.ejs") %> <%- partial("partial/_recipe04-07-samplecard.ejs") %> </div> </div> Open the main.scss file, and comment out all the other imports since some of them clash with this recipe: @import "recipe04-04.scss"; @import "./bower_components/bootstrap/scss/bootstrap.scss"; @import "./bower_components/bootstrap/scss/_mixins.scss"; @import "./bower_components/font-awesome/scss/font-awesome.scss"; @import "./bower_components/hover/scss/hover.scss"; // @import "recipe04-01.scss"; // @import "recipe04-02.scss"; // @import "recipe04-03.scss"; // @import "recipe04-05.scss"; // @import "recipe04-06.scss"; @import "recipe04-07.scss"; // @import "recipe04-08.scss"; // @import "recipe04-09.scss"; // @import "recipe04-10.scss"; // @import "recipe04-11.scss"; // @import "recipe04-12.scss"; Next, we will add the partial file with the single card code in app/partial/_recipe04-07-samplecard.ejs: <div class="card"> <img class="card-img-top img-fluid" src="http://placehold.it/300x250" alt="Card image description"> <div class="card-block"> <h4 class="card-title">Lorem ipsum dolor sit amet.</h4> <p class="card-text">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Officia autem, placeat dolorem sed praesentium aliquid suscipit tenetur iure perspiciatis sint?</p> </div> </div> If you are serving the files on Cloud9 IDE, then reference the placehold.it images from HTTPS so you don't have the warnings appearing in the console. Open this recipe's SCSS file, titled recipe04-07.scss, and paste the following code: .card-columns { @include media-breakpoint-only(sm) { column-count: 2; } @include media-breakpoint-only(md) { column-count: 3; } @include media-breakpoint-only(lg) { column-count: 5; } @include media-breakpoint-only(xl) { column-count: 7; } } Recompile Sass and start the harp server command to view the result. How it works… In step 1, we added our recipe's structure in recipe04-07.ejs. The focus in this file is the div with the class of card-columns, which holds 20 calls to the sample card partial file. In step 2, we included the SCSS file for this recipe, and to make sure that it works, we comment out the imports for all the other recipes' SCSS files. In step 3, we made our single card, as per the Bootstrap documentation. Finally, we customized the .card-columns class in our SCSS by changing the value of the card-columns property using the media-breakpoint-only mixin. The media-breakpoint-only mixin takes the sm, md, lg, and xl values as its parameter. This allows us to easily change the value of the column-count property in our layouts.  Breakpoint-dependent switching of flex direction on card components In this recipe, we will ease into using the flexbox grid in Bootstrap 4 with a simple example of switching the flex-direction property. To achieve this effect, we will use a few helper classes to enable the use of Flexbox in our recipe. To get acquainted with the way Flexbox works in Bootstrap, check out the official documentation at https://v4-alpha.getbootstrap.com/utilities/flexbox/ . Getting ready To get started with the recipe, let's first get an idea of what we will make. Navigate to chapter8complete/app/ and run harp server. Then, preview the completed recipe at localhost:9000/recipe08-01 . You should see a simple layout with four card components lined up horizontally. Now, resize the browser, either by changing the browser's window width or by pressing F12 (which will open developer tools and allow you to narrow down the viewport by adjusting the size of developer tools). At a certain breakpoint (), you should see the cards stacked on top of one another. That is the effect that we will achieve in this recipe. How to do it… Open the folder titled chapter8/start inside source code. Open the currently empty file titled recipe08-01.ejs inside the app folder; copy the below code a it into recipe08-01.ejs: <div class="container"> <h2 class="mt-5">Recipe 08-01: Breakpoint-dependent Switching of Flex Direction on Card Components</h2> <p>In this recipe we'll switch DIRECTION, between a vertical (.flex- {breakpoint}column), and a horizontal (.flex-{breakpoint}-row) stacking of cards.</p> <p>This recipe will introduce us to the flexbox grid in Bootstrap 4.</p> </div><!-- /.container --> <div class="container"> <%- partial("partial/_card0") %> <%- partial("partial/_card0") %> <%- partial("partial/_card0") %> <%- partial("partial/_card0") %> </div> While still in the same file, find the second div with the class of container and add more classes to it, as follows: <div class="container d-flex flex-column flex-lg-row"> Now, open the app/partial folder and copy and paste the following code into the file titled _card0.ejs: <div class="p-3" id="card0"> <div class="card"> <div class="card-block"> <h3 class="card-title">Special title treatment</h3> <p class="card-text">With supporting text below as a natural lead-in to additional content.</p> <a href="#" class="btn btn-primary">Go somewhere</a> </div> </div> </div> Now, run the harp server command and preview the result at localhost:9000/recipe08-01, inside the chapter8start folder. Resize the browser window to see the stacking of card components on smaller resolutions.  How it works… To start discussing how this recipe works, let's first do a little exercise. In the file titled recipe08-01, inside the chapter8start folder, locate the first div with the container class. Add the class of d-flex to s div, so that this section of code now looks like this: <div class="container d-flex"> Save the file and refresh the page in your browser. You should see that adding the helper class of d-flex to our first container has completely changed the way that this container is displayed. What has happened is that our recipe's heading and the two paragraphs (which are all inside the first container div) are now sitting on the same flex row. The reason for this behavior is the addition of Bootstrap's utility class of d-flex, which sets our container to display: flex. With display: flex, the default behavior is to set the flex container to flex-direction: row. This flex direction is implicit, meaning that we don't have to specify it. However, if we want to specify a different value to the flex-direction property, we can use another Bootstrap 4 helper class, for example, flex-row-reverse. So, let's add it to the first div, like this: <div class="container d-flex flex-row-reverse"> Now, if we save and refresh our page, we will see that the heading and the two paragraphs still show on the flex row, but now the last paragraph comes first, on the left edge of the container. It is then followed by the first paragraph, and finally, by the heading itself. There are four ways to specify flex-direction in Bootstrap, that is, by adding one of the following four classes to our wrapping HTML element: flex-row, flex-row-reverse, flex-column, and flex-column-reverse. The first two classes align our flex items horizontally, and the last two classes align our flex items vertically. Back to our recipe, we can see that on the second container, we added the following three classes on the original div (that had only the class of container in step 1): d-flex, flex-column, and flex-lg-row.  Now we can understand what each of these classes does. The d-flex class sets our second container to display: flex. The flex-column class stacks our flex items (the four card components) vertically, with each card taking up the width of the container.  Since Bootstrap is a mobile first framework, the classes we provide also take effect mobile first. If we want to override a class, by convention, we need to provide a breakpoint at which the initial class behavior will be overridden. In this recipe, we want to specify a class, with a specific breakpoint, at which this class will make our cards line up horizontally, rather than stacking them vertically. Because of the number of cards inside our second container, and because of the minimum width that each of these cards takes up, the most obvious solution was to have the cards line up horizontally on resolutions of lg and up. That is why we provide the third class of flex-lg-row to our second container. We could have used any other helper class, such as flex-row, flex-sm-row, flex-md-row, or flex-xl-row, but the one that was actually used made the most sense. Summary In this article, we have covered Customizing the blockquote element with css, Controlling the number of card columns on different breakpoints with SCSS, and Breakpoint-dependent switching of flex direction on card components.  Resources for Article: Further resources on this subject: Web Development with React and Bootstrap [article] Gearing Up for Bootstrap 4 [article] Deep Customization of Bootstrap [article]
Read more
  • 0
  • 0
  • 23594

article-image-qt-installation-on-different-platforms-tutorial
Amrata Joshi
22 Mar 2019
10 min read
Save for later

Qt installation on different platforms [Tutorial]

Amrata Joshi
22 Mar 2019
10 min read
Qt provides a different look for mobile and embedded devices where users expect a different style of presentation. This is controlled within the framework so the developer can concentrate on developing a single application. The Qt framework is released in two separate distributions, one commercial and one open source (known as dual licensing). In this manner, they can support open source-compliant applications for free, while providing unrestricted usage for closed source commercial projects. Before the year 2000 (with the release of 2.2), the source code for the free distribution had been under various licenses that some groups considered incompatible with common open source initiatives. For the 2.2 release, it was changed to GPL licensing, which settled any concerns about the group's commitment to true open source freedoms. In 2007, Qt 4.5 was released and they added LGPL as an option for developers who prefer the more permissive license. This article is an excerpt taken from the book Hands-On GUI Application Development in Go. This book covers the benefits and complexities of building native graphical applications, the procedure for building platform and developing graphical Windows applications using Walk.  This article covers the basics of therecipe/qt, multiple platforms, installation of qt (the bindings), and much more. Getting started with therecipe/qt To begin our exploration of Qt and the binding to Go, we'll build a simple hello world application. To be able to do so, we first need to install therecipe/qt, which depends on various prerequisites that we must first set up. As with Go-GTK, we'll be relying on a native library that requires that we both set up the CGo functionality and install the Qt library appropriate for the current platform. Preparing CGo The Qt Go bindings, like many of the other toolkits featured in this book, require the presence of CGo to utilize native libraries. On a full development system, it's likely that this is already set up. Installing Qt The Qt website offers various methods of installation, including a customized online installer available to anyone with a Qt account (which is free to sign up for). Typically, a Qt installation comes with Qt Creator (the project IDE), the GUI designer, additional tools, and examples. Visiting the preceding site will automatically detect your system and suggest the most appropriate download (this is normally the best option). Be aware that the Qt installation can be quite large. If you don't have at least 40 GB of space on your hard drive, you need to make a little space before installing. Some operating systems offer Qt libraries and tools as part of their package manager, which often provides a more lightweight installation that'll automatically stay up to date. Installing Qt on multiple platforms macOS On Apple macOS, the best approach to installation is to use the installer application available at the Qt download site. Visit www.qt.io/download and download the macOS installer. Once it has downloaded, open the package and run the program inside; this will install the selected compilers, tools, and supporting applications. If you encounter any errors during installation, the first step would be to check that your Xcode installation is complete and up to date. Windows Installing on Windows is more straightforward than some of the other toolkits we've looked at, as the Qt installer has a mingw package bundled to set up most of the compiling requirements (though it's still recommended to have your own compiler set up for the binding phase next). To install it, go to the download page listed previously and access the Windows installer. Run the downloaded executable and follow the onscreen instructions. It's recommended to install to the default location. Once that's done, you're ready to set up the bindings. Linux Using the online installer from Qt's website is the easiest approach, though it may be possible to install through your system's package manager (if you want to try the package manager approach, then first read the Qt Linux documentation. On most Linux platforms, the Qt downloads website will correctly detect the platform and offer a simple run installer. After downloading the file, you should make it executable and then run it: On Linux, you need to make the install file executable and run it This will start the installer just as on macOS; from here, follow the onscreen instructions and complete the installation. License / Qt account When it comes to the login screen, then you should enter your Qt account details if you have them. If you qualify for their open source license (GPL or LGPL), you can skip this step—to do so; make sure the email and password fields are empty. Installing qt (the bindings) To use qt (the Go Qt bindings), we need to download the project and its dependencies and then run a setup script to configure and compile the library. If using Windows, it's recommended to use the MSYS2 Terminal. If you installed the Qt download to anything other than the default location, then make sure to set up the QT_DIR environment variable to the location you chose. First, the library and its dependencies should be installed using the go tools, by running go get github.com/sirupsen/logrus and go get github.com/therecipe/qt. Once the download has completed, we need to run the qtsetup tool, which is included in the qt project; so, within the cmd/qtsetup folder, execute go run main.go. Using a Linux Terminal, it should look something like this: Executing the qtsetup script for therecipe/qt bindings Once this process completes, the bindings should be ready to use. If you encounter errors, then it's probably because the Qt tools aren't correctly installed or the location was customized and you forgot to set the QT_DIR environment variable. Build To build our first qt application with Go, let's make another Hello World application. As with previous examples, we'll make use of a simple vertical box layout within a single application window. The following code should be sufficient to load your first application: package main import ( "os" "github.com/therecipe/qt/widgets" ) func main() { app := widgets.NewQApplication(len(os.Args), os.Args) window := widgets.NewQMainWindow(nil, 0) window.SetWindowTitle("Hello World") widget := widgets.NewQWidget(window, 0) widget.SetLayout(widgets.NewQVBoxLayout()) window.SetCentralWidget(widget) label := widgets.NewQLabel2("Hello World!", window, 0) widget.Layout().AddWidget(label) button := widgets.NewQPushButton2("Quit", window) button.ConnectClicked(func(bool) { app.QuitDefault() }) widget.Layout().AddWidget(button) window.Show() widgets.QApplication_Exec() } Let's note a few details from this code snippet. You'll see that each of the widget constructor functions takes (typically) two parameters, each is the parent widget and a flags parameter. Additional types passed in will usually be added before these values with a note in the function name that there are additional parameters. For example, widgets.NewQLabel2(title, parent, flags) is equivalent to widgets.NewQLabel(parent, flags).SetTitle(title). Additionally, you'll see that the layout is applied to a new widgets.QWidget through SetLayout(layout), and that's set to the window content through window.SetCentralWidget(widget). To load the display and run the application, we call window.Show() and then widgets.QApplication_Exec(). This file is built in the usual way with go build hello.go: Building is simple though the output file is rather large The file built is quite large due to the size of the Qt framework. This will be reduced significantly when packaging for a specific distribution. Run The output of the build phase is a binary that can be executed on the current computer, either on the command line or by double-clicking in a file manager. Additionally, you could execute it directly with go run hello.go—either way, you should see a simple window, as shown here: qt Hello on Linux Running on macOS At this stage, the binaries can be executed on a computer with the same architecture that also has Qt installed. Object model and event handling The Qt framework is written using the C++ language, and so much of its architecture will be familiar to those who've coded in C++ before. It's important to note that Go isn't a complete object-oriented language and, as such, doesn't match these capabilities directly. In particular, we should look at inheritance as it's important to the Qt object model. Inheritance The Qt API is a fully object-oriented model that makes heavy use of the inheritance model. While Go doesn't truly support object-oriented inheritance in the traditional manner, its composition approach is very powerful and works well in its place. The result means that you probably won't notice the difference! Memory management As you'll have noticed in the preceding example, each widget expects the parent to be passed to the constructing function. This enables the Qt framework to handle the tidying up and freeing of memory when a tree of widgets is removed. QObject (which is the base object for all of the Qt API) keeps track of its child objects and so, when being removed, can remove its children too. This makes the creation and deletion of complex widget hierarchies easier to handle correctly. To make use of this feature, you should always remember to pass the parent object to a widget's constructor (the Go functions starting with New...), despite the fact that passing nil may look like it's working. Signals and slots Qt is similar to GTK+, an event-driven framework and uses signals extensively to handle event management and data communications. In Qt, this concept is split into signals and slots; a signal is what will be generated when an event occurs and a slot is what can receive a signal. The action of setting a slot to receive a signal is called connecting and this causes a slot function to be called when its connected signal is invoked. In Qt, these are typed events meaning that each signal has a list of type parameters associated with it. When the signal is defined, this type is set and any slot wishing to connect to the signal will need to have the same type. s.ConnectMySignal( func(msg string) { log.Println("Signalled message", msg) } ) Signals and slots are what power user interfaces generated with Qt Designer and are the recommended way of handling multi-threaded applications. A signal may fire from a background thread and the user interface code can connect this signal to its own slot—in essence, listening for the signal. When the signal fires, any associated data (parameters to the signal) will be passed from one thread to another so it can be used safely within the GUI updates. As Qt is a lightweight binding to the Qt API, the Go-specific documentation is minimal but you can find out a lot more about the Qt design and all of the classes available in the official documentation available at Qt's blog post. In this article, we have learned about the Qt framework and the multiple platforms, therecipe/qt, installation of qt (the bindings), and much more. To know more about Go-GTK and platforms with GTK, check out the book Hands-On GUI Application Development in Go. Qt Creator 4.9 Beta released with QML support, programming language support and more! Qt Design Studio 1.1 released with Qt Photoshop bridge, updated timeline and more Qt for Python 5.12 released with PySide2, Qt GUI and more
Read more
  • 0
  • 0
  • 23592

Packt
25 Oct 2013
5 min read
Save for later

The solvers – these great unknown

Packt
25 Oct 2013
5 min read
(For more resources related to this topic, see here.) Variable-step versus fixed-step solvers A variable-step solver will choose to use a shorter time step when the signal derivative is changing faster in order to obtain a better approximation, while it will use a longer step when the result changes at a slower pace. The most used variable-step solver is the ode45 solver (this is the default Simulink solver as well). A fixed-step solver will, on the other hand, always use the same step size. The simplest is the ode1 solver (the famous Euler method). We've already run our example with a fixed-step solver. Let's change this to a variable-step solver by opening the Model Configuration Parameters window and setting the ode45 solver. We now have the option to configure more Solver parameters; we'll allow it to use a Max step size of 1 second and a Min step size of 0.2 seconds, setting other parameters to the default value. Running the simulation now will give us the following result: We can see that the step size is indeed variable: the second value has been computed after only 0.2 seconds, and then the step size grew bigger and stayed at 1 second. We got a good approximation in the problematic region while using longer steps in the almost constant region, thus achieving a faster simulation time than a fixed-step solver with a step size of 0.2 seconds. But how does the solver determine when it's necessary to reduce the step size? The answer is by looking at the tolerances defined in the Model Configuration Parameters window. Relative tolerance defines the maximum error as a percentage of the actual value. The default value (1e-3) means that an error of 0.1 percent is accepted. Absolute tolerance defines the maximum error as a scalar value. When it is set to auto, Simulink will assume 1e-6 initially (that is, 0.000001), then change it to the maximum error calculated with the relative tolerance. The Shape preservation option, off ( Disable all ) by default, helps to increase the accuracy when the solution's derivative varies rapidly at the cost of a more computing-intensive simulation (thus increasing the overall time to compute the result). Finally, there's the option Number of consecutive min steps , which defines how many times the solver can use a time step smaller than the minimum step size violations defined before issuing a warning or an error. Let's set the Relative tolerance parameter to 1e-5 and run the simulation. We'll obtain this result: This time, the solver chose to use more shorter steps while the signal was changing to improve the accuracy, thanks to the relative tolerance we've set; then the step size grew gradually back to 1 second. Variable-step solvers give us a good trade-off between result approximation and overall simulation times in models, whereas fixed-step solvers are too computationally intensive to be used efficiently. Fixed-step solvers are required for code generation and real-time control purposes because Simulink can't go back in time to give a more accurate result. Continuous versus discrete A system can be continuous or discrete based on the presence of integrators or derivatives in the model (these are called continuous states). In other words, if the system is represented by the means of one or more differential equations, the system is continuous. Our cruise controller is continuous because it has an integral component. The continuous solver can choose to perform several iteration cycles in a single time step to reach the best possible approximation of the final result. Such solvers fall into the ODE ( Ordinary Differential Equation solvers) category. If a system is discrete, there are no differential equations but only memories and mathematical operands. A discrete solver is unable to perform the numerical integration required by continuous states. The continuous system requires continuous solvers, while Simulink will switch to a discrete solver for discrete systems even if a continuous solver has been specified. Try to set a discrete solver in our example system and run a simulation. An error window will appear, telling us that a discrete solver can't be used because the model contains continuous states. Stiff versus nonstiff In a stiff continuous system, certain solvers are unable to compute the result unless the step size is small enough; thus, a stiff solver is needed. These systems can be identified by an overall slowly changing solution that can vary rapidly in a very small time period. Our example is mildly stiff: the ode45 solver had to use a small step size to compute the result close to t = 0, but it managed to get the job done by changing the time step. A more appropriate choice would have been the ode23t solver (moderately stiff), which would have given the following result: Let's choose another nonstiff solver, the fixed-step ode1 , implementing the Euler method. Set the Fixed step size parameter to 1.5 and run the simulation: That's interesting: Using the Euler method with a long time step shows the result to be oscillating around the mathematical result, but the solution is still convergent. Setting the step size to 2 seconds will show that it can't converge anymore, and any step size higher than 2 seconds will make the simulation diverge from the correct result and go towards infinite oscillation. The following figure shows the result with a step size of 2.1 and a simulation time of 20 seconds: The following article presents stiff solvers in a way that is easy to understand: http://blogs.mathworks.com/seth/2012/07/03/why-do-we-need-stiff-ode-solvers/. The documentation center, accessible by pressing F1 , has detailed information about the solver types and parameters. This information can be found by navigating to Simulink | Simulation | Configure simulation . Summary In this article, we've learned how solvers work and how to choose the right solver to run the simulations Resources for Article: Further resources on this subject: 2-Dimensional Image Filtering [Article] GoboLinux: An Interview with Lucas Villa Real [Article] The NGINX HTTP Server [Article]
Read more
  • 0
  • 0
  • 23587

article-image-18-striking-ai-trends-2018-part-1
Sugandha Lahoti
27 Dec 2017
14 min read
Save for later

18 striking AI Trends to watch in 2018 - Part 1

Sugandha Lahoti
27 Dec 2017
14 min read
Artificial Intelligence is the talk of the town. It has evolved past merely being a buzzword in 2016, to be used in a more practical manner in 2017. As 2018 rolls out, we will gradually notice AI transitioning into a necessity. We have prepared a detailed report, on what we can expect from AI in the upcoming year. So sit back, relax, and enjoy the ride through the future. (Don’t forget to wear your VR headgear! ) Here are 18 things that will happen in 2018 that are either AI driven or driving AI: Artificial General Intelligence may gain major traction in research. We will turn to AI enabled solution to solve mission-critical problems. Machine Learning adoption in business will see rapid growth. Safety, ethics, and transparency will become an integral part of AI application design conversations. Mainstream adoption of AI on mobile devices Major research on data efficient learning methods AI personal assistants will continue to get smarter Race to conquer the AI optimized hardware market will heat up further We will see closer AI integration into our everyday lives. The cryptocurrency hype will normalize and pave way for AI-powered Blockchain applications. Advancements in AI and Quantum Computing will share a symbiotic relationship Deep learning will continue to play a significant role in AI development progress. AI will be on both sides of the cybersecurity challenge. Augmented reality content will be brought to smartphones. Reinforcement learning will be applied to a large number of real-world situations. Robotics development will be powered by Deep Reinforcement learning and Meta-learning Rise in immersive media experiences enabled by AI. A large number of organizations will use Digital Twin. 1. General AI: AGI may start gaining traction in research. AlphaZero is only the beginning. 2017 saw Google’s AlphaGo Zero (and later AlphaZero) beat human players at Go, Chess, and other games. In addition to this, computers are now able to recognize images, understand speech, drive cars, and diagnose diseases better with time. AGI is an advancement of AI which deals with bringing machine intelligence as close to humans as possible. So, machines can possibly do any intellectual task that a human can! The success of AlphaGo covered one of the crucial aspects of AGI systems—the ability to learn continually, avoiding catastrophic forgetting. However, there is a lot more to achieving human-level general intelligence than the ability to learn continually. For instance, AI systems of today can draw on skills it learned on one game to play another. But they lack the ability to generalize the learned skill. Unlike humans, these systems do not seek solutions from previous experiences. An AI system cannot ponder and reflect on a new task, analyze its capabilities, and work out how best to apply them. In 2018, we expect to see advanced research in the areas of deep reinforcement learning, meta-learning, transfer learning, evolutionary algorithms and other areas that aid in developing AGI systems. Detailed aspects of these ideas are highlighted in later points. We can indeed say, Artificial General Intelligence is inching closer than ever before and 2018 is expected to cover major ground in that direction. 2. Enterprise AI: Machine Learning adoption in enterprises will see rapid growth. 2017 saw a rise in cloud offerings by major tech players, such as the Amazon Sagemaker, Microsoft Azure Cloud, Google Cloud Platform, allowing business professionals and innovators to transfer labor-intensive research and analysis to the cloud. Cloud is a $130 billion industry as of now, and it is projected to grow.  Statista carried out a survey to present the level of AI adoption among businesses worldwide, as of 2017.  Almost 80% of the participants had incorporated some or other form of AI into their organizations or planned to do so in the future. Source: https://www.statista.com/statistics/747790/worldwide-level-of-ai-adoption-business/ According to a report from Deloitte, medium and large enterprises are set to double their usage of machine learning by the end of 2018. Apart from these, 2018 will see better data visualization techniques, powered by machine learning, which is a critical aspect of every business.  Artificial intelligence is going to automate the cycle of report generation and KPI analysis, and also, bring in deeper analysis of consumer behavior. Also with abundant Big data sources coming into the picture, BI tools powered by AI will emerge, which can harness the raw computing power of voluminous big data for data models to become streamlined and efficient. 3. Transformative AI: We will turn to AI enabled solutions to solve mission-critical problems. 2018 will see the involvement of AI in more and more mission-critical problems that can have world-changing consequences: read enabling genetic engineering, solving the energy crisis, space exploration, slowing climate change, smart cities, reducing starvation through precision farming, elder care etc. Recently NASA revealed the discovery of a new exoplanet, using data crunched from Machine learning and AI. With this recent reveal, more AI techniques would be used for space exploration and to find other exoplanets. We will also see the real-world deployment of AI applications. So it will not be only about academic research, but also about industry readiness. 2018 could very well be the year when AI becomes real for medicine. According to Mark Michalski, executive director, Massachusetts General Hospital and Brigham and Women’s Center for Clinical Data Science, “By the end of next year, a large number of leading healthcare systems are predicted to have adopted some form of AI within their diagnostic groups.”  We would also see the rise of robot assistants, such as virtual nurses, diagnostic apps in smartphones, and real clinical robots that can monitor patients, take care of the elderly, alert doctors, and send notifications in case of emergency. More research will be done on how AI enabled technology can help in difficult to diagnose areas in health care like mental health, the onset of hereditary diseases among others. Facebook's attempt at detection of potential suicidal messages using AI is a sign of things to come in this direction. As we explore AI enabled solutions to solve problems that have a serious impact on individuals and societies at large, considering the ethical and moral implications of such solutions will become central to developing them, let alone hard to ignore. 4. Safe AI: Safety, Ethics, and Transparency in AI applications will become integral to conversations on AI adoption and app design. The rise of machine learning capabilities has also given rise to forms of bias, stereotyping and unfair determination in such systems. 2017 saw some high profile news stories about gender bias, object recognition datasets like MS COCO, to racial disparities in education AI systems. At NIPS 2017, Kate Crawford talked about bias in machine learning systems which resonated greatly with the community and became pivotal to starting conversations and thinking by other influencers on how to address the problems raised.  DeepMind also launched a new unit, the DeepMind Ethics & Society,  to help technologists put ethics into practice, and to help society anticipate and direct the impact of AI for the benefit of all. Independent bodies like IEEE also pushed for standards in it’s ethically aligned design paper. As news about the bro culture in Silicon Valley and the lack of diversity in the tech sector continued to stay in the news all of 2017, it hit closer home as the year came to an end, when Kristian Lum, Lead Statistician at HRDAG, described her experiences with harassment as a graduate student at prominent stat conferences. This has had a butterfly effect of sorts with many more women coming forward to raise the issue in the ML/AI community. They talked about the formation of a stronger code of conduct by boards of key conferences such as NIPS among others. Eric Horvitz, a Microsoft research director, called Lum’s post a "powerful and important report." Jeff Dean, head of Google’s Brain AI unit applauded Lum for having the courage to speak about this behavior. Other key influencers from the ML and statisticians community also spoke in support of Lum and added their views on how to tackle the problem. While the road to recovery is long and machines with moral intelligence may be decades away, 2018 is expected to start that journey in the right direction by including safety, ethics, and transparency in AI/ML systems. Instead of just thinking about ML contributing to decision making in say hiring or criminal justice, data scientists would begin to think of the potential role of ML in the harmful representation of human identity. These policies will not only be included in the development of larger AI ecosystems but also in national and international debates in politics, businesses, and education. 5. Ubiquitous AI: AI will start redefining life as we know it, and we may not even know it happened. Artificial Intelligence will gradually integrate into our everyday lives. We will see it in our everyday decisions like what kind of food we eat, the entertainment we consume, the clothes we wear, etc.  Artificially intelligent systems will get better at complex tasks that humans still take for granted, like walking around a room and over objects. We’re going to see more and more products that contain some form of AI enter our lives. AI enabled stuff will become more common and available. We will also start seeing it in the background for life-altering decisions we make such as what to learn, where to work, whom to love, who our friends are,  whom should we vote for, where should we invest, and where should we live among other things. 6. Embedded AI: Mobile AI means a radically different way of interacting with the world. There is no denying that AI is the power source behind the next generation of smartphones. A large number of organizations are enabling the use of AI in smartphones, whether in the form of deep learning chips, or inbuilt software with AI capabilities. The mobile AI will be a  combination of on-device AI and cloud AI. Intelligent phones will have end-to-end capabilities that support coordinated development of chips, devices, and the cloud. The release of iPhone X’s FaceID—which uses a neural network chip to construct a mathematical model of the user’s face— and self-driving cars are only the beginning. As 2018 rolls out we will see vast applications on smartphones and other mobile devices which will run deep neural networks to enable AI. AI going mobile is not just limited to the embedding of neural chips in smartphones. The next generation of mobile networks 5G will soon greet the world. 2018 is going to be a year of closer collaborations and increasing partnerships between telecom service providers, handset makers, chip markers and AI tech enablers/researchers. The Baidu-Huawei partnership—to build an open AI mobile ecosystem, consisting of devices, technology, internet services, and content—is an example of many steps in this direction. We will also see edge computing rapidly becoming a key part of the Industrial Internet of Things (IIoT) to accelerate digital transformation. In combination with cloud computing, other forms of network architectures such as fog and mist would also gain major traction. All of the above will lead to a large-scale implementation of cognitive IoT, which combines traditional IoT implementations with cognitive computing. It will make sensors capable of diagnosing and adapting to their environment without the need for human intervention. Also bringing in the ability to combine multiple data streams that can identify patterns. This means we will be a lot closer to seeing smart cities in action. 7. Data-sparse AI: Research into data efficient learning methods will intensify 2017 saw highly scalable solutions for problems in object detection and recognition, machine translation, text-to-speech, recommender systems, and information retrieval.  The second conference on Machine Translation happened in September 2017.  The 11th ACM Conference on Recommender Systems in August 2017 witnessed a series of papers presentations, featured keynotes, invited talks, tutorials, and workshops in the field of recommendation system. Google launched the Tacotron 2 for generating human-like speech from text. However, most of these researches and systems attain state-of-the-art performance only when trained with large amounts of data. With GDPR and other data regulatory frameworks coming into play, 2018 is expected to witness machine learning systems which can learn efficiently maintaining performance, but in less time and with less data. A data-efficient learning system allows learning in complex domains without requiring large quantities of data. For this, there would be developments in the field of semi-supervised learning techniques, where we can use generative models to better guide the training of discriminative models. More research would happen in the area of transfer learning (reuse generalize knowledge across domains), active learning, one-shot learning, Bayesian optimization as well as other non-parametric methods.  In addition, researchers and organizations will exploit bootstrapping and data augmentation techniques for efficient reuse of available data. Other key trends propelling data efficient learning research are growing in-device/edge computing, advancements in robotics, AGI research, and energy optimization of data centers, among others. 8. Conversational AI: AI personal assistants will continue to get smarter AI-powered virtual assistants are expected to skyrocket in 2018. 2017 was filled to the brim with new releases. Amazon brought out the Echo Look and the Echo Show. Google made its personal assistant more personal by allowing linking of six accounts to the Google Assistant built into the Home via the Home app. Bank of America unveiled Erica, it’s AI-enabled digital assistant. As 2018 rolls out, AI personal assistants will find its way into an increasing number of homes and consumer gadgets. These include increased availability of AI assistants in our smartphones and smart speakers with built-in support for platforms such as Amazon’s Alexa and Google Assistant. With the beginning of the new year, we can see personal assistants integrating into our daily routines. Developers will build voice support into a host of appliances and gadgets by using various voice assistant platforms. More importantly, developers in 2018 will try their hands on conversational technology which will include emotional sensitivity (affective computing) as well as machine translational technology (the ability to communicate seamlessly between languages). Personal assistants would be able to recognize speech patterns, for instance, of those indicative of wanting help. AI bots may also be utilized for psychiatric counseling or providing support for isolated people.  And it’s all set to begin with the AI assistant summit in San Francisco scheduled on 25 - 26 January 2018. It will witness talks by world's leading innovators in advances in AI Assistants and artificial intelligence. 9. AI Hardware: Race to conquer the AI optimized hardware market will heat up further Top tech companies (read Google, IBM, Intel, Nvidia) are investing heavily in the development of AI/ML optimized hardware. Research and Markets have predicted the global AI chip market will have a growth rate of about 54% between 2017 and 2021. 2018 will see further hardware designs intended to greatly accelerate the next generation of applications and run AI computational jobs. With the beginning of 2018 chip makers will battle it out to determine who creates the hardware that artificial intelligence lives on. Not only that, there would be a rise in the development of new AI products, both for hardware and software platforms that run deep learning programs and algorithms. Also, chips which move away from the traditional one-size-fits-all approach to application-based AI hardware will grow in popularity. 2018 would see hardware which does not only store data, but also transform it into usable information. The trend for AI will head in the direction of task-optimized hardware. 2018 may also see hardware organizations move to software domains and vice-versa. Nvidia, most famous for their Volta GPUs have come up with NVIDIA DGX-1, a software for AI research, designed to streamline the deep learning workflow. More such transitions are expected at the highly anticipated CES 2018. [dropcap]P[/dropcap]hew, that was a lot of writing! But I hope you found it just as interesting to read as I found writing it. However, we are not done yet. And here is part 2 of our 18 AI trends in ‘18. 
Read more
  • 0
  • 0
  • 23572
article-image-container-linking-and-docker-dns
Packt
01 Nov 2016
29 min read
Save for later

Container Linking and Docker DNS

Packt
01 Nov 2016
29 min read
In this article by Jon Langemak, the author of the book Docker Networking Cookbook, has covered the following recipes: Verifying host based DNS configuration inside a container Overriding the default name resolution settings Configuring links for name and service resolution Leveraging Docker DNS (For more resources related to this topic, see here.) Verifying host based DNS configuration inside a container While you might not realize it but Docker, by default, is providing your containers a means to do basic name resolution. Docker passes name resolution from the Docker host, directly into the container. The result is that a spawned container can natively resolve anything that the Docker host itself can. The mechanics used by Docker to achieve name resolution in a container are elegantly simple. In this recipe, we'll walk through how this is done and how you can verify that it's working as expected. Getting Ready In this recipe we'll be demonstrating the configuration on a single Docker host. It is assumed that this host has Docker installed and that Docker is in its default configuration. We'll be altering name resolution settings on the host so you'll need root level access. How to do it… To start with, let's start a new container on our host docker1 and examine how the container handles name resolution: user@docker1:~$ docker run -d -P --name=web8 jonlangemak/web_server_8_dns d65baf205669c871d1216dc091edd1452a318b6522388e045c211344815c280a user@docker1:~$ user@docker1:~$ docker exec web8 host www.google.com www.google.com has address 216.58.216.196 www.google.com has IPv6 address 2607:f8b0:4009:80e::2004 user@docker1:~ $ It would appear that the container has the ability to resolve DNS names. If we look at our local Docker host and run the same test, we should get similar results: user@docker1:~$ host www.google.com www.google.com has address 216.58.216.196 www.google.com has IPv6 address 2607:f8b0:4009:80e::2004 user@docker1:~$ In addition, just like our Docker host, the container can also resolve local DNS records associated with the local domain lab.lab: user@docker1:~$ docker exec web8 host docker4 docker4.lab.lab has address 192.168.50.102 user@docker1:~$ You'll notice that we didn't need to specify a fully qualified domain name in order to resolve the host name docker4 in the domain lab.lab. At this point it's safe to assume that the container is receiving some sort of intelligent update from the Docker host which provides it relevant information about the local DNS configuration. In case you don't know, the resolv.conf file is generally where you define a Linux system's name resolution parameters. In many cases it is altered automatically by configuration information in other places. However – regardless of how it's altered, it should always be the source of truth for how the system handles name resolution. To see what the container is receiving, let's examine the containers resolv.conf file: user@docker1:~$ docker exec -t web8 more /etc/resolv.conf :::::::::::::: /etc/resolv.conf :::::::::::::: # Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8) # DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN nameserver 10.20.30.13 search lab.lab user@docker1:~$ As you can see, the container has learned that the local DNS server is 10.20.30.13 and that the local DNS search domain is lab.lab. Where did it get this information? The solution is rather simple. When a container starts, Docker generates instances of the following three files for each container spawned and saves it with the container configuration: /etc/hostname /etc/hosts /etc/resolv.conf These files are stored as part of the container configuration and then mounted into the container. We can use findmnt tool from within the container to examine the source of the mounts: root@docker1:~# docker exec web8 findmnt -o SOURCE …<Additional output removed for brevity>… /dev/mapper/docker1--vg-root[/var/lib/docker/containers/c803f130b7a2450609672c23762bce3499dec9abcfdc540a43a7eb560adaf62a/resolv.conf /dev/mapper/docker1--vg-root[/var/lib/docker/containers/c803f130b7a2450609672c23762bce3499dec9abcfdc540a43a7eb560adaf62a/hostname] /dev/mapper/docker1--vg-root[/var/lib/docker/containers/c803f130b7a2450609672c23762bce3499dec9abcfdc540a43a7eb560adaf62a/hosts] root@docker1:~# So while the container thinks it has local copies of the hostname, hosts, and resolv.conf, file in its /etc/ directory, the real files are actually located in the containers configuration directory (/var/lib/docker/containers/) on the Docker host. When you tell Docker to run a container, it does 3 things: It examines the Docker hosts /etc/resolv.conf file and places a copy of it in the containers directory. It creates a hostname file in the containers directory and assigns the container a unique hostname. It creates a hosts file in the containers directory and adds relevant records including localhost and a record referencing the host itself. Each time the container is restarted, the container's resolv.conf file is updated based on the values found in the Docker hosts resolv.conf file. This means that any changes made to the resolv.conf file are lost each time the container is restarted. The hostname and hosts configuration files are also rewritten each time the container is restarted losing any changes made during the previous run. To validate the configuration files a given container is using we can inspect the containers configuration for these variables: user@docker1:~$ docker inspect web8 | grep HostsPath "HostsPath": "/var/lib/docker/containers/c803f130b7a2450609672c23762bce3499dec9abcfdc540a43a7eb560adaf62a/hosts", user@docker1:~$ docker inspect web8 | grep HostnamePath "HostnamePath": "/var/lib/docker/containers/c803f130b7a2450609672c23762bce3499dec9abcfdc540a43a7eb560adaf62a/hostname", user@docker1:~$ docker inspect web8 | grep ResolvConfPath "ResolvConfPath": "/var/lib/docker/containers/c803f130b7a2450609672c23762bce3499dec9abcfdc540a43a7eb560adaf62a/resolv.conf", user@docker1:~$ As expected, these are the same mount paths we saw when we ran the findmnt command from within the container itself. These represent the exact mount path for each file into the containers /etc/ directory for each respective file. Overriding the default name resolution settings The method Docker uses for providing name resolution to containers works very well in most cases. However, there could be some instances where you want Docker to provide the containers with a DNS server other than the one the Docker host is configured to use. In these cases, Docker offers you a couple of options. You can tell the Docker service to provide a different DNS server for all of the containers the service spawns. You can also manually override this setting at container runtime by providing a DNS server as an option to the docker run subcommand. In this recipe, we'll show you your options for changing the default name resolution behavior as well as how to verify the settings worked. Getting Ready In this recipe we'll be demonstrating the configuration on a single Docker host. It is assumed that this host has Docker installed and that Docker is in its default configuration. We'll be altering name resolution settings on the host so you'll need root level access. How to do it… As we saw in the first recipe in this article, by default, Docker provides containers with the DNS server that the Docker host itself uses. This comes in the form of copying the host's resolv.conf file and providing it to each spawned container. Along with the name server setting, this file also includes definitions for DNS search domains. Both of these options can be configured at the service level to cover any spawned containers as well as at the individual level. For the purpose of comparison, let's start by examining the Docker hosts DNS configuration: root@docker1:~# more /etc/resolv.conf # Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8) # DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN nameserver 10.20.30.13 search lab.lab root@docker1:~# With this configuration, we would expect that any container spawned on this host would receive the same name server and DNS search domain. Let's spawn a container called web8 to verify this is working as expected: root@docker1:~# docker run -d -P --name=web8 jonlangemak/web_server_8_dns 156bc29d28a98e2fbccffc1352ec390bdc8b9b40b84e4c5f58cbebed6fb63474 root@docker1:~# root@docker1:~# docker exec -t web8 more /etc/resolv.conf :::::::::::::: /etc/resolv.conf :::::::::::::: # Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8) # DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN nameserver 10.20.30.13 search lab.lab As expected, the container receives the same configuration. Let's now inspect the container and see if we see any DNS related options defined: user@docker1:~$ docker inspect web8 | grep Dns "Dns": [], "DnsOptions": [], "DnsSearch": [], user@docker1:~$ Since we're using the default configuration, there is no reason to configure anything specific within the container in regards to DNS server or search domain. Each time the container starts, Docker will apply the settings for the hosts resolv.conf file to the containers DNS configuration files. If we'd prefer to have Docker give containers a different DNS server or DNS search domain, we can do so through Docker options. In this case, the two we're interested in are: --dns=<DNS Server> - Specify a DNS server address that Docker should provide to the containers. --dns-search=<DNS Search Domain> - Specify a DNS search domain that Docker should provide to the containers. Let's configure Docker to provide containers with a public DNS server (4.2.2.2) and a search domain of lab.external. We can do so by passing the following options to the Docker systemd drop-in file: ExecStart=/usr/bin/dockerd --dns=4.2.2.2 --dns-search=lab.external Once the options are configured, reload the systemd configuration, restart the service to load the new options, and restart our container web8: user@docker1:~$ sudo systemctl daemon-reload user@docker1:~$ sudo systemctl restart docker user@docker1:~$ docker start web8 web8 user@docker1:~$ docker exec -t web8 more /etc/resolv.conf search lab.external nameserver 4.2.2.2 user@docker1:~$ You'll note that despite this container initially having the hosts DNS server (10.20.30.13) and search domain (lab.lab) it now has the service level DNS options we just specified. If you recall earlier, we saw that when we inspected this container, it didn't define a specific DNS server or search domain. Since none was specified, Docker now uses the settings from the Docker options which take priority. While this provides some level of flexibility, it's not yet truly flexible. At this point any and all containers spawned on this server will be provided the same DNS server and search domain. To be truly flexible we should be able to have Docker alter the name resolution configuration on a per container level. As luck would have it, these options can also be provided directly at container runtime. The preceding image defines the priority Docker uses when deciding what name resolution settings to apply to a container when it's started. Settings defined at container runtime always take priority. If the settings aren't defined there, Docker then looks to see if they are configured at the service level. If the settings aren't there, it falls back to the default method of relying on the Docker hosts DNS settings. For instance, we can launch a container called web2 and provide different options: root@docker1:~# docker run -d --dns=8.8.8.8 --dns-search=lab.dmz -P --name=web8-2 jonlangemak/web_server_8_dns 1e46d66a47b89d541fa6b022a84d702974414925f5e2dd56eeb840c2aed4880f root@docker1:~# If we inspect the container, we'll see that we now have dns and dns-search fields defined as part of the container configuration: root@docker1:~# docker inspect web8-2 …<output removed for brevity>… "Dns": [ "8.8.8.8" ], "DnsOptions": [], "DnsSearch": [ "lab.dmz" ], …<output removed for brevity>… root@docker1:~# This ensures that if the container is restarted, it will still have the same DNS settings that were initially provided the first time the container was run. Let's make some slight changes to the Docker service to verify the priority is working as expected. Let's change our Docker options to look like this: ExecStart=/usr/bin/dockerd --dns-search=lab.external Now restart the service and run the following container: user@docker1:~$ sudo systemctl daemon-reload user@docker1:~$ sudo systemctl restart docker root@docker1:~# root@docker1:~# docker run -d -P --name=web8-3 jonlangemak/web_server_8_dns 5e380f8da17a410eaf41b772fde4e955d113d10e2794512cd20aa5e551d9b24c root@docker1:~# Since we didn't provide any DNS related options at container run time the next place we'd check would be the service level options. Our Docker service level options include a DNS search domain of lab.external, we'd expect the container to receive that search domain. However, since we don't have a DNS server defined, we'll need to fall back to the one configured on the Docker host itself. And now examine its resolv.conf file to make sure things worked as expected: user@docker1:~$ docker exec -t web8-3 more /etc/resolv.conf search lab.external nameserver 10.20.30.13 user@docker1:~$ Configuring Links for name and service resolution Container linking provides a means for one container to easily communicate with another container on the same host. As we've seen in previous examples, most container to container communication has occurred through IP addresses. Container linking improves on this by allowing linked containers to communicate with each other by name. In addition to providing basic name resolution, it also provides a means to see what services a linked container is providing. In this recipe we'll review how to create container links as well as discuss some of their limitations. Getting Ready In this recipe we'll be demonstrating the configuration on a single Docker host. It is assumed that this host has Docker installed and that Docker is in its default configuration. We'll be altering name resolution settings on the host so you'll need root level access. How to do it… The phrase container linking might imply to some that it involves some kind of network configuration or modification. In reality, container linking has very little to do with container networking. In the default mode, container linking provides a means for one container to resolve the name of another. For instance, let's start two containers on our lab host docker1: root@docker1:~# docker run -d -P --name=web1 jonlangemak/web_server_1 88f9c862966874247c8e2ba90c18ac673828b5faac93ff08090adc070f6d2922 root@docker1:~# docker run -d -P --name=web2 --link=web1 jonlangemak/web_server_2 00066ea46367c07fc73f73bdcdff043bd4c2ac1d898f4354020cbcfefd408449 root@docker1:~# Notice how when I started the second container I used a new flag called --link and referenced the container web1. We would now say that web2 was linked to web1. However, they're not really linked in any sort of way. A better description might be to say that web2 is now aware of web1. Let's connect to the container web2 to show you what I mean: root@docker1:~# docker exec -it web2 /bin/bash root@00066ea46367:/# ping web1 -c 2 PING web1 (172.17.0.2): 48 data bytes 56 bytes from 172.17.0.2: icmp_seq=0 ttl=64 time=0.163 ms 56 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.092 ms --- web1 ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max/stddev = 0.092/0.128/0.163/0.036 ms root@00066ea46367:/# It appears that the web2 container is now able to resolve the container web1 by name. This is because the linking process inserted records into the web2 containers hosts file: root@00066ea46367:/# more /etc/hosts 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters 172.17.0.2 web1 88f9c8629668 172.17.0.3 00066ea46367 root@00066ea46367:/# With this configuration, the web2 container can reach the web1 container either by the name we gave the container at runtime (web1) or the unique hostname Docker generated for the container (88f9c8629668). In addition to the hosts file being updated, web2 also generates some new environmental variables: root@00066ea46367:/# printenv WEB1_ENV_APACHE_LOG_DIR=/var/log/apache2 HOSTNAME=00066ea46367 APACHE_RUN_USER=www-data WEB1_PORT_80_TCP=tcp://172.17.0.2:80 WEB1_PORT_80_TCP_PORT=80 LS_COLORS= WEB1_PORT=tcp://172.17.0.2:80 WEB1_ENV_APACHE_RUN_GROUP=www-data APACHE_LOG_DIR=/var/log/apache2 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin PWD=/ WEB1_PORT_80_TCP_PROTO=tcp APACHE_RUN_GROUP=www-data SHLVL=1 HOME=/root WEB1_PORT_80_TCP_ADDR=172.17.0.2 WEB1_ENV_APACHE_RUN_USER=www-data WEB1_NAME=/web2/web1 _=/usr/bin/printenv root@00066ea46367:/# You'll notice many new environmental variables. Docker will copy any environmental variables from the linked container that were defined as part of the container. This includes: Environmental variables described in the docker image. More specifically, any ENV variables from the images Dockerfile Environmental variables passed to the container at runtime through the --env or -e flag. In this case, these three variables were defined as ENV variables in the images Dockerfile: APACHE_RUN_USER=www-data APACHE_RUN_GROUP=www-data APACHE_LOG_DIR=/var/log/apache2 Since both container images have the same ENV variables defined we'll see the local variables as well as the same environmental variables from the container web1 prefixed with WEB1_ENV_: WEB1_ENV_APACHE_RUN_USER=www-data WEB1_ENV_APACHE_RUN_GROUP=www-data WEB1_ENV_APACHE_LOG_DIR=/var/log/apache2 In addition, Docker also created 6 other environmental variables that describe the web1 container as well as any of its exposed ports: WEB1_PORT=tcp://172.17.0.2:80 WEB1_PORT_80_TCP=tcp://172.17.0.2:80 WEB1_PORT_80_TCP_ADDR=172.17.0.2 WEB1_PORT_80_TCP_PORT=80 WEB1_PORT_80_TCP_PROTO=tcp WEB1_NAME=/web2/web1 Linking also allows you to specify aliases. For instance let's stop, remove, and respawn container web2 using a slightly different syntax for linking… user@docker1:~$ docker stop web2 web2 user@docker1:~$ docker rm web2 web2 user@docker1:~$ docker run -d -P --name=web2 --link=web1:webserver jonlangemak/web_server_2 e102fe52f8a08a02b01329605dcada3005208d9d63acea257b8d99b3ef78e71b user@docker1:~$ Notice that after the link definition we inserted a :webserver. The name after the colon represents the alias for the link. In this case, I've specified an alias for the container web1 as webserver. If we examine the web2 container, we'll see that the alias is now also listed in the hosts file: root@c258c7a0884d:/# more /etc/hosts …<Additional output removed for brevity>… 172.17.0.2 webserver 88f9c8629668 web1 172.17.0.3 c258c7a0884d root@c258c7a0884d:/# Aliases also impact the environmental variables created during linking. Rather than using the container name they'll instead use the alias: user@docker1:~$ docker exec web2 printenv …<Additional output removed for brevity>… WEBSERVER_PORT_80_TCP_ADDR=172.17.0.2 WEBSERVER_PORT_80_TCP_PORT=80 WEBSERVER_PORT_80_TCP_PROTO=tcp …<Additional output removed for brevity>… user@docker1:~$ At this point you might be wondering how dynamic this is. After all, Docker is providing this functionality by updating static files in each container. What happens if a container's IP address changes? For instance, let's stop the container web1 and start a new container called web3 using the same image: user@docker1:~$ docker stop web1 web1 user@docker1:~$ docker run -d -P --name=web3 jonlangemak/web_server_1 69fa80be8b113a079e19ca05c8be9e18eec97b7bbb871b700da4482770482715 user@docker1:~$ If you'll recall from earlier, the container web1 had an IP address of 172.17.0.2 allocated to it. Since I stopped the container, Docker will release that IP address reservation making it available to be reassigned to the next container we start. Let's check the IP address assigned to the container web3: user@docker1:~$ docker exec web3 ip addr show dev eth0 79: eth0@if80: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff inet 172.17.0.2/16 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::42:acff:fe11:2/64 scope link valid_lft forever preferred_lft forever user@docker1:~$ As expected, web3 took the now open IP address of 172.17.0.2 that previously belonged to the web1 container. We can also verify that the container web2 still believes that this IP address belongs to the web1 container: user@docker1:~$ docker exec –t web2 more /etc/hosts | grep 172.17.0.2 172.17.0.2 webserver 88f9c8629668 web1 user@docker1:~$ If we start the container web1 once again, we should see it will get a new IP address allocated to it: user@docker1:~$ docker start web1 web1 user@docker1:~$ docker exec web1 ip addr show dev eth0 81: eth0@if82: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP link/ether 02:42:ac:11:00:04 brd ff:ff:ff:ff:ff:ff inet 172.17.0.4/16 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::42:acff:fe11:4/64 scope link valid_lft forever preferred_lft forever user@docker1:~$ If we check the container web2 again, we should see that Docker has updated it to reference web1's new IP address… user@docker1:~$ docker exec web2 more /etc/hosts | grep web1 172.17.0.4 webserver 88f9c8629668 web1 user@docker1:~$ However, while Docker takes care of updating the host file with the new IP address, it will not take care of updating any of the environmental variables to reflect the new IP address: user@docker1:~$ docker exec web2 printenv …<Additional output removed for brevity>… WEBSERVER_PORT=tcp://172.17.0.2:80 WEBSERVER_PORT_80_TCP=tcp://172.17.0.2:80 WEBSERVER_PORT_80_TCP_ADDR=172.17.0.2 …<Additional output removed for brevity>… user@docker1:~$ Additionally it should be pointed out that the link is only one way. That is, this link does not cause the container web1 to become aware of the web2 container. The container web1 will not receive the host records or the environmental variables referencing the web2: user@docker1:~$ docker exec -it web1 ping web2 ping: unknown host user@docker1:~$ Another reason to provision links is when you use Docker Inter Container Connectivity (ICC) mode set to false. As we've discussed previously, ICC prevents any containers on the same bridge from talking directly to each other. This forces them to talk to each other only though published ports. Linking provides a mechanism to override the default ICC rules. To demonstrate, let's stop and remove all the containers on our host docker1 and then add the following Docker option to the systemd drop in file: ExecStart=/usr/bin/dockerd --icc=false Now reload the systemd configuration, restart the service, and start the following containers: docker run -d -P --name=web1 jonlangemak/web_server_1 docker run -d -P --name=web2 jonlangemak/web_server_2 With ICC mode on you'll notice containers can't talk directly to each other: user@docker1:~$ docker exec web1 ip addr show dev eth0 87: eth0@if88: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff inet 172.17.0.2/16 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::42:acff:fe11:2/64 scope link valid_lft forever preferred_lft forever user@docker1:~$ docker exec -it web2 curl http://172.17.0.2 user@docker1:~$ In the preceding example, web2 is not able to access the web servers on web1. Now let's delete and recreate the web2 container this time linking it to web1: user@docker1:~$ docker stop web2 web2 user@docker1:~$ docker rm web2 web2 user@docker1:~$ docker run -d -P --name=web2 --link=web1 jonlangemak/web_server_2 4c77916bb08dfc586105cee7ae328c30828e25fcec1df55f8adba8545cbb2d30 user@docker1:~$ docker exec -it web2 curl http://172.17.0.2 <body> <html> <h1><span style="color:#FF0000;font-size:72px;">Web Server #1 - Running on port 80</span></h1> </body> </html> user@docker1:~$ We can see with the link in place the communication is allowed as expected. Once again, just like the link, this access is allowed in one direction. It should be noted that linking works differently when using user defined networks. In this recipe we covered what are now being called legacy links. Linking with user defined networks will be covered in the following recipes. Leveraging Docker DNS The introduction of user defined networks signaled a big change in Docker networking. While the ability to provision custom networks was the big news, there were also major enhancements in name resolution. User defined networks can benefit from what's being called embedded DNS. The Docker engine itself now has the ability to provide name resolution to all of the containers. This is a marked improvement from the legacy solution where the only means for name resolution was external DNS or linking which relied on the hosts file. In this recipe, we'll walk through how to use and configure embedded DNS. Getting Ready In this recipe we'll be demonstrating the configuration on a single Docker host. It is assumed that this host has Docker installed and that Docker is in its default configuration. We'll be altering name resolution settings on the host so you'll need root level access. How to do it… As mentioned, the embedded DNS system only works on user defined Docker networks. That being said, let's provision a user defined network and then start a simple container on it: user@docker1:~$ docker network create -d bridge mybridge1 0d75f46594eb2df57304cf3a2b55890fbf4b47058c8e43a0a99f64e4ede98f5f user@docker1:~$ docker run -d -P --name=web1 --net=mybridge1 jonlangemak/web_server_1 3a65d84a16331a5a84dbed4ec29d9b6042dde5649c37bc160bfe0b5662ad7d65 user@docker1:~$ As we saw in an earlier recipe, by default, Docker pulls the name resolution configuration from the Docker host and provides it to the container. This behavior can be changed by providing different DNS servers or search domains either at the service level or at container run time. In the case of containers connected to a user-defined network, the DNS settings provided to the container are slightly different. For instance, let's look at the resolv.conf file for the container we just connected to the user defined bridge mybridge1: user@docker1:~$ docker exec -t web1 more /etc/resolv.conf search lab.lab nameserver 127.0.0.11 options ndots:0 user@docker1:~$ Notice how the name server for this container is now 127.0.0.11. This IP address represents Docker's embedded DNS server and will be used for any container which is connected to a user-defined network. It is a requirement that any container connected to a user-defined use the embedded DNS server. Containers not initially started on a user defined network will get updated the moment they connect to a user defined network. For instance, let's start another container called web2 but have it use the default docker0 bridge: user@docker1:~$ docker run -dP --name=web2 jonlangemak/web_server_2 d0c414477881f03efac26392ffbdfb6f32914597a0a7ba578474606d5825df3f user@docker1:~$ docker exec -t web2 more /etc/resolv.conf :::::::::::::: /etc/resolv.conf :::::::::::::: # Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8) # DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN nameserver 10.20.30.13 search lab.lab user@docker1:~$ If we now connect the web2 container to our user-defined network, Docker will update the name server to reflect the embedded DNS server: user@docker1:~$ docker network connect mybridge1 web2 user@docker1:~$ docker exec -t web2 more /etc/resolv.conf search lab.lab nameserver 127.0.0.11 options ndots:0 user@docker1:~$ Since both our containers are now connected to the same user-defined network they can now reach each other by name: user@docker1:~$ docker exec -t web1 ping web2 -c 2 PING web2 (172.18.0.3): 48 data bytes 56 bytes from 172.18.0.3: icmp_seq=0 ttl=64 time=0.107 ms 56 bytes from 172.18.0.3: icmp_seq=1 ttl=64 time=0.087 ms --- web2 ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max/stddev = 0.087/0.097/0.107/0.000 ms user@docker1:~$ docker exec -t web2 ping web1 -c 2 PING web1 (172.18.0.2): 48 data bytes 56 bytes from 172.18.0.2: icmp_seq=0 ttl=64 time=0.060 ms 56 bytes from 172.18.0.2: icmp_seq=1 ttl=64 time=0.119 ms --- web1 ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max/stddev = 0.060/0.089/0.119/0.030 ms user@docker1:~$ You'll note that the name resolution is bidirectional and works inherently without the use of any links. That being said, with user defined networks, we can still define links for the purpose of creating local aliases. For instance, let's stop and remove both containers web1 and web2 and reprovision them as follows: user@docker1:~$ docker run -d -P --name=web1 --net=mybridge1 --link=web2:thesecondserver jonlangemak/web_server_1 fd21c53def0c2255fc20991fef25766db9e072c2bd503c7adf21a1bd9e0c8a0a user@docker1:~$ docker run -d -P --name=web2 --net=mybridge1 --link=web1:thefirstserver jonlangemak/web_server_2 6e8f6ab4dec7110774029abbd69df40c84f67bcb6a38a633e0a9faffb5bf625e user@docker1:~$ The first interesting item to point out is that Docker let us link to a container that did not yet exist. When we ran the container web1 we asked Docker to link it to the container web2. At that point, web2 didn't exist. This is a notable difference in how links work with the embedded DNS server. In legacy linking Docker needed to know the target containers information prior to making the link. This was because it had to manually update the source containers host file and environmental variables. The second interesting item is that aliases are no longer listed in the containers hosts file. If we look at the hosts file on each container we'll see that the linking no longer generates entries: user@docker1:~$ docker exec -t web1 more /etc/resolv.conf search lab.lab nameserver 127.0.0.11 options ndots:0 user@docker1:~$ docker exec -t web2 more /etc/resolv.conf search lab.lab nameserver 127.0.0.11 options ndots:0 user@docker1:~$ All of the resolution is now occurring in the embedded DNS server. This includes keeping track of defined aliases and their scope. So even without host records, each container is able to resolve the other containers alias through the embedded DNS server: user@docker1:~$ docker exec -t web1 ping thesecondserver -c2 PING thesecondserver (172.18.0.3): 48 data bytes 56 bytes from 172.18.0.3: icmp_seq=0 ttl=64 time=0.067 ms 56 bytes from 172.18.0.3: icmp_seq=1 ttl=64 time=0.067 ms --- thesecondserver ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max/stddev = 0.067/0.067/0.067/0.000 ms user@docker1:~$ docker exec -t web2 ping thefirstserver -c 2 PING thefirstserver (172.18.0.2): 48 data bytes 56 bytes from 172.18.0.2: icmp_seq=0 ttl=64 time=0.062 ms 56 bytes from 172.18.0.2: icmp_seq=1 ttl=64 time=0.042 ms --- thefirstserver ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max/stddev = 0.042/0.052/0.062/0.000 ms user@docker1:~$ The aliases created have a scope that is local to the container itself. For instance, a third container on the same user defined network is not able to resolve the aliases created as part of the links: user@docker1:~$ docker run -d -P --name=web3 --net=mybridge1 jonlangemak/web_server_1 d039722a155b5d0a702818ce4292270f30061b928e05740d80bb0c9cb50dd64f user@docker1:~$ docker exec -it web3 ping thefirstserver -c 2 ping: unknown host user@docker1:~$ docker exec -it web3 ping thesecondserver -c 2 ping: unknown host user@docker1:~$ You'll recall that legacy linking also automatically created a set of environmental variables on the source container. These environmental variables referenced the target container and any ports it might be exposing. Linking in user defined networks does not create these environmental variables: user@docker1:~$ docker exec web1 printenv PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin HOSTNAME=4eba77b66d60 APACHE_RUN_USER=www-data APACHE_RUN_GROUP=www-data APACHE_LOG_DIR=/var/log/apache2 HOME=/root user@docker1:~$ As we saw in the previous recipe, keeping these variables up to date wasn't achievable even with legacy links. That being said, it's not a total surprise the functionality doesn't exist when dealing with user defined networks. In addition to providing local container resolution, the embedded DNS server also handles any external requests. As we saw in the preceding example, the search domain from the Docker host (lab.lab in my case) was still being passed down to the containers and configured in their resolv.conf file. The name server learned from the host becomes a forwarder for the embedded DNS server. This allows the embedded DNS server to process any container name resolution requests and hand off external requests to the name server used by the Docker host. This behavior can be overridden either at the service level or by passing the --dns or --dns-search flag to a container at run time. For instance, we can start two more instances of the web1 container and specify a specific DNS server in either case: user@docker1:~$ docker run -dP --net=mybridge1 --name=web4 --dns=10.20.30.13 jonlangemak/web_server_1 19e157b46373d24ca5bbd3684107a41f22dea53c91e91e2b0d8404e4f2ccfd68 user@docker1:~$ docker run -dP --net=mybridge1 --name=web5 --dns=8.8.8.8 jonlangemak/web_server_1 700f8ac4e7a20204100c8f0f48710e0aab8ac0f05b86f057b04b1bbfe8141c26 user@docker1:~$ Note that web4 would receive 10.20.30.13 as a DNS forwarder even if we didn't specify it explicitly. This is because that's also the DNS server used by the Docker host and when not specified the container inherits from the host. It is specified here for the sake of the example. Now if we try to resolve a local DNS record on either container we can see that in the case of web1 it works since it has the local DNS server defined whereas the lookup on web2 fails because 8.8.8.8 doesn't know about the lab.lab domain: user@docker1:~$ docker exec -it web4 ping docker1.lab.lab -c 2 PING docker1.lab.lab (10.10.10.101): 48 data bytes 56 bytes from 10.10.10.101: icmp_seq=0 ttl=64 time=0.080 ms 56 bytes from 10.10.10.101: icmp_seq=1 ttl=64 time=0.078 ms --- docker1.lab.lab ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max/stddev = 0.078/0.079/0.080/0.000 ms user@docker1:~$ docker exec -it web5 ping docker1.lab.lab -c 2 ping: unknown host user@docker1:~$ Summary In this article we discussed the available options for container name resolution. This includes both the default name resolution behavior as well as the new embedded DNS server functionality that exists with user defined networks. You will get hold on the process used to determine name server assignment under each of these scenarios. Resources for Article: Further resources on this subject: Managing Application Configuration [article] Virtualizing Hosts and Applications [article] Deploying a Play application on CoreOS and Docker [article]
Read more
  • 0
  • 0
  • 23531

article-image-chaos-engineering-company-gremlin-launches-scenarios-making-it-easier-to-tackle-downtime-issues
Richard Gall
26 Sep 2019
2 min read
Save for later

Chaos engineering company Gremlin launches Scenarios, making it easier to tackle downtime issues

Richard Gall
26 Sep 2019
2 min read
At the second ChaosConf in San Francisco, Gremlin CEO Kolton Andrus revealed the company's latest step in its war against downtime: 'Scenarios.' Scenarios makes it easy for engineering teams to simulate a common issues that lead to downtime. It's a natural and necessary progression for Gremlin that is seeing even the most forward thinking teams struggling to figure out how to implement chaos engineering in a way that's meaningful to their specific use case. "Since we released Gremlin Free back in February thousands of customers have signed up to get started with chaos engineering," said Andrus. "But many organisations are still struggling to decide which experiments to run in order to avoid downtime and outages." Scenarios, then, is a useful way into chaos engineering for teams that are reticent about taking their first steps. As Andrus notes, it makes it possible to inject failure "with a couple of clicks." What failure scenarios does Scenarios let engineering teams simulate? Scenarios lets Gremlin users simulate common issues that can cause outages. These include: Traffic spikes (think Black Friday site failures) Network failures Region evacuation This provides a great starting point for anyone that wants to stress test their software. Indeed, it's inevitable that these issues will arise at some point so taking advance steps to understand what the consequences could be will minimise their impact - and their likelihood. Why chaos engineering? Over the last couple of years plenty of people have been attempting to answer why chaos engineering? But in truth the reasons are clear: software - indeed, the internet as we know it - is becoming increasingly complex, a mesh of interdependent services and platforms. At the same time, the software being developed today is more critical than ever. For eCommerce sites downtime means money, but for those in IoT and embedded systems world (like self-driving cars, for example), it's sometimes a matter of life and death. This makes Gremlin's Scenarios an incredibly exciting an important prospect - it should end the speculation and debate about whether we should be doing chaos engineering, and instead help the world to simply start doing it. At ChaosConf Andrus said that Gremlin's mission is to build a more reliable internet. We should all hope they can deliver.
Read more
  • 0
  • 0
  • 23526

article-image-instant-optimizing-embedded-systems-using-busybox
Packt
25 Nov 2013
9 min read
Save for later

Instant Optimizing Embedded Systems Using BusyBox

Packt
25 Nov 2013
9 min read
(For more resources related to this topic, see here.) BusyBox Compiling BusyBox, the Swiss Army Knife of Embedded Linux, it can be compiled into a single binary for different architectures. Before compiling software, we must get a compiler and the corresponding libraries, a build host and a target platform, the build host is the one running the compiler, the target platform is the one running the target binary. Herein, the desktop development system is a 64 bit X86 Ubuntu systems, it will be used as our build host, and an ARM Android system will be used as our target platform. To compile BusyBox on X86-64 Ubuntu system for ARM, we need a cross compiler. The gcc-arm-linux-gnueabi cross compiler can be installed directly on Ubuntu: $ sudo apt-get install gcc-arm-linux-gnueabi On the other Linux distributions, Google's official NDK is a good choice if want to share Android's Bionic C library, but since Bionic C library lacks lots of POSIX C header files, if want to get most of BusyBox applets building, the prebuilt version of Linaro GCC with Glibc is preferable, we can download it from http://www.linaro.org/downloads/, for example: http://releases.linaro.org/13.04/components/toolchain/binaries/gcc-linaro-aarch64-none-elf-4.7-2013.04-20130415_linux.tar.bz2. Before compiling, to simplify the configuration, enable the largest generic configuration with make defconfig and configure cross compiler: arm-linux-gnueabi-gcc with: make menuconfig. $ make defconfig $ make menuconfig Busybox Settings ---> Build Options ---> (arm-linux-gnueabi-) Cross Compiler prefix After configuration, we can simply compile it with: $ make Then, a BusyBox binary will be compiled for ARM: $ file busybox busybox: ELF 32-bit LSB executable, ARM, version 1(SYSV), dynamically linked (uses shared libs), stripped To list the shared libraries required by BusyBox binary for ARM, a command arm-linux-gnueabi-readelf should be used: $ arm-linux-gnueabi-readelf -d ./busybox grep "Shared library:" | cut -d'[' -f2 | tr -d ']'| libm.so.6 libc.so.6 ld-linux.so.3 To get the full path, we can get the library search path at first: $ arm-linux-gnueabi-ld --verbose grep SEARCH | tr ';' 'n' | cut -d'"' -f2 | tr -d '"'| /lib/arm-linux-gnueabi /usr/lib/arm-linux-gnueabi /usr/arm-linux-gnueabi/lib Then, we can find out that /usr/arm-linux-gnueabi/lib is the real search path in our platform and we can get the full path of the libraries as below: $ ls /usr/arm-linux-gnueabi/lib/{libm.so.6,libc.so.6,ld-linux.so.3} /usr/arm-linux-gnueabi/lib/ld-linux.so.3 /usr/arm-linux-gnueabi/lib/libc.so.6/usr/arm-linux-gnueabi/lib/libm.so.6 By default, the binary is dynamically linked, to enable static linking, configure BusyBox as following: Busybox Settings ---> Build Options ---> [*] Build BusyBox as a static binary (no shared libs) If using a new Glibc to compile BusyBox with static linking, to avoid such error: inetd.c:(.text.prepare_socket_fd+0x7e): undefined reference to `bindresvport' We need to disable CONFIG_FEATURE_INETD_RPC: Networking Utilities ---> [*] inetd [ ] Support RPC services Then, recompile it with make. BusyBox Installation This section shows how to install the above compiled BusyBox binaries on an ARM Android system. The installation of BusyBox means to create soft links for all of its built-in applets, use wc applet as an example: $ ln -s busybox wc $ echo "Hello, Busybox." ./wc -w| 2 BusyBox can be installed at the earlier compiling stage or at run-time. To build a minimal embedded file system with BusyBox, we'd better install it at the compiling stage with 'make install' for it helps to create the basic directory architecture of a standard Linux root file system and create soft links to BusyBox under the corresponding directories. With this installation method, we need to configure the target installation directory as following, use ~/busybox-ramdisk/ directory as an example: Busybox Settings ---> Installation Options ("make install" behavior) ---> (~/busybox-ramdisk/) BusyBox installation prefix After installation, we may get such a list of the file and directories: $ ls ~/busybox-ramdisk/ bin linuxrc sbin usr But to install it on an existing ARM Android system, it may be easier to install BusyBox at run-time with its --install option. With --install, by default, hard links will be created, to create soft links (symbolic links), -s option should be appended. If want to create links across different file systems (E.g. in Android system, to install BusyBox to /system/bin but BusyBox itself is put in the /data directory), -s must be used. To use the -s option, BusyBox should be configured as below: Busybox Settings ---> General Configuration ---> [*] Support --install [-s] to install applet links at runtime Now, let’s introduce how to install the above compiled BusyBox binaries to an existing ARM Android system. To do so, the Android system must be rooted to make sure the /data and / directories are writable. We will not show how to root an Android device, please get help from your product maker. Or if no real rooted Android device available, the Android emulator (emulator) provided by ADT (Android Development Toolkit, http://developer.android.com/sdk/index.html) can be used to start a rooted Android system on a virtual ARM Android device. To create a virtual ARM Android device and to use the Android emulator, please read the online documents provided by Google on http://developer.android.com/tools/help/android.html and http://developer.android.com/tools/help/emulator.html. Now, let's assume a rooted Android system is already running there with the USB debugging option enabled for Android Debug Bridge (adb, http://developer.android.com/tools/help/adb.html) support, for example, to check if such a device is started, we can run: $ adb devices List of devices attached emulator-5554 device As we can see, a virtual Android device running on Android emulator: emulator-5554 is there. Now, we are able to show how to install the BusyBox binaries on the existing Android system. Since the dynamically linked and statically linked BusyBox binaries are different, we will introduce how to install them respectively. Install the statically linked Busybox binary To install the statically installed Busybox binary, we only need to upload the BusyBox binary itself: $ adb push busybox /data/ Afterwards, install it with the --install option, for example, install it to the /bin directory of the Andriod system: $ adb shell root@android:/ # mount -o remount,rw / root@android:/ # mkdir /bin/ root@android:/ # /data/busybox --install -s /bin To be able to create the /bin directory for installation, the / directory is remounted to be writable. After installation, soft links are created under /bin for all of the built-in applets. If the -s option is not used, it will fail to create the hard links across the /bin and /data directories, that's why we must use -s option, the failure log looks like: busybox: /bin/[: Invalid cross-device link busybox: /bin/[[: Invalid cross-device link busybox: /bin/acpid: Invalid cross-device link busybox: /bin/add-shell: Invalid cross-device link (...truncated...) To execute the just installed applets, use md5sum as an example: $ /bin/md5sum /bin/ls 19994347b06d5ef7dbcbce0932960395 /bin/ls To run the applets without the full path, the /bin directory should be appended to the PATH variable: $ export PATH=$PATH:/bin Then, all of the BusyBox applets can be executed directly, that means we have installed Busybox successfully. To make the settings permanently, the above commands can be added to a script and such a script can be run as an Android service. Install the statically linked Busybox binary To install the statically installed Busybox binary, we only need to upload the BusyBox binary itself: $ adb push busybox /data/ Afterwards, install it with the --install option, for example, install it to the /bin directory of the Andriod system: $ adb shell root@android:/ # mount -o remount,rw / root@android:/ # mkdir /bin/ root@android:/ # /data/busybox --install -s /bin To be able to create the /bin directory for installation, the / directory is remounted to be writable. After installation, soft links are created under /bin for all of the built-in applets. If the -s option is not used, it will fail to create the hard links across the /bin and /data directories, that's why we must use -s option, the failure log looks like: busybox: /bin/[: Invalid cross-device link busybox: /bin/[[: Invalid cross-device link busybox: /bin/acpid: Invalid cross-device link busybox: /bin/add-shell: Invalid cross-device link (...truncated...) To execute the just installed applets, use md5sum as an example: $ /bin/md5sum /bin/ls 19994347b06d5ef7dbcbce0932960395 /bin/ls To run the applets without the full path, the /bin directory should be appended to the PATH variable: $ export PATH=$PATH:/bin Then, all of the BusyBox applets can be executed directly, that means we have installed Busybox successfully. To make the settings permanently, the above commands can be added to a script and such a script can be run as an Android service. Install the dynamically linked BusyBox binary For a dynamically linked BusyBox, to install it, besides the installation of the BusyBox binary itself, the required dynamic linker/loader (ld-linux.so) and the dependent shared libraries (libc.so and libm.so) should be installed too. For the basic installation procedure are the same as the one for statically linked BusyBox, herein, we only introduce how to install the required ld-linux.so.3, libc.so.6 and libm.so.6. Without the above dynamic linker/loader and libraries, we may get such error while running the dynamically linked BusyBox: $ /data/busybox --install -s /bin /system/bin/sh: /data/busybox: No such file or directory Before installation, create another /lib directory on target Android system and then upload the above files to it: $ adb shell mkdir /lib $ adb push /usr/arm-linux-gnueabi/lib/ld-linux.so.3 /lib/ $ adb push /usr/arm-linux-gnueabi/lib/libc.so.6 /lib/ $ adb push /usr/arm-linux-gnueabi/lib/libm.so.6 /lib/ With the above installation, the dynamically linked BusyBox binary should also be able to run and we are able to install and execute its applets as before: $ /data/busybox --install -s /bin As we can see, the installation of the dynamically linked BusyBox binary requires extra installation of the dependent linker/loader and libraries. For real embedded system development, if only the BusyBox binary itself uses the shared libraries, the static linking should be used instead at compiling to avoid the extra installation and the time-cost run-time linking, otherwise, if many binaries share the same libraries, to reduce the total size cost for the size critical embedded systems, dynamic linking may be preferable. Summary In this article we learned about BusyBox compiling and its installation. BusyBox is a free GPL-licensed toolbox aimed at the embedded world. It is a collection of the tiny versions of many common Unix utilities. Resources for Article : Further resources on this subject: Embedding Doctests in Python Docstrings [Article] The Business Layer (Java EE 7 First Look) [Article] Joomla! 1.6: Organizing and Managing Content [Article]
Read more
  • 0
  • 0
  • 23486
article-image-implementing-cost-effective-iot-analytics-for-predictive-maintenance-tutorial
Prasad Ramesh
04 Sep 2018
10 min read
Save for later

Implementing cost-effective IoT analytics for predictive maintenance [Tutorial]

Prasad Ramesh
04 Sep 2018
10 min read
Predictive maintenance is a common value proposition cited for IoT analytics. In this tutorial will look at a value formula for net savings. Then we walk through an example as a way to highlight how to think financially about when it makes sense to implement a decision and when it does not. The economics of predictive maintenance may not be entirely obvious. Believe it or not, it does not always make sense, even if you can predict early failures accurately. In many cases, you will actually lose money by doing it. Even when it can save you money, there is an optimal point for when it should be used. The optimal point depends on the costs and the accuracy of the predictive model. This article is an excerpt from a book written by Andrew Minteer titled Analytics for the Internet of Things (IoT). The value formula A formula to guide decision making compares the cost of allowing a failure to occur versus the cost to proactively repair the component while considering the probability of predicting the failure: Net Savings = (Cost of Failure * (Expected Number of Failures - Expected True Positive Predictions)) - (Proactive Repair Cost * (Expected True Positives + Expected False Positives)) If the cost of failure is the same as the proactive repair cost, even with a perfect prediction model, then there will be no savings. Make sure to include intangible costs into the cost of failure. Some examples of intangible costs include legal expenses, loss of brand equity, and even the customer's expenses. Predictive repair does make sense when there is a large spread between the cost of failure and the cost of proactive replacement, combined with a well-performing prediction model. For example, if the cost of a failure is a locomotive engine replacement at $1 million USD and the cost of a proactive repair is $200 USD, then the accuracy of the model does not even have to be all that great before a proactive replacement program makes financial sense. On the other hand, if the failure is a $400 USD automotive turbocharger replacement, and the proactive repair cost is $350 USD for a turbocharger actuator subcomponent replacement, the predictive model would need to be highly accurate for that to make financial sense. An example of making a value decision To illustrate the example, we will walk through a business situation and then some R code that simulates a cost-benefit curve for that decision. The code will use a fitted predictive model to calculate the net savings (or lack thereof) to generate a cost curve. The cost curve can then be used in a business decision on what proportion of units with predicted failures should have a proactive replacement. Imagine you work for a company that builds diesel-powered generators. There is a coolant control valve that normally lasts for 4,000 hours of operation until there is a planned replacement. From the analysis, your company has realized that the generators built two years prior are experiencing an earlier than the expected failure of the valve. When the valve fails, the engine overheats and several other components are damaged. The cost of failure, including labor rates for repair personnel and the cost to the customer for downtime, is an average of $1,000 USD. The cost of a proactive replacement of the valve is $253 USD. Should you replace all coolant valves in the population? It depends on how high a failure rate is expected. In this case, about 10% of the current non-failed units are expected to fail before the scheduled replacement. Also, importantly, it matters how well you can predict the failures. The following R code simulates this situation and uses a simple predictive model (logistic regression) to estimate a cost curve. The model has an AUC of close to 0.75. This will vary as you run the code since the dataset is randomly simulated: #make sure all needed packages are installed if(!require(caret)){ install.packages("caret") } if(!require(pROC)){ install.packages("pROC") } if(!require(dplyr)){ install.packages("dplyr") } if(!require(data.table)){ install.packages("data.table") } #Load required libraries library(caret) library(pROC) library(dplyr) library(data.table) #Generate sample data simdata = function(N=1000) { #simulate 4 features X = data.frame(replicate(4,rnorm(N))) #create a hidden data structure to learn hidden = X[,1]^2+sin(X[,2]) + rnorm(N)*1 #10% TRUE, 90% FALSE rare.class.probability = 0.1 #simulate the true classification values y.class = factor(hidden<quantile(hidden,c(rare.class.probability))) return(data.frame(X,Class=y.class)) } #make some data structure model_data = simdata(N=50000) #train a logistic regression model on the simulated data training <- createDataPartition(model_data$Class, p = 0.6, list=FALSE) trainData <- model_data[training,] testData <- model_data[-training,] glmModel <- glm(Class~ . , data=trainData, family=binomial) testData$predicted <- predict(glmModel, newdata=testData, type="response") #calculate AUC roc.glmModel <- pROC::roc(testData$Class, testData$predicted) auc.glmModel <- pROC::auc(roc.glmModel) print(auc.glmModel) #Pull together test data and predictions simModel <- data.frame(trueClass = testData$Class, predictedClass = testData$predicted) # Reorder rows and columns simModel <- simModel[order(simModel$predictedClass, decreasing = TRUE), ] simModel <- select(simModel, trueClass, predictedClass) simModel$rank <- 1:nrow(simModel) #Assign costs for failures and proactive repairs proactive_repair_cost <- 253 # Cost of proactively repairing a part failure_repair_cost <- 1000 # Cost of a failure of the part (include all costs such as lost production, etc not just the repair cost) # Define each predicted/actual combination fp.cost <- proactive_repair_cost # The part was predicted to fail but did not (False Positive) fn.cost <- failure_repair_cost # The part was not predicted to fail and it did (False Negative) tp.cost <- (proactive_repair_cost - failure_repair_cost) # The part was predicted to fail and it did (True Positive). This will be negative for a savings. tn.cost <- 0.0 # The part was not predicted to fail and it did not (True Negative) #incorporate probability of future failure simModel$future_failure_prob <- prob_failure #Function to assign costs for each instance assignCost <- function(pred, outcome, tn.cost, fn.cost, fp.cost, tp.cost, prob){ cost <- ifelse(pred == 0 & outcome == FALSE, tn.cost, # No cost since no action was taken and no failure ifelse(pred == 0 & outcome == TRUE, fn.cost, # The cost of no action and a repair resulted ifelse(pred == 1 & outcome == FALSE, fp.cost, # The cost of proactive repair which was not needed ifelse(pred == 1 & outcome == TRUE, tp.cost, 999999999)))) # The cost of proactive repair which avoided a failure return(cost) } # Initialize list to hold final output master <- vector(mode = "list", length = 100) #use the simulated model. In practice, this code can be adapted to compare multiple models test_model <- simModel # Create a loop to increment through dynamic threshold (starting at 1.0 [no proactive repairs] to 0.0 [all proactive repairs]) threshold <- 1.00 for (i in 1:101) { #Add predicted class with percentile ranking test_model$prob_ntile <- ntile(test_model$predictedClass, 100) / 100 # Dynamically determine if proactive repair would apply based on incrementing threshold test_model$glm_failure <- ifelse(test_model$prob_ntile >= threshold, 1, 0) test_model$threshold <- threshold # Compare to actual outcome to assign costs test_model$glm_impact <- assignCost(test_model$glm_failure, test_model$trueClass, tn.cost, fn.cost, fp.cost, tp.cost, test_model$future_failure_prob) # Compute cost for not doing any proactive repairs test_model$nochange_impact <- ifelse(test_model$trueClass == TRUE, fn.cost, tn.cost) # *test_model$future_failure_prob) # Running sum to produce the overall impact test_model$glm_cumul_impact <- cumsum(test_model$glm_impact) / nrow(test_model) test_model$nochange_cumul_impact <- cumsum(test_model$nochange_impact) / nrow(test_model) # Count the # of classified failures test_model$glm_failure_ct <- cumsum(test_model$glm_failure) # Create new object to house the one row per iteration output for the final plot master[[i]] <- test_model[nrow(test_model),] # Reduce the threshold by 1% and repeat to calculate new value threshold <- threshold - 0.01 } finalOutput <- rbindlist(master) finalOutput <- subset(finalOutput, select = c(threshold, glm_cumul_impact, glm_failure_ct, nochange_cumul_impact) ) # Set baseline to costs of not doing any proactive repairs baseline <- finalOutput$nochange_cumul_impact # Plot the cost curve par(mfrow = c(2,1)) plot(row(finalOutput)[,1], finalOutput$glm_cumul_impact, type = "l", lwd = 3, main = paste("Net Costs: Proactive Repair Cost of $", proactive_repair_cost, ", Failure cost $", failure_repair_cost, sep = ""), ylim = c(min(finalOutput$glm_cumul_impact) - 100, max(finalOutput$glm_cumul_impact) + 100), xlab = "Percent of Population", ylab = "Net Cost ($) / Unit") # Plot the cost difference of proactive repair program and a 'do nothing' approach plot(row(finalOutput)[,1], baseline - finalOutput$glm_cumul_impact, type = "l", lwd = 3, col = "black", main = paste("Savings: Proactive Repair Cost of $", proactive_repair_cost, ", Failure cost $", failure_repair_cost,sep = ""), ylim = c(min(baseline - finalOutput$glm_cumul_impact) - 100, max(baseline - finalOutput$glm_cumul_impact) + 100), xlab = "% of Population", ylab = "Savings ($) / Unit") abline(h=0,col="gray")   As seen in the resulting net cost and savings curves, based on the model's predictions, the optimal savings would be from a proactive repair program of the top 30 percentile units. The savings decreases after this, although you would still save money when replacing up to 75% of the population. After this point, you should expect to spend more than you save. The following set of charts is the output from the preceding code: Cost and savings curves for the proactive repair $253 and failure cost at $1,000 scenario Note the changes in the following graph when the failure cost drops to $300 USD. At no point do you save money, as the proactive repair cost will always outweigh the reduced failure cost. This does not mean you should not do a proactive repair; you may still want to do so in order to satisfy your customers. Even in such a case, this cost curve method can help in decisions on how much you are willing to spend to address the problem. You can rerun the code with proactive_repair_cost set to 253 and failure_repair_cost set to 300 to generate the following charts: Cost and savings curves for the proactive repair $253 and failure cost at $300 scenario And finally, notice how the savings curve changes when the failure cost moves to $5,000. You will notice that the spread between the proactive repair cost and the failure cost determines much of when doing a proactive repair makes business sense. You can rerun the code with proactive_repair_cost set to 253 and failure_repair_cost set to 5000 to generate the following charts: Cost and savings curves for the proactive repair $253 and failure cost at $5,000 scenario Ultimately, the decision is a business case based on the expected costs and benefits. ML modeling can help optimize savings under the right conditions. Utilizing cost curves helps to determine the expected costs and savings of proactive replacements. In this tutorial, we looked at implementing economically cost effective IoT analytics for predictive maintenance with example.  To further explore IoT Analytics and cloud check out the book Analytics for the Internet of Things (IoT). AWS IoT Analytics: The easiest way to run analytics on IoT data, Amazon says Build an IoT application with Azure IoT [Tutorial] Intelligent Edge Analytics: 7 ways machine learning is driving edge computing adoption in 2
Read more
  • 0
  • 0
  • 23484

article-image-exciting-features-haxeflixel
Packt
02 Nov 2015
4 min read
Save for later

The Exciting Features of HaxeFlixel

Packt
02 Nov 2015
4 min read
This article by Jeremy McCurdy, the author of the book Haxe Game Development Essentials, uncovers the exciting features of HaxeFlixel. When getting into cross-platform game development, it's often difficult to pick the best tool. There are a lot of engines and languages out there to do it, but when creating 2D games, one of the best options out there is HaxeFlixel. HaxeFlixel is a game engine written in the Haxe language. It is powered by the OpenFL framework. Haxe is a cross-platform language and compiler that allows you to write code and have it run on a multitude of platforms. OpenFL is a framework that expands the Haxe API and allows you to have easy ways to handle things such as rendering an audio in a uniform way across different platforms. Here's a rundown of what we'll look at: Core features Display Audio Input Other useful features Multiplatform support Advanced user interface support Visual effects  (For more resources related to this topic, see here.)  Core features HaxeFlixel is a 2D game engine, originally based off the Flash game engine Flixel. So, what makes it awesome? Let's start with the basic things you need: display, audio, and input. Display In HaxeFlixel, most visual elements are represented by objects using the FlxSprite class. This can be anything from spritesheet animations to shapes drawn through code. This provides you with a simple and consistent way of working with visual elements. Here's an example of how the FlxSprite objects are used: You can handle things such as layering by using the FlxGroup class, which does what its name implies—it groups things together. The FlxGroup class also can be used for collision detection (check whether objects from group A hit objects from group B). It also acts an object pool for better memory management. It's really versatile without feeling bloated. Everything visual is displayed by using the FlxCamera class. As the name implies, it's a game camera. It allows you to do things such as scrolling, having fullscreen visual effects, and zooming in or out of the page. Audio Sound effects and music are handled using a simple but effective sound frontend. It allows you to play sound effects and loop music clips with easy function calls. You can also manage the volume on a per sound basis, via global volume controls, or a mix of both. Input HaxeFlixel supports many methods of input. You can use mouse, touch, keyboard, or gamepad input. This allows you to support players on every platform easily. On desktop platforms, you can easily customize the mouse cursor without the need to write special functionalities. The built-in gamepad support covers mappings for the following controllers: Xbox PS3 PS4 OUYA Logitech Other useful features HaxeFlixel has a bunch of other cool features. This makes it a solid choice as a game engine. Among these are multiplatform support, advanced user interface support, and visual effects. Multi-platform support HaxeFlixel can be built for many different platforms. Much of this comes from it being built using OpenFL and its stellar cross-platform support. You can build desktop games that will work natively on Windows, Mac, and Linux. You can build mobile games for Android and iOS with relative ease. You can also target the Web by using Flash or the experimental support for HTML5. Advanced user interface support By using the flixel-ui add-on library, you can create complex game user interfaces. You can define and set up these interfaces with this by using XML configuration files. The flixel-ui library gives you access to a lot of different control types, such as 9-sliced images, the check/toggle buttons, text input, tabs, and drop-down menus. You can even localize UI text into different languages by using the firetongue library of Haxe. Visual effects Another add-on is the effects library. It allows you to warp and distort sprites by using the FlxGlitchSprite and FlxWaveSprite classes. You can also add trails to objects by using the FlxTrail class. Aside from the add-on library, HaxeFlixel also has built-in support for 2D particle effects, camera effects such as screen flashes and fades, and screen shake for an added impact. Summary In this article, we discussed several features of HaxeFlixel. This includes the core features of display, audio, and input. We also covered the additional features of multiplatform support, advanced user interface support, and visual effects. Resources for Article: Further resources on this subject: haXe 2: The Dynamic Type and Properties [article] Being Cross-platform with haXe [article] haXe 2: Using Templates [article]
Read more
  • 0
  • 0
  • 23481
Modal Close icon
Modal Close icon