From 540a4a24a9b6b6fa06350e394d1906afabd8e9d7 Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Tue, 30 Aug 2022 23:37:19 +0000 Subject: [PATCH] Spelling and one more blog post --- .devcontainer/devcontainer.json | 3 +- .vscode/settings.json | 18 ++++++ .../blogs/auto-posting-to-dev-to/index.md | 4 +- blog/content/blogs/github-codespaces/index.md | 2 +- .../banner.png | Bin 0 -> 49410 bytes .../index.md | 53 ++++++++++++++++++ blog/content/blogs/led-ticker-tape/index.md | 12 ++-- blog/content/blogs/tiny-ml-farts/index.md | 10 ++-- 8 files changed, 87 insertions(+), 15 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 blog/content/blogs/installing-scikit-learn-on-an-apple-m1/banner.png create mode 100644 blog/content/blogs/installing-scikit-learn-on-an-apple-m1/index.md diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 417bee77..5029b308 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -30,7 +30,8 @@ // Add the IDs of extensions you want installed when the container is created. "extensions": [ "bungcip.better-toml", - "davidanson.vscode-markdownlint" + "davidanson.vscode-markdownlint", + "docsmsft.docs-authoring-pack" ] } }, diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..ce2d8661 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,18 @@ +{ + "cSpell.words": [ + "Adafruit", + "Codespace", + "Codespaces", + "devcontainer", + "jimbobbennett", + "microcontrollers", + "milliwatts", + "Miniforge", + "numpy", + "raspberrypi", + "roadmap", + "Scikit", + "Seeed", + "TLDR" + ] +} \ No newline at end of file diff --git a/blog/content/blogs/auto-posting-to-dev-to/index.md b/blog/content/blogs/auto-posting-to-dev-to/index.md index f0fe044b..8c314f4a 100644 --- a/blog/content/blogs/auto-posting-to-dev-to/index.md +++ b/blog/content/blogs/auto-posting-to-dev-to/index.md @@ -21,9 +21,9 @@ It's slightly annoying to have to remember to run this every time you create or GitHub actions is GitHubs CI/CD solution. CI is continuous integration, meaning every time code changes, your code can be built and tested. CD is continuous deployment, so once code is tested it can be deployed automatically. -Essentialy you can specify code that is run whenever someone checks in any changes, merges a branch or PR, or raises issues, creates PRs, any task really that you can do in GitHub. GitHub manages spinning up a VM to run everything, all you have to do is write your action, and pay (obviously - the best things in life are not always free). +Essentially you can specify code that is run whenever someone checks in any changes, merges a branch or PR, or raises issues, creates PRs, any task really that you can do in GitHub. GitHub manages spinning up a VM to run everything, all you have to do is write your action, and pay (obviously - the best things in life are not always free). -GitHub actions are deined using YAML inside your repository (in a `.github\workflows` folder), and you can call out to *actions* that do things, such as checking out code, tagging, running scripts, anything you need. You can also build custom actions. +GitHub actions are defined using YAML inside your repository (in a `.github\workflows` folder), and you can call out to *actions* that do things, such as checking out code, tagging, running scripts, anything you need. You can also build custom actions. ## Custom actions diff --git a/blog/content/blogs/github-codespaces/index.md b/blog/content/blogs/github-codespaces/index.md index aacc0da2..c63e7af5 100644 --- a/blog/content/blogs/github-codespaces/index.md +++ b/blog/content/blogs/github-codespaces/index.md @@ -33,7 +33,7 @@ The first step is to open the repo in Codespaces. From the repo in GitHub, selec ![The new codespace button](image-2.png) -This will set up your code in a new Codespace - essentiall a blank VM using a default image from GitHub. This image is based off Ubuntu, and comes pre-configured with Python. Node, Docker and other stuff. You can read up on this default image at [aka.ms/ghcs-default-image](https://aka.ms/ghcs-default-image). +This will set up your code in a new Codespace - essentially a blank VM using a default image from GitHub. This image is based off Ubuntu, and comes pre-configured with Python. Node, Docker and other stuff. You can read up on this default image at [aka.ms/ghcs-default-image](https://aka.ms/ghcs-default-image). This image contains almost everything I need - the tools are all installed, and VS Code is running with my code in it. diff --git a/blog/content/blogs/installing-scikit-learn-on-an-apple-m1/banner.png b/blog/content/blogs/installing-scikit-learn-on-an-apple-m1/banner.png new file mode 100644 index 0000000000000000000000000000000000000000..f0dba832b730207cfcd6a47d1937bfc3e607ea11 GIT binary patch literal 49410 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 Wa1q9g2o2sGLPkPSyhhY8@P7a%j-I*z literal 0 HcmV?d00001 diff --git a/blog/content/blogs/installing-scikit-learn-on-an-apple-m1/index.md b/blog/content/blogs/installing-scikit-learn-on-an-apple-m1/index.md new file mode 100644 index 00000000..b224bf00 --- /dev/null +++ b/blog/content/blogs/installing-scikit-learn-on-an-apple-m1/index.md @@ -0,0 +1,53 @@ +--- +title: "Installing Scikit-Learn on a Apple Silicon" +date: 2021-01-31T17:01:05Z +draft: false +featured_image: banner.png +images: + - blogs/installing-scikit-learn-on-an-apple-m1/banner.png +tags: ["ai", "python", "scikit-learn" ,"apple", "applesilicon"] +description: Learn how to install and run Scikit-learn on Apple Silicon (Apple M1) +--- + +At the end of last year I splashed out on a shiny new Apple MacBookAir with the M1 processor as I was fed up with an old Intel-based MacBookPro that was quite honestly crippled by corporate anti-virus software. + +Out the box this machine is amazing. It's ridiculously fast, and lasts for ever on battery. Seriously - I charge it every 2 days and manage a full day of coding, writing, emails, Teams, the lot. Did I also mention it's fast? I can have all the things running and it barely breaks a sweat, even with only 8GB of RAM. + +The downside is that not all software works on the new ARM-64 architecture. Apple have a translation layer called Rosetta 2 (Rosetta 1 was their translation from PowerPC to Intel), and this works great most of the time for every day apps, but it doesn't always work for development tools and libraries, as the mix of translated and untranslated stuff just breaks down. + +One library I needed to use that isn't supported is Scikit-Learn. Now I'm no Python expert, and I don't really understand what Scikit-Learn does, I just know I need it to train some TinyML models to [recognize wake words on an Arduino Nano 33 sense board](https://eloquentarduino.github.io/2020/08/better-word-classification-with-arduino-33-ble-sense-and-machine-learning/). If I try a normal pip install scikit-learn, I get a whole wall of errors, both using Python 3.9 for the M1, and Python 3.8 under Rosetta. + +So what to do? + +It turns out the solution is to use [Miniforge](https://github.com/conda-forge/miniforge), a version of Conda that is comparable to Miniconda, but supports various CPU architectures. Whatever that means. As I said, I'm no Python expert, but this tool essentially allows me to create virtual environments and install packages compiling them for the M1 chip! Any packages it doesn't support can then be installed from pip. + +So how do I install all this? + +Firstly - I need to install Miniforge. The install script is on the GitHub page, or you can download it by clicking [this link](https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-MacOSX-arm64.sh). It wanted to activate it in every terminal, which I didn't want so I turned that off by running: + +```bash +conda config --set auto_activate_base false +``` + +Next I went to the folder containing my Python code, and created a virtual environment: + +```bash +conda create -n .venv python +``` + +This is pretty much the same as creating a virtual environment with Python, just using a different tool. Like with Python, the virtual environment then needs to be activated: + +```bash +conda activate .venv +``` + +Finally, I can install Scikit-Learn: + +```bash +conda install scikit-learn +``` + +Done! For the particular thing I'm working on, I needed a package that isn't available from Miniforge, so I just installed it with pip: + +pip install micromlgen +Done! I could then run my Python script as normal, and it all worked nicely. And fast - my M1 ran the script in question in 2 seconds, 5 times faster than the 10 seconds my Surface Book took. \ No newline at end of file diff --git a/blog/content/blogs/led-ticker-tape/index.md b/blog/content/blogs/led-ticker-tape/index.md index 384723b9..1485f6e7 100644 --- a/blog/content/blogs/led-ticker-tape/index.md +++ b/blog/content/blogs/led-ticker-tape/index.md @@ -15,7 +15,7 @@ Anyone who knows me knows I'm a big fan of IoT and LEDs. I love using IoT device I've been giving the .NET IoT libraries a spin for an upcoming project using a Raspberry Pi Zero W 2. I'm usually a Python person when using a Pi, but the project I'm working on needs a service that doesn't have Python libraries that run on a Pi. Instead it has a .NET library, so it was time to break out my C# skills for the first time in years. -I wanted to build an LED panel that can display text, either static text or scrolling text. So I picked up this [WS2812B panel from Amazon (allifliate link)](https://amzn.to/3sVjF7M), and started to dig into the .NET IoT libraries. WS2818b LEDs are also known as NeoPixels, and are addressable multicolor LEDs, so you can light up individual ones in any color you like. They are addressed based on the order they are connected to your device, so the first pixel in a string of LEDs is 0, the next is 1 and so on. You can add as many as you like, and the addresses just keep going up. +I wanted to build an LED panel that can display text, either static text or scrolling text. So I picked up this [WS2812B panel from Amazon (affiliate link)](https://amzn.to/3sVjF7M), and started to dig into the .NET IoT libraries. WS2818b LEDs are also known as NeoPixels, and are addressable multicolor LEDs, so you can light up individual ones in any color you like. They are addressed based on the order they are connected to your device, so the first pixel in a string of LEDs is 0, the next is 1 and so on. You can add as many as you like, and the addresses just keep going up. The .NET IoT libraries are on GitHub at [github.com/dotnet/iot](https://github.com/dotnet/iot) and available as a NuGet. They support a wide range of boards including the Raspberry Pi. @@ -54,7 +54,7 @@ var neoPixels = new Ws2812b(spi, 256); When you create the pixels, you pass in the length of the strip. I've been using a 8x32 panel, which is actually a 256 pixel long strip arranged in and up/down pattern. -Once created, you light pixels by getting a `BitmapImage` from them, and setting colors on that. This image is a lengthx1 image, so 1 pixel tall, and as wide as the length of the LEDs. For example, my 8x32 panel is 256 pixels long, so is a bitmap of 256x1. +Once created, you light pixels by getting a `BitmapImage` from them, and setting colors on that. This image is a `length x 1` image, so 1 pixel tall, and as wide as the length of the LEDs. For example, my 8x32 panel is 256 pixels long, so is a bitmap of 256x1. ```csharp var img = neoPixels.Image; @@ -68,7 +68,7 @@ img.SetPixel(1, 0, Color.Green); img.SetPixel(2, 0, Color.Blue); ``` -Once the pixels are set in the image, it is commited and the LEDs updated. +Once the pixels are set in the image, it is committed and the LEDs updated. ```csharp neoPixels.Update(); @@ -103,7 +103,7 @@ This will set the pixels 0 and 1 to off, and pixel 2 to blue. ## Writing text -I wanted to make my panel show text, either short static text, or scrolling text. The first thing I needed was a font - something that defines how to create letters from pixels. I found a simplar project based on Aruino in a [GitHub project from Josh Levine](https://github.com/bigjosh/SimpleTickerTape/tree/main/fonts) so leveraged this code for a font and re-wrote it in C#. +I wanted to make my panel show text, either short static text, or scrolling text. The first thing I needed was a font - something that defines how to create letters from pixels. I found a similar project based on Arduino in a [GitHub project from Josh Levine](https://github.com/bigjosh/SimpleTickerTape/tree/main/fonts) so leveraged this code for a font and re-wrote it in C#. Next I needed the mapping code. These fonts are defined as columns of binary data, so the bits to set. Each character is 8 bits tall (the size of my panel), and 6 bits wide. This mapping code was a bit of fun as I not only needed to divide up my pixels into columns, and map from a pixel in the 1 dimensional strip to a character pixel, but the strips go up and down! @@ -122,7 +122,7 @@ This gives for the first few columns: 7 8 23 24 ``` -This means that the mapping code needs to alternate - for 0dd numbered columns the pixles go down, for even numbered the pixels go up so the mapping has to be reversed! +This means that the mapping code needs to alternate - for 0dd numbered columns the pixels go down, for even numbered the pixels go up so the mapping has to be reversed! I'm not going to dive into all this mapping here, but you can find the code with full documentation in my [NeoPixelTickerTape GitHub repo](https://github.com/jimbobbennett/NeoPixelTickerTape). @@ -134,6 +134,6 @@ I then added scrolling code that writes text starting at the right-most column, You can find the code with full documentation in my [NeoPixelTickerTape GitHub repo](https://github.com/jimbobbennett/NeoPixelTickerTape). -You can also download a NuGet pckage to use in your apps: +You can also download a NuGet package to use in your apps: ![Select this to access the nuget](https://img.shields.io/nuget/v/JimBobBennett.NeoPixelTickerTape.svg?style=flat&logo=nuget) diff --git a/blog/content/blogs/tiny-ml-farts/index.md b/blog/content/blogs/tiny-ml-farts/index.md index 9d2b28a9..6eadcd31 100644 --- a/blog/content/blogs/tiny-ml-farts/index.md +++ b/blog/content/blogs/tiny-ml-farts/index.md @@ -17,7 +17,7 @@ My 8-year-old daughter bought me "Farts - a spotters guide" - a book with some b ## TinyML -TinyML is a relatively new field, and is all about creating tiny machine learning models that can run on microcontrollers. These models are really tiny - in the order of kilobytes instead of the usual megabytes or gigabytes. They need to be this tiny to run on microcontrollers that typically have killobytes of RAM. These models also draw little power, typically in the single-digit milliwatts or lower. +TinyML is a relatively new field, and is all about creating tiny machine learning models that can run on microcontrollers. These models are really tiny - in the order of kilobytes instead of the usual megabytes or gigabytes. They need to be this tiny to run on microcontrollers that typically have kilobytes of RAM. These models also draw little power, typically in the single-digit milliwatts or lower. What are the use cases for TinyML? Well there are loads, anywhere you want to run ML models offline with minimal power draw. You may even have some TinyML models running in your house right now. For example, smart voice controlled devices listen for a wake word, and this needs to be offline and draw minimal power - perfect for a TinyML model. Another use case is in healthcare with devices that can monitor your health that run for years on tiny batteries. It's also being used in animal smart collars and trackers, [using audio to monitor the health of elephants in the wild](https://www.hackster.io/contests/ElephantEdge). So yes - a fart detector has a real world application! @@ -27,7 +27,7 @@ To build a TinyML model you need to decide what type of model to build, gather t ## Building a fart detector -For my fart detector, I needed to build an audio classifier that could run on a microcontroller. Becuase I'm terrible at electronics and understanding I2C, SPI and all that other stuff, I decided to use an all-in-one Arduino board that has a microphone built in allowing me to use off-the-shelf Arduino libraries to gather audio data. The board of choice was the Arduino Nano 33 Sense BLE board, a small Arduino board with a whole raft of sensors including a microphone, tempersture, pressure, humidity, light level and color, gesture and proximity. That's a lot of sensors in such a tiny board! +For my fart detector, I needed to build an audio classifier that could run on a microcontroller. Because I'm terrible at electronics and understanding I2C, SPI and all that other stuff, I decided to use an all-in-one Arduino board that has a microphone built in allowing me to use off-the-shelf Arduino libraries to gather audio data. The board of choice was the Arduino Nano 33 Sense BLE board, a small Arduino board with a whole raft of sensors including a microphone, temperature, pressure, humidity, light level and color, gesture and proximity. That's a lot of sensors in such a tiny board! ![An arduino Nano sense 33 BLE IoT board](nano-sense.jpg) @@ -37,7 +37,7 @@ To code this board, I could use the free Arduino IDE, but I prefer to use [Visua To train TinyML models you not only need the model to by tiny, but you also need small inputs - the more data that goes into training the model or inference (that is running the model), the larger it is. Audio data can be quite large - for example CD quality audio (remember CDs?) is 44.1KHz/16-bit which means it captures 2 bytes of data 44,100 times per second, or 176KB per second! That's a lot of data - if we wanted to use all of it and train our model with 2 seconds worth of data it wouldn't be TinyML any more. -A great trick with audio data is realising you don't need all of it to classify particular sounds. Instead you can get an average value that represents many samples and use that as the data. In the case of the Arduino, the library that captures audio, [PDM](https://www.arduino.cc/en/Reference/PDM), captures audio at 16KHz in 512 byte buffers, containing 256 2-byte samples. This means each buffer has 1/64th of a second of audio data in it. We can then calculate a root mean square (RMS) of all this data to get a single 4-byte floating point value. If we do this for every buffer, we end up with 64 4-byte floats per second, or 256 bytes per second. Much smaller than raw audio at the PDM sample rate of 16KHz giving 32,000 bytes per second! +A great trick with audio data is realizing you don't need all of it to classify particular sounds. Instead you can get an average value that represents many samples and use that as the data. In the case of the Arduino, the library that captures audio, [PDM](https://www.arduino.cc/en/Reference/PDM), captures audio at 16KHz in 512 byte buffers, containing 256 2-byte samples. This means each buffer has 1/64th of a second of audio data in it. We can then calculate a root mean square (RMS) of all this data to get a single 4-byte floating point value. If we do this for every buffer, we end up with 64 4-byte floats per second, or 256 bytes per second. Much smaller than raw audio at the PDM sample rate of 16KHz giving 32,000 bytes per second! ```cpp #define BUFFER_SIZE 512U @@ -61,7 +61,7 @@ You can find the full code to capture audio samples in the [Microsoft IoT Curric ### Train the model -To train the model, we need a good range of audio data captured from the Arduino device - ideally 15-30 samples per audio we want to classify. A classifier distinguishes the input between multiple labels, so we need to gather data for multiple lables. For example, to classify the farts from my fart book I'd need to gather 15-30 samples for at least 2 different farts. +To train the model, we need a good range of audio data captured from the Arduino device - ideally 15-30 samples per audio we want to classify. A classifier distinguishes the input between multiple labels, so we need to gather data for multiple labels. For example, to classify the farts from my fart book I'd need to gather 15-30 samples for at least 2 different farts. The audio data sent to the serial monitor from the Arduino can be captured into .csv files, and these can be loaded by a Python script and used to train a model. @@ -101,7 +101,7 @@ You can find the training code in the [Microsoft IoT Curriculum resource GitHub The C++ code that comes out of the training can then be added to the microcontroller code. Instead of dumping the audio data to the serial port, it can be sent to the classifier code, and the label of the best match is returned. ```cpp -void procesSamples() +void processSamples() { // Write out the classification to the serial port Serial.print("Label: ");