freeCodeCamp.org

How Neural Machine Translation Works: Build Your Own Translation App with React Native and QVAC

8.5内容质量
How Neural Machine Translation Works: Build Your Own Translation App with React Native and QVAC

TL;DR · AI 摘要

Transformer架构革新神经机器翻译,结合QVAC与React Native实现翻译应用开发。

核心要点

  • Transformer通过自注意力机制解决长句翻译中的记忆丢失问题
  • QVAC提供支持构建翻译应用的架构方案
  • React Native实现跨平台翻译应用开发

结构提纲

按章节快速跳转。

  1. 揭示现代翻译技术从规则系统到神经网络的革命性转变

  2. 解析神经机器翻译如何通过学习人类翻译模式突破传统规则限制

  3. Transformer架构

    详解自注意力机制如何解决长距离依赖问题

  4. ·QVAC架构

    阐述QVAC框架对翻译应用开发的支撑能力

  5. 指导使用React Native构建跨平台翻译应用的完整流程

思维导图

用一张图看清主题之间的关系。

查看大纲文本(无障碍 / 无 JS 友好)
  • 神经机器翻译实现
    • Transformer架构
      • 自注意力机制
      • 编码器-解码器结构
    • 技术实现
      • QVAC框架
      • React Native开发

金句 / Highlights

值得收藏与分享的关键句。

#Transformer#NMT#React Native#QVAC
打开原文

神经机器翻译的原理:使用React Native和QVAC构建自己的翻译应用

2026年7月17日

/

#AI

Jibril-M🍀

过去10年里,我们见证了翻译技术的显著进步。我们从机械式的翻译系统,发展到如今能够理解句子中每个单词含义,并掌握单词在完整句子语境中如何衔接的智能系统。

例如,当前的翻译系统能够区分像这样的句子中"bank"一词的不同含义:

"我今天无法去银行存款"和"我们将在河岸附近见面。"

这两个句子都包含"bank"这个词,但含义完全不同。

那么我们是如何走到这一步的?这场重大变革始于2017年6月,当时由8位谷歌研究人员组成的团队(因研究影响力被称为"8位武士")发布了一篇题为《注意力即一切》("Attention Is All You Need")的论文。这个时间点标志着现代人工智能系统和架构的重大转折。

作为背景信息,这个框架是当前大型语言模型(如ChatGPT)的基础。

创建Transformer架构的8位谷歌研究人员

那么,什么是神经机器翻译(NMT)?谷歌工程师是如何开发出使机器能够理解句子中每个单词语义含义的框架?

目录

  • 揭开NMT的面纱:屏幕背后的大脑
  • Transformer如何认知世界
  • 为什么这很重要
  • 人工智能的民主化
  • 什么是QVAC?
  • QVAC支持的架构
  • 推理流程
  • 项目搭建
  • 完整实现
  • 结论
  • 资源与进一步阅读

揭开NMT的面纱:屏幕背后的大脑

要理解这项突破,我们首先需要揭开NMT(神经机器翻译)真正含义的面纱。

几十年来,计算机翻译采用的是"基于规则"的方法。计算机本质上被赋予一个庞大的双语词典和一套语法规则。它会逐字翻译句子,调整一些词的位置,并希望得到理想的结果。

这就是为什么早期的翻译显得如此生硬和机械:计算机试图像解决数学问题一样处理语言。

NMT通过引入神经网络彻底改变了这一局面。神经网络是一种受人脑启发的计算机系统。与记忆严格规则不同,NMT系统通过分析数百万条现有的人类翻译数据进行学习。它观察人类如何翻译短语,捕捉模式,并学习单词在现实世界中的实际交互方式。

但即使是早期的NMT系统也存在重大缺陷:它们按顺序从左到右阅读句子。如果句子过长,系统在到达句子末尾时会"忘记"句子的开头。

这正是谷歌研究人员实现历史性突破的地方。

Transformer如何认知世界

《注意力即一切》论文通过引入一种全新的架构——Transformer——解决了记忆问题。与逐字阅读句子不同,Transformer能够一次性读取整个句子。

