Use nvidia-docker to create awesome Deep Learning Environments for R (or Python) PT I

How long does it take you to install your complete GPU-enabled deep learning environment including RStudio or jupyter and all your packages? And do you have to do that on multiple systems? In this blog post series I’m going to show you how and why I manage my data science environment with GPU enabled docker containers. In this first post you will read about:

  • Why I completely switched to containers for my data science workflow
  • What docker is and how it compares to virtual machines (skip that if you already know!)
  • How I build my data science images
  • How to build a container with r-base, Keras and TensorFlow with GPU support

The status quo

How are you managing your data science stack? I was never really satisfied in how I did it. Installing the whole stack including all the packages I use, GPU-Support, Keras and TensorFlow for R and the underlying Python stuff on different machines is a tedious and cumbersome process. You end up with a pretty fragile toolchain and I’m the kind of guy that tends to fiddle around and break things. And there are more downsides to that. You don’t have the same environment on different machines for example. I’m doing data science on at least three different machines: My laptop, my workstation with a GTX 1080 Ti and on AWS instances. A decoupling of my dev-environment and the host OS was long time overdue. I always knew, that VM’s are not the way to go, because they create way to much overhead and you have to allocate all the resources you want to use in advance.

Docker is perfect for this purpose. It gives you an isolated development environment which shields you from messing things up. When I saw the NVIDIA GPU Cloud (NGC) I knew, that this would solve my problems. NGC is not a cloud service but a container registry where you can download pre-build and GPU enabled docker images that are optimized for different workflows. There are images for TensorFlow, pytorch, caffe and other frameworks. The good thing is: You don’t have to care about any driver, framework or package installation. You can launch python, import TensorFlow and torture your GPU right away. Unfortunately the images contain proprietary software from NVIDIA to optimize computation. You can use it for free for all kinds of purposes including commercial stuff, but you are not allowed to redistribute their images. That’s probably the reason why you will not find them in in projects like Rocker. What you can of course do is publishing Dockerfiles that use those images as base images and that’s what I’m going to do in this blog post.

Why Docker?

If you already know what Docker is and how it compares to Virtual Machines you can just skip this section. I’ll just give you a short overview, there are plenty of good tutorials out there to learn how Docker works. By the way: I’m far from being an expert for Docker, but you really don’t have to be one to do stuff like this. Have a look at the picture below. On the left side is shown how VM’s are working: Each instance emulates a complete Operating system. For me that was never really an option for data science environments. It’s creating some overhead but most importantly you have to allocate the resources in advance and they are not shared with your host system. For a setup where you use the host as your all-purpose system and the VM for data science on the same machine that’s not practical.

Isolation with containers is much better for this scenario: It’s basically just an isolated file system. It’s using the kernel of your host system and that’s of course adding very little overhead. Unfortunately this is not true for Docker on Windows or Mac, because it’s using a Linux virtual machine underneath. I always tinkered with completely switching to Linux even on my Laptop and because of this I did it. But even if you want to stick with Windows or Mac: You are probably using a workstation or cloud instance with Linux. The really interesting part is shown on the right side of the image: Containers with the nvidia-docker runtime. Those containers can use the GPU of the host system. You just need a CUDA enabled GPU and the drivers on the host system and nothing more.

Now a few words on how docker works and the terms that are associated with it. The two most important concepts in Docker are images and containers. The image contains the blueprint to create a container. It’s build in layers and it can contain just the fundamental basics of an operating system or a more complex stack of software. A container is an instance of a docker image. It feels exactly the same as a virtual machine except that you have access to the resources of your host system. So, when you start a docker container from an image (and tell it to be interactive) you end up in a shell environment just like you would login to a VM. Also a container starts up very fast. It’s almost not recognizable whereas in a VM you have to start up the OS.

To build a docker image you have to write what’s called a Dockerfile. This can be done in two fashions: Using a parent image or starting from scratch, which you only need, when you want to create a new base image. In most cases you will use a base image as parent and build your software stack on top of that. If you want to build your image on let’s say Ubuntu 16.04 the first line of your Dockerfile would be:  FROM ubuntu:16.04. The most common way of getting images is pulling them from docker hub, which is a platform to share images. Docker does that automatically or you can pull an image manually with docker pull ImageName. In the case of Ubuntu and other commonly used images there are officially maintained images from the docker staff. As you will see in the next section you can also pull images from other repositories than docker hub.

