<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: knarik</title>
    <description>The latest articles on DEV Community by knarik (@liracinai).</description>
    <link>https://dev.to/liracinai</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F909732%2F17eb84a0-1a53-4d09-a58b-b62c36c82fb2.png</url>
      <title>DEV Community: knarik</title>
      <link>https://dev.to/liracinai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/liracinai"/>
    <language>en</language>
    <item>
      <title>Shipping large ML models with electron</title>
      <dc:creator>knarik</dc:creator>
      <pubDate>Tue, 11 Apr 2023 13:53:34 +0000</pubDate>
      <link>https://dev.to/modeinspect/shipping-large-ml-models-with-electron-5c96</link>
      <guid>https://dev.to/modeinspect/shipping-large-ml-models-with-electron-5c96</guid>
      <description>&lt;p&gt;by &lt;a href="https://twitter.com/matoantos" rel="noopener noreferrer"&gt;@matoantos&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How do I ship a large machine learning model with an electron app? Not so long ago, I couldn't find a resource to solving this problem, so I decided to do a write up of my experience, which could be useful for others. Let's dive right in.&lt;/p&gt;

&lt;p&gt;Since the beginning of our work on &lt;a href="https://acreom.com/" rel="noopener noreferrer"&gt;acreom&lt;/a&gt;, we wanted it to have an IDE-like experience with real-time autocomplete suggestions in the context of knowledge base and tasks.&lt;/p&gt;

&lt;p&gt;The first problem we decided to experiment with was to classify free text as a task or event. It turns out that building a binary classifier for such a use case is relatively easy; however, shipping it with Electron is the tricky part.&lt;/p&gt;

&lt;p&gt;But why ship it with electron in the first place? It mainly boils down to speed of inference and user privacy. Hence, the problem presents the following specs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fast inference (required for real-time suggestions)&lt;/li&gt;
&lt;li&gt;minimum memory footprint&lt;/li&gt;
&lt;li&gt;a good user experience means high accuracy with low false positive rate&lt;/li&gt;
&lt;li&gt;user privacy - no API calls, fully offline, shipped on the client.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The ML part: exploration &amp;amp; fine-tuning
&lt;/h2&gt;

&lt;p&gt;In spite of having no prior datasets available, this was a relatively fast and easy task. I have manually created a small dataset of roughly 1200 samples, where a task / event class looks like this: &lt;code&gt;code tomorrow morning&lt;/code&gt; and negative class like &lt;code&gt;code is simple&lt;/code&gt;, with a roughly 50/50 class balance.&lt;/p&gt;

&lt;p&gt;An interesting side observation was to learn the semantics of such examples where 2 opposite samples share the same words but not the meaning. Later on, this allowed me to take a few actions to increase the overall performance of the model. I left out the lemmatization in the preprocessing pipeline and created feature engineering that applies additional weights to queries which start with verbs or include time for example. I have used &lt;a href="https://github.com/Acreom/quickadd" rel="noopener noreferrer"&gt;quickadd&lt;/a&gt;, an open-source library for parsing time &amp;amp; date I have forked from ctparse (and upgraded with a lots of modifications).&lt;/p&gt;

&lt;p&gt;After many experiments with different techniques and models, I settled with a bi-directional &lt;a href="https://en.wikipedia.org/wiki/Long_short-term_memory" rel="noopener noreferrer"&gt;LSTM&lt;/a&gt; written in Pytorch. This worked surprisingly well considering the tiny dataset it was trained on. After some additional fine-tuning, I was happy to end up with &lt;a href="https://scikit-learn.org/stable/modules/generated/sklearn.metrics.f1_score.html" rel="noopener noreferrer"&gt;F1 scores&lt;/a&gt; around 0.95, which is in the production territory for this use case. Great, the model works. Now all I need is to figure out the electron stuff.&lt;/p&gt;

&lt;h2&gt;
  
  
  Figuring out the electron stuff
&lt;/h2&gt;

&lt;p&gt;This is where things get hairy. Firstly, The trained LSTM model with its custom word embeddings was not small in size by any means. Its dependencies, with custom word embedding and ~70k parameter model, had over 4GB in size all together!&lt;/p&gt;

&lt;p&gt;Secondly, I wanted to keep our ML development process lean and fast when it comes to shipping in production. A few fundamental building blocks were necessary, so I could build future models systematically.&lt;/p&gt;

