<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.7.3">Jekyll</generator><link href="https://evgeniymamchenko.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://evgeniymamchenko.com/" rel="alternate" type="text/html" /><updated>2018-07-15T12:08:13+00:00</updated><id>https://evgeniymamchenko.com/</id><title type="html">Evgeniy Mamchenko</title><subtitle>Deep Learning Engineer</subtitle><entry><title type="html">Dual Neural Style Transfer with Docker Compose</title><link href="https://evgeniymamchenko.com/dual-neural-style-transfer-with-docker-compose/" rel="alternate" type="text/html" title="Dual Neural Style Transfer with Docker Compose" /><published>2018-05-30T00:00:00+00:00</published><updated>2018-05-30T00:00:00+00:00</updated><id>https://evgeniymamchenko.com/dual-neural-style-transfer-with-docker-compose</id><content type="html" xml:base="https://evgeniymamchenko.com/dual-neural-style-transfer-with-docker-compose/">&lt;p&gt;&lt;em&gt;How to extend NST with a second style and what are the challenges of deploying it as a single unit using TensorFlow, Flask, ASP.NET Core, Angular and Docker Compose.&lt;/em&gt;&lt;/p&gt;

&lt;p style=&quot;text-align: right; font-style: italic&quot;&gt;Programming is easy like riding a bike. Except the bike is on fire and you're on fire and everything is on fire and you're actually in hell.&lt;/p&gt;

&lt;p style=&quot;text-align: right; font-style: italic&quot;&gt;&lt;a href=&quot;https://twitter.com/matixmatix/status/537607604743438337?lang=en&quot;&gt;Matic Jurglič&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img id=&quot;coding-horror-on-fire&quot; src=&quot;../images/coding-horror-on-fire.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The picture is generated by the application under discussion, its content is taken from &lt;a href=&quot;https://blog.codinghorror.com/on-the-meaning-of-coding-horror/&quot;&gt;Jeff Atwood&lt;/a&gt; who took it from Steve McConnell’s book, Code Complete.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A live demo is available at &lt;a href=&quot;http://nst-online.evgeniymamchenko.com&quot;&gt;http://nst-online.evgeniymamchenko.com&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;The sources are available at &lt;a href=&quot;https://gitlab.com/OutSorcerer/NeuralStyleTransferWeb&quot;&gt;https://gitlab.com/OutSorcerer/NeuralStyleTransferWeb&lt;/a&gt;. There you will also find the instructions on how to launch the application locally with Docker Compose.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#how-to-use-the-application&quot; id=&quot;markdown-toc-how-to-use-the-application&quot;&gt;How to use the application&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#what-is-neural-style-transfer&quot; id=&quot;markdown-toc-what-is-neural-style-transfer&quot;&gt;What is neural style transfer?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#how-i-extended-the-original-neural-style-transfer-algorithm&quot; id=&quot;markdown-toc-how-i-extended-the-original-neural-style-transfer-algorithm&quot;&gt;How I extended the original neural style transfer algorithm&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#architecture&quot; id=&quot;markdown-toc-architecture&quot;&gt;Architecture&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#angular&quot; id=&quot;markdown-toc-angular&quot;&gt;Angular&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#aspnet-core&quot; id=&quot;markdown-toc-aspnet-core&quot;&gt;ASP.NET Core&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#flask&quot; id=&quot;markdown-toc-flask&quot;&gt;Flask&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#tensorflow&quot; id=&quot;markdown-toc-tensorflow&quot;&gt;TensorFlow&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#docker-compose&quot; id=&quot;markdown-toc-docker-compose&quot;&gt;Docker Compose&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#challenges&quot; id=&quot;markdown-toc-challenges&quot;&gt;Challenges&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#installing-tensorflow-with-gpu-support-on-windows&quot; id=&quot;markdown-toc-installing-tensorflow-with-gpu-support-on-windows&quot;&gt;Installing TensorFlow with GPU support on Windows&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#the-communication-between-net-core-and-python&quot; id=&quot;markdown-toc-the-communication-between-net-core-and-python&quot;&gt;The communication between .NET Core and Python&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#the-usage-of-the-server-side-rendering-and-angular-from-net&quot; id=&quot;markdown-toc-the-usage-of-the-server-side-rendering-and-angular-from-net&quot;&gt;The usage of the server-side rendering and Angular from .NET&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#leaking-tensorflow-graph-nodes-and-therefore-memory&quot; id=&quot;markdown-toc-leaking-tensorflow-graph-nodes-and-therefore-memory&quot;&gt;Leaking TensorFlow graph nodes (and therefore memory)&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#broken-switchmap&quot; id=&quot;markdown-toc-broken-switchmap&quot;&gt;Broken switchMap&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#long-response-times&quot; id=&quot;markdown-toc-long-response-times&quot;&gt;Long response times&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#possible-improvements-and-further-reading&quot; id=&quot;markdown-toc-possible-improvements-and-further-reading&quot;&gt;Possible improvements and further reading&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#feed-forward-neural-style-transfer&quot; id=&quot;markdown-toc-feed-forward-neural-style-transfer&quot;&gt;Feed-forward neural style transfer&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#an-arbitrary-image-size-and-proportions&quot; id=&quot;markdown-toc-an-arbitrary-image-size-and-proportions&quot;&gt;An arbitrary image size and proportions&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#processing-multiple-user-requests-simultaneously&quot; id=&quot;markdown-toc-processing-multiple-user-requests-simultaneously&quot;&gt;Processing multiple user requests simultaneously&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#tensorflow-serving&quot; id=&quot;markdown-toc-tensorflow-serving&quot;&gt;TensorFlow Serving&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#credits&quot; id=&quot;markdown-toc-credits&quot;&gt;Credits&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;how-to-use-the-application&quot;&gt;How to use the application&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Click on the “Content Image” button or the placeholder below and upload a content image.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Pick a style image.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Optionally pick a second style image.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Optionally change the parameters.&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;Iterations. The number of optimization steps taken. The higher the number, the lower the cost and, generally, the more beautiful the result.&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Content cost weight. The multiplier of the content cost term in the total cost. The higher the number, the more similar to the content image the result is.&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Style cost weight. The multiplier of the style cost term in the total cost. The higher the number, the more similar to the style image the result is.&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Click “Launch” button. The transfer is starting, the resulting image and other transfer details are shown on the screen.&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;../images/nst-ui.png&quot; style=&quot;height: 50%; width: 50%&quot;&gt;&lt;img id=&quot;dual-nst-image&quot; src=&quot;../images/nst-ui.png&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;&lt;em&gt;This is how the UI looks in the middle of a style transfer when &lt;strong&gt;two&lt;/strong&gt; styles (a starry sky and neon lights) are simultaneously applied to a single content image.&lt;/em&gt;&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Please note that &lt;a href=&quot;http://nst-online.evgeniymamchenko.com&quot;&gt;http://nst-online.evgeniymamchenko.com&lt;/a&gt; works just on 2-cores machine, where 100 iterations of transfer take about 30 minutes. My mobile GPU Nvidia GeForce GTX 1050 works about &lt;em&gt;100 times&lt;/em&gt; faster. Unfortunately, a GPU in the cloud is too expensive right now (one of the cheapest GPU instances with Nvidia TESLA K80 Module costs &lt;a href=&quot;https://cloud.google.com/products/calculator/#id=d189e6b1-bd96-4ce5-ae7d-7e89b7de42f3&quot;&gt;about $270 per month&lt;/a&gt;).&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;If your job status is “queued”, that means that the back-end is busy at the moment, but it will start processing your input as soon as all previous jobs are done.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Click “Stop” button to abort the transfer.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;what-is-neural-style-transfer&quot;&gt;What is neural style transfer?&lt;/h2&gt;

&lt;p&gt;Intuitively it can be defined as generating a picture which content is similar to one input image and which style is similar to another input image.&lt;/p&gt;