Get Docker and the TensorFlow container

Docker and the nvidia runtime are really easy to install.  The following commands install the free docker community edition with an installation script from get.docker.com. This script can install docker on all the common Linux distributions (have a look at get.docker.com for details):

To install the nvidia-docker runtime you must add their package repositories and install and reload the docker daemon. I’m using Ubuntu 16.04 as host system, but this should work on pretty much any debian based distribution using apt.

To check if everything worked out you can load a cuda image and execute nvidia-smi:

This command is downloading the cuda image from docker hub, firing up a container based on this image, executing the command nvidia-smi inside the container and then immediately leaving the container and deleting it. You should see something like this:

This means that my GTX 1080Ti is available inside the container! This cuda image is one of the images NVIDIA is hosting on docker hub. For the optimized deep learning containers you have to register for the NVIDIA GPU Cloud (NGC) which is not a cloud service provider but a container registry similar to docker hub. It’s free and you can use the containers for your own or commercial purposes, but you are not allowed to redistribute them. Once you are signed up select configuration in the menu on the left and generate an API-Key. With this key you can register your docker installation for the NVIDIA registry. You should save the key somewhere safe. Use the command docker login nvcr.io to register. As username you have to use $oauthtoken (with the $ sign!) and the API-Key as password. Now if you use the following command docker should download the optimized TensorFlow container:

How I build

I’m creating the environment I use in different stages. In the first image I’m just installing R-base, Keras for Python and R, TensorFlow for R and their dependencies. In the next image I’m installing RStudio-Server and jupyterlab on top of that. In the third image I’m customizing RStudio for my needs and install all the packages I want to use.

The last step is for installing further packages. If I recognize that I need an additional package during a working session I can just install it like I would naturally do that inside RStudio. After I finished I’m adding this package to the fourth image. If I would use the third one I would have to reinstall all the packages every time and that takes some time. With docker you can also commit changes to a container. That’s also a way to permanently install new packages. I decided not to do that to keep track of what I’m adding to my images. There is another benefit of creating those containers in steps: You can use the images for different use cases. For example, you can use your RStudio image to create a neural net and save it. Then you can take this model load it into the base container and make a python-based flask microservice to deploy it. Or you want to make a shiny app that uses image recognition. You can use the r-base container, add shiny and deploy it. Or you want to provide differently customized RStudio-Versions for your colleagues.

Install R-base and Keras

As we already pulled the TensorFLow container from NVIDIA we can now start to build the r-base image. Building a docker image is essentially just writing a shell script that installs all the things you want in your image. Like I already pointed out it’s written down in a textfile with the name Dockerfile. When you run the command  docker build -t YourNameTag .  in the folder of your Dockerfile docker is starting a container from a base image that you defined with FROM and runs the stuff you’ve written in your Dockerfile inside this container. What docker executes inside this container is defined with RUN. Have a look at the following Dockerfile:

If I run docker build on this file docker does the following: It’s starting a container based on our downloaded TensorFlow container, adding some environment variables ( ARG and  ENV) and creating a group and a user. Note that  ARG defines variables that are only available during the build process and that you can pass a value for those variables with the  docker build  command. So when you start the command with docker build --build-arg USER=Kai the default  USER="docker"  would be overwritten during the build process. Variables you define with  ENV persist and are also available in containers you start from a created image. In the next step we install packages that are needed with apt:

The next thing is installing R. I’m using the binaries CRAN is providing for Ubuntu. Normally CRAN has the latest releases very fast, but at the moment (May 19th) R 3.5 is not available via CRAN (read here why). I will wait for 3.5 to appear on CRAN. If you desperately want to  have R 3.5 you can install it like in this Dockerfile and install packages from source. The installation shown in the next step is the same as in the r-base Dockerfile from the Rocker project. It’s installing littler, which is a handy CLI interface for R, r-base, r-base-dev and r-recommended for the R version specified in the R_BASE_VERSION environment variable. Also it’s creating links for littler in  /usr/local/bin to make it available in the command line.

The TensorFlow and Keras packages for R are connecting to python via the reticulate package. That means we need both packages installed for R and python. Both R packages are installed via the github installation routine provided by littler. Since we’re using the NVIDIA TensorFlow image we only have to care about Keras for python (TensorFlow is already installed). If you ever used Keras for R before you probably noticed that you have to call the function install_keras() to install the python backend. In our case this is already done. The only additional thing is that the function creates the virtual environment r-tensorflow. It’s good practice to use virtual environments in python although this would not be necessary in a docker container because it’s kind of double isolated.