为了实现这一点,它将任务分为两个主要部分:编码器(Encoder)和解码器(Decoder)。

编码器(阅读者)

将编码器想象成一个高度分析的阅读者。当你将一个句子输入系统时,编码器的任务是阅读它并构建一个"心智地图",准确理解句子的含义。

它通过一种称为自注意力机制(Self-Attention)的机制实现这一点。你可以将自注意力机制想象成一系列聚光灯。当计算机查看特定单词时,它会将聚光灯投射到句子中的其他所有单词上,以观察它们之间的关联性。

回到我们之前的例子:

"We shall meet near the river bank."

当编码器(Encoder)处理单词 "bank" 时,其自注意力机制的聚光灯会立即定位到单词 "river"。由于这两个单词在AI的心智地图中高度关联,系统会立即意识到我们讨论的是水边的陆地,而不是金融机构。它在进入下一步之前就锁定了这个"语义含义"。

解码器(The Writer)

当编码器完成对句子真实含义的映射后,会将这个蓝图交给解码器(Decoder)。

解码器就是"写作者"。它的唯一任务是将这个蓝图转化为目标语言。但它不会直接输出预设的模板。而是逐词构建新句子,不断回看编码器的蓝图(通过一种称为交叉注意力(Cross-Attention)的技巧),以确保保持正确的上下文、语气和语法。

如果它将我们的"river bank"句子翻译成法语,就会知道要写"la rive"(河岸)而不是"la banque"(银行),因为编码器的蓝图提前警告了它。

为什么这很重要

通过教会机器关注整体画面而非单个单词,谷歌工程师不仅构建了更好的翻译器,更创造了一个终于能理解人类语言细微差别、习语和上下文的系统。

事实证明,如果AI能足够理解句子的上下文以实现翻译,它也可以利用同样的上下文来撰写文章、回答复杂问题和编写代码。2017年的翻译引擎意外地成为了整个AI时代的基石。

AI的民主化

Transformer发明几年后,使用它进行开发仍只是富裕阶层的玩具。如果你想实现哪怕是最简单的翻译功能,一旦超出谷歌等大科技公司提供的微小免费额度,就必须向它们支付巨额费用。

试图绕过它们的主导地位几乎是不可能的,因为当时几乎没有资源供独立开发者使用。那时,仅仅理解Transformer的基本数学原理就需要学术博士学位。没有庞大的研究部门支持,从零开始构建自己的解决方案将是一场极其昂贵的噩梦。

幸运的是,开源开发者社区一直在不懈努力,使AI技术民主化。如今,我们拥有任何人都可以免费下载和使用的强大模型。

此外,我们个人设备中的处理器变得异常强大。这种硬件进化意味着复杂的AI模型现在可以直接在你的智能手机上本地运行,确保最大数据隐私并消除对外部服务器的依赖。

俗话说:"今天需要整栋大楼才能运行,明天就能装进口袋。"当然,这句话是我编的 😅,但你应该明白我的意思!

为了实际操作,我们将使用Expo和QVAC构建一个移动应用,将英语翻译成法语。

什么是QVAC?

QVAC(QuantumVerse Automatic Computer)是由Tether创建的去中心化、以本地优先的AI开发平台和SDK。

与需要云连接的传统AI工具不同,QVAC允许用户在自己的设备上完全运行AI模型。通过将计算过程保留在本地并实现离线运行,它确保您的数据始终保持私密、安全且完全由您掌控。

设备端翻译的关键概念

要理解QVAC如何在移动设备上运行,我们需要牢记以下几个关键概念:

#### 1. 设备端推理:

在本地执行模型计算。与依赖单一引擎或云API的传统方式不同,QVAC根据任务需求支持多种专用本地推理后端。

在翻译场景中,其底层使用Bergamot引擎。这些引擎会将量化后的模型权重直接映射到设备的RAM中,并通过原生硬件加速执行计算。

#### 2. 量化

一种数学优化技术,用于压缩模型权重。这使得模型能够在消费级移动硬件的内存限制下运行,同时保持输出质量。