&lt;p&gt;In a more detailed way, it is a result of iterative optimization of a specific cost function defined on a resulting image. On each step of the optimization, we compute the gradient of that cost function with respect to each pixel of resulting image and slightly change the resulting image in the direction opposite to the gradient as we always do in the &lt;a href=&quot;https://en.wikipedia.org/wiki/Gradient_descent&quot;&gt;Gradient descent algorithm&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The interesting fact here is that, typically, when training convolutional neural networks, images are fixed and weights of the network are the subject of optimization. On the contrary, in NST algorithm the weights of CNN are fixed, while the input image is being optimized. In &lt;a href=&quot;https://arxiv.org/abs/1508.06576&quot;&gt;the original paper&lt;/a&gt; the CNN with &lt;a href=&quot;https://www.quora.com/What-is-the-VGG-neural-network&quot;&gt;VGG architecture&lt;/a&gt; pre-trained on &lt;a href=&quot;https://en.wikipedia.org/wiki/ImageNet&quot;&gt;ImageNet dataset&lt;/a&gt;. This application also loads weights of pre-trained VGG model. What is interesting is &lt;a href=&quot;https://www.reddit.com/r/MachineLearning/comments/7rrrk3/d_eat_your_vggtables_or_why_does_neural_style/&quot;&gt;there could be something special in VGG architecture that makes it especially good for neural style transfer&lt;/a&gt; although some people achieved good results with other architectures too.&lt;/p&gt;

&lt;p&gt;The mentioned cost function is the sum of two terms.&lt;/p&gt;

&lt;p&gt;The first one is the content cost multiplied by its weight (a parameter that can be configured in UI as mentioned above). The content cost is the squared Euclidean distance between (the squared L2-norm of the difference of) the values of an intermediate convolution layer on the content image and the resulting image normalized by the input size.&lt;/p&gt;

&lt;p&gt;The second one is thereafter the style cost multiplied by the style cost weight parameter.&lt;/p&gt;

&lt;p&gt;Unlike the content cost, the style cost is computed on multiple layers, the style costs for each level are multiplied by their weights (which are also model parameters) and summed up to give the total style cost. In the context of this application, there are five such layers, each weight is equal to 0.2 and cannot be changed from the UI yet. The shallower layers are responsible for lower-level features like those  that detect horizontal lines, vertical lines, diagonal lines and other simple geometrical shapes, while deeper layers are responsible for higher-level features like those that detect parts of objects or entire objects like eyes, flowers, cars, cats and dogs, although sometimes it is pretty hard to figure out what a particular feature detects.&lt;/p&gt;

&lt;p&gt;For a single layer, the style cost is the squared Euclidean distance between &lt;a href=&quot;https://en.wikipedia.org/wiki/Gramian_matrix&quot;&gt;Gram matrices&lt;/a&gt; of that layer activations (with &lt;code class=&quot;highlighter-rouge&quot;&gt;(number_of_channels, height*width)&lt;/code&gt; shape) for the resulting image and the style image.&lt;/p&gt;

&lt;p&gt;Gram matrix is approximately proportional to the covariance matrix (in case values are centered). Its diagonal elements are just squared L2 norms of the corresponding channel activations reshaped as one-dimensional vectors.&lt;/p&gt;

&lt;p&gt;For a detailed explanation of neural style transfer you can &lt;a href=&quot;https://arxiv.org/abs/1508.06576&quot;&gt;see the original paper&lt;/a&gt;, read &lt;a href=&quot;https://gitlab.com/OutSorcerer/NeuralStyleTransferWeb&quot;&gt;my source code&lt;/a&gt; or &lt;a href=&quot;https://www.coursera.org/learn/convolutional-neural-networks/home/week/4&quot;&gt;watch the video and complete the corresponding programming assignment&lt;/a&gt; from the Coursera CNN course, on which my code is based (all the videos are available for free &lt;a href=&quot;https://www.youtube.com/watch?v=Re2C9INXCNc&amp;amp;index=38&amp;amp;list=PLBAGcD3siRDjBU8sKRk0zX9pMz9qeVxud&quot;&gt;on YouTube&lt;/a&gt; but Coursera subscription is required to complete the programming assignment).&lt;/p&gt;

&lt;h2 id=&quot;how-i-extended-the-original-neural-style-transfer-algorithm&quot;&gt;How I extended the original neural style transfer algorithm&lt;/h2&gt;

&lt;p&gt;To make the application more interesting, I decided to extend the original algorithm. What if we try to apply two styles simultaneously? Surprisingly, it worked quite well: styles are not overlapping but rather being applied to different parts of the image, depending on which part is more suitable for each style. Let us call it “dual NST”.&lt;/p&gt;

&lt;p&gt;See &lt;a href=&quot;#dual-nst-image&quot;&gt;the example illustration above&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To implement a second style an extra style term is added to the total cost.&lt;/p&gt;

&lt;p&gt;Feel free to play with it yourself, I would appreciate if you would share your results in comments.&lt;/p&gt;

&lt;h2 id=&quot;architecture&quot;&gt;Architecture&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;../images/nst_architecture.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Let us talk in more detail about the building blocks of the application.&lt;/p&gt;

&lt;h3 id=&quot;angular&quot;&gt;Angular&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://angular.io/&quot;&gt;Angular 5&lt;/a&gt; component is the face of the application. It is responsible for validating a user’s input and sending it to the server.&lt;/p&gt;

&lt;p&gt;When a transfer job is in progress, it does polling with a one-second interval to show results in real time. &lt;a href=&quot;https://angular.io/guide/rx-library&quot;&gt;rxjs&lt;/a&gt; is a nice tool for filtering, mapping and combining streams of asynchronous events where tasks like HTTP polling are solved in a powerful and concise way. For example, if some request was not able to complete in one second and the next request already started, it would make no sense to wait for both of them and waste connections, as the data from the previous request would be already obsolete. &lt;a href=&quot;https://www.learnrxjs.io/operators/transformation/switchmap.html&quot;&gt;switchMap&lt;/a&gt; operator nicely solves this problem:&lt;/p&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;polling&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Observable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;timer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;switchMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;baseUrl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`api/neural-style-transfer/jobs/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;jobId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Angular 5 is my framework of choice because of my love to statically typed languages like C# or TypeScript and also because of &lt;a href=&quot;#the-usage-of-the-server-side-rendering-and-angular-from-net&quot;&gt;the nice SPA template&lt;/a&gt; included with .NET Core CLI. That template is even capable of the &lt;a href=&quot;https://angular.io/guide/universal&quot;&gt;server-side rendering&lt;/a&gt;, which is a nice tool for SEO and improving the user experience, as it significantly decreases the initial page load time.&lt;/p&gt;

&lt;p&gt;The template (and this application) uses &lt;a href=&quot;https://getbootstrap.com/&quot;&gt;Bootstrap&lt;/a&gt; library which looks a bit old-fashioned in 2018, I hope the next template from Microsoft will use &lt;a href=&quot;https://material.angular.io/&quot;&gt;Angular Material&lt;/a&gt; which is more modern-looking.&lt;/p&gt;

&lt;p&gt;Considering React vs. Angular, I prefer Angular because, among other things, it is a whole framework with “batteries included” experience featuring things like built-in &lt;a href=&quot;https://angular.io/guide/dependency-injection&quot;&gt;Dependency Injection&lt;/a&gt;, which could be very useful for tests. So you have fewer decisions to make in the beginning, while it is still highly extendable (for example, if you would like, you could use Redux-like &lt;a href=&quot;https://github.com/ngrx/platform/tree/master/docs/store&quot;&gt;@ngrx/store&lt;/a&gt;).&lt;/p&gt;

&lt;h3 id=&quot;aspnet-core&quot;&gt;ASP.NET Core&lt;/h3&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/aspnet/Home&quot;&gt;ASP.NET Core&lt;/a&gt; component receives REST requests from the Angular SPA, resizes images to 400x300 and puts NST jobs in a queue. Since TensorFlow uses 100% of CPU power, it is not practical to perform two transfers simultaneously, that is why that queue comes in handy. The ASP.NET Core component is also responsible for responding to the polling from the Angular SPA and doing the polling of Python back-end itself.&lt;/p&gt;

