当前位置: 首页 > article >正文

webassembly009 transformers.js 网页端侧推理 NLLB翻译模型

在这里插入图片描述
在这里插入图片描述

应用页面

  • 运行代码:npm i , npm run dev
    在这里插入图片描述

App.jsx

在这里插入图片描述

import { useEffect, useRef, useState } from 'react';
import LanguageSelector from './components/LanguageSelector'; // 语言选择器组件
import Progress from './components/Progress'; // 进度条组件

import './App.css'; // 应用样式

function App() {

  // 状态管理
  const [ready, setReady] = useState(null); // 模型准备状态
  const [disabled, setDisabled] = useState(false); // 翻译按钮是否禁用
  const [progressItems, setProgressItems] = useState([]); // 进度条
  // 输入输出状态管理
  const [input, setInput] = useState('I love walking my dog.'); // 输入文本
  const [sourceLanguage, setSourceLanguage] = useState('eng_Latn'); // 源语言
  const [targetLanguage, setTargetLanguage] = useState('fra_Latn'); // 目标语言
  const [output, setOutput] = useState(''); // 输出文本


  // 创建一个对worker对象的引用
  const worker = useRef(null);
  // 使用`useEffect`钩子在组件挂载时设置worker
  useEffect(() => {
    if (!worker.current) {
      // 如果worker不存在,则创建一个新的Worker实例
      worker.current = new Worker(new URL('./worker.js', import.meta.url), {
        type: 'module' // 指定worker脚本类型为ES模块
      });
    }

    // 给worker添加消息事件监听器
    worker.current.addEventListener('message', onMessageReceived);
    
    // 定义从worker接收消息后的回调处理函数
    const onMessageReceived = (e) => {
      switch (e.data.status) {
        case 'initiate':     // 开始加载模型文件时,更新状态并添加进度项
          setReady(false);
          setProgressItems(prev => [...prev, e.data]);
          break;
        case 'progress':    // 更新模型文件加载进度
          setProgressItems(prev => prev.map(item => item.file === e.data.file ? { ...item, progress: e.data.progress } : item));
          break;
        case 'done':        // 模型文件加载完成,从进度列表中移除该项目
          setProgressItems(prev => prev.filter(item => item.file !== e.data.file));
          break;
        case 'ready':       // 工作线程准备好接受消息
          setReady(true);
          break;
        case 'update':      // 更新输出文本
          setOutput(e.data.output);
          break;
        case 'complete':    // 翻译完成,启用“翻译”按钮
          setDisabled(false);
          break;
      }
    };

    // 返回一个清理函数,在组件卸载时移除事件监听器
    return () => worker.current.removeEventListener('message', onMessageReceived);
  });

  // 翻译函数
  const translate = () => {
    setDisabled(true); // 禁用翻译按钮
    worker.current.postMessage({ // 发送消息给worker开始翻译
      text: input,
      src_lang: sourceLanguage,
      tgt_lang: targetLanguage,
    });
  }


  // 渲染UI
  return (
    <>
      <h1>Transformers.js</h1>
      <h2>ML-powered multilingual translation in React!</h2>

      {/* UI布局,关键在于调用translate函数 */}
      <div className='container'>
        <div className='language-container'>
          <LanguageSelector type={"Source"} defaultLanguage={"eng_Latn"} onChange={x => setSourceLanguage(x.target.value)} />
          <LanguageSelector type={"Target"} defaultLanguage={"fra_Latn"} onChange={x => setTargetLanguage(x.target.value)} />
        </div>
        <div className='textbox-container'>
          <textarea value={input} rows={3} onChange={e => setInput(e.target.value)}></textarea>
          <textarea value={output} rows={3} readOnly></textarea>
        </div>
      </div>

      <button disabled={disabled} onClick={translate}>Translate</button>

      <div className='progress-bars-container'>
        {ready === false && <label>Loading models... (only run once)</label>}
        {progressItems.map(data => (
          <div key={data.file}>
            <Progress text={data.file} percentage={data.progress} />
          </div>
        ))}
      </div>
    </>
  );
}

export default App; // 导出默认组件

worker.js

  • 代码定义了一个使用单例模式的MyTranslationPipeline,并通过Web Worker监听主线程的消息来执行翻译任务(Worker线程消息传递):
import { pipeline } from '@xenova/transformers'; // 导入pipeline函数用于加载模型


 // 使用单例模式确保仅加载一次翻译管道实例。由于加载管道是一个昂贵的操作,我们不希望每次翻译句子时都重新加载。
class MyTranslationPipeline {
    static task = 'translation'; // 任务类型
    static model = 'Xenova/nllb-200-distilled-600M'; // 模型名称
    static instance = null; // 静态变量:存储单例实例

    /**
     * 获取或创建一个翻译管道的实例。
     * @param progress_callback - 进度回调函数,用于追踪模型加载进度。@returns {Promise<*>} - 返回已加载的翻译管道实例。
     */
    static async getInstance(progress_callback = null) {
        if (this.instance === null) { // 如果尚未创建实例,则加载新的翻译管道并保存
            // 1. pipeline函数transformers.js模型加载:默认情况下,模型将从 Hugging Face Hub 下载并存储在 浏览器缓存 中,有关更多信息,请参见https://hugging-face.cn/docs/transformers.js/custom_usage。
            this.instance = await pipeline(this.task, this.model, { progress_callback });
        }
        return this.instance; // 返回实例
    }
}

// 监听来自主线程的消息事件
self.addEventListener('message', async (event) => {
    // 获取翻译管道实例,首次调用时将加载管道并保存以备将来使用
    let translator = await MyTranslationPipeline.getInstance(x => {
        // 添加进度回调函数,以便跟踪模型加载进度
        self.postMessage(x);
    });

    // 2. transformers.js模型执行实际的翻译操作
    let output = await translator(event.data.text, {
        tgt_lang: event.data.tgt_lang, // 目标语言
        src_lang: event.data.src_lang, // 源语言

        // 允许部分输出,并通过回调函数发送更新
        callback_function: x => {
            self.postMessage({
                status: 'update', // 状态更新
                output: translator.tokenizer.decode(x[0].output_token_ids, { skip_special_tokens: true }) // 解码输出文本
            });
        }
    });

    // 将翻译结果发送回主线程
    self.postMessage({
        status: 'complete', // 完成状态
        output: output, // 最终的翻译输出
    });
});

http://www.kler.cn/a/546418.html

相关文章:

  • 智能背后的阴影:LLM安全风险
  • 华为支付-商户基础支付场景准备
  • Flask和Django相比哪个更适合新手?
  • 利用背景图像进行信息泄露和 LPE:AnyDesk CVE-2024-12754、ZDI-24-1711
  • 内网穿透简单使用
  • uniapp商城之首页模块
  • Apache Iceberg 与 Apache Hudi:数据湖领域的双雄对决
  • USB Flash闪存驱动器安全分析(第一部分)
  • 文心一言4月起全面免费,6月底开源新模型:AI竞争进入新阶段?
  • Redis 01 02章——入门概述与安装配置
  • Go语言实现单例模式
  • 基于opencv的HOG+角点匹配教程
  • Day1 25/2/14 FRI
  • 执行js生成json文件并动态写入数据
  • HTTP请求报文头和相应报文头
  • 深入探索C语言中的字符串处理函数:strstr与strtok
  • 科研绘图系列:R语言绘制地图和山脊图(map ridge plot)
  • LVS集群(DR/NAT)
  • 知识拓展:设计模式之装饰器模式
  • 【docker知识】快速找出服务器中占用内存较高的容器