网页也能跑大模型?

写在最前

本故事主要介绍在网页上部署模型的来龙去脉,你想问的问题,可能都可以在这里找到答案

在这个 AI 内容生成泛滥的时代,依然有一批人"傻傻"坚持原创,如果您能读到最后,还请点赞或收藏或关注支持下我呗,感谢 ( ̄︶ ̄)↗

能在网页上跑模型吗?

丹尼尔:嘿,蛋兄,你这是要去哪儿遛弯呢?

蛋先生:刚吃完饭,准备散下步消消食

丹尼尔:一起呗。蛋兄,我最近对 AI 有点着迷,突然冒出个念头,你说咱们能不能在网页上跑机器学习模型呢?

蛋先生:这个嘛,确实可以

为什么可以在网页上跑模型?

丹尼尔:这我就纳闷了,一个专门看网页的浏览器,怎么还能"兼职"跑模型呢?蛋兄,快给我讲讲呗

蛋先生:你想啊,一颗种子能不能发芽,得看它有没有适合生存的环境。模型也一样,得有个能跑的"土壤"------runtime,还得有足够的"阳光"和"水"------也就是算力和存储

丹尼尔:哦,我好像有点懂了。但我还是不明白,浏览器是怎么做到这一点的

蛋先生:自从浏览器有了 WebAssembly 之后,它的"胃口"可就大了去了!现在,很多用 C、C++、Rust 等编程语言写的应用,都能编译成 WASM 格式,在浏览器里跑。这样一来,浏览器就能处理更加复杂的计算任务了

丹尼尔:原来如此!也就是说,原来用 C++ 等写的模型 runtime,现在可以直接放进浏览器里,成了模型的"土壤"了!

蛋先生:对头!而且,浏览器的 WebGL、WebGPU 这些技术,还能让你的应用用上 GPU 资源,速度更上一层楼!否则,能跑,但很慢,也没啥意义

丹尼尔:哈哈,我总结一下啊,WebAssembly 让模型有了土壤,WebGL、WebGPU 让算力提升成为可能!

蛋先生:不错不错,总结得挺到位!

为什么要跑在浏览器呢?

蛋先生:那我问你,你为什么想把模型跑在浏览器上呢?

丹尼尔:额~,这~,就觉得挺酷的嘛!不过说实话,我还真没认真想过这个问题。蛋兄,你给说道说道?

蛋先生:来,咱们从请求链路说起。模型部署在浏览器上,是不是就不用请求服务器了?

丹尼尔:那是肯定的

蛋先生:对于客户端,请求没有离开用户设备,这样是不是就可以更好地保护用户隐私了?

丹尼尔:是哦

蛋先生:计算是在浏览器本地进行的,距离用户更近,也没有网络请求的损耗,响应速度通常更快,这样是不是就可以提升用户体验了?

丹尼尔:是哦

蛋先生:还有,模型已经部署在浏览器了,只要应用本身支持离线访问,那是不是就可以离线使用了?

丹尼尔:是哦

蛋先生:对于服务端,因为把计算压力分摊出去了,是不是就可以减轻服务器的计算压力,降低运营成本呢?

丹尼尔:是哦

蛋先生:剩下的你自己琢磨琢磨吧

怎么跑在浏览器呢?

丹尼尔:好嘞,那具体要怎么实现呢?

蛋先生:主流的机器学习框架除了训练模型外,还能部署和推理模型。比如大名鼎鼎的 Tensorflow 就有 tensorflow.js,它可以将模型部署在浏览器端。不过今天我要给你说的是 onnxruntime-web

丹尼尔:onnxruntime-web?这名字听着有点新鲜啊!

蛋先生:onnxruntime-web,可以把这个拆成 onnx,onnxruntime 和 onnxruntime-web 来说

丹尼尔:您继续

蛋先生:onnx 就是个模型格式,就像你存音乐用的 mp3 格式一样,但它存的是机器学习模型;onnxruntime 呢,就是运行这些模型的"播放器";而 onnxruntime-web,则是让这个"播放器"能在网页上跑起来的神奇工具

丹尼尔:哦,那模型都有哪些格式呢?用这个 onnx 有什么优势呢?

蛋先生:正所谓合久必分,分久必合

丹尼尔:这是要讲三国的节奏吗

蛋先生:各个机器学习框架都有自己的模型格式,在没有 onnx 之前,你得用 tf 来部署 tensorflow 的模型,用 pytorch 来部署 pytorch 的模型。可用户只想部署个模型而已,能不能把问题简单化呢?

丹尼尔:确实

蛋先生:于是就有了 onnx 这个开放标准。各家的模型格式都能转换成这种标准格式,然后你就可以用一个 onnxruntime 来部署和推理模型了!

丹尼尔:那这个 onnxruntime 是用什么实现的呢?

蛋先生:它是用 C++ 实现的,在浏览器运行时会被编译成 WASM 格式。然后 onnxruntime-web 提供了 JS API 来与 WASM 进行交互