Okay now we have a base image for deep learning with R! You can find the complete Dockerfile here. In the next part I’m going to create the images with RStudio and the customization.

Finding conjunctions in groups of interest on Facebook

I recently wanted to mine data from Facebook to find out how many people are liking in different groups of interest on Facebook. Let’s say you want to know how many people give likes to different political parties or different companies. The following blog post shows how to use the Rfacebook package, which provides an interface to the Facebook API, to find those conjunctions. I never encountered a limit for requests to the API, my biggest run with the script were about 12 hours. I mined 10 million likes from 73.000 posts in this time. For the reader interested in german politics: I used this script to find conjunctions between the political parties of the current parliament (+ a party called AfD) and right-wing extremists. You can find the (german) blog post here.

Get the pages

To get access to the API you need to register to the Facebook developer program and create a dummy application. Here you can find a very good tutorial on how to get R connected to Facebook including a workaround for recent changes Facebook made to the API. In this tutorial you are going to generate a token for authentication (fb_token). I would recommend to save this token so you can use it just by loading the file. I saved the token with save(fb_token, file = 'home/MyFolder'). Ok, so first let’s load the packages we need for the analysis and define our working directory.

For each group of interest I created a file in a folder which contains the URL’s for one group line wise. Make sure to use the top level domain of the sites which is usually: https://www.facebook.com/SiteName. In my project I wanted to check political parties in Germany and I decided to use the sites from all party associations of our federal states and the capitals of those states. That’s adding up to 32 sites for each party. In the next step we are defining this folder and load the above mentioned authentication token. Additionally we are defining the time span from which we want to collect the likes given to the posts of each page.

The Last step before we connect to facebook is to read the names of the sites from the URL’s in our files. In the next code chunk we are going through each file and each URL in this file and extract the name of the site. It also handles the encoding of german mutated vowels. Facebook adds an ID to names with those vowels to avoid them in the URL.

Get the post IDs and likes

So let’s start mining some data from Facebook! The next code chunk goes through each group and each site within this group and extracts all the posts of this site in the given time span. As you can see I limited the maximum number of posts in the getPage call to n = 1000. I’m also dumping the console output, because I wanted to have a simple status print out for each site. Since at this point I just want to look up who liked the posts I’m only extracting the IDs of the post. The rest of the post is dumped after that.

PagePosts contains the IDs now in the following form: Groups –> Sites –> PostIDs. I only wanted to find links between the complete groups, so I collapsed this list to Groups –> PostIDs with the following line:

With the IDs we can extract all the likes given to each post. For each like I’m going to extract the unique user ID of the person who liked the post. Again I’m dumping the console output and generate a custom message for each post.

For each group of sites PostLikes now contains all the ID’S of the users that liked a post in our previously defined time span. Currently those ID’s are stored in lists for each post. So we again have to collapse those lists to get all the ID’s for each group in one list per group. I’m also going to delete duplicate ID’s that occur in one group, because I want to find out if someone liked posts in more than one group and not how often. The remaining code in the next chunk calculates some aggregated values like the overall number of acquired posts.

Find the conjunctions

The last thing to do is to find the intersection between the groups. Who liked in more than one of the groups? To find that out I simply concatenate two groups and count duplicated ID’s. The result is a diagonal square matrix with the number of intersecting ID’s.

Plot the results

A very nice way to show the intersections between the groups are chord diagrams. The package chorddiag provides interactive D3 chord diagrams. The diagram under the next code chunk is the diagram I created for my project.

Please keep in mind that it is very hard to tell if the data gathered for a certain group is representative for this group. It really depends on how you collect the sites to represent the groups. Also there is certainly more stuff one could do with the collected data!

Sag mir, wer dich liked und ich sag dir, wer du bist

Eigentlich hatte ich nur nach einem Beispiel für ein kleines Coding-Project Projekt gesucht. Ich wollte mal schauen was die Facebook Graph-API so hergibt und wie viele Daten man über die Schnittstelle abgreifen kann. Daraus ist geworden, dass ich 10 Mio. Likes von offiziellen Beiträgen politischer Parteien und  rechtsradikalen Facebookseiten gemined habe. Als ich mir die Überschneidungen angeschaut habe, war mir klar, dass ich dem Ergebnis einen eigenen Blog-Post widme.

