通过 BrainJS 在 UI/UX 中使用机器学习

2025-06-08

通过 BrainJS 在 UI/UX 中使用机器学习

有时候,玩代码看看能做什么也挺有意思的。所以几个月前,我开始了一个小项目,想找到一种将机器学习和 UI/UX 结合起来的方法。我的想法很简单:根据用户在浏览页面时更关注的颜色来更新他们的颜色偏好。这有用吗?我不确定,但很有趣,我已经在三个不同的会议上发表过演讲了!

这篇文章是关于我如何完成这个项目以及我在过程中学到的一些知识的指南。如果你感兴趣的话,这里有源代码的链接:https://github.com/flippedcoder/smart-page

神经网络的背景

如果你对机器学习一无所知,那么你应该对神经网络有所了解。神经网络是我们试图复制人类思维的一种粗略尝试。它们建立在算法之上,算法可以根据我们提供的信息识别模式。这些信息可能是房价、股票市场价值以及用户数据等。

在 Web 应用程序中,你会经常看到分类、预测和聚类等一些应用。预测的一个例子是使用股票市场数据,你需要选择一个未来值的范围。分类的一个例子是判断用户举报的消息是否为欺诈消息。你需要获取一些数据并训练一个算法,让它根据你输入的值判断消息是否合法。最后,聚类的一个例子是检查恶意评论。正常的评论会落在一定范围的情绪和陈述中,但恶意评论通常是异常值。

现在你已经了解了神经网络的概念,让我们来谈谈它是如何构建的。神经网络由节点组成。节点就像一个函数,是计算值的地方。节点是模仿大脑中的神经元设计的,因此它们接收一组输入并给出一个输出。这通常是你决定使用神经网络时所期望的值。

节点层.png

这就是你开始使用 BrainJS 所需了解的全部内容。它让神经网络的训练变得异常简单。接下来我会逐步解释一些细节,并尝试逐一讲解。

BrainJS 的背景

现在让我们来看看 JavaScript 库 BrainJS https://github.com/BrainJS/brain.js#node。这是除了 TensorFlowJS 之外,你可以使用的机器学习库之一。如果你对神经网络和 Node 有所了解,它很容易上手,而且它附带了很多很棒的教程和文档。我决定使用的方法是常规的神经网络训练模型。使用循环神经网络也是一个选择,但对于这个小项目来说,一个简单的神经网络就足够了。

BrainJS 让模型训练变得简单,因为你可以更专注于数据集和想要使用的输入,而无需费力学习新的语法。TensorFlowJS 很酷,我也用它做过同样的项目,但我更喜欢 BrainJS。学习完一些教程后,你就可以开始使用了。最重要的是,要真正了解机器学习的用途。

决定使用哪些功能

对于这个项目,我很好奇能否训练一个模型来识别用户喜欢的页面颜色,并将页面上的元素更改为不同的色调或与该特定颜色互补。有趣的是,每次用户刷新页面时,自定义颜色都会重置。这可能是一件好事,具体取决于您的应用程序的用途。对用户界面进行这样的更改可能有助于达成交易,也可能鼓励用户正确填写表单。

我决定做这个项目,源于我和一位 UI 设计师之间长期的反复沟通。我只是想让用户能够告诉我们他们想要看到什么,而不是让我们去猜测哪些颜色代表着“正确的信息”。我主要收集用户的数据是他们鼠标悬停时间最长的元素的颜色。我不知道这种方法是否正确,我只是想知道使用这个指标会产生什么效果。

根据用户鼠标在页面上的坐标,我能够获取他们当前所在元素的 RGB 值。我猜测,我们会潜意识地被自己喜欢的颜色所吸引,因此这种细微的改变可能会影响用户按照你的意愿浏览你的应用。通过观察用户在页面上停留时间最长的位置,你可以推测他们在某种程度上受到了这些颜色的吸引。

