Transformers.js 文档

构建 Next.js 应用程序

您正在查看 main 版本,该版本需要从源代码安装。如果您想要常规 npm 安装,请查看最新的稳定版本 (v3.0.0)。
Hugging Face's logo
加入 Hugging Face 社区

并获得增强的文档体验

开始使用

构建 Next.js 应用程序

在本教程中,我们将构建一个简单的 Next.js 应用程序,该应用程序使用 Transformers.js 执行情感分析!由于 Transformers.js 可以在浏览器或 Node.js 中运行,您可以选择是在 客户端 还是 服务器端 执行推理(我们将向您展示如何同时进行)。无论哪种情况,我们都将使用新的 App Router 范例进行开发。最终产品将如下所示

Demo

实用链接

先决条件

客户端推理

步骤 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 帐户。

  1. 访问 https://huggingface.co/new-space 并填写表格。请记住选择“Static”作为 space 类型。
  2. 单击页面底部的“Create space”按钮。
  3. 转到“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/ 目录中创建两个文件来完成此操作

  1. 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;
  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);
    }

步骤 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 帐户。

  1. 在项目的根文件夹中创建一个新的 Dockerfile。您可以使用我们的 示例 Dockerfile 作为模板。
  2. 访问 https://huggingface.co/new-space 并填写表格。请记住选择“Docker”作为 space 类型(您可以选择“Blank” Docker 模板)。
  3. 单击页面底部的“Create space”按钮。
  4. 转到“Files” → “Add file” → “Upload files”。将项目文件夹中的文件(不包括 node_modules.next,如果存在)拖到上传框中,然后单击“Upload”。上传完成后,向下滚动到按钮并单击“Commit changes to main”。
  5. 将以下行添加到您的 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> 上线了!

< > 在 GitHub 上 更新