Die Situation

Die Welt hat sich verändert. Es ist etwas passiert, das viele von uns nicht für möglich gehalten haben: Die Welle des konservativen, nationalen und rechten Populismus ist auf dem Vormarsch. Plötzlich hört man die Menschen sagen, dass Angela Merkel für ihre Politik erhängt gehört und das man die Flüchtlingswelle dadurch erhöht, dass man in Seenot geratene Menschen rettet. Nicht einfach irgendwo, sondern in deinem unmittelbaren Umfeld.

Rechtspopulistische Meinungsbilder, mit denen man sich noch vor ein paar Jahren ins Abseits gestellt hätte, prasseln heute wie selbstverständlich auf uns ein. Wie oft habe ich schon Menschen sagen gehört: “Ich bin kein Rassist, aber …”. Das Bedürfnis der Menschen, unfassbar komplexe Zusammenhänge in einfache kleine Schächtelchen zu verpacken, in Kombination mit Hetzern, die sich genau das zunutze machen, resultiert in einer brandgefährlichen Mischung. Dabei geht es Deutschland doch gut! Die Wirtschaft boomt, die Arbeitslosenzahlen sind rekordverdächtig niedrig. Natürlich gibt es auch Missstände, an denen gearbeitet werden muss. Die gab es aber immer und die wird es auch immer geben. Was passiert, wenn wir wirklich mal vor Problemen stehen?

Das Schüren von Ängsten ist der Motor rechtspopulistischer Meinungsmache. Und genau das ist heute durch die Verbindung der Menschen durch soziale Netzwerke unglaublich einfach geworden. Dabei ist es vollkommen egal, ob eine Meldung stimmt oder nicht. Die persönliche Echokammer bestätigt deine Wahrnehmung und lässt keinen Raum für Zweifel oder kritisches Hinterfragen. Die Geschichte hat uns doch eindrücklich gezeigt, dass sich auch viele Menschen gemeinsam irren können. Populisten teilen erfundene Nachrichten, erfinden sie selbst, dramatisieren und überspitzen alles, was ihnen hilft, die Menschen aufzuhetzen. Die AfD hat immer wieder gezeigt, dass sie kein Problem hat, sich solcher Mittel zu bedienen. Sie haben eine perfide Systematik der Angstkommunikation entwickelt, bei der im schnellen Wechsel Ängste geschürt werden, um ihnen dann mit einfachen Lösungen entgegenzutreten.

Data Science

Ich beschäftige mich beruflich mit dem Fachgebiet Data Science, bei dem es darum geht, aus Daten Wissen zu extrahieren. Ironischerweise kommen genau aus diesem Fachgebiet auch die Algorithmen, die für Filterblasen und Echokammern in sozialen Netzwerken verantwortlich sind. Vor einiger Zeit hat mich der Hasskommentar eines Facebook Users dazu gebracht tief in seine ultra rechte Filterblase abzutauchen. Ich habe einen fürchterlichen Sumpf aus Propaganda und Hetze vorgefunden. Und eine interessante Tatsache: Immer wieder wurden mir auch Seiten der AfD vorgeschlagen.

Diese Vorschläge kommen zustande, wenn sich die Gruppe der likenden Menschen überschneidet. Mich hat brennend interessiert, wie groß diese Überschneidung ist. Zwar bestimmt die AfD natürlich nicht selbst, wer ihre Beiträge liked, aber eine große Überschneidung zwischen rechtsradikalen Seiten und offiziellen Seiten der AfD offenbart meiner Einschätzung zumindest eine inhaltliche Nähe.

