<?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:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Bitsy AI Labs]]></title><description><![CDATA[👋 Hi, my name is Leigh. I'm building PrintNanny, an automation assistant for 3D printers. I write about embedded AI/ML products and my journey from a software Tech Lead to a hardware Founder.]]></description><link>https://bitsy.ai/</link><image><url>https://bitsy.ai/favicon.png</url><title>Bitsy AI Labs</title><link>https://bitsy.ai/</link></image><generator>Ghost 5.22</generator><lastBuildDate>Fri, 10 Apr 2026 19:38:25 GMT</lastBuildDate><atom:link href="https://bitsy.ai/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[3 Key Ingredients for Embedded Computer Vision Apps]]></title><description><![CDATA[I'll cover the key takeaways I learned from building PrintNanny.ai, a monitoring/automation system for 3D printer farms.]]></description><link>https://bitsy.ai/3-key-ingredients-for-embedded-computer-vision-apps/</link><guid isPermaLink="false">63cf155fef6e2100016e69e9</guid><category><![CDATA[Computer Vision]]></category><category><![CDATA[PrintNanny]]></category><category><![CDATA[TensorFlow Lite]]></category><category><![CDATA[Object Detection]]></category><category><![CDATA[Tech Lead -> Founder]]></category><dc:creator><![CDATA[Leigh Johnson]]></dc:creator><pubDate>Tue, 24 Jan 2023 02:13:25 GMT</pubDate><content:encoded><![CDATA[<p>I&apos;ll cover the key takeaways I learned building PrintNanny.ai, a monitoring/automation system for 3D printer farms.</p><h1 id="introduction">Introduction</h1><p>&#x1F44B; Hi, my name is Leigh and I&apos;m the founder of <a href="https://printnanny.ai/">PrintNanny.ai</a>. I&apos;m currently building PrintNanny OS, a Linux distribution focused on the automation and monitoring of 3D printer farms.</p><p>I&apos;m going to walk you through the architecture of PrintNanny&apos;s <strong>failure detection system</strong>, which uses <strong>computer vision</strong> to monitor the health of a print job. The entire system runs <strong>offline </strong>on a <strong>Raspberry Pi</strong> with a camera because I designed PrintNanny with <strong>privacy</strong> and <strong>reliability</strong> in mind. </p><p>Running a modern computer vision stack on a device as tiny as a Raspberry Pi requires <strong>careful optimizations</strong>. </p><p>Here&apos;s a run-down of my secrets, so you can go out and build cool CV applications.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2023/01/Screenshot-2023-01-23-at-4.28.07-PM.png" class="kg-image" alt loading="lazy" width="2000" height="1124" srcset="https://bitsy.ai/content/images/size/w600/2023/01/Screenshot-2023-01-23-at-4.28.07-PM.png 600w, https://bitsy.ai/content/images/size/w1000/2023/01/Screenshot-2023-01-23-at-4.28.07-PM.png 1000w, https://bitsy.ai/content/images/size/w1600/2023/01/Screenshot-2023-01-23-at-4.28.07-PM.png 1600w, https://bitsy.ai/content/images/size/w2400/2023/01/Screenshot-2023-01-23-at-4.28.07-PM.png 2400w" sizes="(min-width: 720px) 720px"><figcaption>PrintNanny.ai continuously monitors 3D print jobs and uses time-series data to calculate a health score.</figcaption></figure><h1 id="1-stick-with-frameworks">1. Stick with Frameworks</h1><p>I&apos;ve been building embedded computer vision and machine learning applications for the past <strong>eight years.</strong> Around 2015, wrangling a state-of-the-art model often required writing an application harness in <strong>C/C++ </strong>to load data into your model and make decisions based on the model&apos;s predictions. </p><p>Frameworks like TensorFlow Lite (2017) provide an easier path, allowing you to train a neural network using TensorFlow&apos;s GPU/TPU accelerated operations and export the model to a format (.tflite) that can be used on a mobile/edge device.</p><p>In 2023, PrintNanny&apos;s failure detection stack depends on a few frameworks. These frameworks allow me to stand on the shoulders of giants and are essential to accomplishing so much as a solo developer/entrepreneur. </p><h2 id="printnannys-cvml-tech-stack">PrintNanny&apos;s CV/ML tech stack</h2><h3 id="tensorflow-lite"><a href="https://www.tensorflow.org/lite/guide">TensorFlow Lite</a></h3><p><a href="https://www.tensorflow.org/lite/guide">TensorFlow Lite</a> is a set of tools that enables <strong>on-device machine learning</strong> on mobile, embedded, and edge devices (like Raspberry Pi). </p><h3 id="gstreamer"><a href="https://gstreamer.freedesktop.org/">Gstreamer</a></h3><p><a href="https://gstreamer.freedesktop.org/">Gstreamer</a> is an open-source multimedia framework with a robust plugin. Gstreamer is written in C, but provides bindings in Go, Python, Rust, C++, C#.</p><p>PrintNanny depends on Gstreamer&apos;s ecosystem to implement:</p><ol><li>Sliding window aggregate operations, like calculating a histogram and standard deviation of observations in a 60-second lookback window. Implemented as a Gstreamer plugin element.</li><li>Interprocess communication between pipelines, using <a href="https://github.com/RidgeRun/gst-interpipe">gst-interpipe</a>.</li><li>Control audio/video streaming using TCP messages with <a href="https://developer.ridgerun.com/wiki/index.php/GStreamer_Daemon">Gstreamer Daemon</a> (GstD).</li></ol><h3 id="nnstreamer"><a href="https://nnstreamer.ai/">Nnstreamer</a></h3><p><a href="https://nnstreamer.ai/">Nnstreamer</a> provides a set of Gstreamer plugins compatible with neutral network frameworks like &#xA0;Tensorflow, Tensorflow-lite, Caffe2, PyTorch, OpenVINO, ARMNN, and NEURUN.</p><hr><h1 id="2-build-single-purpose-pipelines">2. Build Single-Purpose Pipelines</h1><p>Early iterations of PrintNanny&apos;s detection system were implemented as a <strong>single large Gstreamer pipeline</strong>, which performed the following tasks:</p><ol><li>Buffer raw camera data</li><li>Encode camera data with an H264 codec, then divide the encoded data into Real-time Transport Protocol (RTP) packets, write RTP packets to a UDP socket.</li><li>Re-encode camera data to an RGB pixel format, quantize each frame into 3 channels of 8-bit integers, then feed normalized frames into an object detection model (TensorFlow Lite). </li><li>Aggregate TensorFlow Lite inference results over a sliding 30-60 second window. </li><li>Draw a bounding box overlay from TensorFlow Lite inference results, encode with H264 codec, and synchronize RTP packetization with real-time camera stream</li><li>Write a JPEG-encoded snapshot to disk every n seconds.</li></ol><p><code>tee</code> elements were used to split the pipeline stream into branches. Unfortunately, this meant that a <strong>performance issue ANYWHERE in the pipeline</strong> could cause bottlenecks and blockages for the entire system.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2023/01/one-pipeline-to-rule-them-all-is-a-terrible-idea.gif" class="kg-image" alt loading="lazy" width="1467" height="756" srcset="https://bitsy.ai/content/images/size/w600/2023/01/one-pipeline-to-rule-them-all-is-a-terrible-idea.gif 600w, https://bitsy.ai/content/images/size/w1000/2023/01/one-pipeline-to-rule-them-all-is-a-terrible-idea.gif 1000w, https://bitsy.ai/content/images/2023/01/one-pipeline-to-rule-them-all-is-a-terrible-idea.gif 1467w" sizes="(min-width: 720px) 720px"><figcaption>PrintNanny.ai&apos;s early implementations crammed all functionality into one large Gstreamer pipeline.&#xA0;</figcaption></figure><p>Today, PrintNanny&apos;s pipelines have been broken into single-purpose segments using the <a href="https://github.com/RidgeRun/gst-interpipe">gst-interpipe</a> plugin. <a href="https://github.com/RidgeRun/gst-interpipe">Gst-interpipe</a> provides inter-process communication between Gstreamer pipelines, allowing buffers and events to flow between two or more independent pipelines.</p><p>PrintNanny runs up to 8 single-purpose pipelines, which can be debugged and tuned independent of each other. </p><hr><h1 id="3-put-commoditized-ml-into-production-asap">3. Put Commoditized ML into Production ASAP</h1><p><strong>Before I started training bespoke neural networks</strong> for PrintNanny.ai, I spent $200 to train a Google AutoML Vision model using data I scraped from YouTube. </p><p>This model was used in production for 1.5 years, while I built and validated other parts of PrintNanny (like the core operating system, PrintNanny OS). </p><p>Check out my talk at <a href="https://youtu.be/eJqV5nPjOZQ?t=8602">TensorFlow Everywhere North America (2021)</a> for a deep dive into the prototype process for PrintNanny&apos;s detection system. The tl;dr is:</p><ol><li>Scrape existing timelapse data from YouTube</li></ol><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2023/01/Screenshot-2023-01-23-at-5.12.46-PM.png" class="kg-image" alt loading="lazy" width="2000" height="1119" srcset="https://bitsy.ai/content/images/size/w600/2023/01/Screenshot-2023-01-23-at-5.12.46-PM.png 600w, https://bitsy.ai/content/images/size/w1000/2023/01/Screenshot-2023-01-23-at-5.12.46-PM.png 1000w, https://bitsy.ai/content/images/size/w1600/2023/01/Screenshot-2023-01-23-at-5.12.46-PM.png 1600w, https://bitsy.ai/content/images/size/w2400/2023/01/Screenshot-2023-01-23-at-5.12.46-PM.png 2400w" sizes="(min-width: 720px) 720px"><figcaption>Before PrintNanny had hundreds of users, I had to scrape YouTube for training data.</figcaption></figure><p>2. Train a baseline model using <a href="https://cloud.google.com/vision/automl/docs/label-images-edge-model">AutoML Vision Edge</a>.</p><p>I hand-labeled the first few hundred frames using <a href="https://github.com/microsoft/VoTT">Microsoft&apos;s Visual Object Tracking Tool</a>, then <a href="https://bitsy.ai/automate-bounding-box-annotation-with-tensorflow-and-automl/">trained a guidance model to automate labeling the next few thousand frames.</a></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2023/01/Screenshot-2023-01-23-at-5.12.37-PM.png" class="kg-image" alt loading="lazy" width="2000" height="1130" srcset="https://bitsy.ai/content/images/size/w600/2023/01/Screenshot-2023-01-23-at-5.12.37-PM.png 600w, https://bitsy.ai/content/images/size/w1000/2023/01/Screenshot-2023-01-23-at-5.12.37-PM.png 1000w, https://bitsy.ai/content/images/size/w1600/2023/01/Screenshot-2023-01-23-at-5.12.37-PM.png 1600w, https://bitsy.ai/content/images/size/w2400/2023/01/Screenshot-2023-01-23-at-5.12.37-PM.png 2400w" sizes="(min-width: 720px) 720px"><figcaption>I used Microsoft&apos;s&apos; Visual Object Tracking Tool to handle-label frames.</figcaption></figure><p>3. Evaluate the baseline model&apos;s training performance vs. real-world performance.</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2023/01/Screenshot-2023-01-23-at-5.12.24-PM.png" width="2000" height="1123" loading="lazy" alt srcset="https://bitsy.ai/content/images/size/w600/2023/01/Screenshot-2023-01-23-at-5.12.24-PM.png 600w, https://bitsy.ai/content/images/size/w1000/2023/01/Screenshot-2023-01-23-at-5.12.24-PM.png 1000w, https://bitsy.ai/content/images/size/w1600/2023/01/Screenshot-2023-01-23-at-5.12.24-PM.png 1600w, https://bitsy.ai/content/images/size/w2400/2023/01/Screenshot-2023-01-23-at-5.12.24-PM.png 2400w" sizes="(min-width: 1200px) 1200px"></div></div></div><figcaption>For just over $200 USD, I was able to train a baseline model for PrintNanny, which was used in production for more than a year.</figcaption></figure><p>In 2023 and beyond, I <strong>strongly recommend</strong> validating your idea with an off-the-shelf model or commodity machine learning service before dedicating resources to customization/optimization. </p><h1 id="thank-you-for-reading">Thank you for Reading!</h1><h2 id="work-printnannyai">Work @ PrintNanny.ai</h2><p>If you enjoyed this post and want to work with me,<a href="https://bitsy-ai.notion.site/Founding-Software-Engineer-PrintNanny-ai-2837979bf2c54ffba54d8bc51ff5a9f6"> I&apos;m hiring a product engineer comfortable with Rust/Python/Typescript</a> (or eager to learn). No machine learning or embedded application experience is required. Remote ok.</p><hr><p><strong>Google supported this work by providing Google Cloud credit.</strong></p><p><br></p><p></p>]]></content:encoded></item><item><title><![CDATA[PrintNanny Newsletter [01.2023]]]></title><description><![CDATA[v0.5.x is live, reflecting on 2022, and looking forward to 2023.]]></description><link>https://bitsy.ai/printnanny-newsletter-jan-2023/</link><guid isPermaLink="false">63b8ac580141e60001673215</guid><category><![CDATA[Tech Lead -> Founder]]></category><category><![CDATA[PrintNanny]]></category><category><![CDATA[entrepreneur]]></category><dc:creator><![CDATA[Leigh Johnson]]></dc:creator><pubDate>Sat, 07 Jan 2023 01:31:57 GMT</pubDate><content:encoded><![CDATA[<p>v0.5.x is live, reflecting on 2022, and looking forward to 2023.</p><p>tl;dr:</p><ul><li><a href="https://printnanny.ai/docs/release-history/0.5.x-emerald-langdale/">PrintNanny OS v0.5.x is live (read the patch notes).</a></li><li><a href="https://printnanny.ai/shop/founding-membership">15 PrintNanny Founding Member spots still available (January cohort)</a></li><li><a href="https://www.eventbrite.com/e/women-in-3d-printing-silicon-valley-happy-hour-tickets-471462215177">I organized a happy hour to launch the Silicon Valley chapter of Women in 3D Printing (RSVP if you&apos;re in the Bay area)</a>.</li><li>2023 is going to be &#x1F525;</li></ul><p>Full post moved to <a href="https://printnanny.ai/blog/printnanny-newsletter-jan-2023/">https://printnanny.ai/blog/printnanny-newsletter-jan-2023/</a></p>]]></content:encoded></item><item><title><![CDATA[I needed a sold-out SDWire board, so I learned how to fab PCBs]]></title><description><![CDATA[I needed an SDWire board but couldn't buy one due to imported component shortages. So I learned how to fab PCBs with components made in the USA. ]]></description><link>https://bitsy.ai/preorder-printnanny-sdwire/</link><guid isPermaLink="false">6313cba487c91a0001179a47</guid><category><![CDATA[PrintNanny]]></category><category><![CDATA[PCB]]></category><category><![CDATA[KiCAD]]></category><category><![CDATA[Open-source Hardware]]></category><category><![CDATA[entrepreneur]]></category><category><![CDATA[Tech Lead -> Founder]]></category><dc:creator><![CDATA[Leigh Johnson]]></dc:creator><pubDate>Thu, 17 Nov 2022 05:43:18 GMT</pubDate><media:content url="https://bitsy.ai/content/images/2022/08/SDWire-3D-front-v1.4-r1-2.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://bitsy.ai/content/images/2022/08/SDWire-3D-front-v1.4-r1-2.jpg" alt="I needed a sold-out SDWire board, so I learned how to fab PCBs"><p>&#x1F44B; My name is Leigh, and I&apos;m the founder of <a href="https://printnanny.ai/">PrintNanny.ai</a>. </p><p>I desperately needed a <strong>Tizen SDWire</strong> board to automate smoke tests for <strong>PrintNanny OS</strong>, a Linux distribution I created to manage 3D printers using a Raspberry Pi. The SDWire board would allow me to re-image the Raspberry Pi&apos;s SD card (without physically removing the SD card from the Pi). </p><p>Unfortunately, SDWire boards were <strong>completely sold-out. </strong>Everywhere! <strong>I was willing to pay a huge markup</strong> for just one of these gosh-darn boards ... but no one could sell me one. </p><p>Full post moved to <a href="https://printnanny.ai/blog/i-needed-a-sold-out-sdwire-board-so-i-learned-how-to-fab-pcbs/">https://printnanny.ai/blog/i-needed-a-sold-out-sdwire-board-so-i-learned-how-to-fab-pcbs/</a></p>]]></content:encoded></item><item><title><![CDATA[Open-Sourcing my Solo Founder Application to YC Winter 23 (PrintNanny.ai)]]></title><description><![CDATA[<p>Have you ever stayed up all night, revising a college application/essay right before the deadline? I didn&apos;t: I dropped out as a freshman (a story for another time). Writing an application loaded with this much upside is a new experience for me! </p><p>Maybe that&apos;s why</p>]]></description><link>https://bitsy.ai/open-sourcing-my-wc-23/</link><guid isPermaLink="false">631e98845c0c250001a922b1</guid><category><![CDATA[PrintNanny]]></category><category><![CDATA[News]]></category><dc:creator><![CDATA[Leigh Johnson]]></dc:creator><pubDate>Mon, 12 Sep 2022 02:51:40 GMT</pubDate><content:encoded><![CDATA[<p>Have you ever stayed up all night, revising a college application/essay right before the deadline? I didn&apos;t: I dropped out as a freshman (a story for another time). Writing an application loaded with this much upside is a new experience for me! </p><p>Maybe that&apos;s why I&apos;m obsessing over my Ycombinator application this weekend. I read that YC gets 15,000 - 20,000 applications per batch. The first round of reviewers might read 200 applications/day (oof). Most applications are discarded in 30 seconds (double-oof).</p><p><strong>What should I change to keep my YC app from being thrown out?</strong></p><p>I would appreciate your feedback, no matter who you are! I&apos;ve <a href="https://docs.google.com/document/d/1CW8k4rpiqJlLVOwv_JIK_ObLTBtaLX_qLUPRUTC6BmA/edit?usp=sharing">enabled comments on my draft</a> - if you have 5 minutes to skim. If you prefer email, send your thoughts to leigh+yc23@printnanny.ai</p><p></p><p></p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://docs.google.com/document/d/1CW8k4rpiqJlLVOwv_JIK_ObLTBtaLX_qLUPRUTC6BmA/edit?usp=sharing"><div class="kg-bookmark-content"><div class="kg-bookmark-title">[Leigh Johnson, PrintNanny.ai] YC Application Review</div><div class="kg-bookmark-description">Company Company name: PrintNanny Company URL: https://printnanny.ai/ What is your company going to make? Please describe your product and what it does or will do. PrintNanny is like having a personal assistant for a 3D printing business. There are 600,000 small manufacturers in the United States...</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://ssl.gstatic.com/docs/documents/images/kix-favicon7.ico" alt><span class="kg-bookmark-author">Google Docs</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://lh3.googleusercontent.com/8I3BFN5kMB-5I2gPitWpJPF1YpeBcDI_VXPhOiQ077jIrGz3rV-gm25JKiZMskZ9mz1HVdtf6nfVVA=w1200-h630-p" alt></div></a></figure>]]></content:encoded></item><item><title><![CDATA[Soft-launching an AI/ML Product as a Solo Founder]]></title><description><![CDATA[Technical deep dive into how I built Print Nanny, which uses computer vision to automatically detect 3D printing failures. ]]></description><link>https://bitsy.ai/launching-machine-learning-ai-startup-solo-founder/</link><guid isPermaLink="false">6313cba487c91a0001179a14</guid><category><![CDATA[AutoML]]></category><category><![CDATA[Computer Vision]]></category><category><![CDATA[PrintNanny]]></category><category><![CDATA[Object Detection]]></category><category><![CDATA[Prototypes]]></category><category><![CDATA[Raspberry Pi]]></category><category><![CDATA[TensorFlow Lite]]></category><category><![CDATA[TensorFlow]]></category><dc:creator><![CDATA[Leigh Johnson]]></dc:creator><pubDate>Mon, 19 Apr 2021 06:09:46 GMT</pubDate><media:content url="https://bitsy.ai/content/images/2021/05/PXL_20210213_195122145.MP--2-.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://bitsy.ai/content/images/2021/05/PXL_20210213_195122145.MP--2-.jpg" alt="Soft-launching an AI/ML Product as a Solo Founder"><p><strong>Technical deep dive</strong> into how I built <a href="https://www.print-nanny.com/" rel="noopener">Print Nanny</a>,<strong> which uses computer vision to automatically detect 3D printing failures. </strong>I&#x2019;ll cover each development phase: from <strong>minimum viable prototype</strong> to <strong>scaling up</strong> to meet customer demand.</p><p>Launching an AI/ML-powered product as a solo founder is a risky bet. Here&#x2019;s how I made the most of my limited time by defining a winning ML strategy and leveraging the right <strong>Google Cloud Platform</strong> services at each stage.</p><hr><p>For my birthday last spring, I bought myself what every gal needs: <strong>a fused filament fabrication system</strong> (AKA a 3D printer). I assembled the printer, carefully ran the calibration routines, and broke out my calipers to inspect the test prints.</p><p>Most of my prints were flawless! Occasionally though, a print would fail spectacularly. I setup <a href="https://octoprint.org/" rel="noopener">OctoPrint</a>, which is an open-source web interface for 3D printers. I often peeked at the live camera feed, wondering if this was a small taste of what new parents felt for high-tech baby monitors.</p><p>Surely there must be a better way to automatically monitor print health?</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/IMG_20200521_095442_MP-1.jpg" width="1000" height="1227" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/IMG_20200521_095442_MP-1.jpg 600w, https://bitsy.ai/content/images/2021/05/IMG_20200521_095442_MP-1.jpg 1000w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/00100lrPORTRAIT_00100_BURST20200523012619818_COVER--1-.jpg" width="1000" height="681" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/00100lrPORTRAIT_00100_BURST20200523012619818_COVER--1-.jpg 600w, https://bitsy.ai/content/images/2021/05/00100lrPORTRAIT_00100_BURST20200523012619818_COVER--1-.jpg 1000w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/MVIMG_20200624_121041--1-.jpg" width="1000" height="1333" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/MVIMG_20200624_121041--1-.jpg 600w, https://bitsy.ai/content/images/2021/05/MVIMG_20200624_121041--1-.jpg 1000w" sizes="(min-width: 720px) 720px"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/00000IMG_00000_BURST20200523151806158_COVER--1-.jpg" width="1000" height="935" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/00000IMG_00000_BURST20200523151806158_COVER--1-.jpg 600w, https://bitsy.ai/content/images/2021/05/00000IMG_00000_BURST20200523151806158_COVER--1-.jpg 1000w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/00000IMG_00000_BURST20200527124810495_COVER--1-.jpg" width="1000" height="1333" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/00000IMG_00000_BURST20200527124810495_COVER--1-.jpg 600w, https://bitsy.ai/content/images/2021/05/00000IMG_00000_BURST20200527124810495_COVER--1-.jpg 1000w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption>Images by Author. Every gal needs: good calipers and a fused filament deposition system. Most of my first prints came out flawless, with a few obvious exceptions.</figcaption></figure><h1 id="machine-learning-strategy">Machine Learning Strategy</h1><hr><p>Developing an AI/ML-backed product is an <strong>expensive</strong> proposition&#x200A;&#x2014;&#x200A;with a high risk of failure before realizing any return on investment.</p><p><a href="https://www.rackspace.com/sites/default/files/pdf-uploads/Rackspace-White-Paper-AI-Machine-Learning.pdf" rel="noopener">According to a global study conducted by Rackspace in January 2021,</a> 87% of data science projects <strong>never make it into production.</strong></p><p>In their efforts to overcome the odds, surveyed companies spent on average <strong>$1.06M on their machine learning initiatives. </strong>As a solo founder, I wasn&#x2019;t prepared to spend more than a million dollars on my idea!</p><p>Luckily, I&#x2019;m part of <a href="https://developers.google.com/community/experts" rel="noopener">Google&#x2019;s Developer Expert</a> program&#x200A;&#x2014;&#x200A;which is full of folks who <strong>love sharing their knowledge with the world</strong>. Going into this project, I had a good idea of how I could build a prototype quickly and inexpensively by<strong> </strong>outsourcing the &#x201C;undifferentiated heavy lifting&#x201D; to the right cloud services.</p><hr><h2 id="what-makes-machine-learning-a-risky-bet">What makes Machine Learning a risky bet?</h2><p>Among the 1,870 Rackspace study participants, here&#x2019;s a summary of the current use, future plans, and reasons for failure reported among AI/ML projects.</p><p>A few common themes are apparent among the challenges and barriers:</p><ul><li><strong>All things data:</strong> poor data quality, inaccessible data, lacking data stewardship, and inability to structure/integrate data in a meaningful way.</li><li><strong>Expertise and skilled talent are in short supply. </strong>The good news is: you can develop skills and intuition as you go. Everything I cover in this article can be achieved without an advanced degree!</li><li><strong>Lack of infrastructure to support AI/ML. </strong>I&#x2019;ll show you how to progressively build data and ML infrastructure from scratch.</li><li><strong>Challenges in measuring the business value of AI/ML.</strong></li></ul><p>I&#x2019;ll show you how I beat the odds (less than 1:10 chance of success) with a rapid iteration plan, made possible by leveraging the <strong>right technology and product choices</strong> at each maturity stage of an ML-powered product.</p><p>I&#x2019;ll also demonstrate how I did this <strong>without sacrificing the</strong> level of scientific rigor and real-world result quality most companies <strong>spend millions</strong> attempting to achieve&#x200A;&#x2014;&#x200A;all at a fraction of the cost!</p><p>Let&#x2019;s dive right in. &#x1F913;</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://cdn-images-1.medium.com/max/2160/0*7SneIqWCyRntU2tC.png" width="1106" height="474" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder"></div><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-16-at-7.55.48-PM.png" width="916" height="480" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screen-Shot-2021-04-16-at-7.55.48-PM.png 600w, https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-16-at-7.55.48-PM.png 916w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-16-at-8.03.45-PM.png" width="1096" height="844" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screen-Shot-2021-04-16-at-8.03.45-PM.png 600w, https://bitsy.ai/content/images/size/w1000/2021/05/Screen-Shot-2021-04-16-at-8.03.45-PM.png 1000w, https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-16-at-8.03.45-PM.png 1096w" sizes="(min-width: 720px) 720px"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-16-at-8.13.51-PM.png" width="914" height="780" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screen-Shot-2021-04-16-at-8.13.51-PM.png 600w, https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-16-at-8.13.51-PM.png 914w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-16-at-8.15.01-PM--1-.png" width="1106" height="474" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screen-Shot-2021-04-16-at-8.15.01-PM--1-.png 600w, https://bitsy.ai/content/images/size/w1000/2021/05/Screen-Shot-2021-04-16-at-8.15.01-PM--1-.png 1000w, https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-16-at-8.15.01-PM--1-.png 1106w" sizes="(min-width: 720px) 720px"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screenshot-from-2021-02-26-11-54-03.png" width="1567" height="1449" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screenshot-from-2021-02-26-11-54-03.png 600w, https://bitsy.ai/content/images/size/w1000/2021/05/Screenshot-from-2021-02-26-11-54-03.png 1000w, https://bitsy.ai/content/images/2021/05/Screenshot-from-2021-02-26-11-54-03.png 1567w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-16-at-8.19.26-PM.png" width="756" height="1270" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screen-Shot-2021-04-16-at-8.19.26-PM.png 600w, https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-16-at-8.19.26-PM.png 756w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption>Image credit: <a href="https://www.rackspace.com/sites/default/files/pdf-uploads/Rackspace-White-Paper-AI-Machine-Learning.pdf">Are Organizations Succeeding at AI and Machine Learning? </a>87% of data science projects never make it into production.&#xA0;</figcaption></figure><h1 id="define-problems-opportunities">Define Problems &amp; Opportunities</h1><p>What&#x2019;s the problem? 3D printers are so <strong>gosh-darn unreliable.</strong></p><p><strong>That&#x2019;s because 3D printer technology is still not yet mature! </strong>A few years ago, this technology was the domain of hobbyists. Industrial usage was limited to quick prototyping before committing to a more reliable manufacturing process.</p><p>Today, 3D printing is seeing more use in small-scale manufacturing. This trend was accelerated when existing manufacturing supply lines evaporated because of the COVID-19 pandemic.</p><p>An <strong>unreliable tool </strong>is a nuisance for a hobbyist but a <strong>potential liability</strong> for a small-scale manufacturing business!</p><p>In the following section, I&#x2019;ll outline the problems in this space and underscore the opportunities to solve these with an AI/ML product.</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://lh5.googleusercontent.com/Ru_mK9mmkdq5yF7Whnl2AJ_BtlpvceGZsaQ9xhP9b7PKKeChZP0WU91yZx2UTqoCnlc9D-RAlvf9XyGmcna4lq3sKHCzlYzvfKNXub9Eq6kwCjBT6J3ymcvzG1oz_DyA5Zzmoj5dwKE" width="1024" height="683" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder"></div><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/original-prusa-i3-mk3s-3d-printer--1-.jpg" width="800" height="800" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/original-prusa-i3-mk3s-3d-printer--1-.jpg 600w, https://bitsy.ai/content/images/2021/05/original-prusa-i3-mk3s-3d-printer--1-.jpg 800w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption>Image Credit: <a href="https://outofdarts.com/blogs/news/prusa-3d-print-farm-for-nerf-mods">Prusa 3D Print Farm for Nerf Mods</a> (left), <a href="https://www.3dnatives.com/en/creality-3dprintmill-conveyer-belt-231120204/#!">P</a>rusa MK3S Printer (right)</figcaption></figure><h2 id="problem-3d-printers-are-unreliable-">Problem: 3D Printers are Unreliable &#x1F928;</h2><p>What is it that makes 3D printers so unreliable?</p><ol><li>Print jobs take <strong>hours </strong>(sometimes<strong> days</strong>) to complete and can fail at any moment. Requires near-constant human monitoring.</li><li>Lacks <strong>closed-loop control </strong>of reliable industrial fabrication processes</li><li>The most common form of 3D printing involves <strong>heating material</strong> <strong>until molten</strong> at 190&#xB0;C&#x200A;&#x2014;&#x200A;220&#xB0;C. This is a fire hazard if left unattended!</li></ol><figure class="kg-card kg-image-card"><img src="https://bitsy.ai/content/images/2021/05/Fused-filament-fabrication-FFF-process-reproduced-with-permission-from-1.png" class="kg-image" alt="Soft-launching an AI/ML Product as a Solo Founder" loading="lazy" width="358" height="269"></figure><p><br></p><p><strong>Human error</strong> is a factor as well. </p><p>The instructions read by 3D printers are created from hand-configured settings, using an application known as a &#x201C;slicer.&#x201D; Developing an intuition for the right settings takes time and patience.</p><p>Even after I decided to prototype a failure detector, the strategic line of thinking didn&#x2019;t end there. I identified <strong>additional value propositions</strong> to test by with quick mock-ups, descriptive copy-writing, and surveys.</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://lh4.googleusercontent.com/-UgQd882C4wt046YhGMjqWhgnPujuVofJ3H8McReQBcTnD_nD6ftnHqgDCSmX7z84IT0l04aWupMrsQrehJ-sHfl6zDNT8bVGDYoqrG3tPWQEdSOym7UFUvvoCEdkWPSumC4cCZhnEo" width="1334" height="1047" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder"></div></div></div><figcaption>The dizzying number of hand-configured settings used by &quot;slicer&quot; software, which converts a 3D model to g-code. G-code instructions are widely used by CNC and other computer-aided manufacturing tools.</figcaption></figure><h2 id="problem-the-internet-is-unreliable-">Problem: the Internet is Unreliable! &#x1F631;</h2><p>Most small-scale manufacturers are operating from:</p><ol><li>Home (basement, garage, storage shed)</li><li>Warehouse or industrial space</li></ol><p>In many parts of the world, a <strong>constant upload stream</strong> from multiple cameras can saturate an internet connection&#x200A;&#x2014;&#x200A;it might not be possible or economical to maintain this.</p><p>To ensure Print Nanny would still function offline, I prioritized <strong>on-device inference</strong> in the prototype. This allowed me to test the idea with zero model-serving infrastructure and leverage my research in computer vision for small devices.</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screenshot-from-2021-02-26-14-57-26.png" width="952" height="295" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screenshot-from-2021-02-26-14-57-26.png 600w, https://bitsy.ai/content/images/2021/05/Screenshot-from-2021-02-26-14-57-26.png 952w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screenshot-from-2021-02-26-15-00-27.png" width="969" height="219" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screenshot-from-2021-02-26-15-00-27.png 600w, https://bitsy.ai/content/images/2021/05/Screenshot-from-2021-02-26-15-00-27.png 969w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption>I didn&apos;t want Print Nanny to join this club.</figcaption></figure><h2 id="problem-even-the-failures-are-unreliable-">Problem: Even the Failures are Unreliable &#x1F92A;</h2><p>No two 3D printing failures look alike! There are a number of reasons for this, including:</p><ol><li>Printer hardware is assembled by hand. The same printer model can produce wildly different results! Expert calibration is required to print uniform batches.</li><li>Most 3D printing materials are <strong>hygroscopic </strong>(water-absorbing), resulting in batch variation and defects as volatile as the weather! &#x1F327;&#xFE0F;</li><li>Many <strong>settings </strong>for slicing a <strong>3D model</strong> into X-Y-Z movement instructions. Choosing the right settings requires trial and error to achieve the best results.</li></ol><p>My machine learning strategy would need to allow for <strong>fast iteration</strong> and <strong>continuous improvement </strong>to build customer trust&#x200A;&#x2014;&#x200A;it didn&#x2019;t matter if the first prototype was not great, as long as <strong>subsequent ones showed improvement.</strong></p><p>At all costs, to <strong>stay nimble,</strong> I&#x2019;d need to avoid tactics that saddled me with<strong> </strong>a specific category of machine learning technical debt:</p><h4 id="changing-anything-changes-everything-cace-">Changing Anything Changes Everything (CACE)</h4><p>CACE is a principle proposed in <a href="https://papers.nips.cc/paper/2015/file/86df7dcfd896fcaf2674f757a2463eba-Paper.pdf" rel="noopener">Hidden Technical Debt in Machine Learning Systems</a>, referring to the <strong>entanglement of machine learning outcomes in a system.</strong></p><!--kg-card-begin: markdown--><blockquote>
<p>For instance, consider a system that uses features x1, ...xn in a model. If we change the input distribution of values in x1, the importance, weights, or use of the remaining n &#x2212; 1 features may all change. This is true whether the model is retrained fully in a batch style or allowed to adapt in an online fashion. Adding a new feature xn+1 can cause similar changes, as can removing any feature xj. No inputs are ever really independent.</p>
</blockquote>
<blockquote>
<p>Zheng recently made a compelling comparison of the state ML abstractions to the state of database technology [17], making the point that nothing in the machine learning literature comes close to the success of the relational database as a basic abstraction.</p>
</blockquote>
<p><a href="https://papers.nips.cc/paper/2015/file/86df7dcfd896fcaf2674f757a2463eba-Paper.pdf">Hidden Technical Debt in Machine Learning Systems</a></p>
<!--kg-card-end: markdown--><p>I&#x2019;ll explain how I side-stepped CACE and a few other common pitfalls like <strong>unstable data dependencies</strong> in the next section. I&#x2019;ll also reflect on the abstractions that saved me time and the ones which were a waste of time.</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/wXqzVok_large--1-.jpg" width="480" height="394" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder"></div><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screenshot-from-2021-02-26-15-57-40.png" width="1000" height="804" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screenshot-from-2021-02-26-15-57-40.png 600w, https://bitsy.ai/content/images/2021/05/Screenshot-from-2021-02-26-15-57-40.png 1000w" sizes="(min-width: 720px) 720px"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Srtinging-and-temperature--1-.jpg" width="628" height="472" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Srtinging-and-temperature--1-.jpg 600w, https://bitsy.ai/content/images/2021/05/Srtinging-and-temperature--1-.jpg 628w"></div><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/14325472593_6f89654c19_b--1---1-.jpg" width="1000" height="563" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/14325472593_6f89654c19_b--1---1-.jpg 600w, https://bitsy.ai/content/images/2021/05/14325472593_6f89654c19_b--1---1-.jpg 1000w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption>Image credit: <a href="https://www.vice.com/en/article/78xe9x/3d-printed-mistakes-are-inspiring-a-new-kind-of-glitch-art">3D-Printed Mistakes Are Inspiring a New Kind of Glitch Ar</a>t</figcaption></figure><h1 id="prototype-the-path">Prototype the Path</h1><p>As part of developing a solid product and machine learning strategy, I &#x201C;scoped the prototype down to a skateboard.&#x201D; I use this saying to describe the simplest form of transportation from Point A (the problem) to Point B (where the customer wants to go).</p><p>I&#x2019;ve seen machine learning projects fail by buying into / building solutions that are fully formed, like the car in the picture below. Not only is the feedback received on early iterations useless, but cancellation is also a risk <strong>before the full production run.</strong></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2021/05/9a18e341145e4a2d99ab1daa6e28448eba4ed270--1-.png" class="kg-image" alt="Soft-launching an AI/ML Product as a Solo Founder" loading="lazy" width="1024" height="701" srcset="https://bitsy.ai/content/images/size/w600/2021/05/9a18e341145e4a2d99ab1daa6e28448eba4ed270--1-.png 600w, https://bitsy.ai/content/images/size/w1000/2021/05/9a18e341145e4a2d99ab1daa6e28448eba4ed270--1-.png 1000w, https://bitsy.ai/content/images/2021/05/9a18e341145e4a2d99ab1daa6e28448eba4ed270--1-.png 1024w" sizes="(min-width: 720px) 720px"><figcaption>Image credit: <a href="https://mlsdev.com/blog/minimum-viable-product-examples">mlsdev.com</a></figcaption></figure><h2 id="minimum-awesome-product">Minimum Awesome Product</h2><p>Instead of building a fully-featured web app, I developed the prototype as<strong> </strong>a <strong>plugin</strong> for <a href="https://octoprint.org/" rel="noopener">OctoPrint</a>. OctoPrint provides a web interface for 3D printers, web camera controls, and a thriving community.</p><p>I distilled the <strong>minimum awesome product</strong> down to the following:</p><ol><li><strong>Train</strong> model to detect the following object labels:<br>{print, raft, spaghetti, adhesion, nozzle}</li><li><strong>Deploy</strong> predictor code to Raspberry Pi via OctoPrint plugin</li><li><strong>Calculate</strong> health score trends</li><li><strong>Automatically stop</strong> unhealthy print jobs.</li><li><strong>Provide feedback </strong>about Print Nanny&#x2019;s decisions &#x1F44D;&#x1F44E;</li></ol><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://lh6.googleusercontent.com/AF9X4kAVfGmmnTlMi20dT2_3rGnXJz8MT0YYVBzItqfM7HXojD3hETsQsWfbyfA2PtQ1s3Y8M1zLiTc1NFmBeccpqWKom9tanEFhSs7UkqgRkyA0yd4F0_llBW3IlsRuyJSXZnHGvXw" width="617" height="467" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder"></div><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screenshot-from-2021-02-23-22-21-56.png" width="611" height="674" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screenshot-from-2021-02-23-22-21-56.png 600w, https://bitsy.ai/content/images/2021/05/Screenshot-from-2021-02-23-22-21-56.png 611w"></div><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-17-at-1.21.31-PM.png" width="1000" height="880" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screen-Shot-2021-04-17-at-1.21.31-PM.png 600w, https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-17-at-1.21.31-PM.png 1000w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption>Images by Author. Click to view full-size.</figcaption></figure><h2 id="raw-dataset">Raw Dataset</h2><p>Acquiring quality labeled data is the hardest startup cost to any machine learning project! In my case, enthusiasts often upload timelapse videos to YouTube.</p><ul><li><a href="https://github.com/ytdl-org/youtube-dl" rel="noopener">Youtube-dl</a> to download 3D print timelapse videos (pay attention to the license!)</li><li><a href="https://github.com/microsoft/VoTT" rel="noopener">Visual Object Tagging Tool</a> to annotate images with bounding boxes.</li></ul><p><strong>Bonus efficiency unlocked:</strong> I figured out how to <strong>automatically suggest bounding boxes</strong> in the Virtual Object Tagging Tool using a <strong>TensorFlow.js </strong>model (exported from AutoML Vision).</p><p>I explain how to do this in <a href="https://bitsy.ai/automate-bounding-box-annotation-with-tensorflow-and-automl/" rel="noopener">Automate Image Annotation on a Small Budget. </a>&#x1F9E0;</p><p>Adjusting the guidance model&#x2019;s suggestions increased the number of images labeled per hour <strong>by a factor of 10</strong>, compared to drawing each box by hand.</p><h2 id="prepare-data-for-cloud-automl">Prepare Data for Cloud AutoML</h2><p>I often use Google AutoML products during the prototype phase.</p><p>These products are marketed towards folks with<strong> limited knowledge of machine learning</strong>, so <strong>experienced data scientists</strong> might not be familiar with this product line.</p><p><strong>Why would anyone pay for AutoML </strong>if they&#x2019;re perfectly capable of training a machine learning model on their own?</p><p>Here&#x2019;s why I tend to start with AutoML for every prototype:</p><ul><li><strong>Upfront and fixed cost</strong>, which is way less expensive than &quot;hiring myself&quot; </li></ul><p>Here&apos;s how much I paid to train Print Nanny&apos;s baseline model. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-17-at-11.20.24-AM.png" class="kg-image" alt="Soft-launching an AI/ML Product as a Solo Founder" loading="lazy" width="1000" height="76" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screen-Shot-2021-04-17-at-11.20.24-AM.png 600w, https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-17-at-11.20.24-AM.png 1000w" sizes="(min-width: 720px) 720px"><figcaption>I spent more on a two-year .ai domain lease ($149), to give you a cost anchor.&#xA0;</figcaption></figure><ul><li><strong>Return on investment </strong>is easy to realize! In my experience, algorithm / model performance is <strong>just one of many contributing factors</strong> to data product success. <strong>Discovering the other factors</strong> before committing expert ML resources is a <strong>critical part of picking winning projects.</strong></li><li><strong>Fast results</strong> - I had a production-ready model in under 24 hours, optimized and quantized for performance on an edge or mobile device. </li></ul><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-17-at-2.29.22-PM-1.png" width="1000" height="403" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screen-Shot-2021-04-17-at-2.29.22-PM-1.png 600w, https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-17-at-2.29.22-PM-1.png 1000w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-17-at-2.29.56-PM-2.png" width="1000" height="461" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screen-Shot-2021-04-17-at-2.29.56-PM-2.png 600w, https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-17-at-2.29.56-PM-2.png 1000w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption>Images by Author. The label distribution in my initial dataset (left). Google AutoML provides a GUI for exploring labeled data (right).&#xA0;</figcaption></figure><p>Besides Cloud AutoML Vision, Google provides AutoML services for:</p><p><strong><a href="https://cloud.google.com/automl-tables/docs">Tables</a> - </strong>a battery of modeling techniques (linear, gradient boosted trees, neural networks, ensembles) with automated feature engineering. </p><p><strong>Translation - </strong>train custom translation models.</p><p><strong>Video Intelligence - </strong>classify video frames and segment by labels.</p><p><strong><a href="https://cloud.google.com/automl/docs#automl-natural-language">Natural Language</a></strong></p><ul><li>Classification - predict a category/label </li><li>Entity Extraction - extract data from invoices, restaurant menus, tax documents, business cards, resumes, and other structured documents.</li><li>Sentiment Analysis - identify prevailing emotional opinion </li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2021/05/grant-auto-ml-example.gif" class="kg-image" alt="Soft-launching an AI/ML Product as a Solo Founder" loading="lazy" width="611" height="377" srcset="https://bitsy.ai/content/images/size/w600/2021/05/grant-auto-ml-example.gif 600w, https://bitsy.ai/content/images/2021/05/grant-auto-ml-example.gif 611w"><figcaption>Leveraging AutoML Natural Language Classification for scholarship discovery (acquired 2018). Image by Author.&#xA0;</figcaption></figure><h2 id="train-baseline-model">Train Baseline Model</h2><p>Cloud AutoML Vision Edge trains a TensorFlow model optimized for edge / mobile devices. Under the hood, AutoML&apos;s architecture &amp; parameter search uses reinforcement learning to find the ideal trade-off between speed and accuracy. <br><br>Check out <a href="http://ai.googleblog.com/2018/08/mnasnet-towards-automating-design-of.html">MnasNet: Towards Automating the Design of Mobile Machine Learning Models</a> and <a href="https://arxiv.org/abs/1807.11626">MnasNet: Platform-Aware Neural Architecture Search for Mobile</a> if you&apos;d like to learn more about the inner workings of Cloud AutoML Vision! <br></p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://lh6.googleusercontent.com/fNjVsnstDdDJdUCAgLa5qwkagVL4LnkIopJtyZnh3Knks-yMCYRP1-uV2U0GS9Fh7-Jve0_jkERrUtBOl8iyUxcYzDeHjyaxH6lGl36YyjdN3-4piha4uqgNTMc7EBSyeelYFbPvUjA" width="833" height="918" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder"></div><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-17-at-4.01.51-PM--1-.png" width="1000" height="561" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screen-Shot-2021-04-17-at-4.01.51-PM--1-.png 600w, https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-17-at-4.01.51-PM--1-.png 1000w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption>Images by Author. Google AutoML Vision Edge incorporates speed/accuracy preferences in the reinforcement learning policy for neural network architecture search (left). Evaluating the baseline model (right).</figcaption></figure><h2 id="baseline-model-metrics">Baseline Model Metrics</h2><p>You can fetch AutoML model evaluation metrics via API, which is a handy way to compare candidate models against the baseline. Check out <a href="https://gist.github.com/leigh-johnson/ca8e7748b46dc2420369ec5de702d9be">this gist</a> to see my full example.</p><pre><code class="language-python">from google.cloud import automl
from google.protobuf.json_format import MessageToDict
import pandas as pd