然后,您可以利用这些信息调整元素之间的对比度或网站的整体色调,以匹配特定用户。您甚至可以在浏览器中进行操作,这样就无需处理服务器端代码了。我不确定具体操作方法,但我在文档中看到过可以这样做!现在,这些特性(用户似乎更喜欢的 RGB 值)已经定义好了,是时候开始编写代码并训练神经网络模型了。

训练模型以了解用户行为

简单提醒一下,你不会在这里找到这个项目的全部代码,但你可以在我的 GitHub 仓库中查看:https://github.com/flippedcoder/machine-learning-ui-ux-demos在我们开始写代码之前,你需要了解一些事情。这个应用是我使用 create-react-app 制作的,我没有任何真实数据,所以我只是编造了一些我们使用的数字。这是一个我个人的项目,我觉得分享起来很酷,所以代码……嗯。好了!我们来看看我们需要的 server.js 文件。

这就是我们创建用于神经网络的训练数据的方式。

const trainingInputData = [
    { green: 0.2, blue: 0.4 },
    { green: 0.4, blue: 0.6 },
    { red: 0.2, green: 0.8, blue: 0.8 },
    { green: 1, blue: 1 },
    { red: 0.8, green: 1, blue: 1 },
    { red: 1, green: 1, blue: 1 },
    { red: 1, green: 0.8, blue: 0.8 },
    { red: 1, green: 0.6, blue: 0.6 },
    { red: 1, green: 0.4, blue: 0.4 },
    { red: 1, green: 0.31, blue: 0.31 },
    { red: 0.8 },
    { red: 0.6, green: 0.2, blue: 0.2 }
];

const trainingOutputData = [
    { dark: 0.8 },
    { neutral: 0.8 },
    { light: 0.7 },
    { light: 0.8 },
    { light: 0.9 },
    { light: 1 },
    { light: 0.8 },
    { neutral: 0.7, light: 0.5 },
    { dark: 0.5, neutral: 0.5 },
    { dark: 0.6, neutral: 0.3 },
    { dark: 0.85 },
    { dark: 0.9 }
];

const trainingData = [];

for (let i = 0; i < trainingInputData.length; i++) {
    trainingData.push({
        input: trainingInputData[i],
        output: trainingOutputData[i]
    });
}
Enter fullscreen mode Exit fullscreen mode

trainingInputData 是我们可以预期用户输入的值样本。因此,我们会查看用户最常悬停的元素的背景颜色,并获取相应的 RGB 值。trainingOutputData 是我们试图预测的内容。每个人对亮度的偏好各不相同,我们称特定的 RGB 值对应特定的暗、亮和中性亮度等级。根据输入数据,我们可以为用户精细地调整页面亮度。

现在我们可以创建一个新模型并使用这些数据来训练它,同时获得一些关于模型训练效果的统计数据。

const net = new brain.NeuralNetwork({ hiddenLayers: [3] });

const stats = net.train(trainingData);

console.log(stats);
Enter fullscreen mode Exit fullscreen mode

首先,我们创建了一个包含三个隐藏层的新神经网络,并将其命名为“net”。添加一些隐藏层可以带来更准确的结果,因为您利用了深度学习的优势。深度学习意味着您使用多个隐藏层来分析输入。接下来,我们通过传递训练数据来训练神经网络,并将其赋值给一个名为“stats”的变量。最后,我们将在控制台中打印所有统计数据,例如训练误差和迭代次数。

此时,模型已准备好运行,可以使用您提供的任何输入。这意味着是时候设置一些代码来获取用户的输入并将其输入到模型中了。我们将直接从用户请求中获取输入数据,并为该输入创建一个新的训练数据数组。然后,我们将使用新的训练输入数据运行模型,以获取新的训练输出数据的值。

接下来,我们将使用新的训练数据更新模型,并根据用户提供的最后一个输入运行该模型。我之所以选择这样做,是因为我向服务器发送数据的方式,您将在下一节中更详细地了解。基本上,我会将一个 RGB 值数组以与输入数据相同的形状发送回服务器。在对最后一个输入运行模型后,我们会将预测值传回给用户,并对 UI 进行调整。