&lt;p&gt;C# is my favorite language so the choice of ASP.NET Core is natural for me. Microsoft is doing a great job of improving the C# language (comparing with Java, C# syntax recently &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-6&quot;&gt;was&lt;/a&gt; &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7&quot;&gt;and&lt;/a&gt; &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7-1&quot;&gt;still&lt;/a&gt; &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7-2&quot;&gt;is&lt;/a&gt; &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7-3&quot;&gt;years&lt;/a&gt; &lt;a href=&quot;http://www.dotnetcurry.com/csharp/1440/csharp-8-new-features&quot;&gt;ahead&lt;/a&gt;) and building great tools for the developer productivity like Visual Studio and VS Code (and, thanks to JetBrains there are also great extensions/alternatives like ReSharper or Rider). I admit that the open-source community around C# is not as productive as Java or Python communities, but considering that the framework itself and many Microsoft libraries became open-source and cross-platform and &lt;a href=&quot;https://dotnetfoundation.org/&quot;&gt;Microsoft supports others in building open-source software based on .NET&lt;/a&gt;, everyone should take a closer look at .NET Core.&lt;/p&gt;

&lt;p&gt;Also, using C# here is a nice example of how two micro-services written in different languages possibly by different teams could easily communicate using REST. Python is very popular for machine learning, but in other spheres, people may use Java, .NET, Node.js, etc. so this scenario is what we would often see in the real world.&lt;/p&gt;

&lt;h3 id=&quot;flask&quot;&gt;Flask&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;http://flask.pocoo.org/&quot;&gt;Flask&lt;/a&gt; is a popular Python framework for building Web APIs including RESTful ones.&lt;/p&gt;

&lt;p&gt;It wraps around the TensorFlow model which is running in a background thread while on another thread it responds to requests to start, stop or query a status of an NST job.&lt;/p&gt;

&lt;p&gt;Even though there are special &lt;code class=&quot;highlighter-rouge&quot;&gt;flask_restful&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;flask_jsonpify&lt;/code&gt; packages creating REST/JSON services seems to be not so smooth with Flask as it is with ASP.NET Core. I believe it is not only my lack of experience with it because some parts of my code are based on high-ranked answers on Stack Overflow and instead of using some built-in function they are suggesting to copy-paste their implementations of it.&lt;/p&gt;

&lt;p&gt;To be more clear here is an example of parsing of an HTTP request body in JSON format:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# This decorator takes the class/named tuple to convert any JSON data in incoming request to. &lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;convert_input_to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;class_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;wrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;decorator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;class_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;decorator&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wrap&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@convert_input_to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BackendJobModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;job&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# ...&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;current_job&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;job&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Looks nice, but what if a decorated function had more arguments? I could rewrite the code, of course, but the point is, there should be a built-in and generic solution out-of-the-box in contrast to making users copy-paste code that performs very basic tasks.&lt;/p&gt;