&lt;p&gt;Okay, so maybe I can have some sort of an API interface written in python that would somehow communicate with the electron and it would all be freezed as a separate executable with the model, shipped alongside electron? Maybe this could work.&lt;/p&gt;

&lt;p&gt;Inspired by the IDE &lt;a href="https://microsoft.github.io/language-server-protocol/" rel="noopener noreferrer"&gt;language server protocol&lt;/a&gt;, I created an API interface between the electron and the Python ML interface. &lt;a href="https://zeromq.org/" rel="noopener noreferrer"&gt;ZeroMQ&lt;/a&gt; turned out be an invaluable resource as a fast and lightweight messaging queue between the two.&lt;/p&gt;

&lt;p&gt;Now all I needed was to freeze the Python interface into an executable that would accept requests from the electron, infer from model, and send response back.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/pyinstaller/pyinstaller" rel="noopener noreferrer"&gt;PyInstaller&lt;/a&gt; seemed like the most maintained and developed tool to freeze python script into an executable, so I went with it. As expected, the freezed interface with the model was gigabytes large, so I had to figure out how to squeeze this. Fortunately, &lt;a href="https://onnxruntime.ai/" rel="noopener noreferrer"&gt;Onnx&lt;/a&gt; worked wonders and packaged the model into an inference only state, so I could throw away the Pytorch and Torchtext dependencies when freezing with Pyinstaller.Now the size of the executable with the model was 43MB instead of 4GB.&lt;/p&gt;

&lt;p&gt;Pyinstaller throws a curveball every now and then with missing .dylib files in the process, but nothing that can't be figured out with symbolic links to the local dependencies. What did the trick was to offload heavy Pytorch and Torchtext libraries with their dependencies to the bare minimum so the script could work.&lt;/p&gt;

&lt;p&gt;Here's brief rundown of how all of this works:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2y2zwrkhc8gyqizrllfp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2y2zwrkhc8gyqizrllfp.png" alt="Image description" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;When the electron is opened for the first time, the main process retrieves available port and runs the ML executable, listening to the port. I have built in a retry logic for error handling and for disconnecting handlers on close.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The electron then sends an &lt;code&gt;initialize&lt;/code&gt; message through the ZeroMQ to initialize the ML model, so it listens for requests. This came as an additional logic to prevent sending requests to the executable which was not yet initialized in the step #1. After the initialization, it listens for queries as JSON objects. Here's a sample query:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'{"requestId":{ID}, "action":"infer","service":"classifier","data":{"data":"code tmrw 7-9pm"}}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;When initialized, the executable listens for messages with the &lt;code&gt;service&lt;/code&gt; type, reads its request, and runs it through the appropriate model for the inference.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Since this model is a binary classifier, the response propagated back through the messaging queue to the fronted of the application, like this:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'{"data": "1", "requestId":{ID}, "service": "classifier"}'

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;The Frontend takes care of triggering the visuals and converting the text into a task component upon the confirmation from the user within a timeout of the listener. The end result looks pretty solid!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcc779la3zp60fc6gkqm9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcc779la3zp60fc6gkqm9.gif" alt="Image description" width="1144" height="286"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This experiment went to production soon after, and while it's not the best and most desired UX implementation, it served with good learnings for future work.&lt;/p&gt;

&lt;p&gt;If you have any questions or feedback, feel free to reach me &lt;a href="mailto:martin@acreom.com"&gt;martin@acreom.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>tutorial</category>
      <category>ai</category>
      <category>electronjs</category>
    </item>
    <item>
      <title>Building startup local-first</title>
      <dc:creator>knarik</dc:creator>
      <pubDate>Tue, 28 Mar 2023 09:33:25 +0000</pubDate>
      <link>https://dev.to/modeinspect/building-startup-local-first-53gg</link>
      <guid>https://dev.to/modeinspect/building-startup-local-first-53gg</guid>
      <description>&lt;p&gt;by &lt;a href="https://twitter.com/matoantos" rel="noopener noreferrer"&gt;@matoantos&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the majority of the past 2 years, our small team of 5 (1 designer, 4 engineers) have been working remote-first. While this lately popular trend worked for us in the beginning, soon after we felt something was missing. It was when we set up our first office, the real change came. &lt;/p&gt;

&lt;p&gt;Looking back, it was one of the best decisions we’ve made. Here‘s what we have learned.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Communication is instant&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;When working remotely, we relied on Discord to handle all of our communication. This often meant that a meaningful chunk of our communication was async. We would occasionally miss notifications or had to coordinate our debugging and pair programming sessions. This often resulted in  communication delays , and made it more difficult for us to convey information real-time.&lt;/p&gt;