QVAC支持的架构

在编写代码之前,理解底层实际运行机制至关重要。为实现本地执行且不导致设备过热,QVAC SDK负责管理硬件绑定和模型生命周期,同时接入优化后的推理后端。

在翻译场景中,QVAC使用Bergamot引擎。该引擎最初作为Bergamot项目(为Firefox离线翻译提供支持)的一部分开发,专为在消费级硬件上实现快速、精准的神经机器翻译(NMT)进行了高度优化。

Bergamot引擎的核心机制是:接收源语言句子,通过其编码器-解码器Transformer架构进行处理,并以高效方式预测目标语言的标记。

语言对的运作原理

理解这些模型的训练机制非常重要。Bergamot使用的翻译模型严格遵循单向语言对规则。这意味着BERGAMOT_EN_FR模型仅设计用于将英语翻译为法语,无法反向操作。

如果需要将法语翻译回英语,必须下载并加载一个专门为此方向训练的独立模型。

如果模型被训练为双向(英语↔法语)或多语言(像大型语言模型那样翻译数十种语言),则需要在单一神经网络中存储多个语言方向的数学表示、词汇和语法规则。这会显著增加参数数量,导致文件体积巨大,并需要大量RAM和计算资源进行处理。

通过将任务限定为单一方向(例如BERGAMOT_EN_FR),模型只需让神经网络"理解"英语输入并"生成"法语输出,无需具备生成英语文本的能力。

这种极端的专业化正是Bergamot将模型权重压缩到仅15-35MB大小的原因,这些极小的文件可以即时在本地CPU上运行,而不会导致浏览器卡顿。

推理流程

为直观理解代码中如何与翻译引擎交互,可以将本地翻译想象为在手机内存中运行一个专用的翻译官:

  • 雇佣翻译官(加载模型):我们将压缩后的模型文件(例如BERGAMOT_EN_FR英译法模型)直接映射到设备的RAM中。
  • 传递脚本(文本输入):我们将源文本传递给已加载的引擎。
  • 执行翻译(推理过程):引擎读取文本并进行数学计算预测翻译后的标记,待流程完成后提供翻译结果。
  • 退出流程(卸载):由于神经网络模型占用大量内存,翻译完成后或用户离开界面时,可将模型从RAM中清除以释放资源。

项目初始化

为确保本指南的完整性,让我们快速生成新的Expo应用并安装QVAC SDK。打开终端并运行以下命令:

code
npx create-expo-app translator-app --template blank-typescript
cd translator-app
npm install @qvac/sdk jiti

接下来,你需要在package.json中添加以下对等依赖项,以确保QVAC正常工作。将这些行添加到相应部分:

code
"dependencies": {
    "bare-rpc": "^1.0.0",
    "react-native-bare-kit": "^0.11.5"
  },
  "devDependencies": {
    "bare-pack": "^1.5.1"
  }

添加完成后,通过以下命令安装依赖项:

code
npm install
npx expo install expo-file-system expo-build-properties expo-device

使用JITI配置Expo插件

接下来,我们需要将QVAC SDK插件添加到Expo项目中。由于QVAC SDK的Expo插件以现代ECMAScript模块(ESM)形式分发,但Expo的配置文件(app.config.js)运行在标准Node.js CommonJS环境中,因此我们无法使用标准的require()。

这就是我们安装jiti的原因。它充当桥梁,使我们能够在CommonJS文件中同步加载ESM模块而不会破坏构建过程。

在项目根目录创建或更新app.config.js文件,并按以下方式配置:

code
const createJiti = require("jiti");
const jiti = createJiti(__filename);

// 使用jiti同步加载ESM模块
const qvacModule = jiti("@qvac/sdk/expo-plugin");
const withQvacSDK = qvacModule.withQvacSDK || qvacModule.default;

// (如需使用,包含你的withEscapeBundleShellScript辅助函数)