project_id = &quot;your-project-id&quot;
model_id = &quot;your-automl-model-id&quot;

# Initialize AutoMl API Client
client = automl.AutoMlClient()

# Get the full path of the model
model_full_id = client.model_path(project_id, &quot;us-central1&quot;, model_id)

# Get all evaluation metrics for model
eval_metrics = client.list_model_evaluations(parent=model_full_id, filter=&quot;&quot;)

# Deserialize from protobuf to dict
eval_metrics = [MessageToDict(e._pb) for e in eval_metrics ]

# Initialize a Pandas DataFrame
df = pd.DataFrame(eval_metrics)</code></pre><h2 id="engineer-improvable-outcomes">Engineer Improvable Outcomes</h2><p>You might remember that my <strong>baseline model </strong>had a recall rate of 75% at a 0.5 confidence and IoU threshold. In other words, my model failed to identify roughly 1/4 objects in the test set. The real-world performance was even worse! &#x1F62C; </p><p>Luckily, offloading baseline model training to AutoML gave me time to think deeply about a <strong>winning strategy</strong> for <strong>continuous model improvement. </strong>After an initial brainstorm, I reduced my options to just 2 (very different) strategies. </p><h3 id="option-1-binary-classifier">Option #1 - Binary Classifier</h3><p>The first option is train a binary classifier to predict whether or not a print is failing at any single point in time: predict { fail, not fail }. </p><p>The decision to alert is based on the confidence score of the prediction. &#x1F514;</p><h3 id="option-2-time-series-ensemble">Option #2 - Time Series Ensemble</h3><p>The second option trains a multi-label object detector on a mix of positive, negative, and neural labels like { print, nozzle, blister }. </p><p>Then, a weighted health score is calculated from the confidence of each detected object.</p><p>Finally, a polynomial (trend line) is fit on a time-series view of health scores. </p><p>The decision to alert is based on the direction of the polynomial&apos;s slope and distance from the intercepts. &#x1F514; </p><hr><p>Binary classification is considered the &quot;hello world&quot; of computer vision, with ample examples using the MNIST dataset (classifying hand-written digits). My personal favorite is <a href="https://www.tensorflow.org/tutorials/keras/classification">fashion mnist</a>, which you can <a href="https://colab.research.google.com/github/tensorflow/docs/blob/master/site/en/tutorials/keras/classification.ipynb">noodle around with in a Colab notebook.</a></p><p>Unable to resist some good science, I hypothesized most data scientists and machine learning engineers would choose to implement a binary classifier first.</p><blockquote>I&apos;m developing a brand-new data product. To get over the initial production hump, I want to deploy a model right away and begin measuring / improving performance against real-world data.<br><br>If I&apos;m optimizing for rapid iteration and improvement, which approach should I take?</blockquote><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-17-at-10.32.51-AM-1.png" width="1000" height="562" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screen-Shot-2021-04-17-at-10.32.51-AM-1.png 600w, https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-17-at-10.32.51-AM-1.png 1000w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-17-at-10.33.01-AM.png" width="1000" height="564" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screen-Shot-2021-04-17-at-10.33.01-AM.png 600w, https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-17-at-10.33.01-AM.png 1000w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption>Images by Author. Binary classifiers with complex outcomes can be difficult to explain and interpret (left). Decision-making ensembles or stacked models enable introspection of learned/encoded information per component. (right)</figcaption></figure><p>Most** data scientists and machine learning engineers voted for option #1! <em>**Study awaiting peer review and pending decision </em>&#x1F923;</p><figure class="kg-card kg-image-card"><img src="https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-18-at-9.22.57-PM.png" class="kg-image" alt="Soft-launching an AI/ML Product as a Solo Founder" loading="lazy" width="654" height="210" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screen-Shot-2021-04-18-at-9.22.57-PM.png 600w, https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-18-at-9.22.57-PM.png 654w"></figure><p><strong>Against the wisdom of the crowd</strong>: why did I choose to implement <strong>option 2 </strong>as part of my winning machine learning strategy?</p><h3 id="option-1-changing-anything-changes-everything-cace-">Option 1: Changing Anything Changes Everything (CACE)</h3><p>To recap, a binary classifier predicts whether or not a print is failing at any single point in time: predict { fail, not fail }. </p><p>The model learns to encode a complex outcome, which has many determinants in the training set (and <strong>many more not yet seen by the model</strong>). </p><p>To improve this model, <strong>there are only few levers I can pull</strong>:</p><ul><li>Sample / label weight</li><li>Optimizer hyper parameters, like learning rate</li><li>Add synthetic and/or augmented data</li></ul><p>Changing any of the above <strong>changes all decision outcomes!</strong></p><h3 id="option-2-holistic-understanding-of-the-data">Option 2 - Holistic understanding of the data</h3><p>The second option has more moving pieces, but also more opportunities to introspect the data and each modeled outcome. </p><p>The components are:</p><ol><li>Multi-label object detector (MnasNet or MobileNet + Single-shot Detector).</li><li>Health score, weighted sum of detector confidence.</li><li>Fit polynomial (trend line) over a health score time-series.</li></ol><p>Instead of answering <strong>one complex question</strong>, this approach builds an algorithm from a series of <strong>simple questions:</strong></p><ul><li>What objects are in this image frame?</li><li>Where are the objects located?</li><li>How does confidence for &quot;defect&quot; labels compare to neutral or positive labels? This metric is a proxy for print health.</li><li>Where is the <strong>point of no return</strong> for a failing print job?</li><li>Is health score holding constant? Increasing? Decreasing? How fast (slope) and when did this change start occurring? (y-intercept).</li><li>How does sampling frequency impact the accuracy of the ensemble? Can I get away with sending fewer image frames over the wire?</li></ul><p>Each component can be interpreted, studied, and improved independently - and doing so helps me gain a <strong>holistic understanding</strong> of the data <strong>and draw additional conclusions about problems relevant to my business.</strong></p><p>The additional information is invaluable on my mission to <strong>continuously improve</strong> and <strong>build trust</strong> in the outcomes of my model. </p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/newplot--3-.png" width="1000" height="488" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/newplot--3-.png 600w, https://bitsy.ai/content/images/2021/05/newplot--3-.png 1000w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://lh4.googleusercontent.com/Gn69g01_-D_sln2pJ96kuazigST_OWvXlGRaDJla_BTeX4Y0HeTeu_i6pOinPjYWm8hRmdv8CATf1NiMWJS5E4UHByWawvWjyMSQ1Y5eKeGMn9bvNF5K5zykMzaUwOTCo2hp4Se3KUA" width="1600" height="676" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder"></div><div class="kg-gallery-image"><img src="https://lh3.googleusercontent.com/2_QXGh7bUAHrb8gk5T7irDDS9PgSEasgS0qp-ICDnsSR4AgKWe6FIpOMr0rFsmcmZoOUDF2v8ahnDyBfqduMWAYem0Ir9PxLOh-otlH0QEmHKYNZGd2K7J-ldH0tbg1E--3aGjw7XiA" width="1600" height="678" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder"></div></div></div><figcaption>Images by Author. Detection confidence broken out by label (left). Heath score time series (middle). Polynomial fit over cumulative health score series (right).&#xA0;</figcaption></figure><h3 id="deploy-the-prototype">Deploy the Prototype</h3><p>The first prototype of Print Nanny launched after less than <strong>2 weeks</strong> of development, and saw <strong>worldwide adoption</strong> within days. &#x1F92F;</p><figure class="kg-card kg-image-card"><img src="https://lh4.googleusercontent.com/IAfm9vHxVLd80N8j-t_kzSIIzSzW5FB7oC0Vx0m2k2xWnGWXUxqGJeVmbFlw-sOXNRsnqQ8kt_zLoXa4UTYqAvZqh4RLK2lGL84ZiJhIieGV5sysBPv5hAch-Fpb9NraZosZWDNrFlo" class="kg-image" alt="Soft-launching an AI/ML Product as a Solo Founder" loading="lazy"></figure><p>I used the following tools, tech stack, and services to deploy a proof of concept:</p><ul><li><a href="https://www.djangoproject.com/">Django</a>, <a href="https://www.django-rest-framework.org/">Django Rest Framework</a>, and <a href="https://github.com/pydanny/cookiecutter-django">Cookiecutter Django</a> to create web application managing closed Beta signups and invitations.</li><li><a href="https://themes.getbootstrap.com/product/hyper-responsive-admin-dashboard-template/">Hyper Bootstrap theme</a> for a landing page and UI elements.</li><li><a href="https://cloud.google.com/kubernetes-engine">Google Kubernetes Engine</a> to host the webapp.</li><li><a href="https://cloud.google.com/sql">Cloud SQL</a> for PostgreSQL for a database with automatic backups.</li><li><a href="https://cloud.google.com/memorystore">Google Memorystore</a> (Redis) for Django&apos;s build-in cache.</li><li><a href="https://cloud.google.com/vision/automl/docs/edge-quickstart">Google Cloud AutoML Vision for Edge</a> to train a low-cost and low-effort computer vision model optimized for mobile devices.</li><li><a href="https://www.tensorflow.org/lite">TensorFlow Lite</a> model deployed to a <a href="https://www.raspberrypi.org/">Raspberry Pi</a>, packaged as a plugin for <a href="https://octoprint.org/">OctoPrint</a>. </li></ul><p><strong>Tip:</strong> learning a web application framework will enable you test your ideas in front of your target audience. <a href="https://www.feldroy.com/products/two-scoops-of-django-3-x">Two Scoops of Django</a> by Daniel Feldroy &amp; Audrey Foldroy is a no-nonsense guide to the Django framework. &#x1F49C;</p><h3 id="green-lighting-the-next-phase">Green-lighting the Next Phase</h3><p>After two weeks (and for a few hundred bucks), I was able to put my prototype in front of an audience and begin collecting feedback. A few vanity metrics:</p><ul><li>3.7k landing page hits</li><li>2k closed Beta signups</li><li>200 invitations sent (first cohort)</li><li>100 avg. daily active users - wow!</li></ul><p>Besides the core failure detection system, I tested <strong>extremely </strong>rough mockups to learn more about the features that resonated with my audience.</p><figure class="kg-card kg-gallery-card kg-width-wide"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-17-at-4.51.00-PM.png" width="1000" height="612" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screen-Shot-2021-04-17-at-4.51.00-PM.png 600w, https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-17-at-4.51.00-PM.png 1000w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-17-at-4.47.11-PM.png" width="1000" height="489" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screen-Shot-2021-04-17-at-4.47.11-PM.png 600w, https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-17-at-4.47.11-PM.png 1000w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-17-at-4.47.22-PM.png" width="1000" height="345" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screen-Shot-2021-04-17-at-4.47.22-PM.png 600w, https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-17-at-4.47.22-PM.png 1000w" sizes="(min-width: 720px) 720px"></div></div></div></figure><h1 id="refine-results-add-value">Refine Results, Add Value</h1><p>The next section focuses on <strong>building trust</strong> in machine learning outcomes by refining the model&apos;s results and handling edge cases.</p><h2 id="build-for-continuous-improvement">Build for Continuous Improvement</h2><p>Two feedback mechanisms are used to flag opportunities for learning:</p><ol><li>When a failure is detected: <strong>&quot;Did Print Nanny make a good call?&quot;</strong> &#x1F44D; &#x1F44E;</li><li>Any video can be <strong>flagged for additional review </strong>&#x1F6A9;</li></ol><p>At this phase, I&apos;m sending flagged videos off to a queue to be manually annotated and incorporated into a <strong>versioned training dataset. </strong></p><p>I keep track of aggregate statistics about the dataset overall <strong>and</strong> flagged examples. </p><ul><li>Mean, median, mode, standard deviation of RGB channels</li><li>Subjective brightness (also called <a href="https://en.wikipedia.org/wiki/Relative_luminance">relative luminance</a>)</li><li><strong>Mean average precision</strong> with respect to<strong> intersection over union</strong>, with breakouts per label and among boxes with small dimensions (1/10 total area) vs large boxes (1/4 total area). <br>mAP iou = 0.5, mAP iou = 0.75, mAP small/large</li></ul><p>This lets me understand if my model is under-performing for certain lighting conditions, filament colors, and bounding box size - broken down per label. </p><figure class="kg-card kg-gallery-card kg-width-wide"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screenshot-from-2021-02-23-22-21-56-2.png" width="611" height="674" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screenshot-from-2021-02-23-22-21-56-2.png 600w, https://bitsy.ai/content/images/2021/05/Screenshot-from-2021-02-23-22-21-56-2.png 611w"></div><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screenshot-from-2021-04-11-14-48-53--1-.png" width="1187" height="1053" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screenshot-from-2021-04-11-14-48-53--1-.png 600w, https://bitsy.ai/content/images/size/w1000/2021/05/Screenshot-from-2021-04-11-14-48-53--1-.png 1000w, https://bitsy.ai/content/images/2021/05/Screenshot-from-2021-04-11-14-48-53--1-.png 1187w" sizes="(min-width: 720px) 720px"></div></div></div></figure><h2 id="restrict-area-of-interest">Restrict Area of Interest</h2><p>Soon after I deployed the prototype, I realized value from the flexibility of my failure detection ensemble. When I developed the model, I hadn&apos;t considered that most 3D printers are built with 3D-printed components. &#x1F926;&#x200D;&#x2640;&#xFE0F;</p><p>I added the ability to select an <strong>area of interest </strong>and excluded objects outside from health score calculations. </p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/ignore_mask_example--1-.gif" width="912" height="663" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/ignore_mask_example--1-.gif 600w, https://bitsy.ai/content/images/2021/05/ignore_mask_example--1-.gif 912w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-18-at-9.38.24-PM.png" width="1000" height="821" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screen-Shot-2021-04-18-at-9.38.24-PM.png 600w, https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-18-at-9.38.24-PM.png 1000w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption>&quot;You&apos;re not wrong, but you&apos;re definitely not right either.&quot; Image by Author.&#xA0;</figcaption></figure><h2 id="ingest-telemetry-data">Ingest Telemetry Data</h2><p>How did I go from <strong>on-device predictions</strong> to <strong>versioned datasets </strong>organized <strong>neatly in Google Cloud Storage?</strong></p><p>At first, I handled telemetry events with a REST API. It turns out, REST is not that great for a continuous stream of events over a very-unreliable connection. Luckily, there is a protocol designed for IoT use-cases called MQTT.</p><p>I used <a href="https://cloud.google.com/iot-core">Cloud IoT Core</a> to manage <strong>Raspberry Pi device identity</strong> and establish <strong>two-way</strong> <strong>device communication</strong> using MQTT message protocol. </p><p>MQTT describes three Quality of Service (QoS) levels:</p><ol><li>Message delivered at most once - QoS 0</li><li>Message delivered at least once - QoS 1</li><li>Message delivered exactly once - QoS 2</li></ol><p><a href="https://cloud.google.com/iot/docs/how-tos/mqtt-bridge#quality_of_service_qos"><strong>Note</strong>: Cloud IoT does not support QoS 2</a>! </p><p>Besides whisking away the complexity of managing MQTT infrastructure (like load-balancing and horizontal scaling), Cloud IoT Core provides even more value:</p><ul><li>Device Registry (database of device metadata and fingerprints).</li><li>JSON Web Token (JWT) authentication with HMAC signing.</li><li>MQTT messages automatically republished to Pub/Sub topics.</li></ul><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-17-at-6.58.38-PM-1.png" width="1000" height="501" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screen-Shot-2021-04-17-at-6.58.38-PM-1.png 600w, https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-17-at-6.58.38-PM-1.png 1000w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-17-at-6.51.31-PM-1.png" width="1000" height="559" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screen-Shot-2021-04-17-at-6.51.31-PM-1.png 600w, https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-17-at-6.51.31-PM-1.png 1000w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screenshot-from-2021-02-24-01-00-55.png" width="1032" height="754" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screenshot-from-2021-02-24-01-00-55.png 600w, https://bitsy.ai/content/images/size/w1000/2021/05/Screenshot-from-2021-02-24-01-00-55.png 1000w, https://bitsy.ai/content/images/2021/05/Screenshot-from-2021-02-24-01-00-55.png 1032w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption>Images by Author. Registering a Raspberry Pi running OctoPrint (left). Creating a key pair and CloudIoT Device (middle). CloudIoT&apos;s Device Registry (right).</figcaption></figure><p>After device telemetry messages reach Cloud Pub / Sub, integration into many other cloud components and services is possible.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cloud.google.com/pubsub/images/cps_integration.svg" class="kg-image" alt="Soft-launching an AI/ML Product as a Solo Founder" loading="lazy"><figcaption>Image Credit: <a href="https://cloud.google.com/pubsub/docs/overview">What is Pub/Sub?</a></figcaption></figure><h2 id="data-pipelines-data-lake">Data Pipelines &amp; Data Lake</h2><p>Print Nanny&apos;s data pipelines are written with <a href="https://beam.apache.org/">Apache Beam</a>, which supports writing both streaming and batch processing jobs. Beam is a high-level <strong>programming model</strong>, with SDKs implemented in Java (best), Python (getting there), and Go (alpha). The Beam API is used to build a portable graph of parallel tasks.</p><p>There are many <strong>execution engines</strong> for Beam (called <strong>runners</strong>). Cloud Dataflow is a managed runner, with automatic horizontal auto-scaling. I develop pipelines with Beam&apos;s bundled DirectRunner locally, then push a container image for use with Cloud Dataflow.</p><p>I could easily go on for a whole blog post (or even series) about getting started with Apache Beam! Comment below if you&apos;d be interested in reading more about writing machine learning pipelines with <strong>TensorFlow Extended (TFX)</strong> and <strong>Apache Beam.</strong></p><hr><h3 id="pipelines-flow-into-a-data-lake">Pipelines flow into a Data Lake</h3><p>My first pipeline (left) is quite large - that&apos;s ok! I&apos;ll end up breaking it apart into more common components as needed. </p><p>The key functionality of this pipeline:</p><ul><li>Reading device telemetry data from Pub/Sub</li><li>Windowing and enriching data stream with device calibration and other metadata</li><li>Packing TF Records and Parquet tables</li><li>Writing raw inputs and windowed views to Google Cloud Storage </li><li>Maintaining aggregate metrics across <strong>windowed views</strong> of the data stream.</li></ul><p>On the right is a simpler pipeline, which renders .jpg files into an .mp4 video.</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/print-nanny-pipeline-1.gif" width="1245" height="864" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/print-nanny-pipeline-1.gif 600w, https://bitsy.ai/content/images/size/w1000/2021/05/print-nanny-pipeline-1.gif 1000w, https://bitsy.ai/content/images/2021/05/print-nanny-pipeline-1.gif 1245w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-18-at-8.30.27-AM.png" width="508" height="1092" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder"></div></div></div><figcaption>Image by Author. A large Apache Beam application sinks windowed data views into Google Cloud Storage, viewed in Google Cloud DataFlow&apos;s job graph (left). A smaller application render a video from JPEG frames. (right).&#xA0;</figcaption></figure><h2 id="additional-automl-model-training">Additional AutoML Model Training</h2><p>When scaling up a prototype, I try to get a sense for the <strong>qualitative impact</strong> of performance improvement from the perspective of my customers. <strong>Without this anchor</strong>, it&apos;s easy to fall into the trap of <strong>early optimization.</strong></p><p>In my experience, improving a performance indicator is the easy part - the hard part is understanding:</p><ul><li>Is the performance indicator / metric a good proxy for actual or perceived value?</li><li>If not... why does this metric not reflect real-world value? Can I formulate and study a more expressive metric?</li><li>When will I see diminishing returns for the time invested?</li></ul><p>I leveraged Cloud AutoML Vision again here, this time training on a blended dataset from the beta cohort and YouTube.</p><p>At this stage, I manually analyzed <strong>slices of the data</strong> to understand if the system underperforms on<strong> certain printer models </strong>or <strong>material types.</strong></p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-18-at-11.20.32-AM.png" width="1000" height="553" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screen-Shot-2021-04-18-at-11.20.32-AM.png 600w, https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-18-at-11.20.32-AM.png 1000w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-18-at-11.16.30-AM-2.png" width="1000" height="486" loading="lazy" alt="Soft-launching an AI/ML Product as a Solo Founder" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screen-Shot-2021-04-18-at-11.16.30-AM-2.png 600w, https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-18-at-11.16.30-AM-2.png 1000w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption>Image by Author. Preferred types of filament (left). Preferred 3D printer brands (right).&#xA0;</figcaption></figure><h2 id="training-an-object-detector">Training an Object Detector</h2><p>Finally! This is where the <strong>real</strong> machine learning begins, right?</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-18-at-9.52.39-PM.png" class="kg-image" alt="Soft-launching an AI/ML Product as a Solo Founder" loading="lazy" width="1000" height="520" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screen-Shot-2021-04-18-at-9.52.39-PM.png 600w, https://bitsy.ai/content/images/2021/05/Screen-Shot-2021-04-18-at-9.52.39-PM.png 1000w" sizes="(min-width: 720px) 720px"><figcaption>Source: <a href="https://papers.nips.cc/paper/2015/file/86df7dcfd896fcaf2674f757a2463eba-Paper.pdf">Hidden Technical Debt in Machine Learning Systems</a></figcaption></figure><h3 id="tensorflow-model-garden">TensorFlow Model Garden</h3><p>I started from TensorFlow&apos;s <a href="https://github.com/tensorflow/models">Model Garden</a>. This repo contains reference implementations for many state-of-the-art architectures, as well as a few best practices for running training jobs.</p><p>The repo is divided into collections, based on the level of stability and support:</p><ul><li><a href="https://github.com/tensorflow/models/blob/master/official">Official.</a> Officially maintained, supported, and kept up to date with the latest TensorFlow 2 APIs by TensorFlow, optimized for readability and performance</li><li><a href="https://github.com/tensorflow/models/blob/master/research">Research</a>. Implemented and supported by researchers, TensorFlow 1 and 2.</li><li><a href="https://github.com/tensorflow/models/blob/master/community">Community</a>. Curated list of external Github repositories. </li></ul><p><strong>FYI: </strong>TensorFlow&apos;s <strong>official </strong>framework for vision is <a href="https://github.com/tensorflow/models/tree/master/official/vision/beta">undergoing an upgrade!</a> </p><p>The examples below refer to the Object Detection API, which is a framework in the <strong>research</strong> collection.</p><hr><h3 id="object-detection-api">Object Detection API</h3><p><strong><a href="https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/tf2_detection_zoo.md">TensorFlow 2 Detection Model Zoo</a></strong> is a collection of pre-trained models (COCO2017 dataset). The weights are a helpful initialization point, even if your problem is outside of the domain covered by COCO</p><p>My preferred architecture for <strong>on-device inference </strong>with <strong>Raspberry Pi</strong> is a <strong>MobileNet</strong> feature extractor / backbone with a Single-Shot Detector head. The ops used in these are compatible with the TensorFlow Lite runtime.</p><p>Check out the <a href="https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/tf2.md#quick-start">Quick Start guide </a>if you&apos;d like to see examples in action. </p><p>The Object Detection API uses protobuf files to configure training and evaluation (pictured below). Besides hyper-parameters, the config allows you to specify data augmentation operations (highlighted below). TFRecords are expected as input. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2021/05/object-detection-api-2.gif" class="kg-image" alt="Soft-launching an AI/ML Product as a Solo Founder" loading="lazy" width="1416" height="1687" srcset="https://bitsy.ai/content/images/size/w600/2021/05/object-detection-api-2.gif 600w, https://bitsy.ai/content/images/size/w1000/2021/05/object-detection-api-2.gif 1000w, https://bitsy.ai/content/images/2021/05/object-detection-api-2.gif 1416w" sizes="(min-width: 720px) 720px"><figcaption>Image by Author. Example Object Detection API training pipeline config</figcaption></figure><h3 id="logging-parameters-metrics-artifacts-with-mlflow">Logging parameters, metrics, artifacts with MLFlow</h3><p>Do your experiments and lab notes look like this hot mess of a notebook? This is what my experiment tracking looked like when I ported AnyNet from <a href="https://github.com/facebookresearch/pycls/tree/master/pycls/models">Facebook Research&apos;s model zoo</a> to <a href="https://github.com/leigh-johnson/tf-anynet">TensorFlow 2 / Keras</a>. &#xA0;</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2021/05/evernote-oh-god-1.gif" class="kg-image" alt="Soft-launching an AI/ML Product as a Solo Founder" loading="lazy" width="929" height="830" srcset="https://bitsy.ai/content/images/size/w600/2021/05/evernote-oh-god-1.gif 600w, https://bitsy.ai/content/images/2021/05/evernote-oh-god-1.gif 929w" sizes="(min-width: 720px) 720px"><figcaption>Image by Author. Notebook full of screenshots, metrics, manual experiment tracking.</figcaption></figure><p>I started using <a href="https://www.mlflow.org/docs/latest/index.html">MLFlow</a> to log model hyper-parameters, code versions, metrics, and store training artifacts in a central repository. it changed my life! </p><p><strong>Comment below if you&apos;d like to read a deep dive into my workflow for experiment tracking and training computer vision models!</strong></p><h1 id="thank-you-for-reading-">Thank you for reading! &#x1F33B;</h1><p><strong>There is no one-size-fits-all guide</strong> to building a successful machine learning system - not everything that worked for me is guaranteed to work for you. </p><p>My hope is that by explaining my decision-making process, I demonstrate where <strong>these foundational skills </strong>support a successful AI/ML product strategy.</p><ul><li>Data architecture - everything related to the movement, transformation, storage, and availability of data </li><li>Software engineering</li><li>Infrastructure and cloud engineering</li><li>Statistical modeling</li></ul><hr><p>Are you interested in becoming a Print Nanny beta tester?</p><h2 id="click-here-to-request-a-beta-invite"><a href="https://www.print-nanny.com/request-invite/">Click here to request a beta invite</a></h2><p> <br>Looking for more hands-on examples of Machine Learning for Raspberry Pi and other small devices? <a href="https://bitsy.ai/" rel="noopener nofollow">Sign up for my newsletter</a> to receive new tutorials and deep-dives straight to your inbox.</p><p><strong>Google supported this work by providing Google Cloud credit</strong></p>]]></content:encoded></item><item><title><![CDATA[3 Ways to Install TensorFlow 2 on Raspberry Pi]]></title><description><![CDATA[TensorFlow Lite on Raspberry Pi 4 can achieve performance comparable to NVIDIA's Jetson Nano at a fraction of the cost.]]></description><link>https://bitsy.ai/3-ways-to-install-tensorflow-on-raspberry-pi/</link><guid isPermaLink="false">6313cba487c91a0001179a1b</guid><category><![CDATA[TensorFlow Lite]]></category><category><![CDATA[Raspberry Pi]]></category><category><![CDATA[TensorFlow]]></category><dc:creator><![CDATA[Leigh Johnson]]></dc:creator><pubDate>Sun, 22 Nov 2020 01:36:31 GMT</pubDate><media:content url="https://bitsy.ai/content/images/2021/05/neat-lg@2x-ab285664a2cdd23b239e345cdb3821ee.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://bitsy.ai/content/images/2021/05/neat-lg@2x-ab285664a2cdd23b239e345cdb3821ee.jpg" alt="3 Ways to Install TensorFlow 2 on Raspberry Pi"><p>With the new Raspberry Pi 400 (image credit: <a href="https://www.raspberrypi.org/products/raspberry-pi-400">raspberrypi.org</a>) shipping worldwide, you might be wondering: can this little powerhouse board be used for Machine Learning?</p><p>The answer is, <strong>yes! </strong>TensorFlow Lite models running on Raspberry Pi 4 boards can achieve performance comparable to NVIDIA&apos;s Jetson Nano board. If you add a <a href="https://coral.ai/products/accelerator/">The Coral Edge TPU USB Accelerator</a>, you can achieve <em>real-time</em> performance with state-of-the-art neural network architectures like <a href="https://arxiv.org/abs/1801.04381">MobileNetV2</a>. </p><p>This performance boost unlocks interesting offline TensorFlow applications, like <a href="https://bitsy.ai/real-time-object-tracking-with-tensorflow--raspberry-pi--and-pan-tilt-hat/">detecting and tracking a moving object.</a> Offline inference is done entirely on the Raspberry Pi. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2021/05/1_eS8nkjQHmLUSvTy1tMTAPA.jpeg" class="kg-image" alt="3 Ways to Install TensorFlow 2 on Raspberry Pi" loading="lazy" width="740" height="528" srcset="https://bitsy.ai/content/images/size/w600/2021/05/1_eS8nkjQHmLUSvTy1tMTAPA.jpeg 600w, https://bitsy.ai/content/images/2021/05/1_eS8nkjQHmLUSvTy1tMTAPA.jpeg 740w" sizes="(min-width: 720px) 720px"><figcaption>Image Credit: <a href="https://www.hackster.io/news/benchmarking-tensorflow-lite-on-the-new-raspberry-pi-4-model-b-3fd859d05b98">Benchmarking TensorFlow Lite on the New Raspberry Pi 4, Model B</a> by Alasdair Allan</figcaption></figure><h2 id="installation-is-half-the-battle-">Installation is Half the Battle &#x1F620;</h2><p>Cool! So you&apos;ve decided to build a TensorFlow application for your Raspberry Pi. The first thing you might try is...</p><pre><code class="language-shell">$ pip install --no-cache-dir tensorflow
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting tensorflow
  Downloading https://www.piwheels.org/simple/tensorflow/tensorflow-1.14.0-cp37-none-linux_armv7l.whl </code></pre><p><strong>Oh no</strong>: the version of TensorFlow installed is <strong>1.14. </strong>If you try to specify a higher version, you&apos;ll see an error like:</p><pre><code>$ pip install tensorflow==2.3.1
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting tensorflow==2.3.1
  Could not find a version that satisfies the requirement tensorflow==2.3.1 (from versions: 0.11.0, 1.12.0, 1.13.1, 1.14.0)
