🍓 第十四章 AIoT 实验范例:Node-RED+TensorFlow.js
📄 一句话概括

TensorFlow.js 把神经网络搬进了 JavaScript:浏览器就是推理机——前两章 AI 都住在 Python 里,这一章它们换了个住处,而树莓派只负责当客户。

📝 本节和前面几节的关系

11 章的 YOLO、12 章的 MediaPipe/LiteRT,全跑在 Python 进程里;13 章的 Node-RED 把采集、分发、展示连成了流水线,但没装大脑。本章补上最后一块:让大脑跑在 JavaScript 里——Node-RED 本身就是 Node.js 程序,AI 节点可以无缝插进 13 章的画布。它和 12-3 的 LiteRT 是一组镜像:同一件事(设备端推理),Python 和 JavaScript 各有一条路。

一、为什么需要 TensorFlow.js

第 11、12 章的路线是【Python 写脚本 → 调库推理 → 自己画窗口】,特征是代码全在一个进程里。但 Node-RED 的世界是【数据在节点之间流动】,节点都是 JavaScript。要让 AI 参与这个流,有两条路:

  1. 跨界:Python 进程推理完,结果经 HTTP/MQTT 送进 Node-RED——13 章已经这么干过一次(数据去 ThingSpeak);
  2. 同籍:让 AI 本身跑在 JavaScript 里,直接当画布上一个节点。

TensorFlow.js(简称 TF.js)走的是第 2 条路:Google 把 TensorFlow 的核心用 TypeScript 重写,在浏览器和 Node.js 里都能跑——训练、推理、加载现成模型三件事全包。它 2018 年发布,2026 年仍在维护(最后一个正式版 4.22.0,2024 年 10 月发布;代码库 2026 年年中仍有提交,属于【稳定、不激进】的成熟库)。

对树莓派项目,它的独特价值有三条:

二、一个库,三种玩法

TF.js 官方定位有三种用法,本章会用到前两种:

用法 是什么 对应本课
跑现成模型 直接加载 Google 训练好的模型(COCO-SSD 等) 14-3 的物体识别
迁移学习 在浏览器里用少量数据重训现成模型 14-4 的 Teachable Machine
从零构建 用 Layers API 手写网络结构、自己训练 超出本章,属于 Web ML 开发

第三种了解即可。重点是【现成模型】和【迁移学习】的组合拳:先白嫖别人的大模型,再用自己的一百张照片教它几个新词——这正是 14-4 Teachable Machine 背后发生的事。

三、后端四选一:快不快看这里

TF.js 在浏览器里有四个计算后端,速度天差地别:

后端 原理 什么时候用它
WebGL 借 GPU 的图形管线做矩阵运算 默认后端,大多数场景最稳
WASM WebAssembly 字节码,CPU 原生速度 没有 GPU 的设备(树莓派浏览器常见)
WebGPU 新一代 GPU 接口 较新浏览器才有,覆盖还不齐
CPU 纯 JavaScript 兜底,最慢

树莓派上有个反直觉点:Chromium 的 WebGL 走的可能是软件渲染(没有真 GPU 加速),此时 WASM 后端反而更快。查和切各一条命令:

tf.getBackend();                      // 现在用的是哪个后端
tf.setBackend('wasm').then(() => {}); // 切到 WASM

注意:Node-RED 里的 AI 节点不走浏览器后端,走 tfjs-node(Node.js 原生绑定,底下是真 TensorFlow C++),这是 14-2 最大的坑的根源。

四、和已学 AI 章节的对照表

任务 Python 路线(11/12 章) JavaScript 路线(本章)
通用物体识别 11-5 YOLO(picamera2/OpenCV 喂图) 14-3 COCO-SSD(Node-RED 喂图)
自定义分类 12-4 手势(MediaPipe+手写规则) 14-4 Teachable Machine(真·训练)
设备端推理 12-3 LiteRT(.tflite) LiteRT.js(.tflite 上浏览器,了解)

特别对照 12-4:当时我们掰正过【那不是 AI 是规则】;本章 14-4 是真正的对手戏——这次是真的训练一个模型,只是训练地点从 GPU 机房搬到了你的浏览器标签页里。

五、动手 Demo:一页 HTML,训练并预测

在树莓派桌面建文件 first-tfjs.html,内容如下,然后用 Chromium 打开:

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>TF.js 第一课</title></head>
<body>
<p>训练中,看控制台(F12)输出</p>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/[email protected]/dist/tf.min.js"></script>
<script>
  // 拟合直线 y = 2x - 1
  const model = tf.sequential();
  model.add(tf.layers.dense({units: 1, inputShape: [1]}));
  model.compile({loss: 'meanSquaredError', optimizer: 'sgd'});

  const xs = tf.tensor2d([1, 2, 3, 4, 5], [5, 1]);  // 输入
  const ys = tf.tensor2d([1, 3, 5, 7, 9], [5, 1]);  // 正确答案

  model.fit(xs, ys, {epochs: 100}).then(() => {
    console.log('backend =', tf.getBackend());
    model.predict(tf.tensor2d([10], [1, 1])).print(); // 期望接近 19
  });
</script>
</body>
</html>

看到什么算成功:按 F12 打开控制台,训练 100 轮后打印一行类似:

backend = webgl
Tensor
    [[18.9973736],]

读结果:数据喂的是 y = 2x - 1,问 x = 10 时 y 是多少——代数上等于 19,但模型是【学】出来的不是【算】出来的,所以输出是 18.99 这类带小数的近似值。学出来的答案永远带误差,这是所有模型(包括后面的 COCO-SSD)的共同本性。另外 backend = 那行能直接告诉你这台树莓派的浏览器实际在用哪个后端。

30 秒实验

同一个页面刷新后,在控制台依次输入:

tf.version.tfjs
tf.memory().numTensors

第一条是版本号(应显示 4.22.0 附近的数字);第二条是当前存活张量数——每次 tf.tensor2d 都会占一份显存/内存,忘了释放张量会越跑越慢,这是 TF.js 最经典的内存泄漏来源。

六、常见误区

  1. TensorFlow.js 就是 TensorFlow 的 JS 版、啥都一样 —— 生态是通的(Converter 能把 Python 训的 SavedModel 转过来),但它是独立重写的库,算子覆盖和性能各有边界。
  2. 浏览器里跑 AI 一定慢得没法用 —— 后端选对时(WASM/WebGL),COCO-SSD 这类小模型在浏览器里完全可以实时;慢的印象大多来自后端没选对或模型太大。
  3. 版本号停在 4.22.0 说明项目死了 —— 正式发版频率低(2024 年 10 月后无新 tag)但代码库在维护;【commit 时钟】和【发版时钟】不是同一个。
  4. TF.js 只能推理不能训练 —— 恰恰相反,浏览器训练是它的招牌(14-4 的 Teachable Machine 全程在浏览器里完成训练)。
自测三问(点开对答案) 1. Node-RED 里的 AI 节点用的是浏览器后端吗? 不是。Node-RED 是 Node.js 程序,AI 节点走 tfjs-node 原生绑定(TensorFlow C++),与浏览器那套 WebGL/WASM 后端无关。 2. 树莓派浏览器里 WebGL 后端一定最快吗? 不一定。Chromium 可能走软件渲染,此时 WASM 后端往往更快——以 `tf.getBackend()` 加实测为准。 3. Demo 里预测 x=10 输出 18.99 而不是精确的 19,正常吗? 正常。模型是拟合出来的,输出永远带误差;只要误差在可接受范围,就是训练成功。

相关笔记