module.exports = ({ config }) => {
  config.plugins = [
    [
      "expo-build-properties",
      {
        android: { minSdkVersion: 29 },
      },
    ],
    withQvacSDK,
    "expo-router",
    [
      "expo-splash-screen",
      {
        backgroundColor: "#208AEF",
      },
    ],
    withEscapeBundleShellScript, // 如适用,自定义辅助函数
  ];

  return config;
};

此配置应用了QVAC原生设置脚本,并确保Android需要至少SDK版本29(这是原生库必需的)。

我们的基础配置已准备就绪,现在直接进入翻译代码实现。

完整实现

让我们将所有内容整合起来。我们将实现一个界面,该界面接收英文文本,管理Bergamot引擎的下载和加载状态,将文本翻译成法语,并将输出渲染到屏幕上。

将入口文件src/app/index.tsx替换为以下实现:

code
import { View, ScrollView, TextInput, Text, TouchableOpacity, StyleSheet } from "react-native";
import { useState, useEffect } from "react";
import {
  loadModel,
  translate,
  unloadModel,
  BERGAMOT_EN_FR,
  getModelInfo,
} from "@qvac/sdk";
import { Stack } from "expo-router";

type 翻译状态 = | "空闲" | "正在检查模型..." | "正在下载模型..." | "模型下载成功" | "正在加载模型..." | "正在翻译..." | "正在流式传输翻译..." | "翻译完成" | 错误:${string};

