- 教程
- 文档
- 示例工程
【免费下载链接】website-archive
Archive of the Coding Train website (first version)
在 The Coding Train 的《A Beginner's Guide to Machine Learning in JavaScript》系列第 2.1 节中,Daniel Shiffman 讲解了 ml5 库中feature extractor(特征提取器)背后的**迁移学习(transfer learning)**原理:不必从零训练卷积神经网络,而是复用已在 ImageNet 上预训练好的 MobileNet 模型来抽取图像特征,再基于这些特征训练一个属于自己的小型分类器或回归器。本文将围绕这一主题,结合仓库中 3.1_feature_extractor_classification 与 3.2_feature_extractor_regression 的真实示例源码,从原理到 API、再到可运行的完整代码,带你掌握用摄像头实时训练自定义模型的完整链路。
一、什么是迁移学习:为什么不必从零训练 CNN
训练一个能识别图像的深度卷积神经网络(CNN)通常需要海量标注数据与大量算力。迁移学习的思路是:先借用一个已经在大型数据集上训练好的模型,只替换并重新训练它的"最后一小部分"。
- 预训练模型 MobileNet:一种专为移动端与嵌入式场景设计的高效卷积神经网络,出自《MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications》这篇论文(该文献正是本系列 2.1 节的参考资料之一)。MobileNet 用深度可分离卷积大幅压缩了参数量,因此在浏览器中也能实时运行。
- ImageNet 数据集:MobileNet 的卷积层此前已在 ImageNet 大规模图像分类任务上完成预训练,这些卷积层已经"学会"了如何从图像中提取通用的视觉特征(边缘、纹理、形状、物体部件等)。
- 迁移学习的做法:冻结 MobileNet 中负责提取特征的卷积部分,把它输出的**特征向量(feature vector / logits)**作为输入,接上一个全新的、结构简单的小型全连接层,只针对你自己的数据训练这一小部分。这就是"用别人的眼睛看世界,再教会它区分你的东西"。
ml5 的featureExtractor正是把上述流程封装成了几个简单的 JavaScript 方法,让你可以在 p5.js 草图中、甚至摄像头实时画面上完成"采集样本 → 训练 → 推理"的闭环。
二、ml5 的 featureExtractor 核心 API
仓库中所有 feature extractor 相关示例(3.1 分类、3.2 回归、4.1 保存/加载、5 系列 KNN)都遵循同一个创建模式:
mobilenet = ml5.featureExtractor('MobileNet', modelReady);ml5.featureExtractor(model, callback):创建基于 MobileNet 的特征提取器。modelReady在预训练模型加载完成后触发(源码中打印'Model is ready!!!')。mobilenet.classification(video, videoReady):创建分类器。传入摄像头画面video,返回可用于添加样本、训练与分类的对象。mobilenet.regression(video, videoReady):创建回归器,用于输出连续数值(如滑块位置、音量等)。xxx.addImage(label):把当前视频帧作为一条训练样本,绑定到指定的标签(分类)或数值(回归)。xxx.train(whileTraining):训练自定义分类/回归层。训练过程中whileTraining(loss)会周期性收到当前损失值;当loss == null时表示训练完成,此时应立即启动推理。xxx.classify(gotResults)/xxx.predict(gotResults):对当前帧进行推理,结果通过回调函数返回。features.infer(video):直接取出 MobileNet 对当前帧输出的特征向量,供 KNN 等外部算法使用(见本文第五节)。
三、源码级实战:用摄像头训练自定义图像分类器
以仓库 3.1_feature_extractor_classification/P5/sketch.js 为例,这个例子教会模型区分"开心(happy)"与"难过(sad)"两种表情。完整代码如下:
let mobilenet; let classifier; let video; let label = 'test'; let ukeButton; let whistleButton; let trainButton; function modelReady() { console.log('Model is ready!!!'); } function videoReady() { console.log('Video is ready!!!'); } function whileTraining(loss) { if (loss == null) { console.log('Training Complete'); classifier.classify(gotResults); } else { console.log(loss); } } function gotResults(error, result) { if (error) { console.error(error); } else { // updated to work with newer version of ml5 // label = result; label = result[0].label; classifier.classify(gotResults); } } function setup() { createCanvas(320, 270); video = createCapture(VIDEO); video.hide(); background(0); mobilenet = ml5.featureExtractor('MobileNet', modelReady); classifier = mobilenet.classification(video, videoReady); ukeButton = createButton('happy'); ukeButton.mousePressed(function() { classifier.addImage('happy'); }); whistleButton = createButton('sad'); whistleButton.mousePressed(function() { classifier.addImage('sad'); }); trainButton = createButton('train'); trainButton.mousePressed(function() { classifier.train(whileTraining); }); } function draw() { background(0); image(video, 0, 0, 320, 240); fill(255); textSize(16); text(label, 10, height - 10); }逐段拆解这个训练流程:
- 采集样本:点击
happy或sad按钮时,classifier.addImage('标签')会把当前摄像头画面"喂"进 MobileNet,抽取特征后连同标签存入训练集。注意标签是你自定义的字符串,有多少类别完全由你决定。 - 训练:点击
train后classifier.train(whileTraining)开始训练 MobileNet 之上的自定义分类层。whileTraining回调中的loss参数是关键信号:非 null 表示训练还在进行(打印当前损失值),null表示训练完成,此时调用classifier.classify(gotResults)启动持续推理。 - 推理:
gotResults中的result是分类结果数组,取result[0].label即为置信度最高的类别。代码注释中保留了旧版本label = result的写法,说明在较新版本(本仓库示例使用的 ml5 0.4.3)中结果结构已变为数组,需按result[0].label取值。推理结束后立即递归调用classifier.classify(gotResults),实现逐帧连续分类。
draw()中只是把摄像头画面画到画布上并在底部实时显示当前预测标签label——一个完整的"可教式"分类器就完成了。
四、源码级实战:回归——用图像控制滑块
迁移学习不仅能做分类,还能做回归(regression):让模型输出一个连续数值。仓库 3.2_feature_extractor_regression/P5/sketch.js 展示了如何训练 MobileNet 通过你的表情/动作来控制一个滑块,完整代码如下:
let mobilenet; let predictor; let video; let value = 0; let slider; let addButton; let trainButton; function modelReady() { console.log('Model is ready!!!'); } function videoReady() { console.log('Video is ready!!!'); } function whileTraining(loss) { if (loss == null) { console.log('Training Complete'); predictor.predict(gotResults); } else { console.log(loss); } } function gotResults(error, result) { if (error) { console.error(error); } else { // updated to work with newer version of ml5 // value = result; value = result.value; predictor.predict(gotResults); } } function setup() { createCanvas(320, 270); video = createCapture(VIDEO); video.hide(); background(0); mobilenet = ml5.featureExtractor('MobileNet', modelReady); predictor = mobilenet.regression(video, videoReady); slider = createSlider(0, 1, 0.5, 0.01); addButton = createButton('add example image'); addButton.mousePressed(function() { predictor.addImage(slider.value()); }); trainButton = createButton('train'); trainButton.mousePressed(function() { predictor.train(whileTraining); }); } function draw() { background(0); image(video, 0, 0, 320, 240); rectMode(CENTER); fill(255, 0, 200); rect(value * width, height / 2, 50, 50); fill(255); textSize(16); text(value, 10, height - 10); }与分类版的核心差异:
- 样本标签是数值而非字符串:
predictor.addImage(slider.value())将滑块当前值(0到1之间,步长0.01,默认0.5)绑定到当前帧。例如:你笑得很开心时把滑块拖到1并添加样本,面无表情时拖到0添加样本,模型就能学会"根据表情输出一个 0~1 的连续数值"。 - 推理用
predict而非classify:训练完成(loss == null)后调用predictor.predict(gotResults),结果通过result.value取得(旧版本直接返回数值本身,代码注释同样保留了迁移说明)。 - 输出驱动可视化:
draw()中粉色方块的水平位置value * width由模型输出驱动,画面下方的text(value, ...)实时显示数值——这就是典型的"表情控制滑块"交互。
五、特征向量与 KNN:另一种迁移学习用法
除了分类/回归封装,featureExtractor还暴露了底层特征抽取能力features.infer(video),返回 MobileNet 对当前帧的特征向量(logits)。仓库 5_knn_classification/P5/sketch.js(对应系列 5.1~5.3 节)利用它实现了一个"可教式"的 KNN 分类器,无需训练神经网络:
features = ml5.featureExtractor('MobileNet', modelReady); knn = ml5.KNNClassifier(); function keyPressed() { const logits = features.infer(video); if (key == 'l') { knn.addExample(logits, 'left'); } else if (key == 'r') { knn.addExample(logits, 'right'); } else if (key == 'u') { knn.addExample(logits, 'up'); } else if (key == 'd') { knn.addExample(logits, 'down'); } } function goClassify() { const logits = features.infer(video); knn.classify(logits, function(error, result) { if (error) { console.error(error); } else { label = result.label; goClassify(); } }); }这里的思路是:用 MobileNet 把每一帧压缩成特征向量,再用 K 近邻(KNN)算法直接比较特征向量的相似度。按l/r/u/d键分别为"左/右/上/下"添加特征样本,knn.classify(logits, ...)返回最近邻所属标签。因为 KNN 无需梯度训练,样本添加后即可推理,非常适合快速原型。该示例还附带了一段临时save()实现(源码 94~129 行),把knn.knnClassifier.getClassifierDataset()导出的数据集序列化为 JSON 下载,注释中的knn.load('model.json', ...)则用于加载已有模型。
六、训练成果的保存与复用
feature extractor 训练出的模型可以序列化保存,供下次直接加载,无需重新采集数据。仓库 4.1_save_load_model/P5/sketch.js 演示了这一能力:
saveButton = createButton('save'); saveButton.mousePressed(function() { classifier.save(); // 下载 model.json 等模型文件 }); // 加载(代码中注释展示,modelReady 内调用) // classifier.load('model.json', customModelReady); // function customModelReady() { // console.log('Custom Model is ready!!!'); // label = 'model ready'; // classifier.classify(gotResults); // }- 保存:
classifier.save()会把当前训练好的分类层(连同特征提取器配置)下载到本地。 - 加载:
classifier.load('model.json', customModelReady)在模型就绪回调中恢复分类器,随后即可直接classify,实现"一次训练、处处复用"。
七、运行环境与前提条件
仓库内所有 ml5 示例的页面结构一致,以 3.1 的 index.html 为例:
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.1/lib/p5.min.js"></script> <script src="https://unpkg.com/ml5@0.4.3/dist/ml5.min.js"></script> <script src="sketch.js"></script>运行本系列示例需要满足以下前提:
- 浏览器环境:ml5 运行于浏览器端(基于 TensorFlow.js),这些示例均为纯前端 p5.js 草图。
- 依赖版本:仓库示例固定使用
p5@1.4.1与ml5@0.4.3。注意 ml5 版本迭代较快,classify/predict的回调结果结构存在差异——源码注释明确标注了从"直接返回结果对象"到"返回结果数组、取result[0].label/result.value"的迁移,若使用其他版本请以对应版本的文档为准。 - 摄像头权限:
createCapture(VIDEO)会请求摄像头,需在浏览器中授权;video.hide()隐藏原始视频元素,画面由draw()中image(video, 0, 0, 320, 240)手动绘制。 - 本地服务器:虽然示例不含外部文件加载,但涉及模型文件(如
classifier.load('model.json'))时需要以本地 HTTP 服务器方式访问(如python -m http.server),避免浏览器因file://协议限制而拒绝加载。
八、系列学习路径与延伸阅读
本文对应系列中的第 2.1 节,以下是仓库中可直接查阅的相关章节与源码,形成完整的学习闭环:
- 1.1 图像分类(预训练直接使用):不训练,直接用 MobileNet 分类任意图片,见 _learning/ml5/1.1-image-classification.md 与 1.1 源码,是理解"预训练模型"能力边界的起点。
- 3.1 特征提取器分类:本文第三节的完整讲解版,见 _learning/ml5/3.1-feature-extractor-classification.md。
- 3.2 特征提取器回归:本文第四节的完整讲解版,见 _learning/ml5/3.2-feature-extractor-regression.md。
- 4.1 保存/加载模型:见 _learning/ml5/4.1-save-load-model.md 与 4.1 源码。
- 5.1~5.3 KNN 分类:特征向量 + KNN 的可教式分类器,见 _learning/ml5/5.1-knn-classification-1.md 与 KNN 源码。
- 6.1 训练自己的神经网络:更进一步,讲解如何在 ml5 中从零构建并训练神经网络,见 _learning/ml5/6.1-train-your-own.md。
掌握"预训练 MobileNet + 自定义分类/回归层"的迁移学习模式后,你完全可以在此基础上做出表情识别、手势控制小游戏、甚至"可教式"玩具(Teachable Machine 正是同类思路的成熟产品)。从 learning/ml5 目录下的示例出发,逐个运行、修改标签与数据,就能直观感受到迁移学习在浏览器端"少样本、快上手"的实战价值。
- 教程
- 文档
- 示例工程
【免费下载链接】website-archive
Archive of the Coding Train website (first version)
相关推荐
ml5.js FeatureExtractor 实战指南:基于 MobileNet 特征迁移学习实现图像分类与回归
ml5.js FeatureExtractor 实战指南:基于 MobileNet 特征迁移学习实现图像分类与回归 导读 本篇指南围绕 ml5.js 的 fea
人工智能机器学习深度学习计算机视觉NLPml5.js 图像分类实战:用 MobileNet 预训练模型在 p5.js 中识别图片内容
ml5.js 图像分类实战:用 MobileNet 预训练模型在 p5.js 中识别图片内容 本文围绕 Coding Train 的 ml5.js 初学者指南系
教程文档示例工程安全烧录系统镜像:Balena Etcher的技术实现与生产实践
安全烧录系统镜像:Balena Etcher的技术实现与生产实践 在嵌入式开发和系统部署场景中,误操作导致数据丢失是技术人员面临的常见风险。传统命令行工具如dd
桌面应用开发工具智能硬件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考