发布于 2026-01-06 3 阅读
0

一款可以用JS构建的机器学习应用,无需任何数学运算🙈

一款可以用JS构建的机器学习应用,无需任何数学运算🙈

演示⏯️代码👨‍💻视频演示🎦

机器学习是构建人工智能应用的一种超酷方式——但说实话,它确实需要大量的数学知识。幸运的是,有了ml5.js,我们无需花费数月时间学习机器学习,只需几个小时就能将其应用到我们的应用中,并开发出一些很棒的功能😄

这是一份构建特征提取器的指南——它可以从你的网络摄像头获取视频流,并在用少量数据进行训练后预测它看到的内容——就像上面的视频中一样!

🍴 前端分支

我不想在前端部分浪费您太多时间——我认为直接跳到 JavaScript 和逻辑部分会更好。

你应该 fork 这个 repl - https://repl.it/@jajoosam/feature-extractor-start - 这样你就可以直接使用现有的 HTML 代码了!它本质上只是几个div用来显示视频和其他文本的 s 标签,以及一些用来控制我们应用的按钮!

我已经对所有内容都添加了注释(除了 CSS 代码😛),所以你应该能在几分钟内浏览前端并了解应用程序的基本布局。完成后,请前往……script.js

✏️ 声明变量

我们需要声明很多变量,所以我们首先要做的是创建它们。

我们的应用程序将包含许多功能,所有功能都必须能够访问变量——这就是为什么我们要在代码开头就声明变量的原因。

var featureExtractor, classifier, video, loss, redCount, blueCount;

redCount = blueCount = 0;

我会简要介绍一下这些功能的用途,但随着我们继续开发应用程序,你会更好地理解它们。

featureExtractor这些classifier变量我们将用于存储和初始化机器学习模型。

video是我们存储网络摄像头流的地方,同时loss让我们知道我们的特征提取器训练到了什么程度。

最后,blueCount和分别是每个类别中图像数量的计数器——我们在下一行redCount中将它们的值都初始化为。0

🛠️setup()

setup()这是一个会在我们的代码准备就绪后立即触发的函数。由于使用了p5.js,我们的代码非常易读易懂。

function setup() {
    // Tells p5 to not automatically create a canvas element.
  noCanvas();

    // Starts capturing a video feed from the webcam
  video = createCapture(VIDEO);

    // Puts the video stream into the div in our html, with ID `video`
  video.parent('video'); 

    // Initializes a feature extractor, yet to be trained - from ml5.js
  featureExtractor = ml5.featureExtractor('MobileNet');
  classifier = featureExtractor.classification(video);

    // What we're doing next - setting up buttons!
  setupButtons();
}

这段代码要放在script.js声明变量之后——它的作用是获取视频流并将其显示在页面上,显示在divID 为 `<video>` 的 ` <div>` 标签内video。我们还使用 ml5.js 库创建了一些函数,并将捕获的视频作为参数传递——你很快就会看到这些函数的作用,但还有一些设置工作要做!

如您所见,在代码末尾setup(),我们调用了setupButtons()接下来要编写的函数。这里,我们为 HTML 中的按钮添加了事件监听器,以便在按钮被点击时运行相应的函数。

以下是我们为该函数编写的所有代码setupButtons()👇

// A function to create the buttons
function setupButtons() {

  buttonA = select('#red');
    buttonB = select('#blue');


    
      buttonA.mousePressed(function() {
         redCount++;
        classifier.addImage('red');
        select('#redCount').html(redCount);
      });


     
      buttonB.mousePressed(function() {
         blueCount++;
        classifier.addImage('blue');
        select('#blueCount').html(blueCount);
      });
    
  train = select('#train');
  train.mousePressed(function() {
    classifier.train(function(lossValue) {

    // This is where we're actually training our model

      if (lossValue) {
        loss = lossValue;
        select('#info').html('Loss: ' + loss);
      } else {
        select('#info').html('Done Training! Final Loss: ' + loss);
                select('#train').style("display", "none");
                select('#predict').style("display", "inline");
      }
    });
  });

  // Predict Button
  buttonPredict = select('#predict');
  buttonPredict.mousePressed(classify);
}

这是我们应用程序中最大的艺术作品,我将把它分成几个部分,以便更容易解释!

🧙‍​​♂️但是这些按钮是做什么用的?!