app.post('/getUserMouseCoordinates', (req, res) => {
    let newTrainingInputData = req.body.userData;
    let newTrainingOutputData = [];
    let newTrainingData = [];

    newTrainingInputData.forEach(input => {
        newTrainingOutputData.push(
            net.run(
                { 
                    red: input.input.red, 
                    green: input.input.green, 
                    blue: input.input.blue 
                }
            )
        );
    });

    for (let i = 0; i < newTrainingInputData.length; i++) {
        newTrainingData.push({
            input: newTrainingInputData[i],
            output: newTrainingOutputData[i]
        });
    }

    net.train(newTrainingData);

    let newPredictedValue = net.run(newTrainingInputData[newTrainingInputData.length - 1]);

    res.send(JSON.stringify({ predictedValue: newPredictedValue }));
});
Enter fullscreen mode Exit fullscreen mode

使用模型来更新 DOM

只要不拖慢速度,用户其实并不在意后台运行的这些操作。所以,让我来演示一下我是如何处理将他们的输入发送到服务器的。我会等到输入数组中积累了足够多的用户数据(100 个点)后再进行处理。在达到 100 个数据点之前,这些数据会被添加到数组中,并且我们不会向服务器发送任何数据。我选择 100 个数据点并没有什么技术原因,只是我自己选的这个数字而已。🤷‍♀️

对于神经网络,输入和输出值的范围是 0 - 1。这意味着,如果处理的值大于这个范围,则需要对其进行缩放以适应该范围。这就是我将每个 RGB 值除以 1000 的原因。这些值的范围是 0 - 255,所以我想确保它们都适合神经网络的 0 - 1 范围。有很多更好的方法来缩放值,例如最小-最大归一化或 z 分数归一化,在实际的机器学习项目中,你绝对应该使用类似的方法。

除了缩放数组中的值之外,当输入数组中有 100 个数据点时,我们最终会将其传回服务器。当输入数组传回并处理后,我们将收到一个响应,其中包含用户悬停时颜色的亮色、暗色和中性色的预测值。然后,我们将更新状态变量并调用 changeAlpha 函数来更新用户的 UI。

getUserMouseCoordinates = async (event) => {
        if (this.input.length >= 100) {
            let userData = { userData: this.input };
            let prediction = await fetch('/getUserMouseCoordinates', {
                method: 'POST',
                headers: {
                    'Accept': 'application/json',
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(userData)
                });

            const predict = await prediction.json();
            this.setState({
                light: predict.predictedValue.light,
                dark: predict.predictedValue.dark,
                neutral: predict.predictedValue.neutral
            });
            this.changeAlpha();
        }
        else {
            this.input.push(
                { 'input': { 
                    red: event.target.style.backgroundColor.split(',')[0].substr(4) / 1000, 
                    green: event.target.style.backgroundColor.split(',')[1].trim() / 1000, 
                    blue: event.target.style.backgroundColor.split(',')[2].trim().split(')')[0] / 1000 
                    }
                }
            );
        }
    }
Enter fullscreen mode Exit fullscreen mode

这是我的一个个人项目的例子,我做起来非常开心。我甚至在不同的科技会议上展示过这个项目!虽然代码不够优美,也未完全遵循最佳的机器学习实践,但这没关系。当你试图验证某个东西是否可行时,项目代码不必完美无缺。总有一天我会花时间进行重构和实验,但现在,我还有其他项目想要完成!

你觉得这个怎么样?发布并讨论这样的个人项目有用吗?你希望更详细地了解代码,或者更深入地了解如何构思这样的随机项目吗?我之前没有写过类似的项目,所以我很欢迎大家的反馈!


嘿!你应该在 Twitter 上关注我,理由如下:https://twitter.com/FlippedCoding

鏂囩珷鏉ユ簮锛�https://dev.to/flippedcoding/using-machine-learning-in-ui-ux-with-brainjs-48f3
PREV
使用 Chrome 开发者工具
NEXT
狗联网:如何打造一个价值 50 美元的物联网狗项圈来定位你的宠物