丹尼尔:原来如此!那快给我看个代码示例吧,我都迫不及待了

蛋先生:以下是一个数字图像识别的简单例子,不过接口有点底层哦,你得懂点 tensor 之类的。希望以后有第三方库能封装个高级的接口,比如手写数字识别输入是图片,输出是数字;生成式 AI 输入是 prompt,输出是回答之类的。当然你也可以自己尝试尝试

html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    ...
    <script src="https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.min.js"></script>
    ...
  </head>
  <body>
    ...
    <canvas id="canvas" width="28" height="28" style="display: none"></canvas>

    <script>
      let imageData;

      ...

      async function runModel() {
        // 提取图像数据并规范化为模型的输入格式
        const input = new Float32Array(28 * 28);
        for (let i = 0; i < input.length; i++) {
          const r = imageData.data[i * 4];
          const g = imageData.data[i * 4 + 1];
          const b = imageData.data[i * 4 + 2];
          const gray = 0.299 * r + 0.587 * g + 0.114 * b; // 转换为灰度值
          input[i] = gray / 255.0; // 规范化到 0~1
        }
        const inputTensor = new ort.Tensor("float32", input, [1, 1, 28, 28]);

        // 使用 mnist-12.onnx 模型创建推理会话
        const session = await ort.InferenceSession.create(
          "https://media.githubusercontent.com/media/onnx/models/refs/heads/main/validated/vision/classification/mnist/model/mnist-12.onnx"
        );

        // 推理
        const results = await session.run({ Input3: inputTensor });
        const output = results.Plus214_Output_0.data;

        // 找到最大值(概率最大)的索引,即预测的数字
        const predictedDigit = output.indexOf(Math.max(...output));

        // 显示结果
        document.getElementById("output").innerText = predictedDigit;
      }

      ...
    </script>
  </body>
</html>

有什么限制?

丹尼尔:除了数字识别,还能玩点儿别的花样不?

蛋先生:那当然咯!语音识别、图像分类、对象检测,甚至生成式 AI,都不在话下!

丹尼尔:哇塞,连大语言模型都能搞定?

蛋先生:不过在浏览器上运行有些小小的限制

丹尼尔:让我来猜猜,是不是模型的大小有限制?

蛋先生:对头

丹尼尔:具体能多大呢?

蛋先生:首先我们得加载远程模型

各大浏览器对 ArrayBuffer 的大小都是有限制的,比如 Chrome 就是2G。当你用 fetch 去加载模型时,需要用到 response.arrayBuffer(),如果模型超过2G,就 GG 了

还有啊,ONNX 模型是通过 protobuf 格式进行传输的,protobuf 单个消息的大小限制也刚好是 2G

丹尼尔:所以最多只能加载2G的模型了?

蛋先生:那也不完全是。一次不行,我们可以分次嘛!我们可以将模型分成模型图和权重,权重信息作为外部数据另外加载即可。只要模型图不超过2G,咱就可以突破这2G的限制了

丹尼尔:那岂不是可以加载超级大的模型了?

蛋先生:嘿嘿,别高兴得太早。模型最终是要加载到运行时环境的,而我们的运行时是在 WebAssembly 环境中。根据 WebAssembly 规范,Memory 对象的大小顶多4G。所以理论上4G就是天花板了

丹尼尔:哦......

蛋先生:而且啊,当模型太大时,对硬件的要求就更高了。这些大家伙就不推荐放在浏览器里折腾了

丹尼尔:明白了,那我去试一下咯

蛋先生:好的,祝你好运!

写在最后

亲们,都到这了,要不,点赞或收藏或关注支持下我呗 o( ̄▽ ̄)d

相关推荐
dr李四维16 分钟前
iOS构建版本以及Hbuilder打iOS的ipa包全流程
前端·笔记·ios·产品运营·产品经理·xcode
雯0609~37 分钟前
网页F12:缓存的使用(设值、取值、删除)
前端·缓存
℘团子এ41 分钟前
vue3中如何上传文件到腾讯云的桶(cosbrowser)
前端·javascript·腾讯云
学习前端的小z1 小时前
【前端】深入理解 JavaScript 逻辑运算符的优先级与短路求值机制
开发语言·前端·javascript
彭世瑜1 小时前
ts: TypeScript跳过检查/忽略类型检查
前端·javascript·typescript
FØund4041 小时前
antd form.setFieldsValue问题总结
前端·react.js·typescript·html
Backstroke fish1 小时前
Token刷新机制
前端·javascript·vue.js·typescript·vue
小五Five1 小时前
TypeScript项目中Axios的封装
开发语言·前端·javascript
小曲程序1 小时前
vue3 封装request请求
java·前端·typescript·vue
临枫5411 小时前
Nuxt3封装网络请求 useFetch & $fetch
前端·javascript·vue.js·typescript