No matching distribution found for tensorflow==2.3.1
</code></pre><h2 id="can-i-use-tensorflow-2-">Can I use TensorFlow 2? &#x1F631; </h2><p><strong>Yes! </strong>You&apos;ll have to do a bit of extra work, but here are 3 different ways to use TensorFlow 2.x in your next Raspberry Pi project.</p><h3 id="official-tensorflow-wheels">Official TensorFlow Wheels</h3><p>The TensorFlow team builds and tests binaries for a variety of platform and Python interpreter combination, listed at t<a href="https://www.tensorflow.org/install/pip#package-location">ensorflow.org/install/pip#package-location</a>. Unfortunately, the Raspberry Pi wheels drift a bit behind the latest releases (2.3.1 and 2.4.0-rc2 at the time of this writing). Binaries for aarch64 are not officially supported yet.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2021/05/Screenshot-from-2020-11-21-15-53-11.png" class="kg-image" alt="3 Ways to Install TensorFlow 2 on Raspberry Pi" loading="lazy" width="2000" height="265" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screenshot-from-2020-11-21-15-53-11.png 600w, https://bitsy.ai/content/images/size/w1000/2021/05/Screenshot-from-2020-11-21-15-53-11.png 1000w, https://bitsy.ai/content/images/size/w1600/2021/05/Screenshot-from-2020-11-21-15-53-11.png 1600w, https://bitsy.ai/content/images/2021/05/Screenshot-from-2020-11-21-15-53-11.png 2144w" sizes="(min-width: 720px) 720px"><figcaption><a href="https://www.tensorflow.org/install/pip#system-requirements">Official TensorFlow wheels for Raspberry Pi</a></figcaption></figure><p>Install <code>tensorflow==2.3.0</code> with pip (requires Python 3.5, Raspberry Pi 3)</p><pre><code class="language-shell">$ pip install https://storage.googleapis.com/tensorflow/raspberrypi/tensorflow-2.3.0-cp35-none-linux_armv7l.whl</code></pre><h3 id="community-built-wheels">Community-built Wheels</h3><p>I maintain TensorFlow binaries that I build from source at<a href="https://github.com/bitsy-ai/tensorflow-arm-bin"> github.com/bitsy-ai/tensorflow-arm-bin</a>. Where possible, I recommend using the official wheels because they&apos;re more thoroughly tested.</p><p>Install <code>tensorflow==2.4.0-rc2</code> with pip (requires Python 3.7, Raspberry Pi 4)</p><pre><code class="language-shell">$ pip install https://github.com/bitsy-ai/tensorflow-arm-bin/releases/download/v2.4.0-rc2/tensorflow-2.4.0rc2-cp37-none-linux_armv7l.whl</code></pre><p>Install <code>tensorflow==2.4.0-rc2</code> with pip (requires Python 3.7, Raspberry Pi 4, 64-bit OS)</p><pre><code class="language-shell">$ pip install https://github.com/bitsy-ai/tensorflow-arm-bin/releases/download/v2.4.0-rc2/tensorflow-2.4.0rc2-cp37-none-linux_aarch64.whl</code></pre><h3 id="build-from-source">Build from Source</h3><p>Packaging a code-base is a great way to learn more about it (especially when things do not go as planned). I highly recommend this option! Building TensorFlow has taught me more about the framework&apos;s complex internals than any other ML exercise.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2021/05/Screenshot-from-2020-11-21-15-53-11--1-.png" class="kg-image" alt="3 Ways to Install TensorFlow 2 on Raspberry Pi" loading="lazy" width="2000" height="265" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screenshot-from-2020-11-21-15-53-11--1-.png 600w, https://bitsy.ai/content/images/size/w1000/2021/05/Screenshot-from-2020-11-21-15-53-11--1-.png 1000w, https://bitsy.ai/content/images/size/w1600/2021/05/Screenshot-from-2020-11-21-15-53-11--1-.png 1600w, https://bitsy.ai/content/images/2021/05/Screenshot-from-2020-11-21-15-53-11--1-.png 2144w" sizes="(min-width: 720px) 720px"><figcaption>Image Credit: <a href="https://blog.tensorflow.org/2019/04/mlir-new-intermediate-representation.html">MLIR: A new intermediate representation and compiler framework</a></figcaption></figure><p>The TensorFlow team recommends <strong>cross-compiling</strong> a Python wheel (a type of binary Python package) for Raspberry Pi [1]. For example, you can build a TensorFlow wheel for a 32-bit or 64-bit ARM processor on a computer running an x86 CPU instruction set.</p><p>Before you get started, install the following prerequisites on your build machine:</p><ol><li><a href="https://docs.docker.com/get-docker/">Docker</a></li><li><a href="https://github.com/bazelbuild/bazelisk">bazelisk</a> (bazel version manager, like nvm for Node.js or rvm for Ruby)</li></ol><p>Next, pull down TensorFlow&apos;s source code from git.</p><pre><code class="language-shell">$ git clone https://github.com/tensorflow/tensorflow.git
$ cd tensorflow</code></pre><p>Check out the branch you want to build using git, then run the following to build a wheel for a Raspberry Pi 4 running a 32-bit OS and Python3.7:</p><pre><code class="language-shell">$ git checkout v2.4.0-rc2
$ tensorflow/tools/ci_build/ci_build.sh PI-PYTHON37 \
    tensorflow/tools/ci_build/pi/build_raspberry_pi.sh</code></pre><p>For 64-bit support, add <code>AARCH64</code> as an argument to the <code>build_raspberry_pi.sh</code> script.</p><pre><code>$ tensorflow/tools/ci_build/ci_build.sh PI-PYTHON37 \
    tensorflow/tools/ci_build/pi/build_raspberry_pi.sh AARCH64</code></pre><p>The official documentation can be found at <a href="https://www.tensorflow.org/install/source_rpi">tensorflow.org/install/source_rpi</a>.</p><p>Grab a snack and water while you wait for the build to finish! On my Threadripper 3990X (64 cores, 128 threads), compilation takes roughly <strong>20 minutes. </strong></p><figure class="kg-card kg-embed-card"><blockquote class="twitter-tweet" data-width="550"><p lang="en" dir="ltr">threadripper go brrrr <a href="https://t.co/27NFDbLdhn">pic.twitter.com/27NFDbLdhn</a></p>&#x2014; Leigh (@grepLeigh) <a href="https://twitter.com/grepLeigh/status/1330309785678442503?ref_src=twsrc%5Etfw">November 22, 2020</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