Facebook bietet mit seiner sogenannten Graph API eine Schnittstelle an, mit der auf alle öffentlichen Daten zugegriffen werden kann. Ich habe also ein kleines Programm geschrieben, das sich durch Gruppen von Facebook-Seiten hangelt und dabei von allen Posts dieser Seiten extrahiert, wer sie geliked hat. Jeder von uns hat bei Facebook eine eindeutige ID, sodass einwandfrei feststellbar ist, auf welcher der gescannten Seiten jemand einen Beitrag geliked hat. Den wirklich nicht komplizierten Code des Programmes habe ich für jeden, den es interessiert, hier veröffentlicht. Die Süddeutsche Zeitung hat in einem ihrer Artikel untersucht, wie die AfD in der politischen Welt eingebettet ist. Dazu wurde eine sehr ähnliche Untersuchung wie diese hier mit politisch aktiven Personen durchgeführt, um zu zeigen, dass die AfD im parteipolitischen Spektrum isoliert dasteht. Mich hat eher die breite Masse derjenigen interessiert, die den Inhalten der AfD zustimmt, und wie stark sie wirklich mit der rechtsradikalen Szene zusammenhängt.

Die Analyse

Die Frage war also klar: Wie groß ist die Überschneidung derjenigen, die sowohl rechtsradikale Inhalte als auch die AfD liken? Als Kontrollgruppe habe ich alle im heutigen Bundestag vertretenen Parteien hinzugenommen. Untersucht wurden pro Partei die Seiten aller Landesverbände und der Landeshauptstädte und die Seite der jeweiligen Bundespartei. Dazu etwa 120 Seiten aus der rechten Szene. Hier finden sich z.B. Seiten der NPD und der Identitären. Schaut euch die Seiten hier an, ich bin mir sicher, dass die große Mehrheit nichts mit dieser Gesinnung zu tun haben will. Nach etwa 15 Stunden hat das Programm von all diesen Seiten jeden Like aller Posts eines Jahres ermittelt. Insgesamt waren das knapp 73.000 Beiträge und etwa 10.000.000 Likes von ca. 1.000.000 unterschiedlichen Facebook Usern.

Die folgende Grafik zeigt die Anzahl der geposteten Beiträge aus dem Zeitraum von Mai 2016 bis Mai 2017. Die AfD hat in diesem Zeitraum über 13.000 Beiträge gepostet, während z.B. die Grünen in diesem Zeitraum nur knapp über 4.500 Beiträge gepostet haben. Auch bei der Anzahl der Likes, die ein Beitrag bekommt, ist die AfD weit vorne. So bekommt ein Beitrag der AfD im Durchschnitt fast die siebenfache Menge der Likes der Union. Die Größe der Kreise gibt an, von wie vielen Menschen diese Likes gegeben wurden. Aus dem Rahmen fällt hier die Union, deren Likes aus einer deutlich kleineren Gruppe von Menschen kommt als bei den anderen Parteien. Den größten Kreis hat auch hier die AfD. Die AfD und ihre Anhänger sind auf Facebook also um ein vielfaches aktiver als das gesamte restliche Parteienspektrum.

 

Die nächste Grafik visualisiert die Überschneidung zwischen den Parteien und nun auch der Gruppe der rechtsradikalen Seiten. Über den Umfang des Kreises sind alle Personen dargestellt, die erfasst wurden. Hat ein User z.B. Beiträge der Grünen und der SPD geliked, taucht er in der Verbindungslinie zwischen Grünen und SPD auf. Bei der Gruppe der rechtsradikalen Seiten ist eine Aufteilung in Landesverbänden und Hauptstädten nicht möglich, deswegen habe ich einfach alles gesammelt, was ich finden konnte. Nur aus diesem Grund ist der Gesamtanteil der rechtsradikalen im Diagramm so groß. Die Überschneidung von SPD, Union, Grünen und Linken mit den rechtsradikalen Seiten liegt zwischen 1 – 3%, der Anteil der AfD Gruppe liegt bei über 17%. Bei vorherigen Untersuchungen, bei denen ich mich nicht nur auf die offiziellen Landesverbände beschränkt habe, konnte ich Anteile über 40% ermitteln.

Auch wenn eine sehr große Anzahl von Likes und Personen erfasst wurde, hat diese Analyse keinen repräsentativen Anspruch. Auch ist fraglich, wie gut die ermittelten Daten die Parteibasis widerspiegeln. Eines ist jedoch klar: Ein so großer Unterschied und eine so große Zahl aus der AfD-Gruppe, die auch rechtsradikale Inhalte liken (ca. 120.000 Personen), ist enorm. Alle von euch, die die AfD wählen wollen, um zu protestieren, um gegen das Establishment zu sein oder weil sie die derzeitige Politik nicht gut finden, sollten sich überlegen, ob sie die AfD wirklich zusammen mit rechtsradikalen in unsere Parlamente bringen wollen.