<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Stian Lind Petlund on Medium]]></title>
        <description><![CDATA[Stories by Stian Lind Petlund on Medium]]></description>
        <link>https://medium.com/@stianlindpetlund?source=rss-766cb02adc0a------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*6uwsuByNUcwUKx4r.</url>
            <title>Stories by Stian Lind Petlund on Medium</title>
            <link>https://medium.com/@stianlindpetlund?source=rss-766cb02adc0a------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 14:12:40 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@stianlindpetlund/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Building the digital infrastructure for a transparent and resilient food system]]></title>
            <link>https://medium.com/@stianlindpetlund/building-the-digital-infrastructure-for-a-transparent-and-resilient-food-system-6c9b76e95202?source=rss-766cb02adc0a------2</link>
            <guid isPermaLink="false">https://medium.com/p/6c9b76e95202</guid>
            <category><![CDATA[design-process]]></category>
            <category><![CDATA[tech]]></category>
            <category><![CDATA[agriculture]]></category>
            <category><![CDATA[food-production]]></category>
            <category><![CDATA[startup]]></category>
            <dc:creator><![CDATA[Stian Lind Petlund]]></dc:creator>
            <pubDate>Fri, 10 Jan 2020 08:53:20 GMT</pubDate>
            <atom:updated>2020-01-10T09:33:39.163Z</atom:updated>
            <content:encoded><![CDATA[<p><em>This is a short story about our mission at Dagens, why it is important and why you should join our team in 2020. Dagens is a full stack marketplace enabling food producers to sell directly to customers in HoReCa. Our platform allows food producers to handle their own sales, and cut costly and demanding intermediaries. If you want to join our team, send us an email at </em><strong><em>jobs@dagensmat.no</em></strong><em>.</em></p><p>With industrialized agriculture, the world’s food production has changed from being biodiverse to focusing more on monoculture - a few selected crops. With a smaller and simpler product line, the food producers are able to specialize, and hence produce larger quantities at lower costs. This kind of production is often sold to only one or a few wholesalers who control the regional food market.</p><p>Even though an optimized and monocultural production may yield good return on investment today, it is a short sighted strategy that our ecosystems already have started to pay the price for. Loss of biodiversity is one of the biggest environmental challenges we face today, and the current food system is making it worse. A biodiverse food production is more resilient to rapid and unexpected changes, including climate change. Yet, only 9 plant species account for 2/3rd of the total crop production.</p><p>Small scale food producers who support biodiversity are going out of business every day. Because today’s system is rigged for buying and selling few products in large volumes, bigger and specialized production is taking over. You do not need to be a farmer, or in any way be schooled in this topic to see the evidence, just look at the shelves in your local grocery store. All vegetables are within the weight and size measure of what is expected for each category. For meat and dairy, only products the consumer is familiar with and demands are found in the shelves. The season is global.</p><p>In economics theory, there is a concept known as <em>the long tail</em>. Where a regular product company or shop may sell a limited number of unique products (let’s say less than thousand different products), marketplaces like Ebay, Craigslist or Airbnb have millions of listings, most of them completely unique.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/953/1*h5TzGVMiCC0x6N9jyiZrDg.png" /><figcaption>The green area represents the highly demanded products, the peach area less demanded products. The long tail suggests that with reduced distribution costs, selling more of less can outperform selling only the popular categories.</figcaption></figure><p>A marketplace with a long tail distribution of products can give buyers endless opportunities to find exactly what they are looking for, and can revive unused products or increase profitability on low valued products. Take Airbnb as an example; the supply of housing has always been there, but it took a combination of engineers, designers, luck and timing to build a marketplace that made it possible to check in to someone’s apartment anywhere on the planet. By designing a product that made travelers belong anywhere, and hosts feel safe that their houses were in good hands, it is now possible to check in to this unexploited supply of housing.</p><p>To utilize the diverse and unexploited food resources, we have spent two years building a full stack marketplace enabling producers to sell directly to consumers. We want any food producer, big or small, to be able to compete on equal terms as wholesalers, and we want consumers to have the opportunity to buy directly from a wide variation of produce, and from the producers they trust and value.</p><p>In our first market, Norway, around 50 producers and 150 HoReCa consumers are trading food every week on our platform. By 2020 we plan on multiplying these numbers by 10x, and we are going to need world class engineers and designers to join our mission. Our 2020 roadmap is packed with essential functions that need to be built, and because we hope some of you reading this will join our team this year, we will give you a sneak peek into two of our top priorities on the product roadmap.</p><p>We believe the only way towards a more transparent food system is bringing consumers and producers closer together. The consumers should have as much insight into the production, as if they were<strong> sitting on the tractor</strong> together with the producers. By building planning tools that let producers and consumers collaborate from seed to fork, we help reducing the producers’ production risk. In particular, producers with highly seasonal products, take a huge risk when they start their production every year, not knowing if their crops will be sold. Let 2020 be the year of food futures.</p><p>To enable smaller food producers to compete with the whales, we have to build a system that lets them <strong>move aggregated parcels instead of single pallets</strong>. Today’s system is designed to sell huge volumes in single transactions to wholesalers who get to decide what produce is sellable and at which prices. We will decentralize the system and let more actors enter the market with many small transactions. Last year we built a logistics system for our last mile logistics partners and for internal operations, but soon all producers on our platform will get access to a logistics system where they can drop of parcels at hubs instead of spending time driving to every single customer. By aggregating drops and owning nothing but the digital infrastructure, we are able to do this at fraction of a wholeseller’s cost.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MwkKDzmZCEagEBFBhrq-xA.jpeg" /><figcaption>Co-designing labeling and tracking with Bergsmyrene Gård.</figcaption></figure><p>Our mission is to facilitate a fair market for all food producers, because we believe the world needs a transparent and resilient food system. As we enter 2020 we are looking for developers and designers to join our team. If you have read this story and got exited about redesigning a broken food system, send us your CV and a cover letter explaining your motivation to <strong>jobs@dagensmat.no</strong> or check our positions at <a href="https://startupmatcher.com/s/dagens-1">Startupmatcher</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6c9b76e95202" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Epigram AI — hvem, hva og hvorfor?]]></title>
            <link>https://medium.com/epigramai/epigram-ai-hvem-hva-og-hvorfor-cf35aad36dc2?source=rss-766cb02adc0a------2</link>
            <guid isPermaLink="false">https://medium.com/p/cf35aad36dc2</guid>
            <category><![CDATA[blog]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[startup]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[nyheter]]></category>
            <dc:creator><![CDATA[Stian Lind Petlund]]></dc:creator>
            <pubDate>Tue, 06 Mar 2018 11:11:24 GMT</pubDate>
            <atom:updated>2018-03-06T12:38:50.192Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*B02Iu-M4IyXkDv8QqYqguA.png" /></figure><p>Når denne bloggposten skrives har <a href="http://epigram.ai/">Epigram</a> allerede rukket å fylle ett år. I året som har gått har vi forsøkt å navigere i et hav av produktidéer og mulige forretningsmodeller, mens vi samtidig har lurt på hvilken selskapsform som lar seg best kombinere med visjonen om å bli landets fremste fagmiljø innen maskinlæring.</p><p>Da vi startet Epigram var vi enige om å starte et selskap som skulle være et inkluderende miljø for studenter og andre som ønsket å lære om maskinlæring. Vi var også enige om at Epigram skulle være en motvekt til all hypen rundt maskinlæring og kunstig intelligens, og kommunisere at bak all den tilsynelatende magien befinner det seg et håndfast og forståelig fag. I tillegg til å være inkluderende og faglig relevante, var det også viktig for oss at vi skulle bidra positivt til samfunnet.</p><p>Epigram består av et team teknologer som brenner for faget maskinlæring. Vår jobb er å være best mulig på vårt felt, og vi stoler på at våre kunder brenner like mye for sitt. Vi tror at summen av de to blir en bra match.</p><p>I Epigram er vi opptatt av at studenter skal få komme innom for å leke og lære slik at de blir best mulig i maskinlæring. Denne faglige styrken tar studentene med seg videre til kunder når de i fremtiden utfører konsulentoppdrag som ansatte i Epigram.</p><p>Maskinlæring er ingeniørkunst på samme måte som å bygge biler, hus og apper. På samme måte som at det ikke finnes en app for alt, så finnes det heller ingen generell kunstig intelligens for alt. Ihvertfall ikke ennå.</p><p>Det må fremdeles utvikles løsninger for spesifikke problemer, og det er disse løsningene menneskene i Epigram er flinke på å utvikle.</p><p>I året som har gått har vi fått lov til å vise frem hvor flinke vi er til kunder i næringslivet og hos forskningsinstitusjoner. Akkurat hva vi hjelper kundene med varierer med kundens behov.</p><p>I de fleste tilfeller handler det om å utvikle fullstendige løsninger som kunden kan plugge inn i sin eksisterende teknologi. Andre ganger bidrar vi som rådgivere for å gjøre kunden best mulig i stand til å utvikle løsningen selv.</p><p>Selv om vi i året som har gått har vært svært uenige om hvordan selskapet skal drives og hva vi skal tjene penger på, så har vi alltid vært helt enige om at det maskinlæringsfaglige skal stå i sentrum. Ved inngangen av 2018 ble vi også helt enige om at Epigram skal være et maskinæringsbyrå som hjelper næringslivet og forskningsinstitusjoner å ta i bruk maskinlæring for å løse kompliserte problemer.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/775/1*YYZsie-GJdV-Ilzh2uD-xQ.png" /></figure><p>Denne bloggen skal være stedet der både teknologer og ikke-tekniske kan svippe innom for å lære om maskinlæring. Mye av innholdet vil være rettet mot teknologer både til lands og utlands, men det skal også skrives poster som ufarliggjør maskinlæring og tar konsepter som Big Data, AI og Deep Learning ned på jorda igjen.</p><p>Vi synes også det er veldig kult om lesere har lyst til å skrive innlegg til Epigram-bloggen. Om du sitter inne med en bloggpost i magen, eller kanskje allerede har skrevet noe som passer her, ta kontakt med oss så skal vi få det publisert 😃</p><p><em>Besøk oss på vår </em><a href="http://epigram.ai/"><em>nettside</em></a><em> eller ✉️ oss på hello@epigram.ai 💻</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cf35aad36dc2" width="1" height="1" alt=""><hr><p><a href="https://medium.com/epigramai/epigram-ai-hvem-hva-og-hvorfor-cf35aad36dc2">Epigram AI — hvem, hva og hvorfor?</a> was originally published in <a href="https://medium.com/epigramai">epigramAI</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[TensorFlow Serving 101 pt. 2]]></title>
            <link>https://medium.com/epigramai/tensorflow-serving-101-pt-2-682eaf7469e7?source=rss-766cb02adc0a------2</link>
            <guid isPermaLink="false">https://medium.com/p/682eaf7469e7</guid>
            <category><![CDATA[grpc]]></category>
            <category><![CDATA[tensorflow]]></category>
            <category><![CDATA[tensorflow-serving]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[how-to]]></category>
            <dc:creator><![CDATA[Stian Lind Petlund]]></dc:creator>
            <pubDate>Sun, 28 Jan 2018 22:25:18 GMT</pubDate>
            <atom:updated>2018-05-30T07:29:44.105Z</atom:updated>
            <content:encoded><![CDATA[<h4>Part 2: Sending requests to your model</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/602/1*lHPLELkaI2n_N1uSM80LcA.jpeg" /></figure><p>Welcome back! In this part I’ll take you through the process of sending requests to your model. If you haven’t finished <a href="https://medium.com/p/a79726f7c103/">part 1</a> yet, I strongly recommend you do that first.</p><p>As I mentioned in part 1, the model server is not a regular HTTP endpoint that you can just send a POST request with your data to. It’s a <a href="https://grpc.io/">gRPC</a> service, and even though it may seem complicated at first, it’s actually a pretty cool concept.</p><h3>What you need to know about gRPC</h3><p>From gRPC’s own pages:</p><blockquote>In gRPC a client application can directly call methods on a server application on a different machine as if it was a local object, making it easier for you to create distributed applications and services. As in many RPC systems, gRPC is based around the idea of defining a service, specifying the methods that can be called remotely with their parameters and return types.</blockquote><p>This basically means that if you have an app where a user wants to see all his or her friends, when the user clicks “My Friends” in the UI, the client calls the function <em>getFriends()</em>. No need to send a GET request to the endpoint <em>/getFriends</em>. That sounds like a great concept! The server side code is automatically remotely available in the client. No more GET, POST, PUT, just call the methods! But wait, how can the client know that there’s a method called <em>getFriends</em> on the server? Is the client running the server side code too?</p><p>Actually, the answer is (almost) yes. The gRPC client side has a stub that provides all the methods the server has. Of course, when the data travel over the wire, it’s no more magic than HTTP going on. But it’s the way you as a developer write the communication that is different!</p><p>We’re not aiming to become gRPC experts in this tutorial (and I am not an expert on gRPC), but if you want to learn more I can recommend this 30 minute video. Don’t watch it now, do it when you have finished the tutorial.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FRoXT_Rkg8LA%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DRoXT_Rkg8LA&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FRoXT_Rkg8LA%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/c0d21917c7c31c962f125392ca3a2c67/href">https://medium.com/media/c0d21917c7c31c962f125392ca3a2c67/href</a></iframe><p>For now, let’s focus on using a client I’ve already built. You don’t need to understand or know anything about gRPC to complete the next section.</p><h3>Send requests to the model server</h3><p><strong>The client works without TensorFlow installed</strong>. So if you are using <em>conda</em> or <em>virualenv</em> (or similar environment tool), create a new environment (without TensorFlow of course…) if you don’t believe me.</p><p>The reason we don’t want TensorFlow client side is because anyone who wants to integrate with your model shouldn’t have to deal with TensorFlow to use it.</p><p>Imagine you wrote a kick ass image classifier that can separate <strong>hot dogs</strong> from <strong>not hot dogs</strong>. Now your friends who developed an awesome food app wants to use your model, but they know nothing about TensorFlow. So they say: “Hey, what’s the endpoint? Should we just POST an image to your API or …”.</p><p>Because you are serving the model with TensorFlow Serving and you know that none your app making friends have ever heard about gRPC, you hand them the client and show them how to use it. Give them a hostname, a model name and a model version number, and they’re all set!</p><p>Now, Go ahead and install the client. I should probably submit the client to PyPI, but for now:</p><pre>pip install git+ssh://git@github.com/epigramai/tfserving-python-predict-client.git</pre><p>❗️Some readers have had trouble installing the client using the ssh link, try <em>pip install git+https://github.com/epigramai/tfserving-python-predict-client.git</em>, if the ssh link does not work in your case.</p><p>Make sure the model you saved in <a href="https://medium.com/p/a79726f7c103/">part 1</a> is running. Start it again if you stopped the container when we cleaned up in the end of part 1. Then start Python and test the client:</p><pre>&gt;&gt;&gt; from predict_client.prod_client import ProdClient<br>&gt;&gt;&gt; client = ProdClient(&#39;localhost:9000&#39;, &#39;simple&#39;, 1)<br>&gt;&gt;&gt; req_data = [{&#39;in_tensor_name&#39;: &#39;a&#39;, &#39;in_tensor_dtype&#39;: &#39;DT_INT32&#39;, &#39;data&#39;: 2}]<br>&gt;&gt;&gt; client.predict(req_data)</pre><p>I’ll go through what’s happening here in detail:</p><ul><li>The first line imports the client. <strong>ProdClient</strong> is the client you would use to send requests to an actual model server. I’ve written two other clients as well, they serve different purposes. More on those clients later.</li><li>Pass the <strong>host</strong>, <strong>model name</strong> and <strong>version</strong> to the client’s constructor. Now the client knows which server it’s supposed to communicate with.</li><li>To do a prediction we need to send some request data. As far as the client knows, the server on localhost:9000 could host any model. So we need to tell the client what our input tensor(s) are called, their data types and send data of the correct type and shape (exactly what you do when you pass a <em>feed_dict</em> to<em> sess.run</em>). Now, <em>in_tensor_name</em> ‘a’ is the same ‘a’ that we used in the signature definition in part 1. The input tensor’s data type must also match the one of the placeholder <em>a</em> from part 1. Note that we use the string ‘DT_INT32’ (and not tf.int32), I will show you where to find this mapping in the next section.</li><li>Finally call <em>client.predict</em> and you should get this response:</li></ul><pre>{&#39;add&#39;: 12}</pre><p>Perfect! <strong>a + b = add</strong>, or in this case with actual values: <strong>2 + 10 = 12</strong>.</p><p>That’s pretty much all you need to know to send requests. The next section is for those of you who wants dig deeper and understand how the client uses gRPC.</p><h3>More about gRPC and the client</h3><p>I am not going to make this section very long, but I’ll try to take you through the the process of sending a request and getting a response in more detail. I hope that will answer some of your questions (the same questions I had) on how to use TensorFlow Serving and gRPC.</p><p>All client code can be found here: <a href="https://github.com/epigramai/tfserving-python-predict-client">https://github.com/epigramai/tfserving-python-predict-client</a>. If you choose to clone the repo and play around with the code, make sure to uninstall the client we installed with pip earlier on.</p><p>You don’t need to understand the stuff we go through in this section to use the client. It’s just a more detailed explanation for those who want to learn more.</p><h4>Protocol Buffers</h4><p>Look at the files in the <em>/proto</em> folder. These files define a gRPC service for sending and receiving requests. They don’t look much like Python code, and that’s because they aren’t. These files can be used with any language to create a client, you just need the protocol buffer compiler <strong>protoc</strong> to compile them.</p><p>Python gRPC tools include the protoc compiler, and it can be installed with with pip:</p><pre>python -m pip install grpcio-tools</pre><p>To generate Python code, run:</p><pre>python -m grpc_tools.protoc -I protos/ — python_out=predict_client/pbs — grpc_python_out=predict_client/pbs protos/*</pre><p>❗️The generated files already exist in the repo, so you don’t have to do this! If you run this command you may have to fix some import statements to make the client work again :)</p><p>The files this command generates end up in <em>predict_client/pbs/</em>. I know some of these generated files are almost empty, and you are probably wondering what all this generated code does. The files in this folder are used by the production client, that’s all you need to know. Think of the content in this folder as compiled code, so don’t touch it.</p><h4>Sending a request</h4><p>Open <em>predict_client/prod_client.py</em>, and take a look at these two lines:</p><pre>stub = PredictionServiceStub(channel)<br>...<br>request = PredictRequest()</pre><p>What we’re doing here is creating the PredictionService which is defined in <em>prediction_service.proto</em>. The service object on the client is also known as a <strong>stub</strong> in the gRPC world. Then we create a PredictRequest message defined in <em>predict.proto</em>.</p><p>We assign the model spec in PredictRequest:</p><pre>request.model_spec.name = self.model_name</pre><pre><strong>if </strong>self.model_version &gt; 0:<br>  request.model_spec.version.value = self.model_version</pre><p>Now, if you peak into <em>predict.proto</em> you can see that the PredictRequest has the field <strong>model_spec</strong> and in <em>model.proto</em> you find the ModelSpec with fields <strong>name</strong> and <strong>version</strong>.</p><p>Create tensor proto that is compatible with the model we are serving:</p><pre>tensor_proto = make_tensor_proto(d[‘data’], d[‘in_tensor_dtype’])</pre><p>In the function <em>predict_client.util.make_tensor_proto</em>, you can see how I also assign DataType and TensorShapeProto to the TensorProto message. This is where the upper case DT_INT32 data type comes in. Look in <em>types.proto</em> to see all data types.</p><p>Add the tensor proto to the inputs map of the PredictRequest:</p><pre>request.inputs[d[‘in_tensor_name’]].CopyFrom(tensor_proto)</pre><p>The line <strong>map&lt;string, TensorProto&gt; inputs = 2; </strong>in <em>predict.proto</em> corresponds to the parameter<strong> inputs</strong> that we send to the <strong>signature definition</strong> back in part 1. We chose ‘a’ as our input tensor, that’s why the client must send a tensor proto with ‘a’ as the key in the inputs dictionary.</p><p>Finally we call <em>stub.Predict()</em> in the Python code and the request is sent.</p><h4>Receiving the response</h4><p>I‘ll be a little less verbose as we go from server and back to client:</p><pre>predict_response = stub.Predict(request, timeout=request_timeout)</pre><p>In the bottom of <em>predict.proto</em>, PredictResponse is defined. This message has one field: <strong>map&lt;string, TensorProto&gt; outputs = 1;</strong>. As you probably guessed already, the key in this map is our ‘add’ from part 1.</p><p>The rest of the process is simply converting this response to a dictionary.</p><h3>InMemoryClient and MockClient</h3><p>There are two other clients you can use. Those clients are for test and mock usage, they don’t send the request to a server. Documentation on how to use them can be found in the readme.</p><p>The mock client is basically just mock. Initiate it with some response on the same format you would expect from a real model. In cases where you are developing locally and you don’t have the model served and correct predictions doesn’t matter, use this client. For instance when you are developing an API on your laptop and the API expects a response from a heavy model, you can use the mock client to pretend to get the response from a real model server.</p><p>The in memory client is another way to pretend you have the model served. Instead of using fake data, we can “serve” the model instead. The in memory client loads the model into memory and acts as both client and server. This is convenient if you don’t have a model server at hand, but want to simulate how the client would work in a production setting.</p><p>❕ NB: TensorFlow must be installed to use the in memory client.</p><h3>Wrapping it up</h3><p>There are a few other prediction clients in open source GitHub repos out there. There are a few reasons I wanted to build my own:</p><ul><li>I wanted a client that is generic and supports all types of models where you send a dict and get a dict in response. And it should be installable and ready to use out of the box.</li><li>Until now I’ve only found Python client code that use <em>tf.contrib.util.make_tensor_proto</em> to make the tensor proto. In my opinion the client should be TensorFlow agnostic. <strong>You shouldn’t have to install and import TensorFlow if you want to send requests to an already trained model</strong>.</li><li>When I found out I had to understand how this actually works, I wanted to build my own and share my knowledge with the community.</li></ul><p>Alright, that’s it for now. I hope you learned something about TensorFlow Serving and how you can use this client in your own projects even outside TensorFlow environments :)</p><p>Thank you for reading. I really hope you learned something! Leave a comment if you have questions or comments 😃</p><p>In the end I want to thank a few people:</p><ul><li>This is where I started about a year back: <a href="https://github.com/tobegit3hub/tensorflow_template_application">https://github.com/tobegit3hub/tensorflow_template_application</a>. Thanks to <a href="https://github.com/tobegit3hub/">tobegit3hub</a> and this <a href="https://github.com/tensorflow/serving/issues/271">github issue</a>.</li><li>There’s a <a href="https://github.com/shygiants/tf-serving-nodejs-client">node client here</a> that’s also TensorFlow agnostic. I used the .proto files from here to generate my own client. Thanks to <a href="https://github.com/shygiants">shygiants</a> for writing a node client.</li><li>Thanks to dayzhang for open sourcinng dict-to-protobuf. Really helpful when converting a dictionary to protobuf without using tf.contrib.util.make_tensor_proto: <a href="https://github.com/davyzhang/dict-to-protobuf">https://github.com/davyzhang/dict-to-protobuf</a>.</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=682eaf7469e7" width="1" height="1" alt=""><hr><p><a href="https://medium.com/epigramai/tensorflow-serving-101-pt-2-682eaf7469e7">TensorFlow Serving 101 pt. 2</a> was originally published in <a href="https://medium.com/epigramai">epigramAI</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[TensorFlow Serving 101 pt. 1]]></title>
            <link>https://medium.com/epigramai/tensorflow-serving-101-pt-1-a79726f7c103?source=rss-766cb02adc0a------2</link>
            <guid isPermaLink="false">https://medium.com/p/a79726f7c103</guid>
            <category><![CDATA[tensorflow]]></category>
            <category><![CDATA[tensorflow-serving]]></category>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[how-to]]></category>
            <dc:creator><![CDATA[Stian Lind Petlund]]></dc:creator>
            <pubDate>Sun, 28 Jan 2018 22:24:42 GMT</pubDate>
            <atom:updated>2018-03-21T15:11:08.285Z</atom:updated>
            <content:encoded><![CDATA[<h4>Part 1: Saving and serving your model</h4><p>My goal with this tutorial is to explain, as straightforward as possible, how you can:</p><ul><li>Save a TensorFlow model so it can be loaded with TensorFlow Serving ModelServer and used in production.</li><li>Serve your model with the TensorFlow Serving ModelServer.</li><li>Send requests to your model (and get responses).</li></ul><p>To get the most out of this tutorial you should be familiar with Python and have written a TensorFlow model before. This is not an introduction to TensorFlow itself, but to the serving system TensorFlow Serving.</p><p>I found it convenient to split the tutorial into two parts. In Part 1 you will learn how to save a simple model and serve it with TensorFlow Serving ModelServer. In <a href="https://medium.com/p/682eaf7469e7/">Part 2</a> we will send requests to the model using a Python client.</p><h3>Background</h3><p>Many guides and blog posts have been written about how to save models for production and serve them using TensorFlow Serving. So, why would I write a tutorial when there are numerous guides and examples already written?</p><p>Because I have (and I believe others have too) spent countless hours reading tutorials, documentation, examples and blog posts on how to deploy TensorFlow models for production. Although it’s fairly easy to get your models up and running with TensorFlow Serving, there are many ways to do it and a lot of concepts to digest. These two (many ways to do stuff + new concepts) usually lead down the following paths:</p><ul><li>You spend way too much time exploring all the options and learning new concepts instead of just building what you were supposed to build in the first place.</li><li>You copy some stuff you don’t really understand, but it works. When it eventually breaks or you want to build a new feature, you spend all your time figuring out how it works (again).</li></ul><p>I have been doing a combination of these two for almost a year now. So here’s a straightforward way to save, serve and send requests to your model. This is how we do it at <a href="http://epigram.ai/">Epigram</a>. We found a way that works for us, but keep in mind that TensorFlow Serving is a flexible serving system, and the way I see it, there’s no right or wrong in this space.</p><p>Alright, let’s get started. All code for part 1 of the tutorial can be found here: <a href="https://github.com/epigramai/tfserving-simple-example">https://github.com/epigramai/tfserving-simple-example</a>.</p><h3>1. Write a simple model</h3><p>In the snippet below I have written a simple model. Most of you have probably never written such a primitive TensorFlow like this (and why would you, it’s LSTMs and convolutional neural networks that rocks!). But all we need to save our model for production is a simple graph like the one below, so I am not going to do it more complicated that necessary.</p><p>Open <em>export_simple_model.py</em> if you want to get the full picture.</p><pre>placeholder_name = &#39;a&#39;<br>operation_name = &#39;add&#39;<br><br>a = tf.placeholder(tf.int32, name=placeholder_name)<br>b = tf.constant(10)<br><br># This is our model<br>add = tf.add(a, b, name=operation_name)<br><br><strong>with </strong>tf.Session() <strong>as </strong>sess:<br><br>    # Run a few operations to make sure our model works<br>    ten_plus_two = sess.run(add, feed_dict={a: 2})<br>    print(&#39;10 + 2 = {}&#39;.format(ten_plus_two))<br><br>    ten_plus_ten = sess.run(add, feed_dict={a: 10})<br>    print(&#39;10 + 10 = {}&#39;.format(ten_plus_ten))</pre><p>If you’re not completely new to TensorFlow, the code above should look more or less like the most primitive model you’ve ever written. You feed the model with a number, the model adds that number to 10 and returns the sum.</p><h3>2. Save the model</h3><p>In the example above we created a placeholder, a constant and an operation on the default graph. Then we started a session and ran the add operation. We could wrap this code in an API endpoint written in a Python framework like Flask, Falcon or similar, and voilá we have an API. But there are some really good reasons you don’t want to do it that way:</p><ul><li>If your model(s) are complex and run slowly on CPU, you would want to run your models on more accelerated hardware (like GPUs). Your API-microservice(s), on the other hand, usually run fine on CPU and they’re often running in “everything agnostic” Docker containers. In that case you may want to keep those two kinds of services on different hardware.</li><li>If you start messing up your neat Docker images with heavy TensorFlow models, they grow in every possible direction (CPU usage, memory usage, container image size, and so on). You don’t want that.</li><li>Let’s say your service uses multiple models written in different versions of TensorFlow. Using all those TensorFlow versions in your Python API at the same time is going to be a total mess.</li><li>You could of course wrap one model into one API. Then you would have one service per model and you can run different services on different hardware. Perfect! Except, this is what TensorFlow Serving ModelServer is doing for you. So don’t go wrap an API around your Python code (where you’ve probably imported the entire tf library, tf.contrib, opencv, pandas, numpy, …). TensorFlow Serving ModelServer does that for you.</li><li>Most importantly, the TensorFlow team wrote TensorFlow Serving and the ModelServer for a reason. They are probably better than you when writing a high performance serving system. Use it!</li></ul><p>Great! Now that we’ve agreed on using TensorFlow Serving ModelServer to serve our model. It’s time to save the model. Here’s an outline for the rest of this section:</p><ul><li>First we have to grab the <strong>input</strong> and <strong>output</strong> tensors.</li><li>Create a <strong>signature definition</strong> from the input and output tensors. The signature definition is what the model builder use in order to save something a model server can load.</li><li>Save the model at a specified path where a server can load it from.</li></ul><p>First we have to figure out which nodes are input and output nodes. Our simple math model is <strong>a + b = add</strong>. If we replace the constant, we get <strong>a + 10 = add</strong>. From placeholder <strong>a</strong> and operation <strong>add</strong>, we can grab input tensor <strong>a:0</strong> and output tensor <strong>add:0</strong>.</p><p>In the snippet below we grab our input and output tensors, and build a signature definition that we will use to save the model.</p><pre># Pick out the model input and output<br>a_tensor = sess.graph.get_tensor_by_name(placeholder_name + &#39;:0&#39;)<br>sum_tensor = sess.graph.get_tensor_by_name(operation_name + &#39;:0&#39;)<br><br>model_input = build_tensor_info(a_tensor)<br>model_output = build_tensor_info(sum_tensor)<br><br># Create a signature definition for tfserving<br>signature_definition = signature_def_utils.build_signature_def(<br>    inputs={placeholder_name: model_input},<br>    outputs={operation_name: model_output},<br>    method_name=signature_constants.PREDICT_METHOD_NAME)</pre><p>We’re going to pause here for a few seconds. There is an important thing about input and output naming I want to point out.</p><p>The names <em>placeholder_name</em> and <em>operation_name</em> are the strings ‘a’ and ‘add’. You can use whatever strings you like to name the input and output of your models; ‘inputs’ and ‘outputs’ are also fine names. In fact TensorFlow has defined some constants for us that we can use! These constants are defined in <a href="https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/saved_model/signature_constants.py"><em>signature_constants.py</em></a><em>, </em>and there are three sets of constants: <strong>predictions</strong>, <strong>classification</strong> and <strong>regression</strong>. If you peek into <em>signature_constants.py</em> you’ll see that the input and output constants are ‘inputs’ and ‘outputs’.</p><p>I am going to use ‘a’ and ‘add’ to show you that you don’t have to specify TensorFlows constants here. If you ever write a model with multiple inputs and outputs you have to come up with names yourself. In most cases using the constants makes sense. I am just showing you that it’s not required for your models to work with TensorFlow Serving ModelServer.</p><p>A place where you actually <strong>have</strong> to use a string TensorFlow has defined is the third keyword parameter called <em>method_name.</em> It must be one of <em>tensorflow/serving/predict</em>, <em>tensorflow/serving/classify</em>, <em>tensorflow/serving/regress</em>. They also defined in <em>signature_constants.py</em> as CLASSIFY_METHOD_NAME, PREDICT_METHOD_NAME and REGRESS_METHOD_NAME. I am not sure exactly why we need this to save the model, and the documentation is quite poor here. The model server will give you an error if you don’t use one of these constants.</p><p>Alright, let’s finish saving the model. We pass the path where we want the model stored to the builder. The last part of the path is the model version. For a model that is actually trained on real data, use this number to increase the model version when you retrain them.</p><p>Again, we will pass some constants that TensorFlow has defined for us to the builder. Pass the signature definition we defined in the previous snippet and save the model.</p><pre>builder = saved_model_builder.SavedModelBuilder(&#39;./models/simple_model/1&#39;)<br><br>builder.add_meta_graph_and_variables(<br>    sess, [tag_constants.SERVING],<br>    signature_def_map={<br>        signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY:<br>            signature_definition<br>    })<br><br># Save the model so we can serve it with a model server :)<br>builder.save()</pre><p>Run the code and you should have your model ready for serving. If the code runs without errors you can find the model in <em>models/simple_model/1</em>.</p><h3>3. Serving the model</h3><p>If you finished the two previous steps without trouble, we are ready to serve the model now.</p><p>First, there are many ways to serve your models with TensorFlow Serving. Feel free to dive into <a href="https://www.tensorflow.org/serving/">TensorFlow Serving</a> source code and build your own model server if you like to. I’ll stick to a simple CPU compiled server for the rest of the tutorial.</p><p>You can find the model server we’re going to use <a href="https://www.tensorflow.org/serving/setup">here</a>. It can be installed with apt-get, but I already built a Docker image with the server so I’ll use that. If you are not familiar with Docker or use a Linux system with apt you can install the server on your system.</p><p>To serve the model all you need to do is run the file <a href="https://github.com/epigramai/tfserving-simple-example/blob/master/run_model.sh"><em>run_model.sh</em></a>. I’ll take you through what’s going on when running that file.</p><pre>docker run -it -p 9000:9000 --name simple -v $(pwd)/models/:/models/ epigramai/model-server:light --port=9000 --model_name=simple --model_base_path=/models/simple_model</pre><p>The Docker image is publicly available and Docker wiIl download it for you when you run the script. If the script runs fine, you now have a running <em>container</em>. A container is basically an instance of an image. We pass a lot of options and flags here, so I’ll explain what they all do.</p><ul><li>When you do <em>docker run</em>, you run the image <em>epigramai/model-server:light</em>. The default <em>entrypoint</em> for this image is <em>tensorflow_model_server</em>. This means that when you run the container, you also start the model server.</li><li>Because the model is not built into the image (remember, the image is just the model server) we make sure the container can find the model by mounting (-v) the <em>models/</em> folder to the container.</li><li>The <em>-it</em> option basically tells docker to show you the logs right in the terminal and not run in the background. The name option is just the name of the container, this has nothing to do with TensorFlow or the model.</li><li>Then there’s the <em>-p</em> option, and this one is important. This option tells docker to map its internal port 9000 out to port 9000 of the outside world. The outside world in this case is your computer also known as localhost. If we omitted this option, the model server would serve your model on port 9000 inside the container, but you would not be able to send requests to it from your computer.</li><li>The three last flags are sent all the way to the model server. The <em>port</em> is 9000 (yep, the port we are mapping out to your machine). With the model_name flag we give our model a name. And with the last flag we tell the model server where the model is located. Again, the model is not in the image, but because we used the <em>-v</em> option and mounted the folder to the container, the model server can find the model inside the running container.</li></ul><p>❕ If it looks like I am mixing flags and options here, I am not. The <em>options</em> are for Docker (the ones with a single -) and the <em>flags</em> (port, model_name, model_base_path) are parameters to the model server.</p><p>If you see an output like this, you are now serving the model with TensorFlow Serving!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Srs4oS2OlOgg_q-ME0MCSw.png" /></figure><p>❕The model server installed in <em>epigramai/model-server:light</em> is compiled for newer CPUs. Most computer these days should be able to use it. If you get an error when you run the container, you could try this image instead: <em>epigramai/model-server:light-universal</em>. If you the read the install instructions on <a href="https://www.tensorflow.org/serving/setup">TensorFlow Serving pages</a>, you can also find <strong>tensorflow-model-server-universal</strong> there. That’s the same one I’ve built into <em>epigramai/model-server:light-universal</em>.</p><p>Congrats! You are now serving a model with TensorFlow Serving. I wish I could tell you that the model server is a normal HTTP server and that you can POST your data and get a response. Unfortunately, it’s not that simple. That’s why I chose to split this tutorial in two parts. The model server runs something called a <a href="https://grpc.io/">gRPC</a> service, and I’ll tell you need to know about it (and how to send requests) in part 2.</p><p>You probably want to clean up a bit. If you are familiar with Docker, you know that hitting <em>ctrl + c </em>should stop a running container. Unfortunately, it doesn’t work in this case (at least for me, and I am not really sure why). So open a new terminal tab and run:</p><pre>docker stop simple &amp;&amp; docker rm simple</pre><p>If you cannot open a new terminal tab, it’s possible to hit <em>ctrl + pq</em> to detach from the container to get a free terminal window.</p><p>I like to point things out, so I am going to point out that <em>simple</em> in the two commands above is related to Docker and is the <strong>container name</strong>. We gave the model and container the same name.</p><h3>Is this really production ready? What happened to GPUs?</h3><p>The short answer to this question is: Yes, the model you saved can be hosted on any TensorFlow Serving system. Yay! If your model is complex and should be served on accelerated hardware like GPUs, you cannot use the Docker image / the apt-get version of the model server we use in this tutorial. I am not going to include GPU serving in the scope of this tutorial, but I may write up a new blog post on that topic if I get any requests from readers.</p><p>Thank you for reading, I really hope you learned something! Leave a comment if you have questions or comments. Looking forward to see you in <a href="https://medium.com/p/682eaf7469e7/">part 2</a> 😃</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a79726f7c103" width="1" height="1" alt=""><hr><p><a href="https://medium.com/epigramai/tensorflow-serving-101-pt-1-a79726f7c103">TensorFlow Serving 101 pt. 1</a> was originally published in <a href="https://medium.com/epigramai">epigramAI</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>