ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

使用 Transformers.js 构建 Next.js 情感分析应用:客户端与服务端推理完整指南

使用 Transformers.js 构建 Next.js 情感分析应用:客户端与服务端推理完整指南 使用 Transformers.js 构建 Next.js 情感分析应用客户端与服务端推理完整指南【免费下载链接】transformers.jsState-of-the-art Machine Learning for the web. Run Transformers directly in your browser, with no need for a server!项目地址: https://gitcode.com/GitHub_Trending/tr/transformers.js本篇技术指南基于当前仓库transformers.js官方文档tutorials/next.md展开讲解如何在 Next.jsApp Router 范式中集成 Transformers.js搭建一个完整的 Sentiment Analysis情感分析Web 应用。由于 Transformers.js 既能运行在浏览器中也能运行在 Node.js 环境中你可以自由选择**客户端推理Client-side Inference与服务端推理Server-side Inference**两种方案——本指南将逐一演示并给出可复制的完整代码。读完后你将掌握Web Worker 中加载模型与推理的 Singleton 模式、Next.js Route Handler 下的模型缓存策略以及静态站点与 Docker 化部署的完整链路。前置条件Node.js 版本 18npm 版本 9两条技术路线都基于 Next.js 的App Router范式开发因此请确保你的 Next.js 版本与之兼容。方案概览客户端推理 vs 服务端推理维度客户端推理服务端推理模型加载位置浏览器Web Worker 内Node.js 运行时服务器推理引擎onnxruntime-webWASM/WebGPUonnxruntime-node首次请求体验需下载模型权重之后有浏览器缓存服务器内存常驻缓存热更新后被清除部署形态可静态导出output: export需 Docker / 独立 Node 服务从仓库源码可以佐证这一架构差异在 backends/onnx.js 中Transformers.js 会根据运行环境自动选择 ONNX Runtime 后端——Node 环境下默认执行设备为cpudefaultDevices [cpu]而浏览器环境下默认设备为wasmdefaultDevices [wasm]并且仅在 Web 环境具备 WebGPU/WebNN 时才会额外注册webgpu、webnn-*等执行提供方。同时package.json 中的exports字段区分了node与defaultweb两种入口分别指向dist/transformers.node.mjs与dist/transformers.web.js这正是一套代码、两个运行时的打包基础。一、客户端推理Client-side Inference客户端推理的核心思路把全部 ML 相关代码放进Web Worker确保模型加载与推理期间不阻塞主线程页面组件通过postMessage与 Worker 通信实时展示模型加载进度与推理结果。Step 1初始化项目使用create-next-app创建全新的 Next.js 应用npx create-next-applatest安装过程中会出现一系列交互提示本 Demo 的选择如下加粗项为选中项√ What is your project named? ... next √ Would you like to use TypeScript? ...No/ Yes √ Would you like to use ESLint? ... No /Yes√ Would you like to use Tailwind CSS? ... No /Yes√ Would you like to use src/ directory? ... No /Yes√ Would you like to use App Router? (recommended) ... No /Yes√ Would you like to customize the default import alias? ...No/ Yes这里刻意选择了src/目录与 App Router因为后文所有代码都将位于src/app/下。Step 2安装并配置 Transformers.js从 NPM 安装 Transformers.jsnpm i huggingface/transformers接下来需要修改next.config.js。由于浏览器打包时不应包含 Node 专用的依赖需要通过 webpack 的resolve.alias将sharp与onnxruntime-node直接置空/** type {import(next).NextConfig} */ const nextConfig { // (Optional) Export as a static site // See https://nextjs.org/docs/pages/building-your-application/deploying/static-exports#configuration output: export, // Feel free to modify/remove this option // Override the default webpack configuration webpack: (config) { // See https://webpack.js.org/configuration/resolve/#resolvealias config.resolve.alias { ...config.resolve.alias, sharp$: false, onnxruntime-node$: false, }; return config; }, }; module.exports nextConfig;为什么必须忽略这两个包查看 package.json 可知Transformers.js 的依赖中包含onnxruntime-nodeNode 原生绑定与sharp图像处理原生库。它们是 Node 运行时专用模块若被 webpack 打包进浏览器产物轻则体积膨胀重则因原生二进制无法加载而直接报错。output: export是可选的静态导出配置——启用后npm run build会输出纯静态文件到out目录非常适合部署到任意静态托管平台。接着创建 Web Worker 脚本./src/app/worker.js把全部 ML 相关代码放在这里。本 Demo 使用Xenova/distilbert-base-uncased-finetuned-sst-2-english这是一个约 67M 参数的模型在 Stanford Sentiment Treebank 数据集上微调而来专门用于二分类情感分析import { pipeline, env } from huggingface/transformers; // Skip local model check env.allowLocalModels false; // Use the Singleton pattern to enable lazy construction of the pipeline. class PipelineSingleton { static task text-classification; static model Xenova/distilbert-base-uncased-finetuned-sst-2-english; static instance null; static async getInstance(progress_callback null) { if (this.instance null) { this.instance pipeline(this.task, this.model, { progress_callback }); } return this.instance; } } // Listen for messages from the main thread self.addEventListener(message, async (event) { // Retrieve the classification pipeline. When called for the first time, // this will load the pipeline and save it for future use. let classifier await PipelineSingleton.getInstance((x) { // We also add a progress callback to the pipeline so that we can // track model loading. self.postMessage(x); }); // Actually perform the classification let output await classifier(event.data.text); // Send the output back to the main thread self.postMessage({ status: complete, output: output, }); });这段代码包含两个关键设计均能在仓库源码中找到印证env.allowLocalModels false查看 env.js 可知allowLocalModels的默认值取决于运行环境——浏览器/Web Worker 中默认为falseNode 等非 Web 环境默认为true。在 Web Worker 里显式设置为false可以跳过本地文件检查、直接走远程 Hub 加载避免无谓的路径探测开销。pipeline(task, model, { progress_callback })的参数签名见 pipelines.js除progress_callback外还支持config、cache_dir、local_files_only、revision默认main、device、dtype、subfolder默认onnx、use_external_data_format、model_file_name、session_options等参数。其中dtype可用于量化如q8、q4显著减小模型体积、加快加载速度device可指定webgpu等执行设备设备映射见 backends/onnx.js。关于进度回调的底层机制pipeline 内部会先探测各文件的元数据文件是否存在、大小随后用DefaultProgressCallback包装用户回调见 utils/core.js 的dispatchCallback与DefaultProgressCallback在模型、Tokenizer、Processor 并行加载完成后发送status: ready事件见 pipelines.js。这些事件经由self.postMessage(x)转发给主线程就构成了 UI 上的加载进度。Step 3设计用户界面修改默认的./src/app/page.js使其与 Worker 线程建立连接。由于本方案完全在浏览器内推理需要使用use client指令将其声明为客户端组件use client import { useState, useEffect, useRef, useCallback } from react export default function Home() { /* TODO: Add state variables */ // Create a reference to the worker object. const worker useRef(null); // We use the useEffect hook to set up the worker as soon as the App component is mounted. useEffect(() { if (!worker.current) { // Create the worker if it does not yet exist. worker.current new Worker(new URL(./worker.js, import.meta.url), { type: module }); } // Create a callback function for messages from the worker thread. const onMessageReceived (e) { /* TODO: See below */}; // Attach the callback function as an event listener. worker.current.addEventListener(message, onMessageReceived); // Define a cleanup function for when the component is unmounted. return () worker.current.removeEventListener(message, onMessageReceived); }); const classify useCallback((text) { if (worker.current) { worker.current.postMessage({ text }); } }, []); return ( /* TODO: See below */ ) }注意这里的两个细节new URL(./worker.js, import.meta.url)是 Next.js 官方推荐的 Worker 引入方式配合type: module使 Worker 内可直接使用import语法useEffect返回的清理函数会在组件卸载时移除事件监听避免内存泄漏。在Home组件开头初始化两个状态变量——分别跟踪分类结果与模型加载状态// Keep track of the classification result and the model loading status. const [result, setResult] useState(null); const [ready, setReady] useState(null);并填充onMessageReceived回调根据 Worker 发来的消息更新状态const onMessageReceived (e) { switch (e.data.status) { case initiate: setReady(false); break; case ready: setReady(true); break; case complete: setResult(e.data.output[0]); break; } };最后为Home组件添加一个简单 UI——输入文本框 展示分类结果的pre元素main classNameflex min-h-screen flex-col items-center justify-center p-12 h1 classNametext-5xl font-bold mb-2 text-centerTransformers.js/h1 h2 classNametext-2xl mb-4 text-centerNext.js template/h2 input classNamew-full max-w-xs p-2 border border-gray-300 rounded mb-4 typetext placeholderEnter text here onInput{(e) { classify(e.target.value); }} / {ready ! null ( pre classNamebg-gray-100 p-2 rounded {!ready || !result ? Loading... : JSON.stringify(result, null, 2)} /pre )} /main运行应用npm run dev打开终端中显示的 URL通常是 http://localhost:3000/即可体验。输入文本后输出形如{ label: POSITIVE, score: 0.9996520280838013 }输出结果是怎么算出来的在 pipelines/text-classification.js 中可以看到TextClassificationPipeline的完整实现先由 Tokenizer 完成 padding 与 truncation再经模型前向计算得到logits最后根据problem_type选择激活函数——multi_label_classification用 sigmoid单标签分类默认则用 softmax并借助top_k默认 1取概率最高的标签。理解这条调用链便于后续按需定制例如将classifier(text)改为classifier(text, { top_k: 5 })返回前 5 个候选。可选Step 4构建与部署构建应用npm run build由于next.config.js中启用了output: export构建产物为纯静态文件输出到out文件夹。本 Demo 将其部署为静态 Hugging Face Space也可以部署到任意托管平台访问 Hugging Face 的 Space 创建页面填写表单时Space 类型务必选择 Static点击页面底部的 Create space 按钮进入 Files → Add file → Upload files将out文件夹中的所有文件拖入上传框并点击 Upload上传完成后滚动到底部点击 Commit changes to main。部署完成后应用将托管在https://huggingface.co/spaces/你的用户名/你的Space名。二、服务端推理Server-side Inference服务端推理的方式很多本教程采用最简洁的一种Next.js 的Route Handlers。模型在 Node.js 进程中加载并常驻内存浏览器只负责发起请求与渲染结果。Step 1初始化项目与客户端方案一致npx create-next-applatest选项同样为TypeScriptNo、ESLintYes、TailwindYes、src/目录Yes、App RouterYes、自定义导入别名No。Step 2安装并配置 Transformers.jsnpm i huggingface/transformers修改next.config.js通过serverComponentsExternalPackages告知 Next.js 不要用 webpack 打包sharp与onnxruntime-node——它们应由 Node.js 原生require加载/** type {import(next).NextConfig} */ const nextConfig { // (Optional) Export as a standalone site // See https://nextjs.org/docs/pages/api-reference/next-config-js/output#automatically-copying-traced-files output: standalone, // Feel free to modify/remove this option // Indicate that these packages should not be bundled by webpack experimental: { serverComponentsExternalPackages: [sharp, onnxruntime-node], }, }; module.exports nextConfig;output: standalone是可选的独立输出模式Next.js 会自动追踪运行所需文件产出一个自带依赖的迷你版 Node 服务非常适合 Docker 镜像化部署。接下来在./src/app/classify/目录下创建两个文件构成/classify路由1.pipeline.js—— 负责 pipeline 的构造。这里同样使用 Singleton 模式实现懒加载并针对 Next.js 的开发热重载做了特殊处理import { pipeline } from huggingface/transformers; // Use the Singleton pattern to enable lazy construction of the pipeline. // NOTE: We wrap the class in a function to prevent code duplication (see below). const P () class PipelineSingleton { static task text-classification; static model Xenova/distilbert-base-uncased-finetuned-sst-2-english; static instance null; static async getInstance(progress_callback null) { if (this.instance null) { this.instance pipeline(this.task, this.model, { progress_callback, }); } return this.instance; } }; let PipelineSingleton; if (process.env.NODE_ENV ! production) { // When running in development mode, attach the pipeline to the // global object so that its preserved between hot reloads. // For more information, see https://vercel.com/guides/nextjs-prisma-postgres if (!global.PipelineSingleton) { global.PipelineSingleton P(); } PipelineSingleton global.PipelineSingleton; } else { PipelineSingleton P(); } export default PipelineSingleton;这个开发模式挂 global的技巧非常关键npm run dev下每个模块都可能被热重载多次若每次都重新加载一个 67M 参数的模型开发体验会极其痛苦。将 pipeline 实例挂载到global对象上可以在热重载之间保活生产环境则直接使用模块级单例。2.route.js—— 处理/classify路由的请求import { NextResponse } from next/server; import PipelineSingleton from ./pipeline.js; export async function GET(request) { const text request.nextUrl.searchParams.get(text); if (!text) { return NextResponse.json( { error: Missing text parameter, }, { status: 400 }, ); } // Get the classification pipeline. When called for the first time, // this will load the pipeline and cache it for future use. const classifier await PipelineSingleton.getInstance(); // Actually perform the classification const result await classifier(text); return NextResponse.json(result); }从 pipelines.js 的源码可以看到pipeline()内部最终会调用各组件如 AutoModel / AutoTokenizer的from_pretrained并复用统一的pretrainedOptions因此首次请求触发模型下载与加载、后续请求直接复用内存中的实例这一行为由框架天然保证。另外注意服务端方案中不再需要env.allowLocalModels false——Node 环境下默认允许加载本地模型并且onnxruntime-node作为原生后端由服务器直接使用。Step 3设计用户界面修改默认的./src/app/page.js通过fetch调用/classify路由use client; import { useState } from react; export default function Home() { // Keep track of the classification result and the model loading status. const [result, setResult] useState(null); const [ready, setReady] useState(null); const classify async (text) { if (!text) return; if (ready null) setReady(false); // Make a request to the /classify route on the server. const result await fetch(/classify?text${encodeURIComponent(text)}); // If this is the first time weve made a request, set the ready flag. if (!ready) setReady(true); const json await result.json(); setResult(json); }; return ( main classNameflex min-h-screen flex-col items-center justify-center p-12 h1 classNametext-5xl font-bold mb-2 text-centerTransformers.js/h1 h2 classNametext-2xl mb-4 text-center Next.js template (server-side) /h2 input typetext classNamew-full max-w-xs p-2 border border-gray-300 rounded mb-4 placeholderEnter text here onInput{(e) { classify(e.target.value); }} / {ready ! null ( pre classNamebg-gray-100 p-2 rounded {!ready || !result ? Loading... : JSON.stringify(result, null, 2)} /pre )} /main ); }与客户端方案最大的区别在于这里没有 Worker推理结果来自服务器NextResponse.json(result)的响应。ready状态代表服务器上的模型是否已就绪——首次请求会触发模型加载可能耗时数秒UI 在此之前显示 Loading...。运行应用npm run dev访问 http://localhost:3000/ 即可体验也可以在浏览器地址栏直接访问http://localhost:3000/classify?textI%20love%20Transformers.js验证路由返回的 JSON。可选Step 4构建与部署服务端推理无法静态托管本 Demo 使用Docker Space部署到 Hugging Face Spaces在项目根目录创建Dockerfile可参考仓库中examples/next-server/Dockerfile模板访问 Hugging Face 的 Space 创建页面填写表单时Space 类型选择 Docker可选择 Blank Docker 模板点击 Create space 按钮进入 Files → Add file → Upload files将项目目录中的文件排除node_modules与.next拖入上传框并点击 Upload提交 Commit changes to main在项目README.md顶部添加以下 YAML 元信息务必与 Space 配置一致--- title: Next Server Example App emoji: colorFrom: yellow colorTo: red sdk: docker pinned: false app_port: 3000 ---部署完成后应用将托管在https://huggingface.co/spaces/你的用户名/你的Space名。三、两种方案的选型建议结合本仓库源码与上文两条实操链路给出如下决策参考选客户端推理追求极致的隐私数据不出浏览器、零服务器成本或部署到纯静态平台。代价是首次访问需下载模型权重可配合dtype量化、env.js 中的useBrowserCache浏览器缓存机制优化且终端设备性能直接影响推理速度。选服务端推理模型常驻服务器内存、响应稳定可控适合对延迟敏感或模型体积较大的场景。代价是需要维护一个 Node 服务如 Docker Space且并发请求会消耗服务器 CPUNode 默认cpu执行设备见 backends/onnx.js。如果你希望进一步探索本仓库还提供了text-classification之外的更多 pipeline如image-classification、automatic-speech-recognition等见 pipelines/index.js只需将上述代码中的task与model替换即可迁移到其他任务同时可以研究 docs/source/tutorials/ 下的 React、Electron、浏览器扩展等场景文档把同一套 Singleton 消息通信模式复用到更多前端框架中。【免费下载链接】transformers.jsState-of-the-art Machine Learning for the web. Run Transformers directly in your browser, with no need for a server!项目地址: https://gitcode.com/GitHub_Trending/tr/transformers.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表