Transformers.js 文件

構建 Next.js 應用

您正在檢視的是需要從原始碼安裝。如果您想進行常規的 npm install,請檢視最新的穩定版本 (v3.0.0)。
Hugging Face's logo
加入 Hugging Face 社群

並獲得增強的文件體驗

開始使用

構建 Next.js 應用

在本教程中,我們將使用 Transformers.js 構建一個簡單的 Next.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.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

接下來,我們將建立一個新的 Web Worker 指令碼,我們將在此處放置所有與機器學習相關的程式碼。這是為了確保在模型載入和執行推理時主執行緒不會被阻塞。對於此應用程式,我們將使用 Xenova/distilbert-base-uncased-finetuned-sst-2-english,這是一個在 斯坦福情感樹庫 資料集上進行微調的約 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 元件中新增一個簡單的使用者介面,包括一個輸入文字框和一個預格式化文字元素,以顯示分類結果。

<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(例如 https://:3000/)即可看到您的應用程式執行起來!

(可選)步驟4:構建和部署

要構建您的應用程式,只需執行

npm run build

這將會打包您的應用程式,並將靜態檔案輸出到 out 資料夾。

對於此演示,我們將把我們的應用程式部署為一個靜態的 Hugging Face Space,但您也可以將其部署到任何您喜歡的地方!如果您還沒有,可以在這裡建立一個免費的 Hugging Face 賬戶。

  1. 訪問 https://huggingface.co/new-space 並填寫表單。請記住選擇“Static”作為空間型別。
  2. 點選頁面底部的“建立空間”按鈕。
  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.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

接下來,讓我們設定我們的 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(例如 https://:3000/)即可看到您的應用程式執行起來!

(可選)步驟4:構建和部署

對於此演示,我們將構建我們的應用程式並將其部署到 Hugging Face Spaces。如果您還沒有,可以在這裡建立一個免費的 Hugging Face 帳戶。

  1. 在您專案的根資料夾中建立一個新的 Dockerfile。您可以使用我們的示例 Dockerfile 作為模板。
  2. 訪問 https://huggingface.co/new-space 並填寫表單。請記住選擇“Docker”作為空間型別(您可以選擇“Blank” Docker 模板)。
  3. 點選頁面底部的“建立空間”按鈕。
  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 上更新

© . This site is unofficial and not affiliated with Hugging Face, Inc.