不要恐慌。

我们先从这个模块开始:

buttonA = select('#red');
buttonB = select('#blue');    

buttonA.mousePressed(function() {
    redCount++;
    classifier.addImage('red');
    select('#redCount').html(redCount);
});

buttonB.mousePressed(function() {
    blueCount++;
    classifier.addImage('blue');
    select('#blueCount').html(blueCount);
});

buttonAbuttonB这不正是我们应用程序中的两个不同的按钮吗!

我们.mousePressed()来定义按下这些按钮时会发生什么——具体来说:

  • ++使用运算符将​​计数加 1。
  • 从网络摄像头视频中捕获当前帧并将其添加到分类器中。classifier.addImage()
  • 通过更改按钮文本来更新我们应用程序中的计数,.html()

接下来,我们来看这整个模块——我们在这里训练分类器本身:

train = select('#train');
  train.mousePressed(function() {
    classifier.train(function(lossValue) {

            // This is where we're actually training our model

      if (lossValue) {
        loss = lossValue;
        select('#info').html('Loss: ' + loss);
      } else {
        select('#info').html('Done Training! Final Loss: ' + loss);
                select('#train').style("display", "none");
                select('#predict').style("display", "inline");
      }
    });
  });

Train 🚋我们的应用程序上的按钮被按下时,我们会调用classifier.train()——并且每次迭代都会调用我们在那里提供的函数——这就是为什么我们看到损失值不断变化的原因。

损失值为 时0,我们将隐藏该Train 🚋按钮,并显示之前隐藏的Predict 🔮按钮!

函数的最后两行setupButtons()是关于预测按钮的:

buttonPredict = select('#predict');
buttonPredict.mousePressed(classify);

看来我们正在调用这个classify函数——这就是我们接下来要构建的内容!

继续加油,我们快完成了💯

🔮 预测及结果展示

我们的classifier()职能非常简单明了——我们只做这件事:

function classify() {
  classifier.classify(gotResults);
}

我们只是告诉 ml5classifier将当前帧分类为 🔴 或 🔵 类之一,并将结果发送到gotResults函数——这就引出了我们应用程序的最后一部分!

只要分类器没有向我们发送错误,我们就将整个页面的背景颜色更改为 0redblue1,然后classify()再次调用,这样我们的代码就会一直运行下去,预测结果也会不断出现!

function gotResults(err, result) {
  if (err) {
    console.log(err);
  }
    select("body").style("background", result);
  classify();
}

这就是构建示例特征提取器所需的所有代码。我强烈建议您通读一遍最终代码,以便了解各个部分是如何协同工作的——我知道有很多函数需要记住,但我已经为所有函数添加了注释,方便您理解😅

现在就去试试吧!以下是我为了验证特征提取器是否有效而做的一些操作(结果成功了!)👇

  • 保持我的头在左边🔴,右边🔵
  • 在🔴中画一个✌️,在🔵中画一个✋
  • 在🔴和🔵中佩戴不同颜色的👕

告诉我它还有哪些其他很棒的用途👀

✨ 好吧,但我该用这些什么呢?

你可以做的事情太多了!特征提取器让你能够为你的应用程序添加一种全新的、神秘的控制和输入方式!

这里有一些想法💡

  • 一款可在浏览器中直接畅玩的手势控制游戏🎮
  • 情绪追踪器——整天开着标签页,用不同的面部表情训练它🤔
  • 自选热狗应用🌭
  • 开发一款只能通过手势发送消息的应用程序💬

👨‍🏫 还有什么?

ml5.js 功能强大,令人惊叹!你可以像我一样将图像分类集成到自己的应用中,找到与文本内容相似的词语,获取人体精确的骨骼结构,甚至可以转换图像的风格——看看我的照片,用酷炫的绘画风格重新组合后的效果🖌️

mepaint

你应该看看ml5.js 网站上的众多示例,或者看看Dan Shiffman 关于 ml5.js 的视频,他讲解了很多 ml5.js 的功能,而且讲解得非常生动有趣。我强烈推荐你在Coding Train 的YouTube 频道上观看他的视频!

如果有任何问题,欢迎在推特上联系我,或者在评论区留言 :)

文章来源:https://dev.to/jajoosam/an-ml-app-you-can-build-with-js-and-without-the-math-3dbk