export default function HomeScreen() { const [状态, 设置状态] = useState<翻译状态>("正在检查模型..."); const [已翻译文本, 设置已翻译文本] = useState<string>(""); const [输入文本, 设置输入文本] = useState<string>(""); const [正在翻译, 设置正在翻译] = useState<boolean>(false); const [已下载, 设置已下载] = useState<boolean | null>(null); const [下载进度字符串, 设置下载进度字符串] = useState<string>("");

useEffect(() => { const 检查模型状态 = async () => { try { const 模型 = await getModelInfo({ name: BERGAMOT_EN_FR.name }); 设置已下载(模型.isCached); console.log("模型", 模型); 设置状态("空闲"); } catch (错误) { console.error("检查模型时出错:", 错误); 设置状态("错误:无法检查模型状态"); } }; 检查模型状态(); }, []);

const 处理下载 = async () => { try { 设置正在翻译(true); 设置状态("正在下载模型..."); 设置下载进度字符串("");

const 模型ID = await loadModel({ modelSrc: BERGAMOT_EN_FR, modelType: "nmt", onProgress: (进度: any) => { let 百分比 = 进度.percentage; let 已下载 = 进度.downloaded; let 总计 = 进度.total; if (进度.shardInfo) { 百分比 = 进度.shardInfo.overallPercentage; 已下载 = 进度.shardInfo.overallDownloaded; 总计 = 进度.shardInfo.overallTotal; } const 格式化字节 = (字节: number) => { if (字节 === 0) return "0 B"; const k = 1024; const sizes = ["B", "KB", "MB", "GB"]; const i = Math.floor(Math.log(字节) / Math.log(k)); return ( parseFloat((字节 / Math.pow(k, i)).toFixed(2)) + " " + sizes[i] ); }; 设置下载进度字符串( ${百分比.toFixed(2)}% (${格式化字节(已下载)} / ${格式化字节(总计)}), ); }, modelConfig: { engine: "Bergamot", from: "en", to: "fr", beamsize: 1, normalize: 1, temperature: 0.2, norepeatngramsize: 3, lengthpenalty: 1.2, }, });

await unloadModel({ modelId, clearStorage: false });

设置已下载(true); 设置状态("模型下载成功。"); } catch (错误: any) { console.error(错误); 设置状态(错误:${错误.message}); } finally { 设置正在翻译(false); 设置下载进度字符串(""); } };

const 处理翻译 = async () => { if (!输入文本.trim()) { 设置状态("错误:请输入要翻译的文本"); return; }

try { 设置正在翻译(true); 设置已翻译文本(""); 设置状态("正在加载模型...");

const 模型ID = await loadModel({ modelSrc: BERGAMOT_EN_FR, modelType: "nmt",

modelConfig: { engine: "Bergamot", from: "en", to: "fr", beamsize: 1, normalize: 1, temperature: 0.2, norepeatngramsize: 3, lengthpenalty: 1.2, }, });

tsx
setStatus(`翻译中...`);

const result = translate({
  modelId,
  text: inputText,
  modelType: "nmt",
  stream: false,
});

const text = await result.text;
setTranslatedText(text);

const stats = await result.stats;
if (stats) {
  console.log(`▸ 处理统计信息:`, stats);
}

setStatus("翻译完成");

await unloadModel({ modelId, clearStorage: false });
} catch (error: any) {
  console.error(error);
  setStatus(`错误: ${error.message}`);
} finally {
  setIsTranslating(false);
}
};

return (
<>
<Stack.Screen
  options={{
    headerTitle: "翻译器",
    headerStyle: { backgroundColor: "#000" },
    headerTintColor: "#fff",
  }}
/>
<ScrollView contentContainerStyle={styles.scrollContainer}>
  <View style={styles.card}>
    <View style={styles.header}>
      <Text style={styles.title}>
        英法翻译器
      </Text>
      <Text style={styles.subtitle}>
        输入需要翻译的文本:
      </Text>
    </View>

    <View style={styles.content}>
      <TextInput
        style={[styles.input, isTranslating && styles.disabledText]}
        multiline
        placeholder="在此处输入英文文本..."
        placeholderTextColor="#888"
        value={inputText}
        onChangeText={setInputText}
        editable={!isTranslating}
      />

      <Text style={styles.statusText}>
        状态: {status}
        {downloadProgressStr ? `\n${downloadProgressStr}` : ""}
      </Text>

      {isDownloaded === null ? (
        <TouchableOpacity disabled style={[styles.button, styles.buttonDisabled]}>
          <Text style={styles.buttonText}>
            正在加载...
          </Text>
        </TouchableOpacity>
      ) : isDownloaded ? (
        <TouchableOpacity
          onPress={handleTranslate}
          style={[
            styles.button,
            (isTranslating || !inputText.trim()) && styles.buttonDisabled,
          ]}
          disabled={isTranslating || !inputText.trim()}
        >
          <Text style={styles.buttonText}>
            {isTranslating ? "翻译中..." : "翻译"}
          </Text>
        </TouchableOpacity>
      ) : (
        <TouchableOpacity
          onPress={handleDownload}
          style={[styles.button, isTranslating && styles.buttonDisabled]}
          disabled={isTranslating}
        >
          <Text style={styles.buttonText}>
            {isTranslating ? "下载中..." : "下载模型"}
          </Text>
        </TouchableOpacity>
      )}

      <View style={styles.outputContainer}>
        <Text style={styles.outputText}>
          {translatedText || "翻译结果将显示在这里..."}
        </Text>
      </View>
    </View>
  </View>
</ScrollView>
</>
);
}
javascript
const styles = StyleSheet.create({
  scrollContainer: {
    flexGrow: 1,
    paddingHorizontal: 16,
    paddingTop: 16,
    paddingBottom: 24,
    backgroundColor: "#f9fafb",
  },
  card: {
    backgroundColor: "#ffffff",
    maxWidth: 450,
    width: "100%",
    alignSelf: "center",
    borderRadius: 12,
    padding: 16,
  },
  header: {
    marginBottom: 16,
  },

    textAlign: "center",
    fontSize: 24,
    fontWeight: "bold",
    color: "#111827",
  },
  subtitle: {
    textAlign: "center",
    marginTop: 4,
    fontSize: 16,
    color: "#6b7280",
  },
  content: {
    gap: 24,
  },
  input: {
    borderWidth: 1,
    borderColor: "#e5e7eb",
    backgroundColor: "#ffffff",
    color: "#111827",
    padding: 12,
    borderRadius: 8,
    minHeight: 100,
    textAlignVertical: "top",
  },
  disabledText: {
    opacity: 0.5,
  },
  statusText: {
    fontSize: 14,
    color: "#3b82f6",
    fontWeight: "bold",
    textAlign: "center",
    marginTop: 12,
    marginBottom: 12,
  },
  button: {
    width: "100%",
    height: 48,
    borderRadius: 12,
    backgroundColor: "#3b82f6",
    alignItems: "center",
    justifyContent: "center",
  },
  buttonDisabled: {
    opacity: 0.5,
  },
  buttonText: {
    fontWeight: "600",
    fontSize: 18,
    color: "#ffffff",
  },
  outputContainer: {
    marginTop: 16,
    padding: 16,
    backgroundColor: "#f3f4f6",
    borderRadius: 8,
    minHeight: 100,
  },
  outputText: {
    fontSize: 16,
    color: "#111827",
  },
});