</figure><h2 id="depend-on-tensorflow-2-x-in-a-python-package-">Depend on TensorFlow 2.x in a Python Package&#x1F40D;</h2><p>What if you want to distribute your TensorFlow application for other Raspberry Pi fans?</p><p>Here&apos;s just one way specify which TensorFlow binary your package needs in <code>setup.py</code>.</p><pre><code class="language-python"># setup.py
import os
from setuptools import setup,

arch = os.uname().machine

if arch == &apos;armv7l&apos;:
	tensorflow = &apos;tensorflow @ https://github.com/bitsy-ai/tensorflow-arm-bin/releases/download/v2.4.0-rc2/tensorflow-2.4.0rc2-cp37-none-linux_armv7l.whl&apos;
	
elif arch == &apos;aarch64&apos;:
	tensorflow = &apos;https://github.com/bitsy-ai/tensorflow-arm-bin/releases/download/v2.4.0-rc2/tensorflow-2.4.0rc2-cp37-none-linux_aarch64.whl&apos;	

elif arch == &apos;x86_64&apos;:
	tensorflow = &quot;tensorflow==2.4.0rc2&quot;
else:
	raise Exception(f&apos;Could not find TensorFlow binary for target {arch}. Please open a Github issue.&apos;)
    
 requirements = [
 	tensorflow,
    # specify additional package requirements here
 ]
 
 setup(
 	install_requires=requirements,
    # specify additional setup parameters here
 )
 </code></pre><!--kg-card-begin: html--><table class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip style="box-sizing: border-box; border-spacing: 0px; border-collapse: collapse; tab-size: 8; color: rgb(36, 41, 46); font-family: -apple-system, BlinkMacSystemFont, &quot;Segoe UI&quot;, Helvetica, Arial, sans-serif, &quot;Apple Color Emoji&quot;, &quot;Segoe UI Emoji&quot;; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;"><tbody style="box-sizing: border-box;"><tr style="box-sizing: border-box;"><td id="file-setup-py-LC1" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; color: var(--color-text-primary); overflow-wrap: normal; white-space: pre;"><span class="pl-c" style="box-sizing: border-box; color: rgb(106, 115, 125);"># setup.py</span></td></tr><tr style="box-sizing: border-box;"><td id="file-setup-py-L2" class="blob-num js-line-number" data-line-number="2" style="box-sizing: border-box; padding: 0px 10px; width: 50px; min-width: 50px; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; line-height: 20px; color: var(--color-diff-blob-num-text); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; user-select: none;"></td><td id="file-setup-py-LC2" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; color: var(--color-text-primary); overflow-wrap: normal; white-space: pre;"><span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">import</span> <span class="pl-s1" style="box-sizing: border-box;">os</span></td></tr><tr style="box-sizing: border-box;"><td id="file-setup-py-L3" class="blob-num js-line-number" data-line-number="3" style="box-sizing: border-box; padding: 0px 10px; width: 50px; min-width: 50px; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; line-height: 20px; color: var(--color-diff-blob-num-text); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; user-select: none;"></td><td id="file-setup-py-LC3" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; color: var(--color-text-primary); overflow-wrap: normal; white-space: pre;"><span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">from</span> <span class="pl-s1" style="box-sizing: border-box;">setuptools</span> <span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">import</span> <span class="pl-s1" style="box-sizing: border-box;">setup</span></td></tr><tr style="box-sizing: border-box;"><td id="file-setup-py-L4" class="blob-num js-line-number" data-line-number="4" style="box-sizing: border-box; padding: 0px 10px; width: 50px; min-width: 50px; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; line-height: 20px; color: var(--color-diff-blob-num-text); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; user-select: none;"></td><td id="file-setup-py-LC4" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; color: var(--color-text-primary); overflow-wrap: normal; white-space: pre;">
