使用 Tensorflow.js 为前端开发人员提供机器学习
已发表: 2022-03-10机器学习通常感觉它属于数据科学家和 Python 开发人员的领域。 然而,在过去的几年中,已经创建了开源框架,以使其更易于在不同的编程语言中访问,包括 JavaScript。 在本文中,我们将使用 Tensorflow.js 通过几个示例项目来探索在浏览器中使用机器学习的不同可能性。
什么是机器学习?
在我们开始深入研究一些代码之前,让我们简单地谈谈什么是机器学习以及一些核心概念和术语。
定义
一个常见的定义是计算机无需明确编程即可从数据中学习的能力。
如果我们将其与传统编程进行比较,这意味着我们让计算机识别数据中的模式并生成预测,而无需我们确切地告诉它要寻找什么。
让我们以欺诈检测为例。 没有确定的标准可以知道是什么使交易具有欺诈性; 欺诈可以在任何国家、任何账户、针对任何客户、任何时间等进行。 手动跟踪所有这些几乎是不可能的。
但是,使用多年来收集的有关欺诈费用的先前数据,我们可以训练机器学习算法来理解这些数据中的模式,从而生成一个模型,该模型可以给出任何新交易并预测它是否为欺诈的可能性,而无需准确地告诉它要寻找什么。
核心概念
要理解以下代码示例,我们需要先介绍一些常用术语。
模型
当您使用数据集训练机器学习算法时,模型是此训练过程的输出。 它有点像一个将新数据作为输入并产生预测作为输出的函数。
标签和功能
标签和特征与您在训练过程中提供给算法的数据相关。
标签表示您将如何对数据集中的每个条目进行分类以及如何标记它。 例如,如果我们的数据集是一个描述不同动物的 CSV 文件,我们的标签可以是“猫”、“狗”或“蛇”之类的词(取决于每种动物代表什么)。
另一方面,特征是数据集中每个条目的特征。 对于我们的动物示例,它可能是“胡须、喵喵”、“顽皮、吠叫”、“爬行动物、猖獗”等。
使用这一点,机器学习算法将能够找到特征与其标签之间的某种相关性,并将用于未来的预测。
神经网络
神经网络是一组机器学习算法,它试图通过使用人工神经元层来模仿大脑的工作方式。
我们不需要在本文中深入了解它们的工作原理,但是如果您想了解更多信息,这里有一个非常好的视频:
现在我们已经定义了一些机器学习中常用的术语,让我们来谈谈使用 JavaScript 和 Tensorflow.js 框架可以做什么。
特征
目前提供三个功能:
- 使用预训练模型,
- 迁移学习,
- 定义、运行和使用您自己的模型。
让我们从最简单的开始。
1. 使用预训练模型
根据您尝试解决的问题,可能已经有一个模型已经使用特定数据集和用于特定目的进行了训练,您可以在代码中加以利用和导入。
例如,假设我们正在构建一个网站来预测一张图片是否是一张猫的图片。 一种流行的图像分类模型称为MobileNet ,可作为带有 Tensorflow.js 的预训练模型使用。
代码如下所示:
<html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Cat detection</title> <script src="https://cdn.jsdelivr.net/npm/@tensorflow/[email protected]"> </script> <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/[email protected]"> </script> </head> <body> <img alt="cat laying down" src="cat.jpeg"/> <script> const img = document.getElementById('image'); const predictImage = async () => { console.log("Model loading..."); const model = await mobilenet.load(); console.log("Model is loaded!") const predictions = await model.classify(img); console.log('Predictions: ', predictions); } predictImage(); </script> </body> </html>
我们首先在 HTML 的头部导入 Tensorflow.js 和 MobileNet 模型:
<script src="https://cdnjs.cloudflare.com/ajax/libs/tensorflow/1.0.1/tf.js"> </script> <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/[email protected]"> </script>
然后,在 body 内部,我们有一个用于预测的图像元素:
<img alt="cat laying down" src="cat.jpeg"/>
最后,在script
标签内,我们有 JavaScript 代码,它加载预训练的 MobileNet 模型并对在image
标签中找到的图像进行分类。 它返回一个由 3 个预测组成的数组,这些预测按概率分数排序(第一个元素是最佳预测)。
const predictImage = async () => { console.log("Model loading..."); const model = await mobilenet.load(); console.log("Model is loaded!") const predictions = await model.classify(img); console.log('Predictions: ', predictions); } predictImage();
就是这样! 这是您可以在浏览器中通过 Tensorflow.js 使用预训练模型的方式!
注意:如果你想看看 MobileNet 模型还能分类什么,你可以在 Github 上找到可用的不同类的列表。
需要了解的重要一点是,在浏览器中加载预训练模型可能需要一些时间(有时长达 10 秒),因此您可能需要预加载或调整界面,以免影响用户。
如果您更喜欢使用 Tensorflow.js 作为 NPM 模块,您可以通过以下方式导入模块:
import * as mobilenet from '@tensorflow-models/mobilenet';
随意在 CodeSandbox 上玩这个例子。
现在我们已经了解了如何使用预训练模型,让我们看看第二个可用的功能:迁移学习。
2. 迁移学习
迁移学习是将预训练模型与自定义训练数据相结合的能力。 这意味着您可以利用模型的功能并添加自己的样本,而无需从头开始创建所有内容。
例如,一种算法已经用数千张图像进行了训练以创建图像分类模型,而不是创建自己的图像分类模型,迁移学习允许您将新的自定义图像样本与预先训练的模型相结合以创建新的图像分类器。 这个特性使得拥有一个更加定制化的分类器变得非常快速和容易。
为了提供代码中的示例,让我们重新利用之前的示例并对其进行修改,以便我们可以对新图像进行分类。
注意:最终结果是下面的实验,您可以在这里尝试。
以下是此设置中最重要部分的一些代码示例,但如果您需要查看整个代码,可以在此 CodeSandbox 上找到它。
我们仍然需要从导入 Tensorflow.js 和 MobileNet 开始,但是这次我们还需要添加一个 KNN(k-nearest neighbor)分类器:
<!-- Load TensorFlow.js --> <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script> <!-- Load MobileNet --> <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/mobilenet"></script> <!-- Load KNN Classifier --> <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/knn-classifier"></script>
我们需要分类器的原因是(而不是仅使用 MobileNet 模块)我们正在添加以前从未见过的自定义样本,因此 KNN 分类器将允许我们将所有内容组合在一起并对组合的数据进行预测。
然后,我们可以用video
标签替换猫的图像,以使用来自摄像头的图像。
<video autoplay width="227" height="227"></video>
最后,我们需要在页面上添加一些按钮,我们将用作标签来记录一些视频样本并开始预测。
<section> <button class="button">Left</button> <button class="button">Right</button> <button class="test-predictions">Test</button> </section>
现在,让我们转到 JavaScript 文件,我们将从设置几个重要变量开始:
// Number of classes to classify const NUM_CLASSES = 2; // Labels for our classes const classes = ["Left", "Right"]; // Webcam Image size. Must be 227. const IMAGE_SIZE = 227; // K value for KNN const TOPK = 10; const video = document.getElementById("webcam");
在这个特定的示例中,我们希望能够在我们的头部向左或向右倾斜之间对网络摄像头输入进行分类,因此我们需要两个标记为left
和right
的类。
设置为 227 的图像大小是视频元素的大小(以像素为单位)。 根据 Tensorflow.js 示例,该值需要设置为 227 以匹配用于训练 MobileNet 模型的数据格式。 为了能够对我们的新数据进行分类,后者需要适应相同的格式。
如果你真的需要它更大,这是可能的,但你必须在将数据输入 KNN 分类器之前转换和调整数据大小。
然后,我们将 K 的值设置为 10。KNN 算法中的 K 值很重要,因为它代表了我们在确定新输入的类别时考虑的实例数。
在这种情况下,值 10 意味着,在预测一些新数据的标签时,我们将查看训练数据中的 10 个最近邻,以确定如何对新输入进行分类。
最后,我们得到了video
元素。 对于逻辑,让我们从加载模型和分类器开始:
async load() { const knn = knnClassifier.create(); const mobilenetModule = await mobilenet.load(); console.log("model loaded"); }
然后,让我们访问视频源:
navigator.mediaDevices .getUserMedia({ video: true, audio: false }) .then(stream => { video.srcObject = stream; video.width = IMAGE_SIZE; video.height = IMAGE_SIZE; });
接下来,让我们设置一些按钮事件来记录我们的示例数据:
setupButtonEvents() { for (let i = 0; i < NUM_CLASSES; i++) { let button = document.getElementsByClassName("button")[i]; button.onmousedown = () => { this.training = i; this.recordSamples = true; }; button.onmouseup = () => (this.training = -1); } }
让我们编写我们的函数,它将获取网络摄像头图像样本,重新格式化它们并将它们与 MobileNet 模块结合起来:
// Get image data from video element const image = tf.browser.fromPixels(video); let logits; // 'conv_preds' is the logits activation of MobileNet. const infer = () => this.mobilenetModule.infer(image, "conv_preds"); // Train class if one of the buttons is held down if (this.training != -1) { logits = infer(); // Add current image to classifier this.knn.addExample(logits, this.training); }
最后,一旦我们收集了一些网络摄像头图像,我们就可以使用以下代码测试我们的预测:
logits = infer(); const res = await this.knn.predictClass(logits, TOPK); const prediction = classes[res.classIndex];
最后,您可以处理我们不再需要的网络摄像头数据:
// Dispose image when done image.dispose(); if (logits != null) { logits.dispose(); }
再一次,如果您想查看完整的代码,您可以在前面提到的 CodeSandbox 中找到它。
3. 在浏览器中训练模型
最后一个功能是完全在浏览器中定义、训练和运行模型。 为了说明这一点,我们将构建识别鸢尾花的经典示例。
为此,我们将创建一个神经网络,基于开源数据集将鸢尾花分为三类:Setosa、Virginica 和 Versicolor。
在我们开始之前,这里有一个现场演示的链接,如果你想使用完整的代码,这里是 CodeSandbox。
每个机器学习项目的核心都是数据集。 我们需要采取的第一步是将这个数据集拆分为训练集和测试集。
这样做的原因是我们将使用我们的训练集来训练我们的算法和我们的测试集来检查我们的预测的准确性,以验证我们的模型是否可以使用或需要调整。
注意:为方便起见,我已经将训练集和测试集拆分为两个 JSON 文件,您可以在 CodeSanbox 中找到它们。
训练集包含 130 个项目,测试集 14 个。如果您查看这些数据的样子,您会看到如下内容:
{ "sepal_length": 5.1, "sepal_width": 3.5, "petal_length": 1.4, "petal_width": 0.2, "species": "setosa" }
我们可以看到萼片和花瓣的长度和宽度的四个不同特征,以及物种的标签。
为了能够将它与 Tensorflow.js 一起使用,我们需要将这些数据塑造成框架能够理解的格式,在这种情况下,对于训练数据,它将是[130, 4]
的 130 个样本,每个样本有四个特征虹膜。
import * as trainingSet from "training.json"; import * as testSet from "testing.json"; const trainingData = tf.tensor2d( trainingSet.map(item => [ item.sepal_length, item.sepal_width, item.petal_length, item.petal_width ]), [130, 4] ); const testData = tf.tensor2d( testSet.map(item => [ item.sepal_length, item.sepal_width, item.petal_length, item.petal_width ]), [14, 4] );
接下来,我们还需要对输出数据进行整形:
const output = tf.tensor2d(trainingSet.map(item => [ item.species === 'setosa' ? 1 : 0, item.species === 'virginica' ? 1 : 0, item.species === 'versicolor' ? 1 : 0 ]), [130,3])
然后,一旦我们的数据准备就绪,我们就可以继续创建模型:
const model = tf.sequential(); model.add(tf.layers.dense( { inputShape: 4, activation: 'sigmoid', units: 10 } )); model.add(tf.layers.dense( { inputShape: 10, units: 3, activation: 'softmax' } ));
在上面的代码示例中,我们首先实例化一个顺序模型,添加一个输入和输出层。
您可以看到内部使用的参数( inputShape
、 activation
和units
)超出了本文的范围,因为它们可能会根据您创建的模型、使用的数据类型等而有所不同。
一旦我们的模型准备就绪,我们就可以使用我们的数据对其进行训练:
async function train_data(){ for(let i=0;i<15;i++){ const res = await model.fit(trainingData, outputData,{epochs: 40}); } } async function main() { await train_data(); model.predict(testSet).print(); }
如果这运作良好,您可以开始用自定义用户输入替换测试数据。
一旦我们调用我们的 main 函数,预测的输出将看起来像以下三个选项之一:
[1,0,0] // Setosa [0,1,0] // Virginica [0,0,1] // Versicolor
预测返回一个由三个数字组成的数组,表示数据属于三个类别之一的概率。 最接近 1 的数字是最高预测值。
例如,如果分类的输出为[0.0002, 0.9494, 0.0503]
,则数组的第二个元素最高,因此模型预测新的输入很可能是 Virginica。
这就是 Tensorflow.js 中的简单神经网络!
我们只讨论了 Irises 的一个小数据集,但如果您想继续使用更大的数据集或处理图像,步骤将是相同的:
- 收集数据;
- 在训练集和测试集之间拆分;
- 重新格式化数据以便 Tensorflow.js 可以理解它;
- 选择你的算法;
- 拟合数据;
- 预测。
如果您想保存创建的模型以便能够在另一个应用程序中加载它并预测新数据,您可以使用以下行来执行此操作:
await model.save('file:///path/to/my-model'); // in Node.js
注意:有关如何保存模型的更多选项,请查看此资源。
限制
而已! 我们刚刚介绍了目前使用 Tensorflow.js 可用的三个主要功能!
在我们结束之前,我认为有必要简要提一下在前端使用机器学习的一些限制。
1. 性能
从外部源导入预训练模型可能会对您的应用程序产生性能影响。 例如,某些对象检测模型超过 10MB,这会大大降低您的网站速度。 确保考虑您的用户体验并优化资产的加载以提高您的感知性能。
2. 输入数据的质量
如果您从头开始构建模型,您将不得不收集自己的数据或找到一些开源数据集。
在进行任何类型的数据处理或尝试不同的算法之前,请务必检查输入数据的质量。 例如,如果您尝试构建情绪分析模型来识别文本中的情绪,请确保用于训练模型的数据准确且多样化。 如果使用的数据质量低,那么您的训练输出将毫无用处。
3. 责任
使用开源预训练模型可以非常快速且轻松。 但是,这也意味着您并不总是知道它是如何生成的、数据集是由什么组成的,甚至不知道使用了哪种算法。 有些模型被称为“黑匣子”,这意味着你并不真正知道它们是如何预测某个输出的。
根据您要构建的内容,这可能是一个问题。 例如,如果您使用机器学习模型来帮助根据扫描图像检测某人患有癌症的概率,以防假阴性(模型预测一个人实际上没有患癌症),有可能是一些真正的法律责任,你必须能够解释为什么模型做出了某种预测。
概括
总之,使用 JavaScript 和 Tensorflow.js 等框架是入门和了解更多机器学习的好方法。 即使一个生产就绪的应用程序可能应该使用 Python 之类的语言构建,JavaScript 让开发人员可以真正使用不同的功能并更好地理解基本概念,然后最终继续前进并投入时间学习另一种语言语言。
在本教程中,我们只介绍了使用 Tensorflow.js 的可能性,但是,其他库和工具的生态系统正在增长。 还提供了更多指定的框架,允许您使用机器学习探索其他领域,例如使用 Magenta.js 进行音乐,或使用guess.js 预测网站上的用户导航!
随着工具的性能越来越高,在 JavaScript 中构建支持机器学习的应用程序的可能性可能会越来越令人兴奋,现在是了解更多关于它的好时机,因为社区正在努力使其易于访问。
更多资源
如果您有兴趣了解更多信息,这里有一些资源:
其他框架和工具
- ml5.js
- 毫升.js
- 大脑.js
- Keras.js
- 姿势网
- 张量流游乐场
示例、模型和数据集
- TensorFlow.js 模型
- TensorFlow.js 示例
- 数据集
灵感
- 可示教机器
- 人工智能实验
- AIJS.rocks
- 可创造性
谢谢阅读!