mirror of
https://github.com/LukeHagar/jims-blog.git
synced 2025-12-06 04:20:07 +00:00
Spelling and one more blog post
This commit is contained in:
@@ -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"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
18
.vscode/settings.json
vendored
Normal file
18
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"cSpell.words": [
|
||||
"Adafruit",
|
||||
"Codespace",
|
||||
"Codespaces",
|
||||
"devcontainer",
|
||||
"jimbobbennett",
|
||||
"microcontrollers",
|
||||
"milliwatts",
|
||||
"Miniforge",
|
||||
"numpy",
|
||||
"raspberrypi",
|
||||
"roadmap",
|
||||
"Scikit",
|
||||
"Seeed",
|
||||
"TLDR"
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ The first step is to open the repo in Codespaces. From the repo in GitHub, selec
|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
@@ -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.
|
||||
@@ -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:
|
||||
|
||||

|
||||
|
||||
@@ -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!
|
||||
|
||||

|
||||
|
||||
@@ -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: ");
|
||||
|
||||
Reference in New Issue
Block a user