以下是应用中的翻译示例。

输入(英文)

我告诉你的位置在河岸附近

输出(法文)

L'endroit où je vous ai dit était près de la rive de la rivière

代码库解析

让我们深入了解本地翻译实现如何管理原生模型生命周期并处理流式传输的标记。

#### 1. 管理原生生命周期

加载神经网络权重进行翻译在计算上是昂贵的。当 QVAC 运行时初始化模型时,必须从本地磁盘读取参数并将活动权重复制到设备 RAM 中。

为了高效处理,我们在尝试加载模型前会检查模型是否已缓存。这用于检查模型是否已下载。缓存的含义是:模型已下载到用户的磁盘:

javascript
const model = await getModelInfo({ name: BERGAMOT_EN_FR.name });
setIsDownloaded(model.isCached);

如果模型尚未本地缓存,loadModel 函数会自动从 Hugging Face 集群下载模型。一旦文件在本地可用,它会直接内存映射权重。

#### 2. 翻译文本

模型加载完成后,我们可以将文本传递给翻译引擎:

javascript
const result = translate({
  modelId,
  text: inputText,
  modelType: "nmt",
  stream: false,
});

const text = await result.text;
setTranslatedText(text);

这会等待完整翻译完成后再向用户显示最终结果。

#### 3. 卸载模型

翻译完成后,我们通过 unloadModel 显式销毁模型:

javascript
await unloadModel({ modelId, clearStorage: false });

通过卸载模型,我们确保设备的 RAM 被释放给其他进程。由于模型已下载并缓存到磁盘(且我们明确设置 clearStorage: false),当用户下次需要翻译时重新加载模型将几乎瞬间完成。

结论

将翻译从云端迁移至设备端硬件,为移动应用开发者提供了切实可行的方案。在本地运行模型推理可消除对远程互联网连接的依赖,避免重复的API使用成本,并确保用户输入的文本永远不会离开物理设备。

本地化翻译功能对旅行应用、安全通信工具或教育平台具有显著价值。随着边缘处理器配备专用硬件加速核心,开源模型通过量化研究变得更为高效,以设备端优先的架构为重视隐私、离线稳定性和可预测成本结构的开发者提供了极具吸引力的替代方案。

资源与进一步阅读

若想深入了解本地神经机器翻译、检查源代码或探索移动应用的高级配置,请查阅以下资源:

  • QVAC翻译文档:集成QVAC本地翻译功能的官方文档
  • QVAC Expo集成文档:了解如何在Expo中配置自定义本地模型
  • Bergamot项目:了解更多关于底层神经机器翻译引擎的信息
  • Attention Is All You Need:首次提出Transformer架构的2017年谷歌原始研究论文
  • 完整代码示例:完整代码示例的代码库

👋 我是Jibril Mugisho,一名专注于使用AI和现代后端技术构建智能、可扩展应用的全栈开发者。我的工作重点包括AI代理开发、大语言模型集成以及创建增强自动化和决策能力的自主系统。我喜欢使用LangChain构建AI驱动的工作流程,也热衷于尝试本地推理设置以探索高效且注重隐私的AI解决方案。

如果你读到了这里,请向作者表达支持以示你对他们的重视。说声谢谢

免费学习编程。freeCodeCamp的开源课程已帮助超过40,000人成为开发者。立即开始

ADVERTISEMENT