&lt;p&gt;Working in-person, most of these issues have disappeared. We are able to have more productive and efficient discussions, and we can clear up any misunderstandings or ambiguities more quickly.&lt;/p&gt;

&lt;p&gt;Being in an office setting can lead to distractions from others' conversations when you are trying to stay focused on your work. ANC headphones turned out to be an invaluable tool for us, serving as a 'do not disturb' indicator and enabling us to focus on deep work.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Shipping and learning faster&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Our productivity and learning curve velocity have risen significantly once we started working together in a physical space. We are shipping higher quality software, faster and more frequently. This is partly due to just being together and not having too many distractions in our own familiar space around us.&lt;/p&gt;

&lt;p&gt;Within the first weeks of working together we have found our rhythm and routine which further helps us being more focused on the work by minimizing the need to think about what to do during the day. Our day begins at around 9am by a focus period until 11:45am. After lunch we have a cooldown period until around 1:30pm. In the afternoon, we do another stint of focus until around 5pm to 6pm, interrupted only by the bi-weekly sync at 3pm.&lt;/p&gt;

&lt;p&gt;Another factor contributing to our faster delivery is the accountability we have for each other. If we do not deliver or do a poor job, we receive almost immediate feedback to do better. On the other hand, if we deliver good results, we receive positive feedback to stay on the right path.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Spending time together outside of work&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The serendipity of talking about work as well as life in general, during lunchtime or breaks, often led us to new perspectives and advancements in the development process. It also does wonders for alignment on different issues and the overall direction.&lt;/p&gt;

&lt;p&gt;Similarly, having random discussions about the task you are currently working on can help problem solving both by talking to another person about your problem, and perhaps letting things settle in your head, as well as hearing another opinion and point of view.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Pair programming&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Pair programming plays a big role in the development process. Whether we are stuck on an issue, or want to speed up making of a new feature, we use pair programming. It helps us both to code faster, as well as be more error proof.&lt;/p&gt;

&lt;p&gt;We also do pair VQA with our designer to do final adjustments on features. The effect of this is we always tweak the design just right and also learn to do UI better and speed up future development by understanding the concepts our app is based on.&lt;/p&gt;

&lt;p&gt;Pair programming sessions quite often (intentionally or otherwise) turn into open forums where everyone chips in with their opinion and perspective. Having such discussions serves to align ourselves on the issue, serves as an early feedback session, which in turn speeds up the development process.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Culture&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Building culture is easier in-person. As a small team building a startup from the ground, we work, eat and have fun together. Firstly, there’s so much serendipity that happens just by being side by side in this process. From exploring new ideas randomly to laughing together about how miserably we have failed at something - real-time. We of course could have done all of that remotely, but being present just adds a little bit of something magical to the equation.&lt;/p&gt;

&lt;p&gt;Secondly, we believe culture is a sum of everyone’s decisions. It’s how we approach things, handle situations and work together as a team. More importantly, we not only get to know each other by having a first row seat to this experience, we influence each other. Hiding behind a screen can water down this experience. &lt;/p&gt;

&lt;p&gt;Startup is an intense rollercoaster of highs and lows we get to experience - together. It would indeed suck if we sat in our own isolated cabins throughout the ride.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Takeaways&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The switch to a physical office has had numerous benefits for our team, and we have gathered some important lessons from the experience.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Communication is more effective making it easier to collaborate and coordinate.&lt;/li&gt;
&lt;li&gt;ANC headphones are an awesome 'do not disturb' indicator.&lt;/li&gt;
&lt;li&gt;Productivity and learning curve velocity increases with ideas and resources being shared more easily.&lt;/li&gt;
&lt;li&gt;Interacting throughout the day leads to improved development and improves alignment on different issues.&lt;/li&gt;
&lt;li&gt;Pair programming is more effective when done in-person.&lt;/li&gt;
&lt;li&gt;Working in a physical office helps us build a culture and makes the whole experience more enjoyable.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;➤➤➤   &lt;a href="https://acreom.com" rel="noopener noreferrer"&gt;https://acreom.com&lt;/a&gt; &lt;/p&gt;

</description>
      <category>devops</category>
      <category>productivity</category>
      <category>startup</category>
    </item>
  </channel>
</rss>