</td></tr><tr style="box-sizing: border-box;"><td id="file-setup-py-L5" class="blob-num js-line-number" data-line-number="5" style="box-sizing: border-box; padding: 0px 10px; width: 50px; min-width: 50px; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; line-height: 20px; color: var(--color-diff-blob-num-text); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; user-select: none;"></td><td id="file-setup-py-LC5" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; color: var(--color-text-primary); overflow-wrap: normal; white-space: pre;"><span class="pl-s1" style="box-sizing: border-box;">arch</span> <span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">=</span> <span class="pl-s1" style="box-sizing: border-box;">os</span>.<span class="pl-en" style="box-sizing: border-box; color: rgb(111, 66, 193);">uname</span>().<span class="pl-s1" style="box-sizing: border-box;">machine</span></td></tr><tr style="box-sizing: border-box;"><td id="file-setup-py-L6" class="blob-num js-line-number" data-line-number="6" style="box-sizing: border-box; padding: 0px 10px; width: 50px; min-width: 50px; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; line-height: 20px; color: var(--color-diff-blob-num-text); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; user-select: none;"></td><td id="file-setup-py-LC6" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; color: var(--color-text-primary); overflow-wrap: normal; white-space: pre;">
</td></tr><tr style="box-sizing: border-box;"><td id="file-setup-py-L7" class="blob-num js-line-number" data-line-number="7" style="box-sizing: border-box; padding: 0px 10px; width: 50px; min-width: 50px; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; line-height: 20px; color: var(--color-diff-blob-num-text); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; user-select: none;"></td><td id="file-setup-py-LC7" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; color: var(--color-text-primary); overflow-wrap: normal; white-space: pre;"><span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">if</span> <span class="pl-s1" style="box-sizing: border-box;">arch</span> <span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">==</span> <span class="pl-s" style="box-sizing: border-box; color: rgb(3, 47, 98);">&apos;armv7l&apos;</span>:</td></tr><tr style="box-sizing: border-box;"><td id="file-setup-py-L8" class="blob-num js-line-number" data-line-number="8" style="box-sizing: border-box; padding: 0px 10px; width: 50px; min-width: 50px; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; line-height: 20px; color: var(--color-diff-blob-num-text); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; user-select: none;"></td><td id="file-setup-py-LC8" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; color: var(--color-text-primary); overflow-wrap: normal; white-space: pre;">	<span class="pl-s1" style="box-sizing: border-box;">tensorflow</span> <span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">=</span> <span class="pl-s" style="box-sizing: border-box; color: rgb(3, 47, 98);">&apos;tensorflow @ https://github.com/bitsy-ai/tensorflow-arm-bin/releases/download/v2.4.0-rc2/tensorflow-2.4.0rc2-cp37-none-linux_armv7l.whl&apos;</span></td></tr><tr style="box-sizing: border-box;"><td id="file-setup-py-L9" class="blob-num js-line-number" data-line-number="9" style="box-sizing: border-box; padding: 0px 10px; width: 50px; min-width: 50px; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; line-height: 20px; color: var(--color-diff-blob-num-text); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; user-select: none;"></td><td id="file-setup-py-LC9" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; color: var(--color-text-primary); overflow-wrap: normal; white-space: pre;">	</td></tr><tr style="box-sizing: border-box;"><td id="file-setup-py-L10" class="blob-num js-line-number" data-line-number="10" style="box-sizing: border-box; padding: 0px 10px; width: 50px; min-width: 50px; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; line-height: 20px; color: var(--color-diff-blob-num-text); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; user-select: none;"></td><td id="file-setup-py-LC10" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; color: var(--color-text-primary); overflow-wrap: normal; white-space: pre;"><span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">elif</span> <span class="pl-s1" style="box-sizing: border-box;">arch</span> <span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">==</span> <span class="pl-s" style="box-sizing: border-box; color: rgb(3, 47, 98);">&apos;aarch64&apos;</span>:</td></tr><tr style="box-sizing: border-box;"><td id="file-setup-py-L11" class="blob-num js-line-number" data-line-number="11" style="box-sizing: border-box; padding: 0px 10px; width: 50px; min-width: 50px; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; line-height: 20px; color: var(--color-diff-blob-num-text); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; user-select: none;"></td><td id="file-setup-py-LC11" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; color: var(--color-text-primary); overflow-wrap: normal; white-space: pre;">	<span class="pl-s1" style="box-sizing: border-box;">tensorflow</span> <span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">=</span> <span class="pl-s" style="box-sizing: border-box; color: rgb(3, 47, 98);">&apos;https://github.com/bitsy-ai/tensorflow-arm-bin/releases/download/v2.4.0-rc2/tensorflow-2.4.0rc2-cp37-none-linux_aarch64.whl&apos;</span>	</td></tr><tr style="box-sizing: border-box;"><td id="file-setup-py-L12" class="blob-num js-line-number" data-line-number="12" style="box-sizing: border-box; padding: 0px 10px; width: 50px; min-width: 50px; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; line-height: 20px; color: var(--color-diff-blob-num-text); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; user-select: none;"></td><td id="file-setup-py-LC12" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; color: var(--color-text-primary); overflow-wrap: normal; white-space: pre;">
</td></tr><tr style="box-sizing: border-box;"><td id="file-setup-py-L13" class="blob-num js-line-number" data-line-number="13" style="box-sizing: border-box; padding: 0px 10px; width: 50px; min-width: 50px; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; line-height: 20px; color: var(--color-diff-blob-num-text); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; user-select: none;"></td><td id="file-setup-py-LC13" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; color: var(--color-text-primary); overflow-wrap: normal; white-space: pre;"><span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">elif</span> <span class="pl-s1" style="box-sizing: border-box;">arch</span> <span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">==</span> <span class="pl-s" style="box-sizing: border-box; color: rgb(3, 47, 98);">&apos;x86_64&apos;</span>:</td></tr><tr style="box-sizing: border-box;"><td id="file-setup-py-L14" class="blob-num js-line-number" data-line-number="14" style="box-sizing: border-box; padding: 0px 10px; width: 50px; min-width: 50px; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; line-height: 20px; color: var(--color-diff-blob-num-text); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; user-select: none;"></td><td id="file-setup-py-LC14" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; color: var(--color-text-primary); overflow-wrap: normal; white-space: pre;">	<span class="pl-s1" style="box-sizing: border-box;">tensorflow</span> <span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">=</span> <span class="pl-s" style="box-sizing: border-box; color: rgb(3, 47, 98);">&quot;tensorflow==2.4.0rc2&quot;</span></td></tr><tr style="box-sizing: border-box;"><td id="file-setup-py-L15" class="blob-num js-line-number" data-line-number="15" style="box-sizing: border-box; padding: 0px 10px; width: 50px; min-width: 50px; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; line-height: 20px; color: var(--color-diff-blob-num-text); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; user-select: none;"></td><td id="file-setup-py-LC15" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; color: var(--color-text-primary); overflow-wrap: normal; white-space: pre;"><span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">else</span>:</td></tr><tr style="box-sizing: border-box;"><td id="file-setup-py-L16" class="blob-num js-line-number" data-line-number="16" style="box-sizing: border-box; padding: 0px 10px; width: 50px; min-width: 50px; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; line-height: 20px; color: var(--color-diff-blob-num-text); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; user-select: none;"></td><td id="file-setup-py-LC16" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; color: var(--color-text-primary); overflow-wrap: normal; white-space: pre;">	<span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">raise</span> <span class="pl-v" style="box-sizing: border-box; color: rgb(227, 98, 9);">Exception</span>(<span class="pl-s" style="box-sizing: border-box; color: rgb(3, 47, 98);">f&apos;Could not find TensorFlow binary for target <span class="pl-s1" style="box-sizing: border-box; color: rgb(36, 41, 46);"><span class="pl-kos" style="box-sizing: border-box;">{</span><span class="pl-s1" style="box-sizing: border-box; color: rgb(36, 41, 46);">arch</span><span class="pl-kos" style="box-sizing: border-box;">}</span></span>. Please open a Github issue.&apos;</span>)</td></tr><tr style="box-sizing: border-box;"><td id="file-setup-py-L17" class="blob-num js-line-number" data-line-number="17" style="box-sizing: border-box; padding: 0px 10px; width: 50px; min-width: 50px; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; line-height: 20px; color: var(--color-diff-blob-num-text); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; user-select: none;"></td><td id="file-setup-py-LC17" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; color: var(--color-text-primary); overflow-wrap: normal; white-space: pre;">    </td></tr><tr style="box-sizing: border-box;"><td id="file-setup-py-L18" class="blob-num js-line-number" data-line-number="18" style="box-sizing: border-box; padding: 0px 10px; width: 50px; min-width: 50px; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; line-height: 20px; color: var(--color-diff-blob-num-text); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; user-select: none;"></td><td id="file-setup-py-LC18" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; color: var(--color-text-primary); overflow-wrap: normal; white-space: pre;"><span class="pl-s1" style="box-sizing: border-box;">requirements</span> <span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">=</span> [</td></tr><tr style="box-sizing: border-box;"><td id="file-setup-py-L19" class="blob-num js-line-number" data-line-number="19" style="box-sizing: border-box; padding: 0px 10px; width: 50px; min-width: 50px; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; line-height: 20px; color: var(--color-diff-blob-num-text); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; user-select: none;"></td><td id="file-setup-py-LC19" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; color: var(--color-text-primary); overflow-wrap: normal; white-space: pre;">    <span class="pl-s1" style="box-sizing: border-box;">tensorflow</span>,</td></tr><tr style="box-sizing: border-box;"><td id="file-setup-py-L20" class="blob-num js-line-number" data-line-number="20" style="box-sizing: border-box; padding: 0px 10px; width: 50px; min-width: 50px; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; line-height: 20px; color: var(--color-diff-blob-num-text); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; user-select: none;"></td><td id="file-setup-py-LC20" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; color: var(--color-text-primary); overflow-wrap: normal; white-space: pre;">    <span class="pl-c" style="box-sizing: border-box; color: rgb(106, 115, 125);"># specify additional package requirements here</span></td></tr><tr style="box-sizing: border-box;"><td id="file-setup-py-L21" class="blob-num js-line-number" data-line-number="21" style="box-sizing: border-box; padding: 0px 10px; width: 50px; min-width: 50px; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; line-height: 20px; color: var(--color-diff-blob-num-text); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; user-select: none;"></td><td id="file-setup-py-LC21" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; color: var(--color-text-primary); overflow-wrap: normal; white-space: pre;">]</td></tr><tr style="box-sizing: border-box;"><td id="file-setup-py-L22" class="blob-num js-line-number" data-line-number="22" style="box-sizing: border-box; padding: 0px 10px; width: 50px; min-width: 50px; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; line-height: 20px; color: var(--color-diff-blob-num-text); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; user-select: none;"></td><td id="file-setup-py-LC22" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; color: var(--color-text-primary); overflow-wrap: normal; white-space: pre;"> </td></tr><tr style="box-sizing: border-box;"><td id="file-setup-py-L23" class="blob-num js-line-number" data-line-number="23" style="box-sizing: border-box; padding: 0px 10px; width: 50px; min-width: 50px; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; line-height: 20px; color: var(--color-diff-blob-num-text); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; user-select: none;"></td><td id="file-setup-py-LC23" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; color: var(--color-text-primary); overflow-wrap: normal; white-space: pre;"><span class="pl-en" style="box-sizing: border-box; color: rgb(111, 66, 193);">setup</span>(</td></tr><tr style="box-sizing: border-box;"><td id="file-setup-py-L24" class="blob-num js-line-number" data-line-number="24" style="box-sizing: border-box; padding: 0px 10px; width: 50px; min-width: 50px; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; line-height: 20px; color: var(--color-diff-blob-num-text); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; user-select: none;"></td><td id="file-setup-py-LC24" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; color: var(--color-text-primary); overflow-wrap: normal; white-space: pre;">    <span class="pl-s1" style="box-sizing: border-box;">install_requires</span><span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">=</span><span class="pl-s1" style="box-sizing: border-box;">requirements</span>,</td></tr><tr style="box-sizing: border-box;"><td id="file-setup-py-L25" class="blob-num js-line-number" data-line-number="25" style="box-sizing: border-box; padding: 0px 10px; width: 50px; min-width: 50px; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; line-height: 20px; color: var(--color-diff-blob-num-text); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; user-select: none;"></td><td id="file-setup-py-LC25" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; color: var(--color-text-primary); overflow-wrap: normal; white-space: pre;">    <span class="pl-c" style="box-sizing: border-box; color: rgb(106, 115, 125);"># specify additional setup parameters here</span></td></tr><tr style="box-sizing: border-box;"><td id="file-setup-py-L26" class="blob-num js-line-number" data-line-number="26" style="box-sizing: border-box; padding: 0px 10px; width: 50px; min-width: 50px; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; line-height: 20px; color: var(--color-diff-blob-num-text); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; user-select: none;"></td><td id="file-setup-py-LC26" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, monospace; font-size: 12px; color: var(--color-text-primary); overflow-wrap: normal; white-space: pre;">)</td></tr></tbody></table><!--kg-card-end: html--><h2 id="your-adventure-is-just-beginning-">Your adventure is just beginning &#x2728;</h2><p>I&apos;m excited to hear about your new TensorFlow applications for Raspberry Pi! Drop your project link or tell us about your idea for offline Machine Learning at edge in the comments below. </p><p>If you&apos;re looking to start a computer vision project, check my example applications in <a href="https://towardsdatascience.com/real-time-object-tracking-with-tensorflow-raspberry-pi-and-pan-tilt-hat-2aeaef47e134">Real-time Object Tracking with TensorFlow</a>. A few folks have used these examples to jump-start their own research and applied ML projects.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2021/05/Screenshot-from-2020-11-21-17-16-00.png" class="kg-image" alt="3 Ways to Install TensorFlow 2 on Raspberry Pi" loading="lazy" width="2000" height="1120" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screenshot-from-2020-11-21-17-16-00.png 600w, https://bitsy.ai/content/images/size/w1000/2021/05/Screenshot-from-2020-11-21-17-16-00.png 1000w, https://bitsy.ai/content/images/size/w1600/2021/05/Screenshot-from-2020-11-21-17-16-00.png 1600w, https://bitsy.ai/content/images/2021/05/Screenshot-from-2020-11-21-17-16-00.png 2000w" sizes="(min-width: 720px) 720px"><figcaption><strong>@martin2kid </strong>prototype detects face masks and tracks temperature with a thermal array.</figcaption></figure><figure class="kg-card kg-embed-card kg-card-hascaption"><blockquote class="twitter-tweet" data-width="550"><p lang="en" dir="ltr">Our AI-enabled camera for wildlife surveys is IN for the <a href="https://twitter.com/conservationx?ref_src=twsrc%5Etfw">@ConservationX</a> prize. &#x1F389;&#x1F64C;&#x1F973;<br><br>It detects, tracks, and photographs leopards using <a href="https://twitter.com/Raspberry_Pi?ref_src=twsrc%5Etfw">@Raspberry_pi</a>, <a href="https://twitter.com/hashtag/Python?src=hash&amp;ref_src=twsrc%5Etfw">#Python</a> , <a href="https://twitter.com/hashtag/machinelearning?src=hash&amp;ref_src=twsrc%5Etfw">#machinelearning</a>, &amp; some &#x1FA84;<br><br>Next steps... move beyond proof-of-concept &amp; upgrade hardware for field-testing &#x1F4F8;&#x1F406; 1/n <a href="https://t.co/zBgpMpF8R1">pic.twitter.com/zBgpMpF8R1</a></p>&#x2014; Kasim Rafiq (@explorerkas) <a href="https://twitter.com/explorerkas/status/1318706260792004609?ref_src=twsrc%5Etfw">October 21, 2020</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<figcaption><strong>@explorerkas</strong> submission to @Conservationx detects and tracks leopards in the field.</figcaption></figure><figure class="kg-card kg-embed-card kg-card-hascaption"><blockquote class="twitter-tweet" data-width="550"><p lang="en" dir="ltr">Finally,I can measure the Length &amp; Biomass of the Fish. Trained and customised the Fish detection model <a href="https://twitter.com/EdjeElectronics?ref_src=twsrc%5Etfw">@EdjeElectronics</a>. A BIG MILEStONE for me. BiG Thanks to <a href="https://twitter.com/EdjeElectronics?ref_src=twsrc%5Etfw">@EdjeElectronics</a> <a href="https://twitter.com/reesber?ref_src=twsrc%5Etfw">@reesber</a> <a href="https://twitter.com/grepLeigh?ref_src=twsrc%5Etfw">@grepLeigh</a> for your support. <a href="https://twitter.com/hashtag/oceans?src=hash&amp;ref_src=twsrc%5Etfw">#oceans</a> <a href="https://twitter.com/hashtag/fish?src=hash&amp;ref_src=twsrc%5Etfw">#fish</a> <a href="https://twitter.com/hashtag/MachineLearning?src=hash&amp;ref_src=twsrc%5Etfw">#MachineLearning</a> <a href="https://twitter.com/hashtag/rpi?src=hash&amp;ref_src=twsrc%5Etfw">#rpi</a> <a href="https://twitter.com/hashtag/newtech?src=hash&amp;ref_src=twsrc%5Etfw">#newtech</a> <a href="https://t.co/zhpTwuS5VX">pic.twitter.com/zhpTwuS5VX</a></p>&#x2014; Nitesh Verma (@niteshverma21i1) <a href="https://twitter.com/niteshverma21i1/status/1258282206557958149?ref_src=twsrc%5Etfw">May 7, 2020</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<figcaption><a href="https://twitter.com/niteshverma21i1">@niteshverma21i1</a> measures fish length and biomass for aquaculture automation.</figcaption></figure><p>Looking for more hands-on examples of Machine Learning for Raspberry Pi and other small device? <a href="https://bitsy.ai/" rel="noopener nofollow">Sign up for my newsletter</a>!</p><p>I publish new examples of real-world ML applications (with full source code) and other nifty tricks like <a href="https://bitsy.ai/automate-bounding-box-annotation-with-tensorflow-and-automl/" rel="noopener nofollow">automating away the pains of bounding-box annotation</a>.</p>]]></content:encoded></item><item><title><![CDATA[zsh: no matches found]]></title><description><![CDATA[I've been using zsh and ohmyz.sh for years, but I still occasionally forget this shell interprets square brackets as a pattern on the command line. Two improvements on the default behavior.]]></description><link>https://bitsy.ai/zsh-no-matches-found/</link><guid isPermaLink="false">6313cba487c91a0001179a17</guid><category><![CDATA[zsh]]></category><dc:creator><![CDATA[Leigh Johnson]]></dc:creator><pubDate>Mon, 02 Nov 2020 22:11:50 GMT</pubDate><content:encoded><![CDATA[<p>I&apos;ve been using zsh and <a href="https://ohmyz.sh/">ohmyz.sh</a> for years, but I still occasionally forget this shell interprets square brackets as a pattern on the command line.</p><p>Here&apos;s an example:</p><pre><code class="language-bash">$ which $SHELL
/usr/bin/zsh
$ pip install -e .[develop,plugins]
zsh: no matches found: [develop,plugins]</code></pre><p>Instead of installing the <code>develop</code> and <code>plugins</code> variant of this Python package, zsh attempted to match this pattern. To account for this, I need to escape the square brackets:</p><pre><code>$ pip install -e .\[develop,plugins\] 
Obtaining file:///home/leigh/projects/OctoPrint</code></pre><p>If I need a more permanent fix, I can use an alias to <code>set -o noglob</code> (disable shell globbing) for a command is run by adding this to my <code>.zshrc</code> file:</p><pre><code>alias pip=&apos;noglob pip&apos;</code></pre>]]></content:encoded></item><item><title><![CDATA[Automate Image Annotation on a Small Budget]]></title><description><![CDATA[Learn how to use TensorFlow.js and Automated Machine Learning (AutoML) to prototype a computer vision model, plus increase the efficiency of manual data labeling. ]]></description><link>https://bitsy.ai/automate-bounding-box-annotation-with-tensorflow-and-automl/</link><guid isPermaLink="false">6313cba487c91a0001179a15</guid><category><![CDATA[Prototypes]]></category><category><![CDATA[TensorFlow.js]]></category><category><![CDATA[Computer Vision]]></category><category><![CDATA[Object Detection]]></category><category><![CDATA[Data Annotation]]></category><category><![CDATA[AutoML]]></category><category><![CDATA[PrintNanny]]></category><dc:creator><![CDATA[Leigh Johnson]]></dc:creator><pubDate>Mon, 02 Nov 2020 08:27:04 GMT</pubDate><media:content url="https://bitsy.ai/content/images/2021/05/microsoft-vott-custom-tensorflow-js-model.resized-1--1--1.gif" medium="image"/><content:encoded><![CDATA[<h3 id="learn-how-to-use-tensorflow-js-and-automated-machine-learning-automl-to-prototype-a-computer-vision-model-plus-increase-the-efficiency-of-manual-data-labeling-">Learn how to use TensorFlow.js and Automated Machine Learning (AutoML) to prototype a computer vision model, plus increase the efficiency of manual data labeling. </h3><img src="https://bitsy.ai/content/images/2021/05/microsoft-vott-custom-tensorflow-js-model.resized-1--1--1.gif" alt="Automate Image Annotation on a Small Budget"><p></p><h2 id="introduction-">Introduction &#x1F44B;</h2><p>Data collection and preparation are the foundation of every machine learning application. You&apos;ve heard it before: &quot;Garbage in, garbage out&quot; in reference to an algorithm&apos;s limited capability to correct for inaccurate, poor-quality, or biased input data. </p><p>The cost of quality annotated data prompted a cottage industry of tools/platforms for speeding up the data labeling process. Besides the SaaS/on-prem startup ecosystem, each of the major cloud providers (AWS, Microsoft, Google) launched an automated data labeling product in the last two years. Understandably, these services are often developed with Premium/Enterprise users, features, and price points in mind. </p><h3 id="on-a-limited-budget-am-i-stuck-labeling-everything-by-hand">On a limited budget, am I stuck labeling <em>everything</em> by hand?</h3><p>Good, news! With a bit of elbow grease, you can automate bounding box annotation for yourself or a small team. In this blog post, I will show you the automation technique I used to quickly prototype a 3D print failure detection model. </p><p>You&apos;ll learn how to:</p><ol><li>Create detailed instructions for human labelers </li><li>Train a guidance model </li><li>Automate bounding box annotation with <a href="https://github.com/microsoft/VoTT">Microsoft VoTT</a> (Visual Object Tagging Tool) and TensorFlow.js</li></ol><p>Pictured: Custom TensorFlow model automatically annotates a video frame in Microsoft VoTT (Visual Object Tagging Tool).</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://bitsy.ai/content/images/2021/05/microsoft-vott-custom-tensorflow-js-model.resized-1--1-.gif" class="kg-image" alt="Automate Image Annotation on a Small Budget" loading="lazy" width="1200" height="732" srcset="https://bitsy.ai/content/images/size/w600/2021/05/microsoft-vott-custom-tensorflow-js-model.resized-1--1-.gif 600w, https://bitsy.ai/content/images/size/w1000/2021/05/microsoft-vott-custom-tensorflow-js-model.resized-1--1-.gif 1000w, https://bitsy.ai/content/images/2021/05/microsoft-vott-custom-tensorflow-js-model.resized-1--1-.gif 1200w" sizes="(min-width: 1200px) 1200px"></figure><h2 id="first-annotation-pass-">First Annotation Pass &#x1F3F7;</h2><p>If you&apos;re starting from scratch without labels, you do need to bite the bullet and annotate some data manually. Labeling at least a few hundred examples by hand is required to create written guidelines and evaluation criteria for annotation decisions. </p><h3 id="install-vott-visual-object-tracking-tool-from-source">Install VoTT (Visual Object Tracking Tool) from Source</h3><p>Microsoft VoTT is an Open Source tool for annotating images and videos with bounding boxes &#xA0;(object detection) and polygons (segmentation). I use VoTT because it supports a variety of export formats, can be hosted as a web app, and lets me load a custom TensorFlow.js model to provide bounding box suggestions. </p><p>Prerequisites:</p><ul><li> &#xA0;NodeJS (&gt;= 10.x) and NPM. </li></ul><p>I recommend <a href="https://github.com/nvm-sh/nvm">NVM </a>(Node Version Manager) to manage NodeJS installation and environments.</p><figure class="kg-card kg-code-card"><pre><code class="language-shell">$ git clone https://github.com/microsoft/VoTT
$ cd VoTT
$ npm ci
$ npm i @tensorflow/tfjs@2.7.0
$ npm start</code></pre><figcaption>Upgrading TensorFlow.js package is necessary to use newer ops.</figcaption></figure><p>Refer to <a href="https://github.com/microsoft/VoTT#using-vott">Using VoTT</a> to create a new project and setup data connections.</p><p>Looking for a dataset? <a href="https://datasetsearch.research.google.com">Try Google&apos;s Dataset Search</a>. </p><h3 id="manually-label-some-examples">Manually Label Some Examples</h3><p>The number of images you&apos;ll need to label by hand depends on the problem domain, ranging from a few dozen images to thousands. I obtained reasonable results for my problem (detecting 3D print defects) with:</p><ul><li>5 labels (distribution below)</li><li>67 print time lapse videos, sampled at 3 frames per second</li><li>6,248 images viewed</li><li>9,004 bounding boxes drawn on 3,215 images, average 3 boxes per image. </li><li>8 hours, split over a few days. I caught up on podcasts I&apos;ve been neglecting without a morning commute. </li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2021/05/3d-print-fail-dataset-v1-stats.resized--2-.png" class="kg-image" alt="Automate Image Annotation on a Small Budget" loading="lazy" width="252" height="622"><figcaption>Dataset statistics provided by VoTT (Visual Object Tracking Tool)</figcaption></figure><h3 id="write-annotation-guidelines">Write Annotation Guidelines &#xA0; &#xA0; &#xA0;</h3><p>While the task is still fresh in your mind, take the extra time to write clear guidelines (example below) to keep labels consistent and cover common edge cases. Feel free to use my instructions as a template for your own. </p><hr><!--kg-card-begin: markdown--><h3 id="boundingboxannotationguidelines">Bounding Box Annotation Guidelines</h3>
<h3 id="concept">Concept</h3>
<p>2-3 sentence introduction to the task/concept</p>
<p><em>This dataset contains timelapse videos of failed 3D print jobs. Images are in chronological order. Your task is to draw tight boxes around all recognizable pixels matching a print defect or object.</em></p>
<h4 id="labels">Labels</h4>
<p>id, text name, written description of labels and edge cases, positive and negative examples</p>
<ul>
<li>Should labels start at 0 or 1?</li>
<li>Is 0 reserved for background / unknown class?</li>
</ul>
<h4 id="0background">0 background</h4>
<h4 id="1nozzle">1 nozzle</h4>
<p>The print nozzle is a metal object that extrudes hot filament.</p>
<p>If the nozzle is partially occluded, draw a bounding box around the entirety of the object (including occluded pixels).</p>
<p>If the nozzle is entirely occluded, do not label.</p>
<h4 id="2raft">2 raft</h4>
<p>A &quot;raft&quot; is a thin outline surrounding the first few layers of a print.</p>
<p>If the raft is partially occluded (usually by the print), draw a bounding box around the entire raft.</p>
<h4 id="3print">3 print</h4>
<p>The object(s) being printed. If multiple object or pieces are being printed, draw a bounding box around each distinct object.</p>
<h4 id="4adhesion">4 adhesion</h4>
<p>...</p>
<h4 id="5spaghetti">5 spaghetti</h4>
<p>...</p>
<h4 id="guidelines">Guidelines</h4>
<ul>
<li>Input format</li>
<li>Label format</li>
<li>1 or multiple objects per frame?</li>
<li>1 or multiple boxes per object &quot;instance&quot;?</li>
<li>Tight or loose boxes?</li>
<li>Label reflections (mirrors, water)?</li>
</ul>
<!--kg-card-end: markdown--><hr><h2 id="train-a-guidance-model-with-automl-">Train a Guidance Model with AutoML &#x1F916;</h2><p>AutoML (Automated Machine Learning) is a technique that falls under the &quot;brute force&quot; category of algorithms. Cloud-based AutoML platforms are an excellent tool for validating that your problem <em>can</em> and <em>should </em>be solved with Machine Learning. </p><p>Even if you plan to train a model, consider putting the AutoML model in front of customers first. Collect customer feedback early and incorporate this information into the custom model&apos;s development. An example of some insights...</p><ul><li>Customers were not sensitive to false positives (defect reported via SMS, but print was ok). </li><li>Most customers appreciates visual updates on the print&apos;s progress, even if the detector was incorrect. </li><li>Surprisingly, a few customers &#xA0;reported false positives elicited a sense of security.</li></ul><p>I used <a href="https://cloud.google.com/vision/automl/object-detection/docs">Google Cloud AutoML Vision</a> Edge (Object Detection), which I selected because:</p><ul><li><a href="https://cloud.google.com/vision/automl/object-detection/docs/edge-quickstart">S</a>upports model export to TensorFlow Lite, TensorFlow.js and ops compatible with Edge TPU, ARM, and NVIDIA hardware acceleration.</li><li>Disclosure: I&apos;m a <a href="https://developers.google.com/community/experts">Google Developer Expert</a> &#x1F913;</li></ul><h3 id="export-dataset-from-vott">Export Dataset from VoTT</h3><p>In your project&apos;s export settings, choose the CSV provider. Check &quot;include images,&quot; save the configuration, and then export the data. If you&apos;re exporting thousands of images, this will take a few minutes. Take a break!</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2021/05/Screenshot-from-2020-11-01-12-02-44.png" class="kg-image" alt="Automate Image Annotation on a Small Budget" loading="lazy" width="2000" height="785" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screenshot-from-2020-11-01-12-02-44.png 600w, https://bitsy.ai/content/images/size/w1000/2021/05/Screenshot-from-2020-11-01-12-02-44.png 1000w, https://bitsy.ai/content/images/size/w1600/2021/05/Screenshot-from-2020-11-01-12-02-44.png 1600w, https://bitsy.ai/content/images/2021/05/Screenshot-from-2020-11-01-12-02-44.png 2384w" sizes="(min-width: 720px) 720px"><figcaption>VoTT Export Settings</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2021/05/Screenshot-from-2020-11-01-12-53-10.png" class="kg-image" alt="Automate Image Annotation on a Small Budget" loading="lazy" width="1830" height="554" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screenshot-from-2020-11-01-12-53-10.png 600w, https://bitsy.ai/content/images/size/w1000/2021/05/Screenshot-from-2020-11-01-12-53-10.png 1000w, https://bitsy.ai/content/images/size/w1600/2021/05/Screenshot-from-2020-11-01-12-53-10.png 1600w, https://bitsy.ai/content/images/2021/05/Screenshot-from-2020-11-01-12-53-10.png 1830w" sizes="(min-width: 720px) 720px"><figcaption>Files exported from VoTTIn</figcaption></figure><h3 id="inspect-and-preprocess-data">Inspect and Preprocess Data</h3><p>AutoML Vision requires CSV data in the following format if two vertices are provided:</p><p>&#x2003;<code>SET,gs://path/to/img,label,x_min,y_min,,,x_max,y_max</code></p><p>The coordinates must be <strong>relative to the image&apos;s size</strong>, falling in range [0, 1]. </p><p>Code available in <a href="https://gist.github.com/leigh-johnson/293f3380f15c496934e2846ec7f9ad16">Github Gist</a></p><pre><code class="language-python">import pandas as pd

# load VoTT CSV export
# notice: coordinates are absolute
df = pd.read_csv(&apos;/path/to/vott-csv-export/{project name}-export.csv&apos;)
df.head()</code></pre><figure class="kg-card kg-image-card"><img src="https://bitsy.ai/content/images/2021/05/Screenshot-from-2020-11-01-13-25-17.png" class="kg-image" alt="Automate Image Annotation on a Small Budget" loading="lazy" width="1504" height="402" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screenshot-from-2020-11-01-13-25-17.png 600w, https://bitsy.ai/content/images/size/w1000/2021/05/Screenshot-from-2020-11-01-13-25-17.png 1000w, https://bitsy.ai/content/images/2021/05/Screenshot-from-2020-11-01-13-25-17.png 1504w" sizes="(min-width: 720px) 720px"></figure><pre><code class="language-python">import cv2

base_path = &apos;/path/to/vott-csv-export/&apos;

LOG_INTERVAL=2000

# convert absolute coordinates to relative coordinates in [0, 1] range
for index, row in df.iterrows():
    if index % LOG_INTERVAL == 0:
        print(f&apos;finished {index} / {len(df)}&apos;)
    filename = row[&apos;image_path&apos;].split(&apos;/&apos;)[-1]
    img = cv2.imread(f&apos;{base_path}{filename}&apos;)
    height, width, channels = img.shape
    df.at[index, &apos;x1_n&apos;] = row[&apos;x1&apos;] / width
    df.at[index, &apos;x2_n&apos;]= row[&apos;x2&apos;] / width  
    df.at[index, &apos;y1_n&apos;] = row[&apos;y1&apos;] / height
    df.at[index, &apos;y2_n&apos;] = row[&apos;y2&apos;] / height
 
 
# replace relative image paths with a Google Storage bucket path
df[&apos;set&apos;] = &apos;UNASSIGNED&apos;
df[&apos;gs_path&apos;] = df[&apos;image&apos;] + &apos;gs://bucket-name/path/to/upload&apos;

# write CSV with columns expected by AutoML Vision
# the &quot;none&quot; columns are required for boxes defined by 2 vertices
df[&apos;none&apos;] = &apos;&apos;
df.to_csv(&apos;/home/leigh/datasets/spaghetti/labeled/vott-csv-export/spaghetti_v1-normalized-export.csv&apos;, 
    columns=[&apos;set&apos;, &apos;image_path&apos;, &apos;label&apos;, &apos;x1_n&apos;, &apos;y1_n&apos;, &apos;none&apos;, &apos;none&apos;, &apos;x2_n&apos;, &apos;y2_n&apos;, &apos;none&apos;, &apos;none&apos;],
    index=False
    )</code></pre><p>For additional information, refer to <a href="https://cloud.google.com/vision/automl/object-detection/docs/prepare">preparing your training data</a>.</p><h3 id="upload-data">Upload Data</h3><p>Upload the data to a Google Cloud Storage bucket. <strong>Note:</strong> If you&apos;re creating a new bucket, AutoML Vision exports in later steps require the destination bucket to be in the us-central-1 region.</p><ul><li>New to GCP? Follow the steps in <a href="https://cloud.google.com/vision/automl/docs/edge-quickstart#before_you_begin">Before you Begin</a> to setup a project and authenticate.</li><li><a href="https://cloud.google.com/storage/docs/gsutil">Install gsutil</a></li><li><code>gsutil rsync -r /path/to/vott-csv-export gs://your-bucket-name/vott-csv-export/</code></li></ul><h3 id="import-data-to-automl-vision">Import Data to AutoML Vision</h3><ul><li>Open the <a href="https://console.cloud.google.com/vision/datasets">AutoML Vision Datasets browser</a> in GCP&apos;s console. </li><li>Create a new dataset. In the import tab, select your CSV file from the Storage bucket.</li></ul><figure class="kg-card kg-image-card"><img src="https://bitsy.ai/content/images/2021/05/google-cloud-automl-import-data.resized.png" class="kg-image" alt="Automate Image Annotation on a Small Budget" loading="lazy" width="648" height="568" srcset="https://bitsy.ai/content/images/size/w600/2021/05/google-cloud-automl-import-data.resized.png 600w, https://bitsy.ai/content/images/2021/05/google-cloud-automl-import-data.resized.png 648w"></figure><p>Take a break while the data imports! &#x1F44F;</p><p>Before you train, sanity-check the imported data and verify labels are correct.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://bitsy.ai/content/images/2021/05/Screenshot-from-2020-11-01-15-01-46.png" class="kg-image" alt="Automate Image Annotation on a Small Budget" loading="lazy" width="2000" height="1012" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screenshot-from-2020-11-01-15-01-46.png 600w, https://bitsy.ai/content/images/size/w1000/2021/05/Screenshot-from-2020-11-01-15-01-46.png 1000w, https://bitsy.ai/content/images/size/w1600/2021/05/Screenshot-from-2020-11-01-15-01-46.png 1600w, https://bitsy.ai/content/images/2021/05/Screenshot-from-2020-11-01-15-01-46.png 2000w" sizes="(min-width: 1200px) 1200px"><figcaption>The &quot;raft&quot; of a 3D print is a throwaway supportive structure.</figcaption></figure><h3 id="train-model">Train Model</h3><p>AutoML&apos;s price system is based on <strong>node hours, </strong>which is not the same as &quot;wall clock&quot; or elapsed time. A critique I have is that pricing a training job in advance requires some additional effort.</p><p><a href="https://cloud.google.com/vision/automl/pricing?_ga=2.231171871.-1460275601.1603318024#free-trial">AutoML Vision pricing</a> (USD prices reflected below) varies by feature, with different pricing schedules for:</p><ul><li>Cloud-hosted classification &amp; object detection - $3.15 / node hour, $75.6 / 24 hours</li><li>Edge (classification) &#xA0;- $4.95 / node hour, $118.80 / 24 hours</li><li>Edge (object detection) - $18.00/ node hour, $432 / 24 hours</li></ul><p>If these prices are outside of your project&apos;s budget, I will cover how I train models with <a href="https://github.com/tensorflow/models/tree/master/research/object_detection">TensorFlow&apos;s Object Detection API </a>in a future post. Follow or <a href="https://bitsy.ai/">subscribe to my newsletter</a> to be notified on publication.</p><p>For this particular problem (detecting 3D print defects), I saw reasonable results using the recommended training time (24 node hours) for my dataset size. Drilling down into individual labels, &quot;nozzle&quot; detection performed significantly worse compared to the rest.</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screenshot-from-2020-11-01-15-06-36-1.png" width="2000" height="660" loading="lazy" alt="Automate Image Annotation on a Small Budget" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screenshot-from-2020-11-01-15-06-36-1.png 600w, https://bitsy.ai/content/images/size/w1000/2021/05/Screenshot-from-2020-11-01-15-06-36-1.png 1000w, https://bitsy.ai/content/images/size/w1600/2021/05/Screenshot-from-2020-11-01-15-06-36-1.png 1600w, https://bitsy.ai/content/images/size/w2400/2021/05/Screenshot-from-2020-11-01-15-06-36-1.png 2400w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screenshot-from-2020-11-01-15-43-00-1.png" width="2000" height="768" loading="lazy" alt="Automate Image Annotation on a Small Budget" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screenshot-from-2020-11-01-15-43-00-1.png 600w, https://bitsy.ai/content/images/size/w1000/2021/05/Screenshot-from-2020-11-01-15-43-00-1.png 1000w, https://bitsy.ai/content/images/size/w1600/2021/05/Screenshot-from-2020-11-01-15-43-00-1.png 1600w, https://bitsy.ai/content/images/size/w2400/2021/05/Screenshot-from-2020-11-01-15-43-00-1.png 2400w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption>Left: precision/recall curve for all labels. Right: precision/recall curve for &quot;nozzle&quot;. Click to view full-size images.</figcaption></figure><p>On closer inspection, I could see that a high false positive rate significantly impacted the model&apos;s precision score. Precision is the sum of <em>true positive / (true positive + false positive). </em>I was thrilled to discover a number of examples where <strong>I </strong>had failed to label nozzles in &#xA0;ground truth data. </p><p>&#x1F92F; Even though I had gotten sloppy during the first pass of labeling, the guidance model was already good enough to catch these mistakes. Wow! If I had to manually label the entirety of this data, it would be riddled with errors.</p><figure class="kg-card kg-image-card kg-width-full kg-card-hascaption"><img src="https://bitsy.ai/content/images/2020/11/Screenshot-from-2020-11-01-15-47-25.png" class="kg-image" alt="Automate Image Annotation on a Small Budget" loading="lazy" width="2000" height="745" srcset="https://bitsy.ai/content/images/size/w600/2020/11/Screenshot-from-2020-11-01-15-47-25.png 600w, https://bitsy.ai/content/images/size/w1000/2020/11/Screenshot-from-2020-11-01-15-47-25.png 1000w, https://bitsy.ai/content/images/size/w1600/2020/11/Screenshot-from-2020-11-01-15-47-25.png 1600w, https://bitsy.ai/content/images/2020/11/Screenshot-from-2020-11-01-15-47-25.png 2304w"><figcaption>My model confidently detected &quot;nozzle&quot; objects in these images, which were scored as &quot;false positives&quot; due to human error in labeling the ground truth data.</figcaption></figure><h2 id="automate-vott-labeling-with-tensorflow-js-">Automate VoTT Labeling with TensorFlow.js &#x1F916;</h2><p>The following section will show you how to use a custom &#xA0;TensorFlow.js model to suggest bounding boxes with VoTT&apos;s &quot;Active Learning&quot; feature. </p><p>The &quot;Active Learning&quot; uses a TensorFlow.js model to perform an inference pass over a frame, apply non-max suppression, and draw the best box proposal per detected object. </p><h3 id="export-tensorflow-js-model">Export TensorFlow.js Model</h3><p>After model training completes, you can export a TensorFlow.js package in the &quot;Test &amp; Use&quot; tab. The model will export to a Storage bucket (the destination bucket must be in the us-central-1 region). </p><figure class="kg-card kg-gallery-card kg-width-wide"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/Screenshot-from-2020-11-01-16-50-05.png" width="1868" height="960" loading="lazy" alt="Automate Image Annotation on a Small Budget" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screenshot-from-2020-11-01-16-50-05.png 600w, https://bitsy.ai/content/images/size/w1000/2021/05/Screenshot-from-2020-11-01-16-50-05.png 1000w, https://bitsy.ai/content/images/size/w1600/2021/05/Screenshot-from-2020-11-01-16-50-05.png 1600w, https://bitsy.ai/content/images/2021/05/Screenshot-from-2020-11-01-16-50-05.png 1868w" sizes="(min-width: 1200px) 1200px"></div></div></div></figure><h3 id="create-classes-json-file">Create classes.json file</h3><p>I fixed this manually. AutoML Vision Edge exports a new-line delimited label file. The format required by VoTT is below. Label index MUST start at 1! </p><pre><code class="language-json">[{&quot;id&quot;:1,&quot;displayName&quot;:&quot;nozzle&quot;}, ... ]</code></pre><h3 id="patch-vott-to-fix-tensorflow-1-x-2-x-bugs">Patch VoTT to fix TensorFlow 1.x -&gt; 2.x bugs</h3><p>VoTT ships with v1 of @tensorflow/tfjs. The AutoML Vision Edge model uses ops (e.g. AddV2) that require a more recent version. I fixed a few minor issues with the following patch:</p><ul><li>Model expects float32 input</li><li>Use newer tf.image.nonMaxSupressionAsync() fn </li></ul><pre><code class="language-git">diff --git a/src/providers/activeLearning/objectDetection.ts b/src/providers/activeLearning/objectDetection.ts
index 196db45..a8dff06 100755
--- a/src/providers/activeLearning/objectDetection.ts
+++ b/src/providers/activeLearning/objectDetection.ts
@@ -151,6 +151,8 @@ export class ObjectDetection {
         const batched = tf.tidy(() =&gt; {
             if (!(img instanceof tf.Tensor)) {
                 img = tf.browser.fromPixels(img);
+                // model requires float32 input
+                img = tf.cast(img, &apos;float32&apos;);
             }
             // Reshape to a single-element batch so we can pass it to executeAsync.
             return img.expandDims(0);
@@ -166,7 +168,8 @@ export class ObjectDetection {
         const result = await this.model.executeAsync(batched) as tf.Tensor[];
 
         const scores = result[0].dataSync() as Float32Array;
-        const boxes = result[1].dataSync() as Float32Array;
+        // tf.image.nonMaxSepressionAsync() expects tf.Tensor as input
+        const boxes = result[1].dataSync()
 
         // clean the webgl tensors
         batched.dispose();
@@ -177,10 +180,8 @@ export class ObjectDetection {
         const prevBackend = tf.getBackend();
         // run post process in cpu
         tf.setBackend(&quot;cpu&quot;);
-        const indexTensor = tf.tidy(() =&gt; {
-            const boxes2 = tf.tensor2d(boxes, [result[1].shape[1], result[1].shape[3]]);
-            return tf.image.nonMaxSuppression(boxes2, maxScores, maxNumBoxes, 0.5, 0.5);
-        });
+        const boxes2d = tf.tensor2d(boxes, [result[1].shape[0], result[1].shape[1]]);
+        const indexTensor = await tf.image.nonMaxSuppressionAsync(boxes2d, maxScores, maxNumBoxes, 0.5, 0.5);
 
         const indexes = indexTensor.dataSync() as Float32Array;
         indexTensor.dispose();
@@ -188,7 +189,9 @@ export class ObjectDetection {
         // restore previous backend
         tf.setBackend(prevBackend);
 
-        return this.buildDetectedObjects(width, height, boxes, maxScores, indexes, classes);
+        // _.buildDetectedObjects expects Float32Array input
+        const fboxes = boxes as Float32Array
+        return this.buildDetectedObjects(width, height, fboxes, maxScores, indexes, classes);
     }</code></pre><h2 id="automatic-bounding-box-suggestions-">Automatic Bounding Box Suggestions &#x2728;</h2><ul><li>Run <code>npm start</code> after patching VoTT</li><li>In the &quot;Active Learning&quot; tab, configure &quot;Model Path&quot; to point to your TensorFlow.js export. I recommend enabling the &quot;auto detect&quot; feature, otherwise you&apos;ll have to manually press ctrl/cmd+d to perform a detection pass on each frame. </li></ul><figure class="kg-card kg-image-card kg-width-wide"><img src="https://bitsy.ai/content/images/2021/05/Screenshot-from-2020-11-01-17-10-55.png" class="kg-image" alt="Automate Image Annotation on a Small Budget" loading="lazy" width="1600" height="671" srcset="https://bitsy.ai/content/images/size/w600/2021/05/Screenshot-from-2020-11-01-17-10-55.png 600w, https://bitsy.ai/content/images/size/w1000/2021/05/Screenshot-from-2020-11-01-17-10-55.png 1000w, https://bitsy.ai/content/images/2021/05/Screenshot-from-2020-11-01-17-10-55.png 1600w" sizes="(min-width: 1200px) 1200px"></figure><h2 id="wrapping-up">Wrapping Up</h2><p>You just learned how to leverage a few hours and a few hundred dollars into an automated workflow for bounding box annotation. As a bonus, the guidance model can be adequate for a prototype or even initial production run. I hope this saves you a bit of time/energy in your next object detection project!</p><p>AutoML products are expensive compared to other cloud offerings - but they&apos;re <em>nowhere</em> near as resource intensive as developing a comparable model from scratch, or even with weight transfer learning.</p><p>Are you currently solving problems using an object detector? Tell me more about the concept and the approach you&apos;re taking in the comments below. </p><p>Subscribe to my newsletter @ <a href="https://bitsy.ai/">bitsy.ai</a> f you want to receive more tips and detailed write-ups of ML applications for Raspberry Pi, Arduino, and other small devices. I&apos;m currently building a privacy-first 3D print monitoring plugin for <a href="https://octoprint.org/">Octoprint</a>. </p>]]></content:encoded></item><item><title><![CDATA[Real-time Object Tracking with TensorFlow, Raspberry Pi, and Pan-Tilt HAT]]></title><description><![CDATA[Detect and track an object in real-time using a Raspberry Pi, Pan-Tilt HAT, and TensorFlow. Perfect for hobbyists curious about computer vision & machine learning.]]></description><link>https://bitsy.ai/real-time-object-tracking-with-tensorflow--raspberry-pi--and-pan-tilt-hat/</link><guid isPermaLink="false">6313cba487c91a0001179a0b</guid><category><![CDATA[Computer Vision]]></category><category><![CDATA[Object Detection]]></category><category><![CDATA[Raspberry Pi]]></category><category><![CDATA[Python]]></category><category><![CDATA[Prototypes]]></category><category><![CDATA[Getting Started]]></category><category><![CDATA[TensorFlow]]></category><category><![CDATA[TensorFlow Lite]]></category><dc:creator><![CDATA[Leigh Johnson]]></dc:creator><pubDate>Mon, 09 Dec 2019 00:00:00 GMT</pubDate><media:content url="https://bitsy.ai/content/images/2021/05/1-xAs0SJR4gvvgcuuySdfmAw-1.jpeg" medium="image"/><content:encoded><![CDATA[<h3 id="portable-computer-vision-and-motion-tracking-on-a-budget-">Portable computer vision and motion tracking on a&#xA0;budget.</h3><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2021/05/1-xAs0SJR4gvvgcuuySdfmAw.jpeg" class="kg-image" alt="Real-time Object Tracking with TensorFlow, Raspberry Pi, and Pan-Tilt HAT" loading="lazy" width="800" height="636" srcset="https://bitsy.ai/content/images/size/w600/2021/05/1-xAs0SJR4gvvgcuuySdfmAw.jpeg 600w, https://bitsy.ai/content/images/2021/05/1-xAs0SJR4gvvgcuuySdfmAw.jpeg 800w" sizes="(min-width: 720px) 720px"><figcaption>Pictured: Raspberry Pi 4GB, Pi Camera v2.1, Pimoroni Pan-Tilt HAT, Coral Edge TPU USB Accelerator</figcaption></figure><h2 id="part-1-introduction-">Part 1&#x200A;&#x2014;&#x200A;Introduction &#x1F44B;</h2><img src="https://bitsy.ai/content/images/2021/05/1-xAs0SJR4gvvgcuuySdfmAw-1.jpeg" alt="Real-time Object Tracking with TensorFlow, Raspberry Pi, and Pan-Tilt HAT"><p>Are you just getting started with machine/deep learning, TensorFlow, or Raspberry Pi? Perfect, this blog post is for you! I created <a href="https://github.com/leigh-johnson/rpi-deep-pantilt"><strong>rpi-deep-pantilt</strong></a><strong> </strong>as an interactive demo of object detection in the wild. &#x1F981;</p><p><strong>UPDATE (February 9th, 2020)&#x200A;&#x2014;</strong>&#x200A;Face detection and tracking added!</p><p>I&#x2019;ll show you how to reproduce the video below, which depicts a camera panning and tilting to track my movement across a room.</p><figure class="kg-card kg-embed-card"><blockquote class="twitter-tweet" data-width="550"><p lang="en" dir="ltr">I&apos;m just a girl, standing in front of a tiny computer, reminding you most computing problems can be solved by sheer force of will. &#x1F4AA;<br><br>MobileNetv3 + SSD <a href="https://twitter.com/TensorFlow?ref_src=twsrc%5Etfw">@TensorFlow</a> model I converted <a href="https://twitter.com/hashtag/TFLite?src=hash&amp;ref_src=twsrc%5Etfw">#TFLite</a><a href="https://twitter.com/hashtag/RaspberryPi?src=hash&amp;ref_src=twsrc%5Etfw">#RaspberryPi</a> + <a href="https://twitter.com/pimoroni?ref_src=twsrc%5Etfw">@pimoroni</a> pantilt hat, PID controller.<br><br>Write-up soon! &#x2728; <a href="https://t.co/v63KSJtJHO">https://t.co/v63KSJtJHO</a> <a href="https://t.co/dmyAlWCnWk">pic.twitter.com/dmyAlWCnWk</a></p>&#x2014; Leigh (@grepLeigh) <a href="https://twitter.com/grepLeigh/status/1199910608474263552?ref_src=twsrc%5Etfw">November 28, 2019</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
</figure><p>I will cover the following:</p><ol><li>Build materials and hardware assembly instructions.</li><li>Deploy a <strong>TensorFlow Lite </strong>object detection model <strong>(MobileNetV3-SSD) </strong>to a <strong>Raspberry Pi.</strong></li><li>Send tracking instructions to pan / tilt servo motors using a <strong>proportional&#x2013;integral&#x2013;derivative controller (PID) controller.</strong></li><li>Accelerate inferences of any <strong>TensorFlow Lite</strong> model with <strong>Coral&#x2019;s USB Edge TPU Accelerator</strong> and <strong>Edge TPU Compiler</strong>.</li></ol><hr><h2 id="terms-references-">Terms &amp; References &#x1F4DA;</h2><p><a href="https://www.raspberrypi.org/"><strong>Raspberry Pi</strong></a>&#x200A;&#x2014;&#x200A;a small, affordable computer popular with educators, hardware hobbyists and robot enthusiasts. &#x1F916;</p><p><a href="https://www.raspberrypi.org/downloads/raspbian/"><strong>Raspbian</strong></a><strong>&#x200A;&#x2014;&#x200A;</strong>the Raspberry Pi Foundation&#x2019;s official operating system for the Pi. Raspbian is derived from Debian Linux.</p><p><a href="https://www.tensorflow.org/"><strong>TensorFlow</strong>&#x200A;</a>&#x2014;&#x200A;an open-source framework for <a href="https://en.wikipedia.org/wiki/Dataflow_programming">dataflow</a> programming, used for machine learning and deep neural learning.</p><p><a href="https://www.tensorflow.org/lite"><strong>TensorFlow Lite&#x200A;</strong></a>&#x2014;&#x200A;an open-source framework for deploying <strong>TensorFlow </strong>models on mobile and embedded devices.</p><p><a href="https://towardsdatascience.com/portable-computer-vision-tensorflow-2-0-on-a-raspberry-pi-part-1-of-2-84e318798ce9#e8cc"><strong>Convolutional Neural Network</strong></a><strong>&#x200A;</strong>&#x2014;&#x200A;a type of neural network architecture that is well-suited for image classification and object detection tasks.</p><p><a href="https://towardsdatascience.com/review-ssd-single-shot-detector-object-detection-851a94607d11"><strong>Single Shot Detector (SSD)</strong></a>&#x200A;&#x2014;&#x200A;a type of <strong>convolutional neural network</strong> (CNN) architecture, specialized for real-time object detection, classification, and bounding box localization.</p><p><a href="https://ai.googleblog.com/2018/04/mobilenetv2-next-generation-of-on.html"><strong>MobileNetV3&#x200A;</strong></a><strong>&#x2014;&#x200A;</strong>a state-of-the-art computer vision model optimized for performance on modest mobile phone processors.</p><p><a href="https://github.com/tensorflow/models/tree/master/research/object_detection#nov-13th-2019"><strong>MobileNetV3-SSD</strong>&#x200A;</a>&#x2014;&#x200A;a <strong>single-shot detector </strong>based on<strong> MobileNet </strong>architecture. This tutorial will be using <strong>MobileNetV3-SSD </strong>models available through <a href="https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md">TensorFlow&#x2019;s object detection model zoo.</a></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2021/05/1-4g3BquE6qJQ7BhjhbBGxaQ.png" class="kg-image" alt="Real-time Object Tracking with TensorFlow, Raspberry Pi, and Pan-Tilt HAT" loading="lazy" width="800" height="438" srcset="https://bitsy.ai/content/images/size/w600/2021/05/1-4g3BquE6qJQ7BhjhbBGxaQ.png 600w, https://bitsy.ai/content/images/2021/05/1-4g3BquE6qJQ7BhjhbBGxaQ.png 800w" sizes="(min-width: 720px) 720px"><figcaption>Searching for MobileNetV3</figcaption></figure><p><a href="https://cloud.google.com/edge-tpu/"><strong>Edge TPU</strong></a>&#x200A;&#x2014;&#x200A;a tensor processing unit (TPU) is an integrated circuit for accelerating computations performed by <strong>TensorFlow.</strong> The <strong>Edge TPU </strong>was developed with a small footprint, for mobile and embedded devices &#x201C;at the edge&#x201D;</p><p></p><figure class="kg-card kg-gallery-card kg-width-wide"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/0-qITzpDpM_O7osma6.jpeg" width="400" height="325" loading="lazy" alt="Real-time Object Tracking with TensorFlow, Raspberry Pi, and Pan-Tilt HAT"></div><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/0-rw3z1B2ka-wncLy4.png" width="600" height="382" loading="lazy" alt="Real-time Object Tracking with TensorFlow, Raspberry Pi, and Pan-Tilt HAT" srcset="https://bitsy.ai/content/images/2021/05/0-rw3z1B2ka-wncLy4.png 600w"></div></div></div></figure><hr><h2 id="part-2-build-list-">Part 2&#x2014; Build List&#xA0;&#x1F6E0;</h2><h3 id="essential"><strong>Essential</strong></h3><ul><li><a href="https://www.raspberrypi.org/products/raspberry-pi-4-model-b/">Raspberry Pi 4 (4GB recommended)</a></li><li><a href="https://www.raspberrypi.org/products/camera-module-v2/">Raspberry Pi Camera V2</a></li><li><a href="https://shop.pimoroni.com/products/pan-tilt-hat?variant=22408353287">Pimoroni Pan-tilt HAT Kit</a></li><li>Micro SD card 16+ GB</li><li>Micro HDMI Cable</li></ul><h3 id="optional"><strong>Optional</strong></h3><ul><li><a href="https://www.adafruit.com/product/1648">12&quot; CSI/DSI ribbon for Raspberry Pi Camera</a>.&#xA0;<br>The Pi Camera&#x2019;s stock cable is too short for the Pan-tilt HAT&#x2019;s full range of motion.</li><li><a href="https://www.adafruit.com/product/1426">RGB NeoPixel Stick</a><br>This component adds a consistent light source to your project.</li><li><a href="https://coral.withgoogle.com/products/accelerator">Coral Edge TPU USB Accelerator</a><br>Accelerates inference (prediction) speed on the Raspberry Pi. You don&#x2019;t need this to reproduce the demo.</li></ul><blockquote>&#x1F44B; <strong>Looking for a project with fewer moving piece</strong>s?Check out <a href="https://towardsdatascience.com/portable-computer-vision-tensorflow-2-0-on-a-raspberry-pi-part-1-of-2-84e318798ce9">Portable Computer Vision: TensorFlow 2.0 on a Raspberry Pi</a> to create a hand-held image classier. &#x2728;<br></blockquote><hr><h2 id="part-3-raspberry-pi-setup">Part 3&#x200A;&#x2014;&#x200A;Raspberry Pi&#xA0;Setup</h2><p>There are two ways you can install <strong>Raspbian</strong> to your Micro SD card:</p><ol><li><a href="https://www.raspberrypi.org/documentation/installation/noobs.md">NOOBS</a> (New Out Of the Box Software) is a GUI operation system installation manager. If this is your first Raspberry Pi project, I&#x2019;d recommend starting here.</li><li><a href="https://www.raspberrypi.org/documentation/installation/installing-images/README.md">Write Raspbian Image to SD Card</a>.</li></ol><p>This tutorial and supporting software were written using <a href="https://www.raspberrypi.org/documentation/installation/"><strong>R</strong></a><strong>aspbian (Buster)</strong>. If you&#x2019;re using a different version of Raspbian or another platform, you&#x2019;ll probably experience some pains.</p><p><strong>Before proceeding</strong>, you&#x2019;ll need to:</p><ul><li>Connect your Pi to the internet (<a href="https://projects.raspberrypi.org/en/projects/raspberry-pi-using/4">doc</a>)</li><li>SSH into your Raspberry Pi (<a href="https://www.raspberrypi.org/documentation/remote-access/ssh/">doc</a>)</li></ul><hr><h2 id="part-4-software-installation">Part 4&#x2014; Software Installation</h2><ol><li>Install system dependencies</li></ol><pre><code>$ sudo apt-get update &amp;&amp; sudo apt-get install -y python3-dev libjpeg-dev libatlas-base-dev raspi-gpio libhdf5-dev python3-smbus</code></pre><p>2. Create a new project directory</p><pre><code>$ mkdir rpi-deep-pantilt &amp;&amp; cd rpi-deep-pantilt</code></pre><p>3. Create a new virtual environment</p><pre><code>$ python3 -m venv .venv</code></pre><p>4. Activate the virtual environment</p><pre><code>$ source .venv/bin/activate &amp;&amp; python3 -m pip install --upgrade pip</code></pre><p>5. Install TensorFlow 2.0 from a community-built wheel.</p><pre><code>$ pip install https://github.com/leigh-johnson/Tensorflow-bin/blob/master/tensorflow-2.0.0-cp37-cp37m-linux_armv7l.whl?raw=true</code></pre><p>6. Install the <strong>rpi-deep-pantilt</strong> Python package</p><pre><code>$ python3 -m pip install rpi-deep-pantilt</code></pre><hr><h2 id="part-5-pan-tilt-hat-hardware-assembly">Part 5 &#x2014;Pan Tilt HAT Hardware&#xA0;Assembly</h2><p>If you purchased a <strong>pre-assembled </strong>Pan-Tilt HAT kit, you can skip to the next section.</p><p>Otherwise, follow the steps in <a href="https://learn.pimoroni.com/tutorial/sandyj/assembling-pan-tilt-hat">Assembling Pan-Tilt HAT</a> before proceeding.</p><hr><h2 id="part-6-connect-the-pi-camera">Part 6&#x200A;&#x2014;&#x200A;Connect the Pi&#xA0;Camera</h2><ol><li>Turn off the Raspberry Pi</li><li>Locate the Camera Module, between the USB Module and HDMI modules.</li><li>Unlock the black plastic clip by (gently) pulling upwards</li><li>Insert the Camera Module ribbon cable (metal connectors<strong> facing away</strong> from the ethernet / USB ports on a Raspberry Pi 4)</li><li>Lock the black plastic clip</li></ol><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2021/05/1-cdWRY2ldCqz-8hYnuiTfBw.gif" class="kg-image" alt="Real-time Object Tracking with TensorFlow, Raspberry Pi, and Pan-Tilt HAT" loading="lazy" width="842" height="534" srcset="https://bitsy.ai/content/images/size/w600/2021/05/1-cdWRY2ldCqz-8hYnuiTfBw.gif 600w, https://bitsy.ai/content/images/2021/05/1-cdWRY2ldCqz-8hYnuiTfBw.gif 842w" sizes="(min-width: 720px) 720px"><figcaption>Image Credit: <a href="https://projects.raspberrypi.org/en/projects/getting-started-with-picamera">Getting Started with the Pi Camera</a></figcaption></figure><hr><h2 id="part-7-enable-the-pi-camera">Part 7&#x200A;&#x2014;&#x200A;Enable the Pi&#xA0;Camera</h2><ol><li>Turn the Raspberry Pi on</li><li>Run sudo raspi-config and select Interfacing Options from the Raspberry Pi Software Configuration Tool&#x2019;s main menu. Press ENTER.</li></ol><figure class="kg-card kg-image-card"><img src="https://bitsy.ai/content/images/2021/05/0-7JGOPvfv4JwYEIm-.png" class="kg-image" alt="Real-time Object Tracking with TensorFlow, Raspberry Pi, and Pan-Tilt HAT" loading="lazy" width="800" height="260" srcset="https://bitsy.ai/content/images/size/w600/2021/05/0-7JGOPvfv4JwYEIm-.png 600w, https://bitsy.ai/content/images/2021/05/0-7JGOPvfv4JwYEIm-.png 800w" sizes="(min-width: 720px) 720px"></figure><p>3. Select the Enable Camera menu option and press ENTER.</p><figure class="kg-card kg-image-card"><img src="https://bitsy.ai/content/images/2021/05/1-dFmOlqyBkE98o6JiU0kp6Q.png" class="kg-image" alt="Real-time Object Tracking with TensorFlow, Raspberry Pi, and Pan-Tilt HAT" loading="lazy" width="800" height="234" srcset="https://bitsy.ai/content/images/size/w600/2021/05/1-dFmOlqyBkE98o6JiU0kp6Q.png 600w, https://bitsy.ai/content/images/2021/05/1-dFmOlqyBkE98o6JiU0kp6Q.png 800w" sizes="(min-width: 720px) 720px"></figure><p>4. In the next menu, use the right arrow key to highlight ENABLE and press ENTER.</p><figure class="kg-card kg-image-card"><img src="https://bitsy.ai/content/images/2021/05/1-09oxH02CQXrAUp7qmar6Lg.png" class="kg-image" alt="Real-time Object Tracking with TensorFlow, Raspberry Pi, and Pan-Tilt HAT" loading="lazy" width="800" height="546" srcset="https://bitsy.ai/content/images/size/w600/2021/05/1-09oxH02CQXrAUp7qmar6Lg.png 600w, https://bitsy.ai/content/images/2021/05/1-09oxH02CQXrAUp7qmar6Lg.png 800w" sizes="(min-width: 720px) 720px"></figure><hr><h2 id="part-8-test-pan-tilt-hat">Part 8&#x200A;&#x2014;&#x200A;Test Pan Tilt&#xA0;HAT</h2><p>Next, test the installation and setup of your Pan-Tilt HAT module.</p><ol><li>SSH into your Raspberry Pi</li><li>Activate your Virtual Environment: source&#xA0;.venv/bin/activate</li><li>Run the following command: rpi-deep-pantilt test pantilt</li><li>Exit the test with Ctrl+C</li></ol><p>If you installed the HAT correctly, you should see both servos moving in a smooth sinusoidal motion while the test is running.</p><figure class="kg-card kg-image-card"><img src="https://bitsy.ai/content/images/2021/05/1-SOV1U1PAojGui2RohhhqPA.gif" class="kg-image" alt="Real-time Object Tracking with TensorFlow, Raspberry Pi, and Pan-Tilt HAT" loading="lazy" width="600" height="450" srcset="https://bitsy.ai/content/images/2021/05/1-SOV1U1PAojGui2RohhhqPA.gif 600w"></figure><hr><h2 id="part-9-test-pi-camera">Part 9&#x200A;&#x2014;&#x200A;Test Pi&#xA0;Camera</h2><p>Next, verify the Pi Camera is installed correctly by starting the camera&#x2019;s preview overlay. The overlay will render on the Pi&#x2019;s primary display (HDMI).</p><ol><li>Plug your Raspberry Pi into an HDMI screen</li><li>SSH into your Raspberry Pi</li><li>Activate your Virtual Environment: $ source&#xA0;.venv/bin/activate</li><li>Run the following command: $ rpi-deep-pantilt test camera</li><li>Exit the test with Ctrl+C</li></ol><p>If you installed the Pi Camera correctly, you should see footage from the camera rendered to your HDMI or composite display.</p><hr><h2 id="part-10-test-object-detection">Part 10&#x2014; Test object detection</h2><p>Next, verify you can run an object detection model (<strong>MobileNetV3-SSD</strong>) on your Raspberry Pi.</p><ol><li>SSH into your Raspberry Pi</li><li>Activate your Virtual Environment: $ source&#xA0;.venv/bin/activate</li><li>Run the following command:</li></ol><pre><code>$ rpi-deep-pantilt detect</code></pre><p>Your Raspberry Pi should detect objects, attempt to classify the object, and draw a bounding box around it.</p><pre><code>$ rpi-deep-pantilt face-detect</code></pre><h3 id="note-only-the-following-objects-can-be-detected-and-tracked-using-the-default-mobilenetv3-ssd-model-"><strong>Note: </strong>Only the following objects can be detected and tracked using the default <strong>MobileNetV3-SSD model.</strong></h3><pre><code>$ rpi-deep-pantilt list-labels
[&#x2018;person&#x2019;, &#x2018;bicycle&#x2019;, &#x2018;car&#x2019;, &#x2018;motorcycle&#x2019;, &#x2018;airplane&#x2019;, &#x2018;bus&#x2019;, &#x2018;train&#x2019;, &#x2018;truck&#x2019;, &#x2018;boat&#x2019;, &#x2018;traffic light&#x2019;, &#x2018;fire hydrant&#x2019;, &#x2018;stop sign&#x2019;, &#x2018;parking meter&#x2019;, &#x2018;bench&#x2019;, &#x2018;bird&#x2019;, &#x2018;cat&#x2019;, &#x2018;dog&#x2019;, &#x2018;horse&#x2019;, &#x2018;sheep&#x2019;, &#x2018;cow&#x2019;, &#x2018;elephant&#x2019;, &#x2018;bear&#x2019;, &#x2018;zebra&#x2019;, &#x2018;giraffe&#x2019;, &#x2018;backpack&#x2019;, &#x2018;umbrella&#x2019;, &#x2018;handbag&#x2019;, &#x2018;tie&#x2019;, &#x2018;suitcase&#x2019;, &#x2018;frisbee&#x2019;, &#x2018;skis&#x2019;, &#x2018;snowboard&#x2019;, &#x2018;sports ball&#x2019;, &#x2018;kite&#x2019;, &#x2018;baseball bat&#x2019;, &#x2018;baseball glove&#x2019;, &#x2018;skateboard&#x2019;, &#x2018;surfboard&#x2019;, &#x2018;tennis racket&#x2019;, &#x2018;bottle&#x2019;, &#x2018;wine glass&#x2019;, &#x2018;cup&#x2019;, &#x2018;fork&#x2019;, &#x2018;knife&#x2019;, &#x2018;spoon&#x2019;, &#x2018;bowl&#x2019;, &#x2018;banana&#x2019;, &#x2018;apple&#x2019;, &#x2018;sandwich&#x2019;, &#x2018;orange&#x2019;, &#x2018;broccoli&#x2019;, &#x2018;carrot&#x2019;, &#x2018;hot dog&#x2019;, &#x2018;pizza&#x2019;, &#x2018;donut&#x2019;, &#x2018;cake&#x2019;, &#x2018;chair&#x2019;, &#x2018;couch&#x2019;, &#x2018;potted plant&#x2019;, &#x2018;bed&#x2019;, &#x2018;dining table&#x2019;, &#x2018;toilet&#x2019;, &#x2018;tv&#x2019;, &#x2018;laptop&#x2019;, &#x2018;mouse&#x2019;, &#x2018;remote&#x2019;, &#x2018;keyboard&#x2019;, &#x2018;cell phone&#x2019;, &#x2018;microwave&#x2019;, &#x2018;oven&#x2019;, &#x2018;toaster&#x2019;, &#x2018;sink&#x2019;, &#x2018;refrigerator&#x2019;, &#x2018;book&#x2019;, &#x2018;clock&#x2019;, &#x2018;vase&#x2019;, &#x2018;scissors&#x2019;, &#x2018;teddy bear&#x2019;, &#x2018;hair drier&#x2019;, &#x2018;toothbrush&#x2019;]</code></pre><hr><h2 id="part-11-track-objects-at-8-fps">Part 11&#x2014; Track Objects at ~8&#xA0;FPS</h2><p>This is the moment we&#x2019;ve all been waiting for! Take the following steps to track an object at roughly 8 frames / second using the Pan-Tilt HAT.</p><ol><li>SSH into your Raspberry Pi</li><li>Activate your Virtual Environment: $source&#xA0;.venv/bin/activate</li><li>Run the following command: $ rpi-deep-pantilt track</li></ol><p>By default, this will track objects with the label person. You can track a different type of object using the --label parameter.</p><p>For example, to track a banana you would run:</p><p>$ rpi-deep-pantilt track --label=banana</p><p>On a <strong>Raspberry Pi 4 (4 GB)</strong>, I benchmarked my model at roughly <strong>8 frames per second.</strong></p><pre><code>INFO:root:FPS: 8.100870481091935
INFO:root:FPS: 8.130448201926173
INFO:root:FPS: 7.6518234817241355
INFO:root:FPS: 7.657477766009717
INFO:root:FPS: 7.861758172395542
INFO:root:FPS: 7.8549541944597
INFO:root:FPS: 7.907857699044301</code></pre><hr><h2 id="part-12-track-objects-in-real-time-with-edge-tpu">Part 12&#x2014; Track Objects in Real-time with Edge&#xA0;TPU</h2><p>We can accelerate <strong>model inference speed</strong> with <a href="https://coral.ai/products/accelerator/">Coral&#x2019;s USB Accelerator.</a> The USB Accelerator contains an Edge TPU, which is an <a href="https://en.wikipedia.org/wiki/Application-specific_integrated_circuit">ASIC</a> chip specialized for TensorFlow Lite operations. For more info, check out <a href="https://coral.ai/docs/accelerator/get-started/">Getting Started with the USB Accelerator.</a></p><ol><li>SSH into your Raspberry Pi</li><li>Install the Edge TPU runtime</li></ol><pre><code>$ echo &quot;deb https://packages.cloud.google.com/apt coral-edgetpu-stable main&quot; | sudo tee /etc/apt/sources.list.d/coral-edgetpu.list

$ curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -

$ sudo apt-get update &amp;&amp; sudo apt-get install libedgetpu1-std</code></pre><p>3. Plug in the Edge TPU (prefer a <strong>USB 3.0 port</strong>).<strong> </strong>If your Edge TPU was already plugged in, <strong>remove and re-plug it</strong> so the udev device manager can detect it.</p><p>4. Try the <strong>detect command</strong> with --edge-tpuoption. You should be able to detect objects in real-time! &#x1F389;</p><pre><code>$ rpi-deep-pantilt detect --edge-tpu --loglevel=INFO</code></pre><p><strong>Note: loglevel=INFO </strong>will show you the FPS at which objects are detected and bounding boxes are rendered to the Raspberry Pi Camera&#x2019;s overlay.</p><p>You should see around ~24 FPS, which is the rate at which frames are sampled from the Pi Camera into a frame buffer.</p><pre><code>INFO:root:FPS: 24.716493958392558
INFO:root:FPS: 24.836166606505206
INFO:root:FPS: 23.031063233367547
INFO:root:FPS: 25.467177106703623
INFO:root:FPS: 27.480438524486594
INFO:root:FPS: 25.41399952505432</code></pre><p>5. Try the track command with --edge-tpu option.</p><pre><code>$ rpi-deep-pantilt track --edge-tpu</code></pre><hr><h2 id="part-13-detect-track-faces-new-in-v1-1-x-">Part 13&#x200A;&#x2014;&#x200A;Detect &amp; Track Faces (NEW in&#xA0;v1.1.x)</h2><p>I&#x2019;ve added a <strong>brand new</strong> face detection model in version <strong>v1.1.x </strong>of rpi-deep-pantilt &#x1F389;</p><p>The model is derived from <strong>facessd_mobilenet_v2_quantized_320x320_open_image_v4 </strong>in TensorFlow&#x2019;s <a href="https://github.com/tensorflow/models">research model zoo</a>.</p><p>The new commands are rpi-deep-pantilt face-detect (detect all faces) and rpi-deep-pantilt face-track (track faces with Pantilt HAT). Both commands support the --edge-tpu option, which will accelerate inferences if using the Edge TPU USB Accelerator.</p><pre><code>rpi-deep-pantilt face-detect --help
Usage: cli.py face-detect [OPTIONS]

Options:
  --loglevel TEXT  Run object detection without pan-tilt controls. Pass
                   --loglevel=DEBUG to inspect FPS.
  --edge-tpu       Accelerate inferences using Coral USB Edge TPU
  --help           Show this message and exit.</code></pre><hr><pre><code>rpi-deep-pantilt face-track --help
Usage: cli.py face-track [OPTIONS]

Options:
  --loglevel TEXT  Run object detection without pan-tilt controls. Pass
                   --loglevel=DEBUG to inspect FPS.
  --edge-tpu       Accelerate inferences using Coral USB Edge TPU
  --help           Show this message and exit.</code></pre><hr><h2 id="wrapping-up-">Wrapping Up&#xA0;&#x1F33B;</h2><p>Congratulations! You&#x2019;re now the proud owner of a DIY object tracking system, which uses a <strong>single-shot-detector </strong>(a type of <strong>convolutional neural network</strong>) to classify and localize objects.</p><h3 id="pid-controller">PID Controller</h3><p>The pan / tilt tracking system uses a <a href="https://en.wikipedia.org/wiki/PID_controller"><strong>proportional&#x2013;integral&#x2013;derivative controller (PID) controller</strong></a><strong> </strong>to smoothly track the centroid of a bounding box.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2021/05/1-9sQpP7SqMHrhAwySJBJffQ.png" class="kg-image" alt="Real-time Object Tracking with TensorFlow, Raspberry Pi, and Pan-Tilt HAT" loading="lazy" width="800" height="599" srcset="https://bitsy.ai/content/images/size/w600/2021/05/1-9sQpP7SqMHrhAwySJBJffQ.png 600w, https://bitsy.ai/content/images/2021/05/1-9sQpP7SqMHrhAwySJBJffQ.png 800w" sizes="(min-width: 720px) 720px"><figcaption>PID Controller Architecture, Leigh Johnson&#xA0;2019</figcaption></figure><h3 id="tensorflow-model-zoo">TensorFlow Model&#xA0;Zoo</h3><p>The models in this tutorial are derived from <a href="http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v3_small_coco_2019_08_14.tar.gz"><strong>ssd_mobilenet_v3_small_coco</strong></a> and <a href="https://storage.cloud.google.com/mobilenet_edgetpu/checkpoints/ssdlite_mobilenet_edgetpu_coco_quant.tar.gz"><strong>ssd_mobilenet_edgetpu_coco</strong></a><strong> </strong>in the <a href="https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md"><strong>TensorFlow Detection Model Zoo</strong></a><strong>. &#x1F981;&#x1F984;&#x1F43C;</strong></p><p>My models are available for download via <a href="https://github.com/leigh-johnson/rpi-deep-pantilt/releases/tag/v1.0.1">Github releases notes</a> @ <a href="https://github.com/leigh-johnson/rpi-deep-pantilt">leigh-johnson/rpi-deep-pantilt</a>.</p><p>I added the custom <strong>TFLite_Detection_PostProcess</strong> operation, which implements a variation of <strong>Non-maximum Suppression (NMS) </strong>on model output. <strong>Non-maximum Suppression </strong>is technique that filters many bounding box proposals using <a href="https://www.probabilitycourse.com/chapter1/1_2_2_set_operations.php"><strong>set operations</strong>.</a></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2021/05/0-weFV951XGeWqehMc.png" class="kg-image" alt="Real-time Object Tracking with TensorFlow, Raspberry Pi, and Pan-Tilt HAT" loading="lazy" width="800" height="368" srcset="https://bitsy.ai/content/images/size/w600/2021/05/0-weFV951XGeWqehMc.png 600w, https://bitsy.ai/content/images/2021/05/0-weFV951XGeWqehMc.png 800w" sizes="(min-width: 720px) 720px"><figcaption>Image Credit: <a rel="noopener" href="https://towardsdatascience.com/non-maximum-suppression-nms-93ce178e177c">Non-maximum Suppression (NMS)</a></figcaption></figure><h2 id="special-thanks-acknowledgements-">Special Thanks &amp; Acknowledgements &#x1F917;</h2><p><strong>MobileNetEdgeTPU SSDLite</strong> contributors: Yunyang Xiong, Bo Chen, Suyog Gupta, Hanxiao Liu, Gabriel Bender, Mingxing Tan, Berkin Akin, Zhichao Lu, Quoc Le.</p><p><strong>MobileNetV3 SSDLite </strong>contributors: Bo Chen, Zhichao Lu, Vivek Rathod, Jonathan Huang.</p><p>Special thanks to <strong>Adrian Rosebrock </strong>for writing <a href="https://www.pyimagesearch.com/2019/04/01/pan-tilt-face-tracking-with-a-raspberry-pi-and-opencv/"><strong>Pan/tilt face tracking with a Raspberry Pi and OpenCV</strong></a><strong>, </strong>which was the inspiration for this whole project!</p><p>Special thanks to <strong>Jason Zaman </strong>for reviewing this article and early release candidates. &#x1F4AA;

</p>]]></content:encoded></item><item><title><![CDATA[Portable Computer Vision: Tensorflow 2 on a Raspberry Pi]]></title><description><![CDATA[Are you just getting started with computer vision, TensorFlow, or Raspberry Pi? Perfect, this post is for you!
]]></description><link>https://bitsy.ai/portable-computer-vision--tensorflow-2-0-on-a-raspberry-pi/</link><guid isPermaLink="false">6313cba487c91a0001179a0c</guid><category><![CDATA[Computer Vision]]></category><category><![CDATA[Prototypes]]></category><category><![CDATA[Python]]></category><category><![CDATA[Raspberry Pi]]></category><category><![CDATA[Getting Started]]></category><category><![CDATA[TensorFlow]]></category><category><![CDATA[TensorFlow Lite]]></category><dc:creator><![CDATA[Leigh Johnson]]></dc:creator><pubDate>Mon, 24 Jun 2019 00:00:00 GMT</pubDate><media:content url="https://bitsy.ai/content/images/2021/05/1-p69rZCP1ydnI55lXQ7LyNg.jpeg" medium="image"/><content:encoded><![CDATA[<h3 id="tiny-low-cost-object-detection-and-classification-">Tiny, low-cost object detection and classification.</h3><h2 id="part-1-introduction">Part 1&#x200A;&#x2014;&#x200A;Introduction</h2><img src="https://bitsy.ai/content/images/2021/05/1-p69rZCP1ydnI55lXQ7LyNg.jpeg" alt="Portable Computer Vision: Tensorflow 2 on a Raspberry Pi"><p>For roughly $100 USD, you can add deep learning to an embedded system or your next internet-of-things project.</p><p>Are you just getting started with machine/deep learning, TensorFlow, or Raspberry Pi? Perfect, this post is for you!</p><p>By the end of this post, you&apos;ll know:</p><ol><li>How to setup Raspberry Pi Camera and install software dependencies.</li><li>Basics of Convolutional Neural Networks for image classification </li><li>How to deploy a pre-trained model (<strong>MobileNetV2</strong>) <strong>to </strong>Raspberry Pi</li><li>Convert a model to <strong>TensorFlow Lite, </strong>a model format optimized for embedded and mobile devices.</li><li>Accelerate inferences of any <strong>TensorFlow Lite</strong> model with Coral&#x2019;s <strong>USB Edge TPU Accelerator</strong> and <strong>Edge TPU Compiler.</strong></li></ol><hr><h2 id="terms-references-">Terms &amp; References &#x1F4DA;</h2><p><a href="https://www.raspberrypi.org/"><strong>Raspberry Pi</strong></a>&#x200A;&#x2014;&#x200A;a small, affordable computer popular with educators, hardware hobbyists, and roboticists. &#x1F916;</p><p><a href="https://www.tensorflow.org/"><strong>TensorFlow</strong>&#x200A;</a>&#x2014;&#x200A;an open-source platform for machine learning.</p><p><a href="https://www.tensorflow.org/lite"><strong>TensorFlow Lite&#x200A;</strong></a>&#x2014;&#x200A;a lightweight library for deploying <strong>TensorFlow </strong>models on mobile and embedded devices.</p><p><strong>Convolutional Neural Network&#x200A;</strong>&#x2014;&#x200A;a type of deep-learning model well-suited for image classification and object detection applications.</p><p><a href="https://ai.googleblog.com/2018/04/mobilenetv2-next-generation-of-on.html"><strong>MobileNetV2&#x200A;</strong></a><strong>&#x2014;&#x200A;</strong>a state-of-the-art image recognition model optimized for performance on modest mobile phone processors.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2021/05/1-f57O6E5hQ61JmSJIemGZzg.png" class="kg-image" alt="Portable Computer Vision: Tensorflow 2 on a Raspberry Pi" loading="lazy" width="800" height="627" srcset="https://bitsy.ai/content/images/size/w600/2021/05/1-f57O6E5hQ61JmSJIemGZzg.png 600w, https://bitsy.ai/content/images/2021/05/1-f57O6E5hQ61JmSJIemGZzg.png 800w" sizes="(min-width: 720px) 720px"><figcaption>MobileNetV2: The Next Generation of On-Device Computer Vision&#xA0;Networks</figcaption></figure><p><a href="https://cloud.google.com/edge-tpu/"><strong>Edge TPU</strong></a>&#x200A;&#x2014;&#x200A;a tensor processing unit (TPU) is an integrated circuit for accelerating computations performed by <strong>TensorFlow.</strong> The <strong>Edge TPU </strong>was developed with a small footprint, for mobile and embedded devices &#x201C;at the edge&#x201D;</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/1-in1ZwQElextZ9mR4HRMoUA.jpeg" width="600" height="395" loading="lazy" alt="Portable Computer Vision: Tensorflow 2 on a Raspberry Pi" srcset="https://bitsy.ai/content/images/2021/05/1-in1ZwQElextZ9mR4HRMoUA.jpeg 600w"></div><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/1-vyGk6VQn1tppT4u-AiG4wQ-1.jpeg" width="400" height="325" loading="lazy" alt="Portable Computer Vision: Tensorflow 2 on a Raspberry Pi"></div><div class="kg-gallery-image"><img src="https://bitsy.ai/content/images/2021/05/1-T0wROrsc7ik5DA3yTYioLA-1.png" width="600" height="382" loading="lazy" alt="Portable Computer Vision: Tensorflow 2 on a Raspberry Pi" srcset="https://bitsy.ai/content/images/2021/05/1-T0wROrsc7ik5DA3yTYioLA-1.png 600w"></div></div></div><figcaption>TPUv1 (left), TPUv2 (middle), Edge TPU (right)</figcaption></figure><hr><h2 id="part-2-build-list-">Part 2&#x200A;&#x2014;&#x200A;Build List&#xA0;&#x2705;</h2><h3 id="starter-kit">Starter Kit</h3><p>If you&#x2019;re just getting started with Raspberry Pi, I recommend the <a href="https://www.arrow.com/en/products/3275/adafruit-industries">Pi Camera Pack</a> ($90) by Arrow. It includes everything you need begin immediately:</p><ul><li>5V 2.4A MicroUSB Power Supply</li><li>320x240 2.8&quot; TFT Model PiTFT Resistive Touch-screen</li><li>Raspberry Pi 3 Model B</li><li>Raspberry Pi Camera v2</li><li>Plastic Case</li><li>8GB MicroSD Card with NOOBS installation manager pre-loaded</li></ul><h3 id="coral-usb-edge-tpu-accelerator-optional-">Coral USB Edge TPU Accelerator (Optional)</h3><p>You can compile <strong>TensorFlow Lite</strong> models to run on Coral&#x2019;s USB Accelerator (<a href="https://coral.withgoogle.com/products/accelerator/">Link</a>), for quicker model predictions.</p><p><strong>Real-time applications</strong> benefit significantly from this speed-up. An example would be the decision-making module of an autonomous self-driving robot.</p><p>Some applications can tolerate a higher prediction speed and might not require TPU acceleration. For example, you would not need TPU acceleration to build a smart doggie door that unlocks for your pooch (but keeps raccoons out).</p><p>If you&#x2019;re just getting started, skip buying this component.</p><p>Are you not sure if you need the USB Accelerator? The MobileNet benchmarks below might help you decide. The measurements below depict inference speed (in ms)&#x200A;&#x2014;&#x200A;lower speeds are better!</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2021/05/1-Aqy5-TnnCCn-uL2UwQ7ksw.png" class="kg-image" alt="Portable Computer Vision: Tensorflow 2 on a Raspberry Pi" loading="lazy" width="582" height="392"><figcaption>Source: <a href="https://www.hackster.io/news/benchmarking-tensorflow-and-tensorflow-lite-on-the-raspberry-pi-43f51b796796">Benchmarking TensorFlow and TensorFlow Lite on the Raspberry Pi</a> by Alasdair Allan</figcaption></figure><h3 id="custom-build">Custom Build</h3><p>If you already have a Raspberry Pi or some components laying around, the starter kit might include items you don&#x2019;t need.</p><p>Here are the parts I used for my own builds (approximately $250 / unit).</p><ul><li>Raspberry Pi Model 3 B+ ($35)</li><li>Raspberry Pi Camera v2 ($30)</li><li>Coral USB Edge TPU Accelerator&#x200A;&#x2014;&#x200A;accelerates model inferencing ($75, <a href="https://coral.withgoogle.com/products/accelerator">link</a>)</li><li>Pi Foundation Display&#x200A;&#x2014;&#x200A;7&quot; Touchscreen Display ($80, <a href="https://www.adafruit.com/product/2718">link</a>)</li><li>SmartiPi Touch Stand ($25, <a href="http://www.adafruit.com/product/3187">link</a>)</li><li>Adjustable Pi Camera Mount ($5, <a href="https://www.adafruit.com/product/1434">link</a>)</li><li>Flex cable for RPi Camera 24&apos;&#x2019; ($3, <a href="https://www.adafruit.com/product/1731">link</a>)</li></ul><p>I would love to hear about your own build list! &#x2764;&#xFE0F; Tweet me <a href="https://twitter.com/grepLeigh">@grepLeigh</a> or comment below.</p><hr><h2 id="part-3-raspberry-pi-setup-">Part 3&#x2014; Raspberry Pi Setup&#xA0;&#x1F370;</h2><p>If you purchased an SD card pre-loaded with NOOBS, I recommend walking through this overview first: <a href="https://projects.raspberrypi.org/en/projects/raspberry-pi-setting-up/2">Setting up your Raspberry Pi</a></p><p><strong>Before proceeding</strong>, you&#x2019;ll want to:</p><ul><li>Connect your Pi to the internet (<a href="https://projects.raspberrypi.org/en/projects/raspberry-pi-using/4">doc</a>)</li><li>SSH into your Raspberry Pi (<a href="https://www.raspberrypi.org/documentation/remote-access/ssh/">doc</a>)</li></ul><hr><h2 id="part-4-primary-computer-download-install-dependencies">Part 4&#x2014; Primary Computer: Download &amp; Install Dependencies</h2><p>rpi-vision is a set of tools that makes it easier for you to:</p><ul><li>Install a lot of dependencies on your Raspberry Pi (TensorFlow Lite, TFT touch screen drivers, tools for copying PiCamera frame buffer to a TFT touch screen).</li><li>Deploy models to a Raspberry Pi.</li><li>Train new models on your computer or Google Cloud&#x2019;s AI Platform.</li><li>Compile 8-bit quantized models for an Edge TPU.</li></ul><ol><li>Clone the rpi-vision repo on your <strong>primary computer</strong> (not your Raspberry Pi)</li></ol><pre><code>$ git clone git@github.com:leigh-johnson/rpi-vision.git &amp;&amp; cd rpi-vision</code></pre><p>2. On your <strong>primary computer</strong>, create a new virtual environment, then install the rpi-vision package.</p><pre><code>$ pip install virtualenv; virtualenv -p $(which python3) .venv &amp;&amp; source .venv/bin/activate &amp;&amp; pip install -e .</code></pre><p>3. Verify you can <strong>SSH into your Raspberry Pi</strong> before proceeding.</p><p>If you&#x2019;re using the default Raspbian image, your Pi&#x2019;s hostname will beraspberrypi.local</p><pre><code>$ ssh pi@raspberry.local</code></pre><hr><h2 id="part-5-primary-computer-create-configuration-files">Part 5&#x2014; Primary Computer: create configuration files</h2><p>rpi-vision uses <strong>Ansible</strong> to manage deployments and tasks on your Raspberry Pi. <strong>Ansible </strong>is a framework for automating the configuration of computers.</p><p>Create 2 configuration files required by Ansible:</p><h3 id="-env-my-inventory-ini"><strong>.env/my-inventory.ini</strong></h3><p>If you&#x2019;re using a custom hostname for your Pi, replace raspberrypi.local.</p><pre><code>tee -a .env/my-inventory.ini &lt;&lt;EOF
[rpi_vision]
raspberrypi.local

[rpi_vision:vars]
ansible_connection=ssh
ansible_user=pi
ansible_python=/usr/bin/python3
EOF</code></pre><h3 id="-env-my-vars-json">.env/my-vars.json</h3><p>If you&#x2019;re using a custom hostname for your Pi, replace raspberrypi.local.</p><pre><code>tee -a .env/my-vars.ini &lt;&lt;EOF
{ 
  &quot;RPI_HOSTNAME&quot;: &quot;raspberrypi.local&quot;,
  &quot;VERSION&quot;: &quot;release-v1.0.0&quot;
}
EOF</code></pre><hr><h2 id="part-6-raspberry-pi-install-dependencies">Part 6&#x2014; Raspberry Pi: Install Dependencies</h2><pre><code>$ make rpi-install</code></pre><p>You&#x2019;ll see the output of an <strong>Ansible playbook</strong>. <a href="https://docs.ansible.com/"><strong>Ansible</strong></a> is a framework for automating the configuration of computers.</p><p>A quick summary of what&#x2019;s being installed on your Pi:</p><ul><li>rpi-vision repo</li><li>rpi-fbcp (a tool for copying framebuffer from PiCamera to TFT touch screen display)</li><li>TFT touch screen drivers and X11 configuration</li></ul><p>You can inspect the tasks run on your Raspberry Pi by opening playbooks/bootstrap-rpi.yml</p><p>While the installation is running, read through the next section to learn <em><strong>how</strong></em> CNNS<strong> </strong>work and <em><strong>why</strong></em> they are useful for<strong> computer vision</strong> tasks.</p><hr><h2 id="part-7-introduction-to-cnns-convolutional-neural-networks-">Part 7&#x2014; Introduction to CNNs (convolutional neural networks)</h2><p>CNNs are the key technology powering self-driving cars and image search engines. The technology is common for computer vision, but can also be applied to any problem with a <strong>hierarchical pattern in the data</strong>, where a <strong>complex pattern</strong> can be <strong>assembled from simpler patterns</strong>.</p><h3 id="modeling-the-visual-cortex">Modeling the Visual&#xA0;Cortex</h3><p>In the late 1950s and 1960s, David H. Hubel and Torton Wielson performed experiments on cats and monkeys to better understand the visual cortex.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2021/05/1-XTuIUDFclEBtSfgL25FHmg.png" class="kg-image" alt="Portable Computer Vision: Tensorflow 2 on a Raspberry Pi" loading="lazy" width="600" height="351" srcset="https://bitsy.ai/content/images/2021/05/1-XTuIUDFclEBtSfgL25FHmg.png 600w"><figcaption>SINGLE UNIT ACTIVITY IN STRIATE CORTEX OF UNRESTRAINED CATS</figcaption></figure><p>They demonstrated neurons in the striate cortex respond to stimuli in a <strong>limited visual field, </strong>which they called a <strong>receptive field.</strong></p><p>They<strong> </strong>noted concentric overlapping responses, where complex patterns were combinations of lower-level patterns.</p><p>Their findings also revealed <strong>specialization</strong>, where some neurons would <strong>only respond </strong>to a <strong>specific shape</strong> or pattern.</p><p>In the 1980s, inspired by Hubel and Wielson, Kunihiko Fukushima published on the <strong>neocognitron</strong>,<strong> </strong>a neural network capable of learning patterns with geometrical similarity.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2021/05/1-3Ou8n845wH09j7Qi8XVkqw.png" class="kg-image" alt="Portable Computer Vision: Tensorflow 2 on a Raspberry Pi" loading="lazy" width="423" height="600"><figcaption>Neocognitron: A Self-organizing Neural Network Model for a Mechanism of Pattern Recognition Unaffected by Shift in&#xA0;Position</figcaption></figure><p>The <strong>neocogitron</strong> has two key properties:</p><ul><li><em><strong>Learned patterns are hierarchal.</strong></em> Increasingly complex patterns are composed from simpler patterns.</li><li><em><strong>Learned patterns are position-invariant and translation-invariant</strong></em><strong>. </strong>After the network learns a pattern, it can recognize the pattern at different locations. After <strong>learning how to classify a dog</strong>, the network can accurately classify an upside-down dog <strong>without learning an entirely new pattern.</strong></li></ul><p>The <strong>neocogitron</strong> model is the inspiration for modern <strong>convolutional neural networks.</strong></p><hr><h3 id="visualizing-a-convolution-operation-2d">Visualizing a Convolution Operation: 2D</h3><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2021/05/1-RovKo3n98vGb9au_EdlAiw.jpeg" class="kg-image" alt="Portable Computer Vision: Tensorflow 2 on a Raspberry Pi" loading="lazy" width="600" height="372" srcset="https://bitsy.ai/content/images/2021/05/1-RovKo3n98vGb9au_EdlAiw.jpeg 600w"><figcaption>(Left) 2D 4x4 Input matrix. (Middle) 2D 2x2 kernel. (Right) 2D 2x2 output feature&#xA0;map.</figcaption></figure><p>The <strong>input layer</strong> is fed into <strong>convolutional layers, </strong>which transform <strong>regions</strong> of the input using a <strong>filter.</strong></p><p>The <strong>filter</strong> is also referred to as a <strong>kernel.</strong></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2021/05/1-IBqBbX2F698Nd91mBx2ACQ.gif" class="kg-image" alt="Portable Computer Vision: Tensorflow 2 on a Raspberry Pi" loading="lazy" width="600" height="524" srcset="https://bitsy.ai/content/images/2021/05/1-IBqBbX2F698Nd91mBx2ACQ.gif 600w"><figcaption>The filter &#x201C;slides&#x201D; to each possible position, and the result is added to the feature&#xA0;map.</figcaption></figure><p>For each position in the input matrix, the <strong>convolution operation</strong> performs matrix multiplication on each element.</p><p>The resulting matrix is summed and stored in a <strong>feature map.</strong></p><p>The operation is repeated for each position in the input matrix.</p><h3 id="visualizing-a-convolution-operation-3d">Visualizing a Convolution Operation: 3D</h3><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2021/05/1-sDaGvIgnrePW9BRQoTAtLw@2x.gif" class="kg-image" alt="Portable Computer Vision: Tensorflow 2 on a Raspberry Pi" loading="lazy" width="600" height="351" srcset="https://bitsy.ai/content/images/2021/05/1-sDaGvIgnrePW9BRQoTAtLw@2x.gif 600w"><figcaption>Applied Deep Learning&#x200A;&#x2014;&#x200A;Part 4: Convolutional Neural&#xA0;Networks</figcaption></figure><p>The <strong>input layer </strong>of a CNN is usually a 3D data structure with <em><strong>height</strong></em>, <em><strong>width</strong></em>, and <em><strong>channel </strong></em>(RGB or greyscale values).</p><p>The deeper we go in the feature map stack, the <strong>sparser </strong>each map layer becomes. That means the filters detect fewer features.</p><p>The <strong>first few layers</strong> of the feature map stack <strong>detect simple edges and shapes</strong>, and look similar to the input image. As we go <strong>deeper</strong> into a feature map stack, <strong>features become more abstract</strong> to the human eye. Deeper feature layers <strong>encode classification data, </strong>like &#x201C;cat face&#x201D; or &#x201C;cat ear&#x201D;.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://bitsy.ai/content/images/2021/05/1-c-QNnJz-l-6dgGHeukVEOg.png" class="kg-image" alt="Portable Computer Vision: Tensorflow 2 on a Raspberry Pi" loading="lazy" width="800" height="152" srcset="https://bitsy.ai/content/images/size/w600/2021/05/1-c-QNnJz-l-6dgGHeukVEOg.png 600w, https://bitsy.ai/content/images/2021/05/1-c-QNnJz-l-6dgGHeukVEOg.png 800w" sizes="(min-width: 720px) 720px"><figcaption>Applied Deep Learning&#x200A;&#x2014;&#x200A;Part 4: Convolutional Neural&#xA0;Networks</figcaption></figure><hr><h3 id="do-you-want-to-learn-more-about-cnns">Do you want to learn more about&#xA0;CNNS?</h3><p>Your dependency installation is probably done by now. To forge ahead, skip to <strong>Part 8 - Deploy Pre-trained Model MobileNetV2.</strong></p><p>If you plan on training a custom classifier or want to read more about convolutional neural networks, start here:</p><ul><li><a href="https://towardsdatascience.com/applied-deep-learning-part-4-convolutional-neural-networks-584bc134c1e2">Applied Deep Learning&#x200A;&#x2014;&#x200A;Part 4: Convolutional Neural Networks</a></li><li><a href="https://www.amazon.com/Hands-Machine-Learning-Scikit-Learn-TensorFlow/dp/1491962291">Hands on Machine Learning with Scikit-learn and TensorFlow</a>, <em>Chapter 13, Convolutional Neural Networks</em>, by Aur&#xE9;lien G&#xE9;ron</li><li><a href="https://www.amazon.com/Deep-Learning-Python-Francois-Chollet/dp/1617294438/">Deep Learning with Python</a>, <em>Chapter 5 Deep Learning for Computer Vision,</em> by Francois Chollet</li></ul><hr><h2 id="part-8-deploy-pre-trained-model-mobilenetv2-">Part 8&#x200A;&#x2014;&#x200A;Deploy Pre-trained Model (MobileNetV2)</h2><h3 id="live-demo-using-tensorflow-2-0-">Live Demo (using TensorFlow 2.0)</h3><ol><li>SSH into your Raspberry Pi</li></ol><pre><code>$ ssh raspberrypi.local</code></pre><p>2. Start a new tmux session</p><pre><code>pi@raspberryi:~ $ tmux new-session -s mobilenetv2</code></pre><p>3. Split the tmux session vertically by pressing control+b, then &#x201C;</p><p>4. Start an fbcp process, which will copy framebuffer from the PiCamera to the TFT display via SPI interface. Leave this process running.</p><pre><code>pi@raspberryi:~ $ fbcp</code></pre><p>5. Switch tmux panes by pressing control+b, then o.</p><p>6. Activate the virtual environment installed in earlier, in Part 6.</p><pre><code>pi@raspberryi:~ $ cd ~/rpi-vision &amp;&amp; . .venv/bin/activate</code></pre><p>7. Start a mobilenetv2 agent process. The agent will take roughly 60 seconds to initialize.</p><pre><code>pi@raspberryi:~/rpi-vision $ python rpi_vision/agent/mobilenet_v2.py</code></pre><p>You&#x2019;ll see a summary of the model&#x2019;s base, and then the agent will print inferences until stopped. <a href="https://gist.github.com/leigh-johnson/14541749afbd8e4471b85699ddd0c9f5">Click for a gist</a> of what you should see.</p><p>This demo uses weights for <strong>ImageNet</strong> classifiers, which you can look up at <a href="http://image-net.org/explore">image-net.org</a>.</p><h2 id="wrapping-up">Wrapping Up</h2><p>Congratulations, you just deployed an image classification model to your Raspberry Pi! &#x2728;</p><p>Follow me @grepLeigh to get updates on this blog series. In my <strong>next post</strong>, I will show you how to:</p><ol><li>Convert a model to <strong>TensorFlow Lite, </strong>a model format optimized for embedded and mobile devices.</li><li>Accelerate inferences of any <strong>TensorFlow Lite</strong> model with Coral&#x2019;s <strong>USB Edge TPU Accelerator</strong> and <strong>Edge TPU Compiler.</strong></li><li>Employ <strong>transfer learning </strong>to re-train MobileNetV2 with a <strong>custom image classifier.</strong></li></ol>]]></content:encoded></item></channel></rss>