微控制器上的机器学习模块应用

在之前的一篇文章《第一台微控制器》中,讨论了如何使用微控制器(MCU)来创建家庭自动化系统。本文将在此基础上,进一步探讨微控制器的强大功能和灵活性,尽管它们存在一定的局限性。本文的核心是DIY精神,即从零开始的精神。

什么是微控制器(MCU)?

微控制器是一种非常小型的计算机,它们没有常见的外设(如扬声器、显示器、键盘、鼠标等),但它们拥有一些新的特性:I/O点,这些点可以连接到其他设备,以监控和管理这些外部设备。微控制器是智能设备如洗衣机、汽车燃油喷射系统、手机等的核心。

Raspberry Pi Pico

下图是Raspberry Pi Pico(简称Pico),将在这款微控制器上实现一个包含KNN分类算法的报警系统。整个板子的尺寸仅为21毫米×51毫米。

问题描述

如果已经阅读了《第一台微控制器》,就会知道它使用两个温度测量值来判断冰箱门是否意外地被打开。Pico每5秒获取一次这两个温度,评估它们的差异,并在冰箱门下方的温度比环境温度低5摄氏度以上时点亮红色LED。可以在GitHub仓库中看到运行在Pico上的Python代码。

KNN算法在Pico上的应用

选择使用KNN算法,因为它易于理解(因此也易于调试),并且网络上有许多从头开始编写的版本。虽然KNN不需要事先训练,但它需要训练数据。典型的机器学习数据集(例如鸢尾花数据集)基于经验观察,但这个应用的训练数据完全是合成的。

train_data = [] def generate_training_data(): global train_data train_data = [] for temp_amb in [10 + 2.5 * n for n in range(13)]: for temp_door in [10 + 2.5 * n for n in range(13)]: if temp_door < temp_amb + 2.5: train_data.append([temp_amb, temp_door, 'FAULT']) else: train_data.append([temp_amb, temp_door, 'NORMAL']) generate_training_data()

训练数据包含169个样本,每个样本包含两个特征(温度)和一个类别(NORMAL、ALARM或FAULT)。下图展示了这些训练数据的散点图。

Pico上的KNN算法

由于Pico的MicroPython不支持NumPy,通常的机器学习库如Scikit-learn(几乎所有这些都依赖于NumPy)自动被排除在外。因此,使用从头开始的KNN版本是不可避免的。

from math import sqrt def encode_class_names(dataset): class_names = [row[-1] for row in dataset] class_names_set = set(class_names) index_lookup = {name: i for i, name in enumerate(class_names_set)} for row in dataset: row[-1] = index_lookup[row[-1]] return {i: name for i, name in enumerate(class_names_set)} def distance(row1, row2): feature_pairs = zip(row1, row2) deltas = [(a - b) for a, b in feature_pairs] sum_diagonals_squared = sum([delta ** 2 for delta in deltas]) return sqrt(sum_diagonals_squared) def nearest_neighbors(dataset, test_row, num_neighbors): distances = [distance(test_row, train_row) for train_row in dataset] row_distance_pairs = list(zip(dataset, distances)) row_distance_pairs.sort(key=lambda tup: tup[1]) neighbor_rows = [pair[0] for pair in row_distance_pairs[:num_neighbors]] return neighbor_rows def predict(dataset, test_row, num_neighbors): neighbors = nearest_neighbors(dataset, test_row, num_neighbors) neighbor_class_names = [row[-1] for row in neighbors] prediction = max(set(neighbor_class_names), key=neighbor_class_names.count) return prediction

上述代码是基于Jason Brownlee的教程“从头开始用Python开发k-最近邻算法”(访问日期:2021年7月26日)。该教程提供了KNN工作原理的详细解释。

算法测试

dataset_copy = [e[:] for e in train_data[:]] predictions = [] for temp_amb in [t / 2 for t in range(20, 81)]: for temp_door in [t / 2 for t in range(20, 81)]: label = predict(dataset_copy, [temp_amb, temp_door], num_neighbors) predictions.append([temp_amb, temp_door, label]) df2 = pd.DataFrame(predictions, columns=['temp-ambient', 'temp-door', 'state']) print(len(df2)) print(df2['state'].value_counts()) colormap = {'FAULT': 'royalblue', 'ALARM': 'coral', 'FIRE': 'maroon', 'NORMAL': 'limegreen'} colors = df2['state'].map(colormap) ax = df2.plot(kind='scatter', x='temp-ambient', y='temp-door', grid=True, figsize=(8, 8), c=colors, s=96) ax.set_xlabel('环境温度 (C)', fontsize=14) ax.set_xlim([8, 42]) ax.set_ylabel('门温度 (C)', fontsize=14) ax.set_ylim([8, 42])
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485