&lt;p&gt;For comparison the similar place in ASP.NET Core does not use any custom code and looks like this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;[HttpPut(&quot;jobs&quot;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ActionResult&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StartJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FromBody&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JobModel&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I would appreciate if someone would recommend me a nicer replacement of Flask for REST on Python.&lt;/p&gt;

&lt;h3 id=&quot;tensorflow&quot;&gt;TensorFlow&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://www.tensorflow.org/&quot;&gt;TensorFlow&lt;/a&gt; is an open-source software library for numerical computation using data flow graphs. It has a built-in gradient computation, many supported operations from simple matrix addition or multiplication to pre-implemented convolution or sampling layers, optimizers from &lt;code class=&quot;highlighter-rouge&quot;&gt;GradientDescentOptimizer&lt;/code&gt; to &lt;code class=&quot;highlighter-rouge&quot;&gt;AdamOptimizer&lt;/code&gt;, ability to run on GPUs and TPUs and many more which makes it one of the most popular tools for building neural network models.&lt;/p&gt;

&lt;p&gt;The program starts with loading weights of the pre-trained VGG network and building a computational graph. A nice thing is that a single graph and a single TensorFlow session can be used for handling different user inputs which makes initialization time much faster.&lt;/p&gt;

&lt;p&gt;Since initialization and training take significant time, but we want to keep a user up to date by responding to HTTP requests, TensorFlow code works in a separate thread.&lt;/p&gt;

&lt;p&gt;This code is based on an assignment &lt;em&gt;Art Generation with Neural Style Transfer&lt;/em&gt; from Andrew’s Ng &lt;a href=&quot;https://www.coursera.org/learn/convolutional-neural-networks&quot;&gt;course&lt;/a&gt;, which is a part of &lt;a href=&quot;https://www.coursera.org/specializations/deep-learning&quot;&gt;the Deep Learning specialization&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;My changes include the support for a second style image that I described &lt;a href=&quot;#how-the-original-neural-style-transfer-algorithm-was-extended&quot;&gt;above&lt;/a&gt; and various performance improvements that I am going to describe &lt;a href=&quot;#leaking-tensorflow-graph-nodes-and-therefore-memory&quot;&gt;below&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;docker-compose&quot;&gt;Docker Compose&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://www.docker.com/&quot;&gt;Docker&lt;/a&gt; as a containerization software that provides an &lt;em&gt;immutable environment&lt;/em&gt; which helps a lot with making deployment predictable and reducing time expenses for it. That is useful both for Python and .NET Core / Angular parts of the application. They are both wrapped into Docker containers.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.docker.com/compose/overview/&quot;&gt;Docker Compose&lt;/a&gt; is, in turn, a tool to run multi-container applications. One of its abilities is virtual networks where we can put our services so that they are visible to each other, but not to the outside world. In this example, the Python container should not communicate with a user directly so it does not publish any ports to outside and it can only receive requests from the .NET Core container, which, on the contrary, publishes port 80 to receive user requests.&lt;/p&gt;

&lt;p&gt;With Docker and Docker Compose you can launch this application just in minutes without spending much time on environment preparation (which might be a tedious task). You will find the detailed instructions in the README file of &lt;a href=&quot;https://gitlab.com/OutSorcerer/NeuralStyleTransferWeb&quot;&gt;the corresponding GitLab repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;challenges&quot;&gt;Challenges&lt;/h2&gt;

&lt;p&gt;It was fun to work on this project. But, unfortunately sometimes your code fails, it could be not your guilt, but it is your responsibility to make everything work. There was plenty of moments when something was not working and I had no idea why, that is the reason I chose &lt;a href=&quot;#coding-horror-on-fire&quot;&gt;Coding Horror illustration&lt;/a&gt; for this post. But more satisfying it was to finally figure everything out.&lt;/p&gt;

&lt;h3 id=&quot;installing-tensorflow-with-gpu-support-on-windows&quot;&gt;Installing TensorFlow with GPU support on Windows&lt;/h3&gt;

&lt;p&gt;TensorFlow itself installs easily &lt;a href=&quot;https://www.tensorflow.org/install/install_windows#requirements_to_run_tensorflow_with_gpu_support&quot;&gt;by following the official instructions&lt;/a&gt; with just &lt;code class=&quot;highlighter-rouge&quot;&gt;pip install&lt;/code&gt;, but Nvidia could definitely try better with deploying their seriously great software to an end-user.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://developer.nvidia.com/cuda-90-download-archive&quot;&gt;CUDA 9.0&lt;/a&gt; (with &lt;code class=&quot;highlighter-rouge&quot;&gt;tensorflow-gpu&lt;/code&gt; 1.8.0 package you need exactly CUDA 9.0 version, not the most recent one) itself is wrapped in a nice installer, but unfortunately it fails to install Visual Studio integration module and does not even try to install other modules afterwards even though they do not depend on the VS integration.&lt;/p&gt;

&lt;p&gt;Luckily, they have a forum where the user &lt;em&gt;oregonduckman&lt;/em&gt; posted his &lt;a href=&quot;https://devtalk.nvidia.com/default/topic/1033111/cuda-setup-and-installation/cuda-9-1-cannot-install-due-to-failed-visual-studio-integration/post/5259187/#5259187&quot;&gt;workaround&lt;/a&gt;. I was even able to simplify it a bit and also &lt;a href=&quot;https://devtalk.nvidia.com/default/topic/1033111/cuda-setup-and-installation/cuda-9-1-cannot-install-due-to-failed-visual-studio-integration/post/5260927/#5260927&quot;&gt;contributed&lt;/a&gt; to that forum thread. The solution was surprisingly simple: install everything except VS integration, unzip installer file as an archive and manually launch executables in the VS integration folder. Another unobvious step was that you should &lt;strong&gt;not&lt;/strong&gt; install the latest GPU driver, instead, you may need to remove existing Nvidia GPU driver by replacing it with a generic one prior to the CUDA installation. Also, see &lt;a href=&quot;https://docs.nvidia.com/cuda/cuda-installation-guide-microsoft-windows/index.html&quot;&gt;the official installation instructions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;cuDNN installation is even more a shame since here &lt;a href=&quot;https://docs.nvidia.com/deeplearning/sdk/cudnn-install/#installwindows&quot;&gt;you have to copy some files manually&lt;/a&gt; from unzipped “installer” and manually set some environment variables.&lt;/p&gt;

&lt;p&gt;I hope I live to see the day when NVidia finally makes a proper installer or maybe even starts using a Windows package manager like &lt;a href=&quot;https://chocolatey.org/&quot;&gt;Chocolatey&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Docker could help here a lot, but unfortunately using GPU from Docker &lt;a href=&quot;https://github.com/NVIDIA/nvidia-docker/issues/197&quot;&gt;is not possible on Windows right now&lt;/a&gt;. Although I could imagine it working in a bright future, as the answer to the question “Why is it not possible?” looks promising: “No not possible, we would need GPU passthrough (i.e DDA) which is only available in Windows server 2016.” If it worked on Windows Server, it could come to other editions of Windows as well.&lt;/p&gt;

&lt;h3 id=&quot;the-communication-between-net-core-and-python&quot;&gt;The communication between .NET Core and Python&lt;/h3&gt;

&lt;p&gt;My first approach to this was launching a Python process from .NET Core by &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.process.start&quot;&gt;System.Diagnostics.Process.Start&lt;/a&gt;. At first, it looked as a nice and simple while cross-platform way, but it had a number of disadvantages.&lt;/p&gt;

&lt;p&gt;How is .NET Core supposed to pass parameters and input images to Python? How should Python pass resulting images and costs to .NET Core? On Windows and Linux there are various ways of inter-process communication like named pipes or sockets, but they are not cross-platform. So initially I chose a common temporarily folder as a mean of communication. The .NET Core process was just placing input files there and passing transfer parameters like iterations count as command-line arguments to the Python process. The Python process, in turn, was writing result files in that folder and the .NET Core process was subscribing to changes in that folder with &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.io.filesystemwatcher&quot;&gt;FileSystemWatcher&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A big disadvantage was that the 500Mb weights of the pre-trained VGG network were reloading from disk to memory for each transfer job. TensorFlow graph was also rebuilding from scratch each time. All that was resulting in initialization time of about one minute.&lt;/p&gt;

&lt;p&gt;The solution was to the make Python process long-running and communicate with it by REST with help of Flask framework. So the weights are now loaded just once, the graph is built just once and, as a result, the request initialization time on GPU went from one minute to thirty seconds.&lt;/p&gt;

&lt;p&gt;Since the traffic between .NET Core and Python components is quite small (about 300 kB per second) HTTP/JSON is fine for this use-case. In case that would become a bottleneck something like &lt;a href=&quot;https://en.wikipedia.org/wiki/WebSocket&quot;&gt;WebSocket&lt;/a&gt; and a binary serialization protocol like &lt;a href=&quot;https://en.wikipedia.org/wiki/Protocol_Buffers&quot;&gt;Protocol Buffers&lt;/a&gt; could be used. Another alternative is Google’s &lt;a href=&quot;https://github.com/grpc/grpc&quot;&gt;gRPC&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-usage-of-the-server-side-rendering-and-angular-from-net&quot;&gt;The usage of the server-side rendering and Angular from .NET&lt;/h3&gt;

&lt;p&gt;I &lt;a href=&quot;#angular&quot;&gt;mentioned above&lt;/a&gt; that I used a nice Angular 5 template for .NET Core. The issue is that in the current (2.0) version of .NET the CLI for .NET or Visual Studio this template is missing but there is the Angular 4 template instead.&lt;/p&gt;

&lt;p&gt;I foolishly tried to update it manually to Angular 5, that initially worked fine. But as soon as I started to build Docker images, that included building the Angular 5 application in production mode with SSR, it broke. It turned out that there were breaking changes in SSR from Angular 4 to Angular 5.&lt;/p&gt;

&lt;p&gt;I was already choosing between giving up SSR or giving up Angular 5 when I luckily found that &lt;a href=&quot;https://github.com/aspnet/JavaScriptServices/issues/1288#issuecomment-346003334&quot;&gt;Microsoft created the new SPA template&lt;/a&gt; with the support of both Angular 5 and &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/spa/angular?view=aspnetcore-2.1&amp;amp;tabs=visual-studio#server-side-rendering&quot;&gt;SSR&lt;/a&gt; (although SSR is &lt;em&gt;not&lt;/em&gt; turned on by default). The point was that template was still in beta and it had to be installed manually with&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet new &lt;span class=&quot;nt&quot;&gt;--install&lt;/span&gt; Microsoft.DotNet.Web.Spa.ProjectTemplates::2.0.0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and used by&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet new angular
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When .NET Core SDK 2.1 is released, that will not be required anymore.&lt;/p&gt;

&lt;h3 id=&quot;leaking-tensorflow-graph-nodes-and-therefore-memory&quot;&gt;Leaking TensorFlow graph nodes (and therefore memory)&lt;/h3&gt;

&lt;p&gt;The original implementation of NST from &lt;a href=&quot;https://www.coursera.org/specializations/deep-learning&quot;&gt;the Deep Learning specialization on Coursera&lt;/a&gt; worked nicely when it was launched just once or twice from Jupiter notebook. But, as it turned out, it was not production-ready at all.&lt;/p&gt;

&lt;p&gt;The first thing, that caught my eye, was that content cost and style cost nodes of the computation graph in TensorFlow were not reused but created for each input image:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# a_C was a NumPy array (not a Tensor) with an activation of a hidden layer&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    when a network input is set to a content image&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# a_G was also a NumPy array with the same activation &lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    when a resulting image is set as a network input&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# J_content is the content cost tensor (a graph node in a computation graph)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;J_content&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compute_content_cost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a_C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a_G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It was rewritten as:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;a_C_var&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Variable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;zeros&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trainable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dtype&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'float32'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;J_content&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compute_content_cost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a_C_var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a_G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The same was done for the style cost, the content cost weight, the style cost weight and the total cost.&lt;/p&gt;

&lt;p&gt;That allowed to reuse their graph nodes just by assigning new values to the corresponding variables. That was done intuitively in the attempt to cache what can be cached considering that the initialization time at the moment was too long. That ended up to be a step in the right direction, but a serious problem still remained.&lt;/p&gt;

&lt;p&gt;I also realized that a current session must not be created from scratch for each transfer but a single session can be reused.&lt;/p&gt;

&lt;p&gt;Everything seemed to be perfect, I deployed the application to a Google Cloud instance and started to test it more intensively. And then I faced “out of memory” errors. At first, I thought that it is just a peculiarity of the Python/TensorFlow memory management and it can be solved just by increasing the instance memory but that just postponed the error, not fixed it entirely. I looked at the TensorFlow process memory consumption and saw that it was steadily growing.&lt;/p&gt;

&lt;p&gt;Long story short, the reason was &lt;a href=&quot;https://github.com/tensorflow/tensorflow/issues/4151&quot;&gt;leaking TensorFlow computation graph nodes, specifically assign nodes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Unlike an assign operation from C++ or Python, an assign operation in TensorFlow is just another graph node. Using these operations to set variables to new inputs was adding new and new nodes to a graph, which was causing “out of memory” errors.&lt;/p&gt;

&lt;p&gt;More accurately, the assign operation itself is not consuming memory, but &lt;a href=&quot;https://github.com/tensorflow/tensorflow/issues/2311&quot;&gt;it implicitly creates a constant node with an assigning value&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By the way, there is a nice way to validate if you program is free of such kind of bugs, by adding &lt;code class=&quot;highlighter-rouge&quot;&gt;tf.get_default_graph().finalize()&lt;/code&gt; immediately before the training loop. It is not done automatically just because &lt;a href=&quot;https://github.com/tensorflow/tensorflow/issues/2311#issuecomment-219524374&quot;&gt;it would break a huge amount of existing code&lt;/a&gt;. But maybe it would be a good thing…&lt;/p&gt;

&lt;p&gt;So, instead of:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_variable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content_image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;there must be:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;input_placeholder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;placeholder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dtype&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'float32'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shape&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IMAGE_HEIGHT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IMAGE_WIDTH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;COLOR_CHANNELS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; 
&lt;span class=&quot;n&quot;&gt;graph&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'input'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Variable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_placeholder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dtype&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'float32'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;during initialization, and then for each request:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_variable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;initializer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;feed_dict&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_placeholder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content_image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You must also remove:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;global_variables_initializer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Because that would also try to initialize variables like &lt;code class=&quot;highlighter-rouge&quot;&gt;input_variable&lt;/code&gt; without defining the corresponding placeholder values which would cause an error. Each variable must be initialized manually instead.&lt;/p&gt;

&lt;p&gt;You will most likely also have to initialize the variables implicitly created by the optimizer. It can be done like:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# The following line must be executed only once before the computation graph is finalized.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;optimizer_variables_initializer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;variables_initializer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;optimizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# The following line should be executed for each transfer.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;optimizer_variables_initializer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That fix also sped up the application significantly. A second run with a GPU started to initialize just in a few seconds instead of half a minute.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Here is &lt;a href=&quot;https://www.kdnuggets.com/2017/05/how-not-program-tensorflow-graph.html&quot;&gt;a post on KDnuggets with more typical problems in TensorFlow graphs&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;broken-switchmap&quot;&gt;Broken switchMap&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://www.learnrxjs.io/operators/transformation/switchmap.html&quot;&gt;switchMap&lt;/a&gt; is a nice operation except it does not work. When I opened network tab in Chrome debugging tools, I was shocked as I saw that requests were not cancelled when they were taking more than one second, instead they were running indefinitely and, what was even worse, they were piling up and, since Chrome executes just a limited number of requests, that meant that pending time for each new request was growing.&lt;/p&gt;

&lt;p&gt;So why &lt;code class=&quot;highlighter-rouge&quot;&gt;switchMap&lt;/code&gt; may not work? It is obvious, you just need to replace&lt;/p&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Observable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;rxjs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;with&lt;/p&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Observable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;rxjs/Observable&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and it starts to work. In exchange for that you now have to explicitly import every rxjs operator that was previously imported automatically like&lt;/p&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'rxjs/add/operator/switchMap'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Why it helps? I do not know, but the good thing is &lt;a href=&quot;https://github.com/ReactiveX/rxjs/issues/3306#issuecomment-374346659&quot;&gt;they fixed it in 6.0 version&lt;/a&gt;. Thanks to &lt;a href=&quot;https://github.com/Airblader&quot;&gt;Airblader&lt;/a&gt; who created a &lt;a href=&quot;https://github.com/ReactiveX/rxjs/issues/3306&quot;&gt;GitHub issue&lt;/a&gt; where I found this.&lt;/p&gt;

&lt;h3 id=&quot;long-response-times&quot;&gt;Long response times&lt;/h3&gt;

&lt;p&gt;But why HTTP requests where taking so long for server to handle in the first place? Responses were just about 300 kB, so it was not the Internet speed.&lt;/p&gt;

&lt;p&gt;The reason was that the TensorFlow thread was using 100% CPU, so there were not enough resources for the Flask thread and for the ASP.NET Core process.&lt;/p&gt;

&lt;p&gt;Another consequence was &lt;a href=&quot;https://github.com/aspnet/JavaScriptServices/issues/1514&quot;&gt;weird exceptions from ASP.NET Core application&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;System.InvalidOperationException: The SPA default page middleware could not return the default page '/index.html' because it was not found,
and no other middleware handled the request.
Your application is running in Production mode, so make sure it has been published, or that you have built your SPA manually. Alternatively
you may wish to switch to the Development environment.

   at Microsoft.AspNetCore.SpaServices.SpaDefaultPageMiddleware.&amp;lt;&amp;gt;c__DisplayClass0_0.&amp;lt;Attach&amp;gt;b__1(HttpContext context, Func`1 next)
   at Microsoft.AspNetCore.Builder.RouterMiddleware.&amp;lt;Invoke&amp;gt;d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Luckily Docker Compose file format has a solution for that:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;2.3'&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;neural-style-transfer-ui&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;cpu_shares&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;4096&lt;/span&gt;

    &lt;span class=&quot;na&quot;&gt;neural-style-transfer-backend&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;cpus&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;1.9&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;cpu_shares&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;1024&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;cpus&lt;/code&gt; sets &lt;a href=&quot;https://docs.docker.com/config/containers/resource_constraints/#configure-the-default-cfs-scheduler&quot;&gt;the maximum number of cores&lt;/a&gt; that a service can occupy (in that case there was two-core instance).&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;cpu_shares&lt;/code&gt; sets &lt;a href=&quot;https://docs.docker.com/config/containers/resource_constraints/#configure-the-default-cfs-scheduler&quot;&gt;a weight of a service&lt;/a&gt;, which takes effect only during the moments when CPU resources are limited.&lt;/p&gt;

&lt;p&gt;I was confused at first by the fact that &lt;a href=&quot;https://docs.docker.com/compose/compose-file/compose-versioning/#version-3&quot;&gt;those settings were removed in the version 3 of the Compose file format&lt;/a&gt;. The reason is that the version 3 is mainly for running stacks of containers in Docker Swarm, which has its own way of limiting CPU usage, while the version 2 is for good old Docker Compose. And &lt;a href=&quot;https://github.com/docker/compose/issues/4513#issuecomment-377311337&quot;&gt;the version 2.3 is not so old as it was introduced almost at the same time as 3.4&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To make it even easier for the server I started to send the resulting image only when it changes. On a two-core instance it happens once in about 15 seconds, and the image size is about 300 kB, while for the rest of polling responses, that are performed each second, payload size is just a few hundreds of bytes.&lt;/p&gt;

&lt;h2 id=&quot;possible-improvements-and-further-reading&quot;&gt;Possible improvements and further reading&lt;/h2&gt;

&lt;h3 id=&quot;feed-forward-neural-style-transfer&quot;&gt;Feed-forward neural style transfer&lt;/h3&gt;

&lt;p&gt;The Deep Learning field is developing incredibly fast, and the original neural style transfer paper called &lt;a href=&quot;https://arxiv.org/abs/1508.06576&quot;&gt;A Neural Algorithm of Artistic Style
&lt;/a&gt; from September 2, 2015 already became obsolete in terms of the implementation details of the style transfer idea (while the idea itself is still actual, moreover, &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.neuralprisma&quot;&gt;it had a huge impact even outside of the scientific community&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;One major breakthrough was the following paper that introduced a fast feed-forward method of neural style transfer, &lt;a href=&quot;https://arxiv.org/abs/1603.03417&quot;&gt;Texture Networks: Feed-forward Synthesis of Textures and Stylized Images&lt;/a&gt; from March 10, 2016. That method involves just a single forward propagation through a neural network instead of an iterative process and thus it is few orders of magnitude faster. The trade-off is that a network must be trained in advance for each style image and that process is even slower than the original style transfer iterative process. You can &lt;a href=&quot;https://demos.algorithmia.com/deep-style/&quot;&gt;try that algorithm online&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Another paper that proposed a feed-forward method was &lt;a href=&quot;https://arxiv.org/abs/1603.08155&quot;&gt;Perceptual Losses for Real-Time Style Transfer and Super-Resolution&lt;/a&gt; from March 27, 2016. It looks like it is cited more often, but it appeared a bit later.&lt;/p&gt;

&lt;p&gt;The next great discovery was a method of arbitrary style transfer that generalized the previous feed-forward approach to an arbitrary style in &lt;a href=&quot;https://arxiv.org/abs/1703.07255&quot;&gt;ZM-Net: Real-time Zero-shot Image Manipulation Network&lt;/a&gt; from March 21, 2017.&lt;/p&gt;

&lt;p&gt;Other approaches to arbitrary style transfer are &lt;a href=&quot;https://arxiv.org/abs/1705.06830&quot;&gt;Exploring the structure of a real-time, arbitrary neural artistic stylization network&lt;/a&gt; from August 24, 2017 and &lt;a href=&quot;https://arxiv.org/abs/1705.08086&quot;&gt;Universal Style Transfer via Feature Transforms&lt;/a&gt; from November 17, 2017.&lt;/p&gt;

&lt;p&gt;See &lt;a href=&quot;https://medium.com/artists-and-machine-intelligence/neural-artistic-style-transfer-a-comprehensive-look-f54d8649c199&quot;&gt;a Medium post with an overview of the history of NST&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So, the next step for my application could be the replacement of the current iterative implementation with a feed-forward one based on one of the previous papers. What could still be challenging is how to implement it with a second style.&lt;/p&gt;

&lt;h3 id=&quot;an-arbitrary-image-size-and-proportions&quot;&gt;An arbitrary image size and proportions&lt;/h3&gt;

&lt;p&gt;The current implementation like the underlying VGG network can only process images of the fixed size (400x300), so if a chosen image size is different, it is resized by .NET Core application, before it is assigned as an input of a neural network.&lt;/p&gt;

&lt;p&gt;In a &lt;a href=&quot;http://www.fast.ai/2018/04/30/dawnbench-fastai/&quot;&gt;recent post on fast.ai&lt;/a&gt; Jeremy Howard mentioned adaptive pooling layers, which could help to process an image of an arbitrary size (as far as I understand it is based on &lt;a href=&quot;https://arxiv.org/abs/1406.4729&quot;&gt;Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition&lt;/a&gt;). That would be useful for neural style transfer too.&lt;/p&gt;

&lt;h3 id=&quot;processing-multiple-user-requests-simultaneously&quot;&gt;Processing multiple user requests simultaneously&lt;/h3&gt;

&lt;p&gt;The bottleneck of the application is an optimization process in TensorFlow. Although multiple transfers are queued, the slow speed of a transfer on CPU makes it impractical on multi-user scenarios.&lt;/p&gt;

&lt;p&gt;With GPU the performance is much better, but still, currently, only a single user at a time can have his transfer running, while others will wait in a queue. Running two TensorFlow sessions simultaneously &lt;a href=&quot;https://github.com/tensorflow/tensorflow/issues/4196&quot;&gt;is not stable, most likely due to GPU memory allocation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On my 2GB GPU an attempt to run two TensorFlow sessions from two Python processes results in the following error:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;2018-06-03 00:15:43.736350: E T:\src\github\tensorflow\tensorflow\stream_executor\cuda\cuda_dnn.cc:455] could not create cudnn handle: CUDNN_STATUS_ALLOC_FAILED
2018-06-03 00:15:43.741649: E T:\src\github\tensorflow\tensorflow\stream_executor\cuda\cuda_dnn.cc:427] could not destroy cudnn handle: CUDNN_STATUS_BAD_PARAM
2018-06-03 00:15:43.747966: F T:\src\github\tensorflow\tensorflow\core\kernels\conv_ops.cc:713] Check failed: stream-&amp;gt;parent()-&amp;gt;GetConvolveAlgorithms( conv_parameters.ShouldIncludeWinogradNonfusedAlgo&amp;lt;T&amp;gt;(), &amp;amp;algorithms)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That could be solved by using multiple machines with multiple replicas of the Python Docker container. That would also require Docker Swarm, Kubernetes or another orchestrator that runs against a cluster instead of Docker Compose, which runs against a single machine.&lt;/p&gt;

&lt;p&gt;An alternative solution is the usage of &lt;a href=&quot;https://www.tensorflow.org/deploy/distributed&quot;&gt;distributed TensorFlow on a cluster&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;tensorflow-serving&quot;&gt;TensorFlow Serving&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://www.tensorflow.org/serving/serving_basic&quot;&gt;TensorFlow Serving&lt;/a&gt; does not seem to be applicable currently, as it serves an already trained model, but here is a training process. However, with a feed-forward approach, it could replace the Flask part.&lt;/p&gt;

&lt;p&gt;Moreover, &lt;a href=&quot;https://github.com/tensorflow/serving/issues/335#issuecomment-305886225&quot;&gt;it can also serve multiple models on a single GPU simultaneously&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;credits&quot;&gt;Credits&lt;/h2&gt;

&lt;p&gt;Thanks to Andrew Ng and the whole &lt;a href=&quot;https://www.deeplearning.ai/&quot;&gt;deeplearning.ai&lt;/a&gt; and Coursera teams for their great work on the &lt;a href=&quot;https://www.coursera.org/specializations/deep-learning&quot;&gt;Deep Learning specialization&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href=&quot;https://gitlab.com/&quot;&gt;GitLab.com&lt;/a&gt; for generously providing 10Gb repositories for free without limits on individual files sizes (unlike 100Mb limit for a single file size on GitHub).&lt;/p&gt;

&lt;p&gt;Thanks to Google Cloud for their $300 / 12-month &lt;a href=&quot;https://cloud.google.com/free/&quot;&gt;Free Tier&lt;/a&gt; where the application is running now.&lt;/p&gt;

&lt;p&gt;Thanks to my wife for her support and valuable advice.&lt;/p&gt;</content><author><name></name></author><summary type="html">How to extend NST with a second style and what are the challenges of deploying it as a single unit using TensorFlow, Flask, ASP.NET Core, Angular and Docker Compose.</summary></entry><entry><title type="html">Data transformation pipeline in Kaggle competitions with doit build-tool</title><link href="https://evgeniymamchenko.com/data-transformation-pipeline-in-kaggle-competitions-with-doit-build-tool/" rel="alternate" type="text/html" title="Data transformation pipeline in Kaggle competitions with doit build-tool" /><published>2017-11-12T00:00:00+00:00</published><updated>2017-11-12T00:00:00+00:00</updated><id>https://evgeniymamchenko.com/data-transformation-pipeline-in-kaggle-competitions-with-doit-build-tool</id><content type="html" xml:base="https://evgeniymamchenko.com/data-transformation-pipeline-in-kaggle-competitions-with-doit-build-tool/">&lt;p&gt;&lt;em&gt;How to automate downloading, extracting, transforming a dataset and training a model on it in a Kaggle competition.&lt;/em&gt;&lt;/p&gt;

&lt;image src=&quot;../images/kaggle-pipeline.png&quot; /&gt;

&lt;p&gt;&lt;em&gt;The picture above is (almost) unrelated and belongs to &lt;a href=&quot;https://comsysto.wordpress.com/2013/12/20/taking-part-in-a-kaggle-competition-our-experience/&quot;&gt;comSysto’s Labs&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The full sample code is available at &lt;a href=&quot;https://github.com/OutSorcerer/kaggle-data-transformation-pipeline&quot;&gt;https://github.com/OutSorcerer/kaggle-data-transformation-pipeline&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;what-is-this-about&quot;&gt;What is this about?&lt;/h2&gt;

&lt;p&gt;It is a structured and extensible implementation of a typical data transformation workflow made with &lt;a href=&quot;http://pydoit.org/&quot;&gt;doit build-tool&lt;/a&gt; illustrated with &lt;a href=&quot;https://www.kaggle.com/c/statoil-iceberg-classifier-challenge&quot;&gt;Statoil/C-CORE Iceberg Classifier Challenge on Kaggle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Assume that your have the following workflow:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Download archives with datasets from Kaggle website.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Unzip them.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Transform data by converting from json to numpy format and adding a third layer to image computed as mean between given two layers (HH and HV bands).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Train your model (CNN) and save its coefficients to file.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Imagine that you do not want to push these huge files to your version control system and/or Docker registry. You computer may have much slower internet connection than AWS instances where are training your models so downloading and transforming data each time is much faster for you.&lt;/p&gt;

&lt;p&gt;Imagine that you also want to cache intermediate results of these steps so that re-running pipeline does not require repeating unnecessary actions.&lt;/p&gt;

&lt;p&gt;But when something is actually changed (your code or input data) cached data should be invalidated and recomputed.&lt;/p&gt;

&lt;p&gt;The straitforward solution is just manually implement these steps and caching logic. The obvious disadvantages of this approach are many conditional statements and complexity of detection that inputs of some step are changed and its requires recomputation.&lt;/p&gt;

&lt;p&gt;Such problem is historically adressed by software like &lt;a href=&quot;https://en.wikipedia.org/wiki/Make_(software)&quot;&gt;Make build automation tool&lt;/a&gt;. This is already much better but it is based on shell scripts while &lt;a href=&quot;http://pydoit.org/&quot;&gt;doit task management and automation tool&lt;/a&gt; also supports Python code in tasks and is easily extended with Python code.&lt;/p&gt;

&lt;h2 id=&quot;how-doit-implementation-looks-like&quot;&gt;How doit implementation looks like?&lt;/h2&gt;

&lt;p&gt;Assuming that &lt;a href=&quot;#dependencies&quot;&gt;all dependencies are installed&lt;/a&gt; and your &lt;a href=&quot;#kaggle-credentials&quot;&gt;specified your Kaggle credentials&lt;/a&gt; just run the following shell command:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;doit
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It looks for &lt;code class=&quot;highlighter-rouge&quot;&gt;dodo.py&lt;/code&gt; and for configuration there, finds &lt;code class=&quot;highlighter-rouge&quot;&gt;'default_tasks': ['train']&lt;/code&gt; and launches a task called &lt;code class=&quot;highlighter-rouge&quot;&gt;train&lt;/code&gt;. Its defenition looks like:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;task_train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;'actions'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;baseline_model&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;'file_dep'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'baseline_model.py'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'data/train.npy'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;baseline_model.train&lt;/code&gt; is a Python function that reads data from &lt;code class=&quot;highlighter-rouge&quot;&gt;data/train.npy&lt;/code&gt; and trains a CNN on them.&lt;/p&gt;

&lt;p&gt;Note that it depends not only on data but on source file (&lt;code class=&quot;highlighter-rouge&quot;&gt;baseline_model.py&lt;/code&gt;) which is correct because if both data and training code is not changed retraining is not required, but if at least one of the inputs changed the model should be also retrained.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;data/train.npy&lt;/code&gt; in turn is configured as a &lt;code class=&quot;highlighter-rouge&quot;&gt;target&lt;/code&gt; of a task called &lt;code class=&quot;highlighter-rouge&quot;&gt;convert_train_to_numpy&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;task_convert_train_to_numpy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;'actions'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;baseline_model&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;convert_train_to_numpy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;'file_dep'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'baseline_model.py'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'data/train.json'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;'targets'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'data/train.npy'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;By combining target and dependency files doit tool is able to determine which task depends on which and which task target is up-to-date. This is a default workflow that could be easily extended by implementing a custom Python function to check if task is outdated or not.&lt;/p&gt;

&lt;h2 id=&quot;dependencies&quot;&gt;Dependencies&lt;/h2&gt;

&lt;p&gt;I used &lt;a href=&quot;https://anaconda.org/anaconda/python&quot;&gt;Anaconda&lt;/a&gt; distribution that already includes packages like &lt;a href=&quot;https://github.com/numpy/numpy&quot;&gt;numpy&lt;/a&gt;, &lt;a href=&quot;https://github.com/pandas-dev/pandas&quot;&gt;pandas&lt;/a&gt; and &lt;a href=&quot;https://github.com/scikit-learn/scikit-learn&quot;&gt;scikit-learn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This sample is also using &lt;a href=&quot;https://github.com/fchollet/keras&quot;&gt;Keras&lt;/a&gt;, &lt;a href=&quot;https://github.com/tensorflow/tensorflow&quot;&gt;TensorFlow&lt;/a&gt;, &lt;a href=&quot;https://github.com/pydoit/doit&quot;&gt;doit&lt;/a&gt;, &lt;a href=&quot;https://github.com/WoLpH/python-progressbar&quot;&gt;progressbar&lt;/a&gt; and &lt;a href=&quot;https://github.com/MechanicalSoup/MechanicalSoup&quot;&gt;MechanicalSoup&lt;/a&gt; that are all easily installed by &lt;code class=&quot;highlighter-rouge&quot;&gt;pip&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;conda&lt;/code&gt; except if you want to install &lt;a href=&quot;https://www.tensorflow.org/install/&quot;&gt;Tensorflow with GPU support&lt;/a&gt; and especially &lt;a href=&quot;https://www.tensorflow.org/install/install_windows&quot;&gt;on Windows&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;kaggle-credentials&quot;&gt;Kaggle credentials&lt;/h2&gt;

&lt;p&gt;Kaggle requires your login and password to download a dataset. Specify your credentials with environment variables &lt;code class=&quot;highlighter-rouge&quot;&gt;KAGGLE_LOGIN&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;KAGGLE_PASSWORD&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;.credentials.ini&lt;/code&gt; in the following format (file has higher priority):&lt;/p&gt;

&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[kaggle]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;your_login&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;your_password&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Be carefult and never push this file to a public repository like GitHub (it is already added to &lt;code class=&quot;highlighter-rouge&quot;&gt;.gitignore&lt;/code&gt; for convinience).&lt;/p&gt;

&lt;h2 id=&quot;credits&quot;&gt;Credits&lt;/h2&gt;

&lt;p&gt;Thanks to Gerrit Gruben (&lt;a href=&quot;https://github.com/uberwach&quot;&gt;@uberwach&lt;/a&gt;) for his idea of using makefiles for data transformation on Kaggle competiontions. See his &lt;a href=&quot;https://github.com/uberwach/cookiecutter-kaggle&quot;&gt;Kaggle competition project structure template&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href=&quot;https://www.kaggle.com/devm2024&quot;&gt;DeveshMaheshwari&lt;/a&gt; for his &lt;a href=&quot;https://www.kaggle.com/devm2024/keras-model-for-beginners-0-210-on-lb-eda-r-d/notebook&quot;&gt;kernel with basic CNN model&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href=&quot;https://github.com/floydwch/kaggle-cli&quot;&gt;Kaggle-CLI&lt;/a&gt; contributors.&lt;/p&gt;</content><author><name></name></author><summary type="html">How to automate downloading, extracting, transforming a dataset and training a model on it in a Kaggle competition.</summary></entry><entry><title type="html">Why eidos.ml?</title><link href="https://evgeniymamchenko.com/why-eidos-ml/" rel="alternate" type="text/html" title="Why eidos.ml?" /><published>2017-09-05T00:00:00+00:00</published><updated>2017-09-05T00:00:00+00:00</updated><id>https://evgeniymamchenko.com/why-eidos-ml</id><content type="html" xml:base="https://evgeniymamchenko.com/why-eidos-ml/">&lt;p&gt;&lt;em&gt;This post tells what is eidos.ml and why it was created.&lt;/em&gt;&lt;/p&gt;

&lt;image src=&quot;../images/plato.jpg&quot; /&gt;

&lt;h2 id=&quot;about-eidosml&quot;&gt;About eidos.ml&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/OutSorcerer/eidos&quot;&gt;eidos.ml&lt;/a&gt; is a C# / .NET Standard library for productive development of machine learning pipelines that is based on &lt;a href=&quot;https://github.com/mathnet/mathnet-numerics&quot;&gt;MathNet.Numerics&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It is inspired by &lt;a href=&quot;https://github.com/scikit-learn/scikit-learn&quot;&gt;scikit-learn&lt;/a&gt; but uses advantages of C# syntax and .NET runtime.&lt;/p&gt;

&lt;h3 id=&quot;status&quot;&gt;Status&lt;/h3&gt;

&lt;p&gt;Currently the library is in the early development stage and the suggested use-cases are participation in competitions like &lt;a href=&quot;https://www.kaggle.com/&quot;&gt;Kaggle&lt;/a&gt; or learning and experimenting with ML algorithms.&lt;/p&gt;

&lt;p&gt;There &lt;strong&gt;will&lt;/strong&gt; be breaking changes. But after the release of 1.0 it will be stable and suitable for production for small-scale problems (that fit to a single machine’s memory).&lt;/p&gt;

&lt;h2 id=&quot;why-invent-the-wheel&quot;&gt;Why invent the wheel?&lt;/h2&gt;

&lt;p&gt;There are other great ML libraries for other languages and for C# too. Why create yet another?&lt;/p&gt;

&lt;p&gt;Not only for fun.&lt;/p&gt;

&lt;h3 id=&quot;why-c&quot;&gt;Why C#?&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;C# today is one of the most &lt;a href=&quot;https://www.tiobe.com/tiobe-index/&quot;&gt;popular&lt;/a&gt;, &lt;a href=&quot;https://insights.stackoverflow.com/survey/2017#most-loved-dreaded-and-wanted&quot;&gt;loved and wanted&lt;/a&gt; general-purpose programming languages, it has strong backing by Microsoft, enterprises and the community.&lt;/p&gt;

    &lt;p&gt;Microsoft and others make a lot of effort to improve the language and its infrastructure and make it even more appealing.&lt;/p&gt;

    &lt;p&gt;The set of C# features like operator overloading, LINQ, async/await, string interpolation, tuples, type deconstructors and others (and &lt;a href=&quot;https://www.infoq.com/news/2017/06/CSharp-7.2&quot;&gt;even more&lt;/a&gt; are coming soon), the speed how these features are released, lots of NuGet packages, great IDEs make C# one of the most pleasant languages to work with.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Languages that are mainly used for ML (at least on Kaggle) Python, R and Julia are all dynamically typed. There is nothing bad in it but there should be a statically typed alternative.&lt;/p&gt;

    &lt;p&gt;Static typing may also produce better performance and more predictable behaviour in run-time.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Since C# is (JIT) compiled while Python and R are interpreted it has better performance. Performance is not only useful in production but also during experiments when faster code let you tune your model faster.&lt;/p&gt;

    &lt;p&gt;While R and Python often use native libraries to boost their performance .NET Core interoperability also support that and thanks to NuGet it is convenient to ship native binaries for different platforms as part of the same NuGet package.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;With the release of .NET Core 1.0 in 2016 Microsoft finally made C# / .NET truly cross-platform and open source with free developments tools like VS Code or VS Community Edition available. That encourages its use for scientific computing.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;why-not-c&quot;&gt;Why not C#?&lt;/h3&gt;

&lt;p&gt;You might ask if C# language is so good why it is not so popular for ML? I believe the main reason for that is not technical issues but the inertia of public opinion, for example a tendency to associate C# with “evil” and proprietary Microsoft (which is, in a degree, true, but it is by the same degree true for Oracle, Apple, Google or any other corporation that is &lt;em&gt;surprisingly&lt;/em&gt; trying to earn money).&lt;/p&gt;

&lt;p&gt;Moreover, by the amount of open source projects the relative position the C# community is not as good as desired, but the growth rate of C# open source is &lt;a href=&quot;https://octoverse.github.com/&quot;&gt;quite good&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Considering the ML sector of open source the situation seems to be even worse than with the rest open source. It is a kind of vicious circle when people who are interested in ML even if C# is their favorite language have to use libraries other languages like Python or R and later they begin to contribute to such libraries, post code samples for them, are hired by and later hire people who use languages other than C#.&lt;/p&gt;

&lt;p&gt;So this is of course a big challenge to take part in breaking this vicious circle about open source / ML in C#, but it is also a great opportunity.
I am aware of other attempts to make similar libraries but I cannot say that they were as successful as, for example, scikit-learn.&lt;/p&gt;

&lt;p&gt;I do not think that I am smarter or better by any means that authors of these libraries. I believe the main reason is the mentioned vicious circle, but I also hope that the situation is changing (and I will not deny a bit of NIH but as a redemption I am going to contribute to other projects too).&lt;/p&gt;

&lt;p&gt;Is it a good time already? My current plan is to push the development for at least a year up to 1.0 version no matter how many people will be interested in the beginning (although I understand that ultimately such a library should have many contributors to be able to compete with others and I am going to do what is in my power to attract contributors).&lt;/p&gt;

&lt;p&gt;In the end it is the amount of GitHub forks and MRs/ NuGet downloads that shows if it is a good idea or not. Practice is the criterion of truth.&lt;/p&gt;

&lt;p&gt;In any case the code is open source so someone else could use it for their purposes. And I will, at the very least, have fun.&lt;/p&gt;

&lt;h3 id=&quot;why-eidosml&quot;&gt;Why eidos.ml?&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;The name &lt;em&gt;eidos&lt;/em&gt; comes from the Plato’s &lt;a href=&quot;https://en.wikipedia.org/wiki/Theory_of_forms&quot;&gt;theory of Forms&lt;/a&gt;, where it means non-physical (but substantial) forms (or ideas) representing the most accurate reality. Since machine learning models should simulate the reality as accurately as possible an &lt;em&gt;eidos&lt;/em&gt; would be an ideal model.&lt;/p&gt;

    &lt;p&gt;As a materialist  I do not share Plato’s idea that such forms &lt;em&gt;really&lt;/em&gt; exist independently from matter but they definitely exist in the minds of humankind.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The libraries for C# typically implement much less algorithms then scikit-learn mentioned above. eidos.ml will support all major ML algorithms including deep learning (when necessary wrapping around other libraries like &lt;a href=&quot;https://github.com/dmlc/xgboost&quot;&gt;xgboost&lt;/a&gt; or &lt;a href=&quot;https://github.com/tensorflow/tensorflow&quot;&gt;TensorFlow&lt;/a&gt;).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The performance of eidos.ml should not worse than competitors’ performance.&lt;/p&gt;

    &lt;p&gt;eidos.ml should utilize multiple CPU cores and GPU acceleration (at least by wrapping other native libraries).&lt;/p&gt;

    &lt;p&gt;There will be reproducible benchmarks with other popular libraries. There will be also performance tests.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;eidos.ml is free / open source (MIT license).&lt;/p&gt;

    &lt;p&gt;By the way, there is another C#-based ML solution called Azure ML that may be great for enterprise use but it is not free / open source that is not good for studying ML and competing on Kaggle.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;eidos.ml will be a truly object oriented ML library, that allows to create a solution of high complexity by building a data processing pipeline as a composition of standard building blocks (preprocessing, training, evaluation algorithms) and with full extensibility.&lt;/p&gt;

    &lt;p&gt;Good objects should have some properties like &lt;a href=&quot;https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)&quot;&gt;SOLID principles&lt;/a&gt; or &lt;a href=&quot;http://www.yegor256.com/2014/11/20/seven-virtues-of-good-object.html&quot;&gt;immutability&lt;/a&gt;. But the library should stay pragmatic and do not make user jump through the hoops when it is not necessary (so I do not share &lt;a href=&quot;http://www.yegor256.com/2014/11/20/seven-virtues-of-good-object.html]&quot;&gt;Yegor Bugayenko’s opinion&lt;/a&gt; that static utility classes are evil).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Scientific code tends to be the opposite to the definition of clean code. The code of eidos.ml should be clean, and that will be enforced both automatically and by code review. The code must be also covered by automated tests that are launched for every commit on CI server.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;eidos.ml is cross-platform since it is implemented as .NET Standard 2.0 library. In case of addition of native binaries they will be supplied for Windows, Linux and Mac and it will always remain cross-platform. Continuos integration and automated tests will enforce portability.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;It will support import/export to allow creating huge 
ensembles of models when particular models are pre-trained in a cloud and then restored saving hours or possibly days of re-training.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;It must be easy to learn, by having good documentation and samples.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;It should ultimately support automated ML like &lt;a href=&quot;https://github.com/automl/auto-sklearn&quot;&gt;auto-sklearn&lt;/a&gt; or &lt;a href=&quot;https://github.com/rhiever/tpot&quot;&gt;TPOT&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;It must be easy to contribute (project should be easily buildable after clone on  any developer’s machine without any magic).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;eidos.ml is on the early stage of development, so it is not yet very useful but if you have great ideas about architecture or implementation of such a library it is open for any ideas and contributions. Do not hesitate and just create an issue with your improvement idea / bug report, do a merge request or just comment under this post.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;</content><author><name></name></author><summary type="html">This post tells what is eidos.ml and why it was created.</summary></entry></feed>