Transformers.js 文档
构建 Next.js 应用程序
并获得增强的文档体验
开始使用
构建 Next.js 应用程序
在本教程中,我们将构建一个简单的 Next.js 应用程序,该应用程序使用 Transformers.js 执行情感分析!由于 Transformers.js 可以在浏览器或 Node.js 中运行,您可以选择是在 客户端 还是 服务器端 执行推理(我们将向您展示如何同时进行)。无论哪种情况,我们都将使用新的 App Router 范例进行开发。最终产品将如下所示
实用链接
先决条件
客户端推理
步骤 1:初始化项目
首先使用 create-next-app
创建一个新的 Next.js 应用程序
npx create-next-app@latest
安装时,您将看到各种提示。对于此演示,我们将选择以下以粗体显示的选项
√ 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
步骤 2:安装和配置 Transformers.js
您可以使用以下命令从 NPM 安装 Transformers.js
npm i @huggingface/transformers
我们还需要更新 next.config.js
文件,以在为浏览器捆绑时忽略特定于节点的模块
/** @type {import('next').NextConfig} */
const nextConfig = {
// (Optional) Export as a static site
// See https://nextjs.net.cn/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.cn/configuration/resolve/#resolvealias
config.resolve.alias = {
...config.resolve.alias,
"sharp$": false,
"onnxruntime-node$": false,
}
return config;
},
}
module.exports = nextConfig
接下来,我们将创建一个新的 Web Worker 脚本,我们在其中放置所有与 ML 相关的代码。这是为了确保在模型加载和执行推理时主线程不会被阻塞。对于此应用程序,我们将使用 Xenova/distilbert-base-uncased-finetuned-sst-2-english
,这是一个在 Stanford Sentiment Treebank 数据集上微调的约 67M 参数模型。将以下代码添加到 ./src/app/worker.js
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,
});
});
步骤 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 */ )
}
在 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,其中包含一个输入文本框和一个预格式化的文本元素,以显示分类结果
<main className="flex min-h-screen flex-col items-center justify-center p-12">
<h1 className="text-5xl font-bold mb-2 text-center">Transformers.js</h1>
<h2 className="text-2xl mb-4 text-center">Next.js template</h2>
<input
className="w-full max-w-xs p-2 border border-gray-300 rounded mb-4"
type="text"
placeholder="Enter text here"
onInput={e => {
classify(e.target.value);
}}
/>
{ready !== null && (
<pre className="bg-gray-100 p-2 rounded">
{ (!ready || !result) ? 'Loading...' : JSON.stringify(result, null, 2) }
</pre>
)}
</main>
您现在可以使用以下命令运行您的应用程序
npm run dev
访问终端中显示的 URL(例如,http://localhost:3000/)以查看您的应用程序运行!
(可选)步骤 4:构建和部署
要构建您的应用程序,只需运行
npm run build
这将捆绑您的应用程序并将静态文件输出到 out
文件夹。
对于此演示,我们将把我们的应用程序部署为静态 Hugging Face Space,但您可以将其部署到任何您喜欢的地方!如果您还没有帐户,可以在此处创建一个免费的 Hugging Face 帐户。
- 访问 https://huggingface.co/new-space 并填写表格。请记住选择“Static”作为 space 类型。
- 单击页面底部的“Create space”按钮。
- 转到“Files” → “Add file” → “Upload files”。将
out
文件夹中的文件拖到上传框中,然后单击“Upload”。上传完成后,向下滚动到按钮并单击“Commit changes to main”。
就是这样! 您的应用程序现在应该在 https://huggingface.co/spaces/<your-username>/<your-space-name>
上线了!
服务器端推理
虽然有很多不同的方法可以执行服务器端推理,但最简单的方法(我们将在本教程中讨论)是使用新的 Route Handlers 功能。
步骤 1:初始化项目
首先使用 create-next-app
创建一个新的 Next.js 应用程序
npx create-next-app@latest
安装时,您将看到各种提示。对于此演示,我们将选择以下以粗体显示的选项
√ 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
步骤 2:安装和配置 Transformers.js
您可以使用以下命令从 NPM 安装 Transformers.js
npm i @huggingface/transformers
我们还需要更新 next.config.js
文件,以防止 Webpack 捆绑某些包
/** @type {import('next').NextConfig} */
const nextConfig = {
// (Optional) Export as a standalone site
// See https://nextjs.net.cn/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
接下来,让我们设置 Route Handler。我们可以通过在新的 ./src/app/classify/
目录中创建两个文件来完成此操作
pipeline.js
- 用于处理 pipeline 的构建。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 it's 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;
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); }
步骤 3:设计用户界面
现在我们将修改默认的 ./src/app/page.js
文件,以向我们新创建的 Route Handler 发出请求。
'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 we've made a request, set the ready flag.
if (!ready) setReady(true);
const json = await result.json();
setResult(json);
};
return (
<main className="flex min-h-screen flex-col items-center justify-center p-12">
<h1 className="text-5xl font-bold mb-2 text-center">Transformers.js</h1>
<h2 className="text-2xl mb-4 text-center">Next.js template (server-side)</h2>
<input
type="text"
className="w-full max-w-xs p-2 border border-gray-300 rounded mb-4"
placeholder="Enter text here"
onInput={e => {
classify(e.target.value);
}}
/>
{ready !== null && (
<pre className="bg-gray-100 p-2 rounded">
{
(!ready || !result) ? 'Loading...' : JSON.stringify(result, null, 2)}
</pre>
)}
</main>
)
}
您现在可以使用以下命令运行您的应用程序
npm run dev
访问终端中显示的 URL(例如,http://localhost:3000/)以查看您的应用程序运行!
(可选)步骤 4:构建和部署
对于此演示,我们将构建应用程序并将其部署到 Hugging Face Spaces。如果您还没有帐户,可以在此处创建一个免费的 Hugging Face 帐户。
- 在项目的根文件夹中创建一个新的
Dockerfile
。您可以使用我们的 示例 Dockerfile 作为模板。 - 访问 https://huggingface.co/new-space 并填写表格。请记住选择“Docker”作为 space 类型(您可以选择“Blank” Docker 模板)。
- 单击页面底部的“Create space”按钮。
- 转到“Files” → “Add file” → “Upload files”。将项目文件夹中的文件(不包括
node_modules
和.next
,如果存在)拖到上传框中,然后单击“Upload”。上传完成后,向下滚动到按钮并单击“Commit changes to main”。 - 将以下行添加到您的
README.md
顶部--- title: Next Server Example App emoji: 🔥 colorFrom: yellow colorTo: red sdk: docker pinned: false app_port: 3000 ---
就是这样! 您的应用程序现在应该在 https://huggingface.co/spaces/<your-username>/<your-space-name>
上线了!